Move arrival count to loading footer and prevent startup flash

This commit is contained in:
2026-10-09 23:02:33 +09:00
parent 736da09846
commit f84fe1fec9
11 changed files with 40 additions and 14 deletions
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
import{a as e,c as t,d as n,f as r,h as i,i as a,l as o,m as s,n as c,o as l,p as u,r as ee,u as d}from"./index-BYcEjj7E.js";var f=`uniform sampler2D uSceneTexture,uSceneDepth; import{a as e,c as t,d as n,f as r,h as i,i as a,l as o,m as s,n as c,o as l,p as u,r as ee,u as d}from"./index-DoRt0pZc.js";var f=`uniform sampler2D uSceneTexture,uSceneDepth;
uniform sampler2D uProtectionDepthMap; uniform sampler2D uProtectionDepthMap;
uniform mat4 uContentViewProjection,uContentInverseViewProjection; uniform mat4 uContentViewProjection,uContentInverseViewProjection;
uniform mat4 uPetWorldToLocal; uniform mat4 uPetWorldToLocal;
@@ -1 +1 @@
import{a as e,d as t,h as n,l as r,n as i,o as a,s as o,u as s}from"./index-BYcEjj7E.js";var c=class{renderer;background;targetA=new n(1,1,{type:e});targetB=new n(1,1,{type:e});blitMaterial=new o({toneMapped:!1,depthTest:!1,depthWrite:!1});blit=new a(new s(2,2),this.blitMaterial);blitScene=new t;blitCamera=new r(-1,1,1,-1,0,1);disposed=!1;constructor(e,t){this.renderer=e,this.background=t,this.blitScene.add(this.blit)}resize(e,t){for(let n of[this.targetA,this.targetB])(n.width!==e||n.height!==t)&&n.setSize(e,t)}composite(e,t){this.renderer.setRenderTarget(null),this.renderer.setScissorTest(!1);let n=this.renderer.getClearColor(new i),r=this.renderer.getClearAlpha();this.renderer.setClearColor(this.background,1),this.renderer.clear(),this.renderer.setClearColor(n,r),this.renderer.setScissorTest(!0);try{for(let n=0;n<e.length;n++){let r=e[n];this.renderer.setViewport(r.x,t-r.y-r.height,r.width,r.height),this.renderer.setScissor(r.x,t-r.y-r.height,r.width,r.height),this.blitMaterial.map=n===0?this.targetA.texture:this.targetB.texture,this.renderer.render(this.blitScene,this.blitCamera)}}finally{this.renderer.setScissorTest(!1),this.renderer.setViewport(0,0,this.renderer.domElement.clientWidth,t)}}dispose(){this.disposed||(this.disposed=!0,this.renderer.getContext().isContextLost()||(this.renderer.setRenderTarget(null),this.renderer.setScissorTest(!1)),this.targetA.dispose(),this.targetB.dispose(),this.blit.geometry.dispose(),this.blitMaterial.dispose(),this.blitScene.clear())}};export{c as PhysicalGlassComparison}; import{a as e,d as t,h as n,l as r,n as i,o as a,s as o,u as s}from"./index-DoRt0pZc.js";var c=class{renderer;background;targetA=new n(1,1,{type:e});targetB=new n(1,1,{type:e});blitMaterial=new o({toneMapped:!1,depthTest:!1,depthWrite:!1});blit=new a(new s(2,2),this.blitMaterial);blitScene=new t;blitCamera=new r(-1,1,1,-1,0,1);disposed=!1;constructor(e,t){this.renderer=e,this.background=t,this.blitScene.add(this.blit)}resize(e,t){for(let n of[this.targetA,this.targetB])(n.width!==e||n.height!==t)&&n.setSize(e,t)}composite(e,t){this.renderer.setRenderTarget(null),this.renderer.setScissorTest(!1);let n=this.renderer.getClearColor(new i),r=this.renderer.getClearAlpha();this.renderer.setClearColor(this.background,1),this.renderer.clear(),this.renderer.setClearColor(n,r),this.renderer.setScissorTest(!0);try{for(let n=0;n<e.length;n++){let r=e[n];this.renderer.setViewport(r.x,t-r.y-r.height,r.width,r.height),this.renderer.setScissor(r.x,t-r.y-r.height,r.width,r.height),this.blitMaterial.map=n===0?this.targetA.texture:this.targetB.texture,this.renderer.render(this.blitScene,this.blitCamera)}}finally{this.renderer.setScissorTest(!1),this.renderer.setViewport(0,0,this.renderer.domElement.clientWidth,t)}}dispose(){this.disposed||(this.disposed=!0,this.renderer.getContext().isContextLost()||(this.renderer.setRenderTarget(null),this.renderer.setScissorTest(!1)),this.targetA.dispose(),this.targetB.dispose(),this.blit.geometry.dispose(),this.blitMaterial.dispose(),this.blitScene.clear())}};export{c as PhysicalGlassComparison};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+14 -4
View File
@@ -7,8 +7,18 @@
<meta name="description" content="우리집 멍멍이, 할머니 집 유리문 너머로"> <meta name="description" content="우리집 멍멍이, 할머니 집 유리문 너머로">
<title>Behind Glass · 할머니집 창가</title> <title>Behind Glass · 할머니집 창가</title>
<link rel="preload" href="/fonts/BMKkubulim.woff" as="font" type="font/woff" crossorigin> <link rel="preload" href="/fonts/BMKkubulim.woff" as="font" type="font/woff" crossorigin>
<script type="module" crossorigin src="/assets/index-BYcEjj7E.js"></script> <script>
<link rel="stylesheet" crossorigin href="/assets/index-CAEr_JzP.css"> if (document.fonts) {
document.documentElement.dataset.fontLoading = '';
const showPage = () => { delete document.documentElement.dataset.fontLoading; };
const fallback = setTimeout(showPage, 3000);
document.addEventListener('DOMContentLoaded', () => {
document.fonts.load('16px "BM Kkubulim"').then(showPage, showPage).finally(() => clearTimeout(fallback));
}, { once: true });
}
</script>
<script type="module" crossorigin src="/assets/index-DoRt0pZc.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-D7twsXVD.css">
</head> </head>
<body> <body>
<div id="app"> <div id="app">
@@ -46,7 +56,7 @@
<p id="gyro-status" class="sr-only" role="status" aria-live="polite">기울기는 꺼져 있어요. 드래그로 둘러볼 수 있어요.</p> <p id="gyro-status" class="sr-only" role="status" aria-live="polite">기울기는 꺼져 있어요. 드래그로 둘러볼 수 있어요.</p>
</div> </div>
</header> </header>
<section id="welcome" class="welcome" aria-labelledby="welcome-title"> <section id="welcome" class="welcome" data-step="name" aria-labelledby="welcome-title">
<div class="entry-door" aria-hidden="true"><span></span></div> <div class="entry-door" aria-hidden="true"><span></span></div>
<p class="entry-kicker">behind glass</p> <p class="entry-kicker">behind glass</p>
<h1 id="welcome-title">할머니 집에<br><span id="welcome-name">멍멍이가 왔어요</span></h1> <h1 id="welcome-title">할머니 집에<br><span id="welcome-name">멍멍이가 왔어요</span></h1>
@@ -76,7 +86,6 @@
<span id="notice-position" aria-hidden="true">1 / 2</span> <span id="notice-position" aria-hidden="true">1 / 2</span>
<span id="notice-status" class="sr-only" role="status" aria-live="polite" aria-atomic="true"></span> <span id="notice-status" class="sr-only" role="status" aria-live="polite" aria-atomic="true"></span>
</div> </div>
<p class="arrival-count" data-arrival-count hidden></p>
</section> </section>
<section id="flow-overlay" class="flow-overlay" hidden aria-labelledby="flow-title" aria-busy="false"> <section id="flow-overlay" class="flow-overlay" hidden aria-labelledby="flow-title" aria-busy="false">
<div id="journey" class="journey" hidden> <div id="journey" class="journey" hidden>
@@ -113,6 +122,7 @@
<h2 id="flow-title" role="status" aria-live="polite">멍멍이를 할머니 집으로 데려가는 중…</h2> <h2 id="flow-title" role="status" aria-live="polite">멍멍이를 할머니 집으로 데려가는 중…</h2>
<p id="flow-detail" aria-live="polite"></p> <p id="flow-detail" aria-live="polite"></p>
<div id="flow-actions" class="flow-actions"><button id="flow-retry" type="button" hidden>다시 시도하기</button><button id="flow-flat" type="button" hidden>평면 사진으로 보기</button><button class="upload" type="button" data-pick-photo>다른 사진 고르기</button><button id="flow-samples" type="button" hidden>다른 샘플 고르기</button></div> <div id="flow-actions" class="flow-actions"><button id="flow-retry" type="button" hidden>다시 시도하기</button><button id="flow-flat" type="button" hidden>평면 사진으로 보기</button><button class="upload" type="button" data-pick-photo>다른 사진 고르기</button><button id="flow-samples" type="button" hidden>다른 샘플 고르기</button></div>
<p class="arrival-count" data-arrival-count hidden></p>
</section> </section>
</main> </main>
<dialog id="home-dialog" aria-labelledby="home-title" aria-describedby="home-detail"> <dialog id="home-dialog" aria-labelledby="home-title" aria-describedby="home-detail">
+13 -2
View File
@@ -7,6 +7,17 @@
<meta name="description" content="우리집 멍멍이, 할머니 집 유리문 너머로"> <meta name="description" content="우리집 멍멍이, 할머니 집 유리문 너머로">
<title>Behind Glass · 할머니집 창가</title> <title>Behind Glass · 할머니집 창가</title>
<link rel="preload" href="/fonts/BMKkubulim.woff" as="font" type="font/woff" crossorigin> <link rel="preload" href="/fonts/BMKkubulim.woff" as="font" type="font/woff" crossorigin>
<link rel="stylesheet" href="/src/style.css">
<script>
if (document.fonts) {
document.documentElement.dataset.fontLoading = '';
const showPage = () => { delete document.documentElement.dataset.fontLoading; };
const fallback = setTimeout(showPage, 3000);
document.addEventListener('DOMContentLoaded', () => {
document.fonts.load('16px "BM Kkubulim"').then(showPage, showPage).finally(() => clearTimeout(fallback));
}, { once: true });
}
</script>
</head> </head>
<body> <body>
<div id="app"> <div id="app">
@@ -44,7 +55,7 @@
<p id="gyro-status" class="sr-only" role="status" aria-live="polite">기울기는 꺼져 있어요. 드래그로 둘러볼 수 있어요.</p> <p id="gyro-status" class="sr-only" role="status" aria-live="polite">기울기는 꺼져 있어요. 드래그로 둘러볼 수 있어요.</p>
</div> </div>
</header> </header>
<section id="welcome" class="welcome" aria-labelledby="welcome-title"> <section id="welcome" class="welcome" data-step="name" aria-labelledby="welcome-title">
<div class="entry-door" aria-hidden="true"><span></span></div> <div class="entry-door" aria-hidden="true"><span></span></div>
<p class="entry-kicker">behind glass</p> <p class="entry-kicker">behind glass</p>
<h1 id="welcome-title">할머니 집에<br><span id="welcome-name">멍멍이가 왔어요</span></h1> <h1 id="welcome-title">할머니 집에<br><span id="welcome-name">멍멍이가 왔어요</span></h1>
@@ -74,7 +85,6 @@
<span id="notice-position" aria-hidden="true">1 / 2</span> <span id="notice-position" aria-hidden="true">1 / 2</span>
<span id="notice-status" class="sr-only" role="status" aria-live="polite" aria-atomic="true"></span> <span id="notice-status" class="sr-only" role="status" aria-live="polite" aria-atomic="true"></span>
</div> </div>
<p class="arrival-count" data-arrival-count hidden></p>
</section> </section>
<section id="flow-overlay" class="flow-overlay" hidden aria-labelledby="flow-title" aria-busy="false"> <section id="flow-overlay" class="flow-overlay" hidden aria-labelledby="flow-title" aria-busy="false">
<div id="journey" class="journey" hidden> <div id="journey" class="journey" hidden>
@@ -111,6 +121,7 @@
<h2 id="flow-title" role="status" aria-live="polite">멍멍이를 할머니 집으로 데려가는 중…</h2> <h2 id="flow-title" role="status" aria-live="polite">멍멍이를 할머니 집으로 데려가는 중…</h2>
<p id="flow-detail" aria-live="polite"></p> <p id="flow-detail" aria-live="polite"></p>
<div id="flow-actions" class="flow-actions"><button id="flow-retry" type="button" hidden>다시 시도하기</button><button id="flow-flat" type="button" hidden>평면 사진으로 보기</button><button class="upload" type="button" data-pick-photo>다른 사진 고르기</button><button id="flow-samples" type="button" hidden>다른 샘플 고르기</button></div> <div id="flow-actions" class="flow-actions"><button id="flow-retry" type="button" hidden>다시 시도하기</button><button id="flow-flat" type="button" hidden>평면 사진으로 보기</button><button class="upload" type="button" data-pick-photo>다른 사진 고르기</button><button id="flow-samples" type="button" hidden>다른 샘플 고르기</button></div>
<p class="arrival-count" data-arrival-count hidden></p>
</section> </section>
</main> </main>
<dialog id="home-dialog" aria-labelledby="home-title" aria-describedby="home-detail"> <dialog id="home-dialog" aria-labelledby="home-title" aria-describedby="home-detail">
+5 -2
View File
@@ -45,11 +45,14 @@ test('photo snapshot comes from the name store and survives retry without reread
assert.match(fn('showMeshFailure'), /cancelArrivalJourney\(\)/); assert.match(fn('showMeshFailure'), /cancelArrivalJourney\(\)/);
assert.doesNotMatch(fn('generateAIDepth'), /beginMediaLoad|petNameForms/); assert.doesNotMatch(fn('generateAIDepth'), /beginMediaLoad|petNameForms/);
}); });
test('two initially hidden counts sit below entry controls and the settings heading', () => { test('two initially hidden counts sit in the loading footer and below the settings heading', () => {
const nodes = [...html.matchAll(/<p\b[^>]*data-arrival-count[^>]*>/g)]; const nodes = [...html.matchAll(/<p\b[^>]*data-arrival-count[^>]*>/g)];
assert.equal(nodes.length, 2); assert.equal(nodes.length, 2);
for (const [node] of nodes) { assert.match(node, /\bhidden\b/); assert.doesNotMatch(node, /aria-live|role=/); } for (const [node] of nodes) { assert.match(node, /\bhidden\b/); assert.doesNotMatch(node, /aria-live|role=/); }
assert.match(html, /id="notice-status"[^>]*><\/span>\s*<\/div>\s*<p[^>]*data-arrival-count/); const welcome = html.match(/<section id="welcome"[\s\S]*?<\/section>/)[0];
const loading = html.match(/<section id="flow-overlay"[\s\S]*?<\/section>/)[0];
assert.doesNotMatch(welcome, /data-arrival-count/);
assert.match(loading, /<p[^>]*data-arrival-count[^>]*><\/p>\s*<\/section>/);
assert.match(html, /id="settings-title"[\s\S]*?<\/div>\s*<p[^>]*data-arrival-count/); assert.match(html, /id="settings-title"[\s\S]*?<\/div>\s*<p[^>]*data-arrival-count/);
assert.match(css, /\.arrival-count\s*\{[^}]*(?:font-size|line-height)/); assert.match(css, /\.arrival-count\s*\{[^}]*(?:font-size|line-height)/);
}); });
-1
View File
@@ -12,7 +12,6 @@ import { createArrivalTracker } from './analytics/ArrivalTracker';
import { renderArrivalCount } from './ui/ArrivalCount'; import { renderArrivalCount } from './ui/ArrivalCount';
import * as THREE from 'three'; import * as THREE from 'three';
import {readCameraComposition,saveCameraComposition} from './input/CameraComposition'; import {readCameraComposition,saveCameraComposition} from './input/CameraComposition';
import './style.css';
import { SceneHints, type HintKind } from './ui/SceneHints'; import { SceneHints, type HintKind } from './ui/SceneHints';
import './ui/peeking-experiment.css'; import './ui/peeking-experiment.css';
import { comparisonLayout, paneAt, type PaneRect } from './rendering/ComparisonLayout'; import { comparisonLayout, paneAt, type PaneRect } from './rendering/ComparisonLayout';
+3
View File
@@ -31,6 +31,7 @@ body {
font-size:16px; font-size:16px;
line-height:1.65; line-height:1.65;
} }
html[data-font-loading] #app { visibility:hidden; opacity:0; }
button,input,select { button,input,select {
font:inherit; font:inherit;
color:inherit; color:inherit;
@@ -1243,6 +1244,8 @@ body { overflow:hidden; }
/* Confirmed totals stay secondary to the photo experience. */ /* Confirmed totals stay secondary to the photo experience. */
.arrival-count { margin:8px 0 0; color:var(--muted); font-size:12px; line-height:1.6; } .arrival-count { margin:8px 0 0; color:var(--muted); font-size:12px; line-height:1.6; }
.flow-overlay { padding-bottom:max(64px,calc(env(safe-area-inset-bottom) + 44px)); }
.flow-overlay > .arrival-count { position:absolute; left:24px; right:24px; bottom:max(18px,env(safe-area-inset-bottom)); margin:0; }
#settings-dialog .arrival-count { margin:0 0 18px; } #settings-dialog .arrival-count { margin:0 0 18px; }
/* Two short entry steps stay scrollable on small screens and at text zoom. */ /* Two short entry steps stay scrollable on small screens and at text zoom. */