Compare commits

...
1 Commits
Author SHA1 Message Date
koka dbb9e3428d Lay out Gazette article and conversation for beta.165
Build Sanctuary / build (push) Waiting to run
2026-09-24 16:34:38 +02:00
7 changed files with 77 additions and 19 deletions
+6
View File
@@ -1,5 +1,11 @@
# Sanctuary
## beta.165 — article Gazette et conversation
Grande photo et texte dans un même conteneur à gauche, auteur dessous, réponses
à droite ou sous l’article sur petit écran.
[Résultat et vérifications](docs/gazette-scene-beta165.md).
## beta.164 — calendrier et intendance
Date et heure réunies à gauche ; titre serveur aligné sur la carte et les panneaux.
+26
View File
@@ -0,0 +1,26 @@
# beta.165 — Gazette en article et conversation
Photo en tête, titre et description dessous dans le même conteneur sombre.
Auteur, visage, date et actions sous le conteneur. Réponses à droite avec
défilement indépendant et saisie fixe ; sur GUI étroit, réponses sous l’article.
Retour et Actualiser restent en haut, comme pour le tableau.
Interface cliente uniquement, sans changement de stockage ni de monde.
Test client Vulkan réussi en FR/EN aux échelles GUI 2, 3 et 4 :
position du panneau de réponses, repli sous l’article, défilement disponible,
brouillon conservé après redimensionnement et réponse envoyée puis relue.
Le scénario existant du tableau et celui du menu pause passent également.
Captures conservées dans `build/qa-beta165/` (image synthétique de test).
Inspection visuelle FR GUI 2 et GUI 4 effectuée.
Journal : `build/beta165-client.log`.
`check build assemblePack --continue` exécuté : mêmes 23 GameTests en échec
qu’en beta.164, aucun nouvel échec. Comparaison enregistrée dans
`build/session165-server-failures.json`. La vérification générale reste en échec.
Assemblage séparé réussi avec `build assemblePack :sanctuary-test:exportDuoLaunch
-x :sanctuary:check` après cette vérification.
JAR local : `mods/sanctuary/build/libs/sanctuary-beta.165.jar`.
SHA-256 : `03a954caf68089862b1d237dcf1ebaa501e3ce3cfedb1f5184e9412e73fd711d`.
Anciens JAR conservés ; aucune publication distante.
+2 -2
View File
@@ -9,8 +9,8 @@ loom_version=1.17.20
fabric_api_version=0.160.5+26.3
# Release counter: beta.001, .002, .003, ... (see docs/versioning.md).
mod_version=beta.164
pack_version=beta.164
mod_version=beta.165
pack_version=beta.165
resource_pack_version=beta.121
maven_group=fr.koka.sanctuary
jei_version=30.32.0-sanctuary.3
@@ -95,6 +95,7 @@ public final class Community158ClientChecks implements FabricClientGameTest {
c.runOnClient(m->{check(button(m.gui.screen(),"publish").active,"Photo enables publishing");check(edit(m.gui.screen()).getValue().equals("Une journée au village"),"Selection retains draft");press(m.gui.screen(),"sanctuary.community.publish");});
c.waitFor(m->has(m.gui.screen(),"sanctuary.community.reply")&&!CommunityClient.pending(),600);
c.runOnClient(m->{var article=widgets(m.gui.screen()).filter(CommunityArticle.class::isInstance).map(CommunityArticle.class::cast).findFirst().orElseThrow();var face=widgets(m.gui.screen()).filter(PlayerFaceWidget.class::isInstance).map(PlayerFaceWidget.class::cast).findFirst().orElseThrow();check(face.getY()>=article.getBottom(),"Author metadata below the article");check(button(m.gui.screen(),"actions").getY()==face.getY(),"Author metadata next to ellipsis");check(widgets(m.gui.screen()).anyMatch(w->w instanceof AbstractWidget widget && widget.getMessage().getString().contains("/"+java.time.Year.now()+" ")),"Article date includes year");});
articleSceneChecks(c);
c.takeScreenshot("cards158-article"+(database?"-database":"-file"));
world.getServer().runOnServer(s->{try {
var config=CommunityConfig.load(net.fabricmc.loader.api.FabricLoader.getInstance().getConfigDir().resolve("sanctuary-community.json"));
@@ -224,6 +225,29 @@ public final class Community158ClientChecks implements FabricClientGameTest {
System.out.println("SEARCH159_CLIENT_PASS mode="+(database?"database":"file"));
} finally {intro.set(false);}
}
private static void articleSceneChecks(ClientGameTestContext c) {
for(String locale:List.of("fr_fr","en_us"))for(int scale:List.of(2,3,4)) {
c.runOnClient(m->{m.getLanguageManager().setSelected(locale);m.getLanguageManager().onResourceManagerReload(m.getResourceManager());m.options.guiScale().set(scale);m.resizeGui();});
c.waitTicks(3);
c.runOnClient(m->{
var s=m.gui.screen();var article=widgets(s).filter(CommunityArticle.class::isInstance).map(CommunityArticle.class::cast).findFirst().orElseThrow();
check(button(s,"back").getBottom()<=article.getY(),"Gazette toolbar fixed above article");
if(s.width>=522) {
check(body(s).getX()>=article.getRight(),"Gazette conversation on the right");
check(button(s,"reply").getBottom()<=s.height-33,"Gazette composer above footer");
} else {
check(body(s).getY()>article.getBottom(),"Narrow Gazette stacks conversation below article");
check(widgets(s).filter(AbstractScrollArea.class::isInstance).map(AbstractScrollArea.class::cast).anyMatch(a->a.maxScrollAmount()>0),"Narrow article and conversation scroll");
}
body(s).setValue("Brouillon Gazette165");
});
c.takeScreenshot("gazette165-article-"+locale+"-"+scale);
}
c.runOnClient(m->{m.options.guiScale().set(2);m.resizeGui();check(body(m.gui.screen()).getValue().equals("Brouillon Gazette165"),"Gazette draft survives resize");body(m.gui.screen()).setValue("Réponse Gazette165");press(m.gui.screen(),"sanctuary.community.reply");});
c.waitFor(m->!CommunityClient.pending()&&widgets(m.gui.screen()).filter(CommunityBubble.class::isInstance).map(CommunityBubble.class::cast).anyMatch(w->w.getMessage().getString().equals("Réponse Gazette165")),300);
c.takeScreenshot("gazette165-conversation");
System.out.println("GAZETTE165_SCENE_PASS");
}
private static void noticeSceneChecks(ClientGameTestContext c) {
for(String locale:List.of("fr_fr","en_us"))for(int scale:List.of(2,3,4)) {
c.runOnClient(m->{m.getLanguageManager().setSelected(locale);m.getLanguageManager().onResourceManagerReload(m.getResourceManager());m.options.guiScale().set(scale);m.resizeGui();});
@@ -8,30 +8,27 @@ import net.minecraft.client.gui.components.*;
import net.minecraft.client.gui.narration.NarrationElementOutput;
import net.minecraft.network.chat.Component;
import net.minecraft.util.FormattedCharSequence;
import net.minecraft.world.item.component.ResolvableProfile;
import static fr.koka.sanctuary.client.CommunityClient.*;
/** Photo left, generous headline and smaller text right, followed by the separate discussion. */
/** Full-width hero photo, headline and description share one dark article container. */
final class CommunityArticle extends AbstractWidget implements AutoCloseable {
private final JsonObject entry;
private final int imageWidth,textX;
private final int imageWidth,imageHeight;
private final float titleScale=1.35f;
private final List<FormattedCharSequence> title,body;
private CommunityPhotoWidget photo;
CommunityArticle(int width,JsonObject entry) {
super(0,0,width,100,Component.literal(str(entry,"title")));this.entry=entry;imageWidth=Math.max(72,width*42/100-16);textX=imageWidth+24;
super(0,0,width,100,Component.literal(str(entry,"title")));imageWidth=width-20;imageHeight=Math.max(40,imageWidth*9/16);
var font=Minecraft.getInstance().font;
title=font.split(getMessage().copy().withStyle(net.minecraft.ChatFormatting.BOLD),Math.max(30,(int)((width-textX-12)/titleScale)));
body=font.split(Component.literal(str(entry,"body")),Math.max(40,width-textX-12));
height=Math.max(imageWidth*9/16+24,24+title.size()*15+12+body.size()*11);
title=font.split(getMessage().copy().withStyle(net.minecraft.ChatFormatting.BOLD),Math.max(30,(int)((width-24)/titleScale)));
body=font.split(Component.literal(str(entry,"body")),Math.max(40,width-24));
height=12+imageHeight+14+title.size()*15+10+body.size()*11+12;
try {
if(!str(entry,"photo").isEmpty())photo=new CommunityPhotoWidget(imageWidth,Math.max(40,imageWidth*9/16),fr.koka.sanctuary.community.CommunityPhoto.decode(Base64.getDecoder().decode(str(entry,"photo")),960,540),text("photo"),false,null);
if(!str(entry,"photo").isEmpty())photo=new CommunityPhotoWidget(imageWidth,imageHeight,fr.koka.sanctuary.community.CommunityPhoto.decode(Base64.getDecoder().decode(str(entry,"photo")),960,540),text("photo"),false,null);
}catch(Exception ignored){}
active=false;
}
int textOffset(){return textX;}
@Override protected void extractWidgetRenderState(GuiGraphicsExtractor g,int mx,int my,float delta) {
var font=Minecraft.getInstance().font;int x=getX(),y=getY(),tx=x+textX,top=y+12;
var font=Minecraft.getInstance().font;int x=getX(),y=getY(),tx=x+12,top=y+12+imageHeight+14;
g.fill(x,y,getRight(),getBottom(),0xed171d23);
if(photo!=null){photo.setPosition(x+10,y+12);photo.extractRenderState(g,mx,my,delta);}
else g.text(font,text("photo_unavailable"),x+10,y+12,0xffaaaaaa,false);
@@ -55,7 +55,7 @@ public final class CommunityScreen extends BlocodexFrame {
@Override protected void init() {
searchBox=null;
closePhotos();initFrame("history",text(kind)); contentX=(width-contentWidth)/2; int w = contentWidth - 28;
if(!composing && kind.equals("notice") && data.has("thread")) {noticeDetail(data.getAsJsonObject("thread"),w);return;}
if(!composing && (kind.equals("notice") || kind.equals("article")) && data.has("thread")) {communityDetail(data.getAsJsonObject("thread"),w);return;}
var layout = LinearLayout.vertical().spacing(8);
if (!message.isEmpty()) line(layout,w,text(message));
if (!available()) line(layout,w,text("unavailable"));
@@ -120,7 +120,7 @@ public final class CommunityScreen extends BlocodexFrame {
DRAFTS.values().removeIf(value->value==replyDraft);message="saved";needsLoad=true;stale=true;
}));
}
private void noticeDetail(JsonObject thread,int w) {
private void communityDetail(JsonObject thread,int w) {
var entry=thread.getAsJsonObject("entry");
var toolbar=LinearLayout.horizontal().spacing(5);
button(toolbar,90,"back",()->{id="";page=0;data=new JsonObject();needsLoad=true;rebuildWidgets();});
@@ -132,10 +132,15 @@ public final class CommunityScreen extends BlocodexFrame {
var scene=LinearLayout.vertical().spacing(8);
if(!message.isEmpty())line(scene,leftWidth,text(message));
if(!available())line(scene,leftWidth,text("unavailable"));
scene.addChild(new CommunityNoticeScene(leftWidth,entry));
author(scene,leftWidth,entry,true);
if(kind.equals("article")) {
var article=new CommunityArticle(leftWidth,entry);cards.add(article);scene.addChild(article);
articleAuthor(scene,leftWidth,entry);
} else {
scene.addChild(new CommunityNoticeScene(leftWidth,entry));
author(scene,leftWidth,entry,true);
}
if(expandedId.equals(id))controls(scene,leftWidth,entry);
taskDetails(scene,leftWidth,entry);
if(kind.equals("notice"))taskDetails(scene,leftWidth,entry);
if(columns) {
scrollColumn(scene,leftWidth,contentX+8,top,availableHeight);
int rightWidth=w-leftWidth-20,rightX=contentX+8+leftWidth+20;
+1 -1
View File
@@ -1,6 +1,6 @@
name = "Sanctuary"
author = "KOKA99CAB"
version = "beta.164"
version = "beta.165"
pack-format = "packwiz:1.1.0"
[index]