Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions docs/ui/rq-room-name-title.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<!doctype html><meta charset=utf-8><meta name=viewport content='width=device-width,initial-scale=1'><title>Room name title</title><style>
:root{--bg:#f4efe4;--fg:#2b2620;--muted:#6d655a;--card:#fffaf0;--line:#d9cfbd}
@media (prefers-color-scheme:dark){:root:not([data-theme=light]){--bg:#1b1916;--fg:#ece5d8;--muted:#a59c8e;--card:#25221d;--line:#3a352d}}
body{margin:0;background:var(--bg);color:var(--fg);font:15px/1.5 Georgia,serif;padding:20px 16px}
h1{font-size:24px;margin:0 0 4px}h2{font-size:19px;margin:26px 0 4px}p.lede{color:var(--muted);margin:0 0 14px;max-width:820px}
.row{display:flex;flex-wrap:wrap;gap:14px;align-items:flex-start}
figure{margin:0;background:var(--card);border:1px solid var(--line);border-radius:8px;padding:10px;max-width:100%;box-sizing:border-box}
figcaption{font-size:13px;color:var(--muted);margin-top:8px;max-width:640px}figcaption b{color:var(--fg)}
.stage{position:relative;overflow:hidden;border-radius:4px;background:#000 center/cover no-repeat;background-image:url(../../web/public/images/generated/act1-eliors-cottage.png)}
.lap{width:640px;height:360px}.lap.small{width:300px;height:169px}
.phone{width:240px;height:440px;border-radius:18px;border:6px solid #111;background:#14171c;position:relative;overflow:hidden}
.phone .stage{position:absolute;left:0;right:0;top:0;height:200px;border-radius:0}
.phone .words{position:absolute;left:0;right:0;top:200px;bottom:44px;padding:10px 12px;color:#cfc7b8;font:12.5px/1.45 Georgia,serif}
.phone .bar{position:absolute;left:0;right:0;bottom:0;height:44px;background:#1c2027}
.phone .bar i{position:absolute;left:10px;right:10px;top:7px;height:30px;border-radius:7px;background:#f7a823;font:600 13px/30px system-ui;color:#2a1c05;text-align:center;font-style:normal}
.mark{position:absolute;width:10px;height:10px;margin:-5px 0 0 -5px;border-radius:50%;background:#ffd27a;box-shadow:0 0 8px 3px rgba(255,190,80,.85);z-index:3}
.name{position:absolute;white-space:nowrap;font:600 12px/1 system-ui,sans-serif;padding:4px 7px;border-radius:6px;background:rgba(20,18,14,.8);color:#e9e2d4;z-index:3;transform:translate(10px,-50%)}
.name.c{color:#8fb6ea}.name.i{color:#9fd59f}.name.x{color:#ffc65c}
.btns{position:absolute;right:8px;top:8px;display:flex;gap:6px;z-index:4}.btns span{width:30px;height:30px;border-radius:8px;background:rgba(27,30,38,.9);color:#ece5d8;font:14px/30px system-ui;text-align:center}
.actions{position:absolute;left:50%;bottom:10px;transform:translateX(-50%);height:32px;padding:0 18px;border-radius:8px;background:#f7a823;color:#2a1c05;font:600 13px/32px system-ui;z-index:4}
.plate{position:absolute;left:10px;top:10px;background:rgba(22,29,36,.93);color:#ece6d6;border:1px solid #34404b;border-radius:10px;padding:10px 14px;z-index:4;box-shadow:0 6px 24px rgba(0,0,0,.25)}
.plate small{display:block;color:#a59c8e;font:10px system-ui;letter-spacing:.12em;text-transform:uppercase}.plate b{display:block;font:500 21px Georgia,serif;margin-top:3px}
.chip{display:inline-block;font:12px system-ui;padding:4px 10px;border-radius:14px;margin:6px 4px 0 0;border:1px solid #355236;color:#8fcf7a;background:#18261b}.chip.c{border-color:#34506b;color:#7fb0e6;background:#172433}
.ssf{display:inline-block;font:12px system-ui;padding:4px 10px;border-radius:14px;border:1px solid #34404b;background:#202a33;color:#ffb836;margin-left:8px;vertical-align:middle}
.glow{color:#f6efe0;font-family:Georgia,serif;text-shadow:0 0 6px rgba(0,0,0,.9),0 0 18px rgba(0,0,0,.75),0 1px 2px #000}
.title{position:absolute;left:0;right:0;text-align:center;z-index:4}
.title small{display:block;font:11px system-ui;letter-spacing:.3em;text-transform:uppercase;opacity:.85}
.title b{display:block;font-weight:500}
.rule{display:block;width:120px;height:1px;margin:8px auto 0;background:linear-gradient(90deg,transparent,rgba(255,230,180,.8),transparent)}
.corner{position:absolute;left:14px;top:10px;z-index:4}
.corner b{font:500 20px Georgia,serif}
.t{font:12px system-ui;color:var(--muted);margin-bottom:4px}
.dim .mark,.dim .name{opacity:.25}
.arr{position:absolute;left:12%;right:12%;top:30%;text-align:center;z-index:4;font-size:15px;line-height:1.5}
.pill{display:inline-block;margin-top:10px;font:11px system-ui;padding:4px 10px;border-radius:12px;background:rgba(10,10,10,.6);color:#d8d0c0}
hr{margin:28px 0;border:0;border-top:1px solid var(--line)}
</style><h1>The room's name: fades in, then settles into the corner</h1><p class=lede>As C on entering: the name fades in over the middle of the picture and stays about three seconds. Then, instead of vanishing, it fades out of the middle and a small copy stays in the top-left corner, written on the picture as in B. So you get the moment of arrival and can still glance up and see where you are.</p><h2>Entering a room you have seen before</h2><div class=row><figure><div class=t>0 s — you go in</div><div class="stage lap small dim" style=""><div class="title glow" style="top:34%;opacity:0"><small>You are in</small><b style="font-size:34px">Elior's Cottage</b><span class=rule></span></div><div class="corner glow" style="opacity:0"><b style="font-size:17px">Elior's Cottage</b></div><div class=mark style="left:71.3%;top:41.4%"></div><div class=mark style="left:28.000000000000004%;top:52.0%"></div><div class=mark style="left:82.0%;top:52.300000000000004%"></div><div class=mark style="left:46.0%;top:32.5%"></div><div class=mark style="left:53.5%;top:30.0%"></div><div class=mark style="left:43.0%;top:66.0%"></div><div class=mark style="left:53.0%;top:60.0%"></div><div class=mark style="left:93.0%;top:80.0%"></div></div><figcaption>Picture changes; marks faint.</figcaption></figure><figure><div class=t>0.6 s</div><div class="stage lap small dim" style=""><div class="title glow" style="top:34%;opacity:1"><small>You are in</small><b style="font-size:34px">Elior's Cottage</b><span class=rule></span></div><div class="corner glow" style="opacity:0"><b style="font-size:17px">Elior's Cottage</b></div><div class=mark style="left:71.3%;top:41.4%"></div><div class=mark style="left:28.000000000000004%;top:52.0%"></div><div class=mark style="left:82.0%;top:52.300000000000004%"></div><div class=mark style="left:46.0%;top:32.5%"></div><div class=mark style="left:53.5%;top:30.0%"></div><div class=mark style="left:43.0%;top:66.0%"></div><div class=mark style="left:53.0%;top:60.0%"></div><div class=mark style="left:93.0%;top:80.0%"></div></div><figcaption>The name has faded in.</figcaption></figure><figure><div class=t>3.5 s</div><div class="stage lap small " style=""><div class="title glow" style="top:34%;opacity:0.3"><small>You are in</small><b style="font-size:34px">Elior's Cottage</b><span class=rule></span></div><div class="corner glow" style="opacity:0.6"><b style="font-size:17px">Elior's Cottage</b></div><div class=mark style="left:71.3%;top:41.4%"></div><div class=mark style="left:28.000000000000004%;top:52.0%"></div><div class=mark style="left:82.0%;top:52.300000000000004%"></div><div class=mark style="left:46.0%;top:32.5%"></div><div class=mark style="left:53.5%;top:30.0%"></div><div class=mark style="left:43.0%;top:66.0%"></div><div class=mark style="left:53.0%;top:60.0%"></div><div class=mark style="left:93.0%;top:80.0%"></div></div><figcaption>The middle fades out, the corner fades in.</figcaption></figure><figure><div class=t>5 s and on</div><div class="stage lap small " style=""><div class="title glow" style="top:34%;opacity:0"><small>You are in</small><b style="font-size:34px">Elior's Cottage</b><span class=rule></span></div><div class="corner glow" style="opacity:1"><b style="font-size:17px">Elior's Cottage</b></div><div class=mark style="left:71.3%;top:41.4%"></div><div class=mark style="left:28.000000000000004%;top:52.0%"></div><div class=mark style="left:82.0%;top:52.300000000000004%"></div><div class=mark style="left:46.0%;top:32.5%"></div><div class=mark style="left:53.5%;top:30.0%"></div><div class=mark style="left:43.0%;top:66.0%"></div><div class=mark style="left:53.0%;top:60.0%"></div><div class=mark style="left:93.0%;top:80.0%"></div></div><figcaption>Small name in the corner for as long as you stay.</figcaption></figure></div><h2>Laptop, after it has settled</h2><div class=row><figure><div class="stage lap " style=""><div class="title glow" style="top:34%;opacity:0"><small>You are in</small><b style="font-size:34px">Elior's Cottage</b><span class=rule></span></div><div class="corner glow" style="opacity:1"><b style="font-size:17px">Elior's Cottage</b></div><div class=mark style="left:71.3%;top:41.4%"></div><div class="name c" style="left:71.3%;top:41.4%">Grandmother</div><div class=mark style="left:28.000000000000004%;top:52.0%"></div><div class="name " style="left:28.000000000000004%;top:52.0%">Hearth</div><div class=mark style="left:82.0%;top:52.300000000000004%"></div><div class="name i" style="left:82.0%;top:52.300000000000004%">Lantern</div><div class=mark style="left:46.0%;top:32.5%"></div><div class="name " style="left:46.0%;top:32.5%">Shelves</div><div class=mark style="left:53.5%;top:30.0%"></div><div class="name " style="left:53.5%;top:30.0%">Books</div><div class=mark style="left:43.0%;top:66.0%"></div><div class="name " style="left:43.0%;top:66.0%">Table</div><div class=mark style="left:53.0%;top:60.0%"></div><div class="name i" style="left:53.0%;top:60.0%">Bread</div><div class=mark style="left:93.0%;top:80.0%"></div><div class="name x" style="left:80%;top:80.0%">→ Village Square</div><div class=btns><span>☰</span><span>⌖</span><span>◷</span></div><div class=actions>Actions ▴</div></div><figcaption><b>Settled.</b> The same corner name as B, a little smaller.</figcaption></figure></div><h2>Entering a room for the first time</h2><p class=lede>As C: the name heads the arrival words. When you click past the arrival, the small name appears in the corner.</p><h2>Phone</h2><div class=row><figure><div class=phone><div class=stage><div class="title glow" style="top:30%;opacity:1"><small>You are in</small><b style="font-size:22px">Elior's Cottage</b><span class=rule></span></div><div class=mark style="left:71.3%;top:41.4%"></div><div class=mark style="left:28.000000000000004%;top:52.0%"></div><div class=mark style="left:82.0%;top:52.300000000000004%"></div><div class=mark style="left:46.0%;top:32.5%"></div><div class=mark style="left:53.5%;top:30.0%"></div><div class=mark style="left:43.0%;top:66.0%"></div><div class=mark style="left:53.0%;top:60.0%"></div><div class=mark style="left:93.0%;top:80.0%"></div></div><div class=words>You step inside. Grandmother looks up from the hearth and smiles.</div><div class=bar><i>Actions ▴</i></div></div><figcaption><b>0.6 s.</b> The name over the picture.</figcaption></figure><figure><div class=phone><div class=stage><div class="corner glow" style="left:10px;top:8px"><b style="font-size:16px">Elior's Cottage</b></div><span class=ssf style="position:absolute;right:6px;top:6px;z-index:4">⟲ Story so far</span><div class=mark style="left:71.3%;top:41.4%"></div><div class=mark style="left:28.000000000000004%;top:52.0%"></div><div class=mark style="left:82.0%;top:52.300000000000004%"></div><div class=mark style="left:46.0%;top:32.5%"></div><div class=mark style="left:53.5%;top:30.0%"></div><div class=mark style="left:43.0%;top:66.0%"></div><div class=mark style="left:53.0%;top:60.0%"></div><div class=mark style="left:93.0%;top:80.0%"></div></div><div class=words>You step inside. Grandmother looks up from the hearth and smiles.</div><div class=bar><i>Actions ▴</i></div></div><figcaption><b>Settled.</b> Small name left, “⟲ Story so far” right, as in B.</figcaption></figure></div><hr><h2>Agreed details</h2><ul class=lede style="max-width:820px"><li>The middle title reads “YOU ARE IN” over the room's name. The settled corner shows <b>the name alone</b>.</li><li>The title plays on <b>every walk into a room</b>, first visit or not, and <b>when a saved game is loaded</b>. On a first visit the name heads the arrival words and settles when they are done.</li><li>The title <b>never stands in the way</b>. A press on something in the picture works as usual and settles the title at once, and so does any key or a press on empty picture.</li><li>Timing: fades in over about 0.6 s, stays about 3 s, then crosses into the corner over about 1.5 s. Marks wait at a quarter strength while the middle title is up. With “reduce motion” on, there are no fades.</li><li>No one and nothing here is listed by name any more; they are reached on the picture, which a player can also Tab through. The corner name is not a button. Marks step out from under it as they do today for the card.</li><li>Phone: the corner name on the left, “⟲ Story so far” on the right. A long name such as “Submerged Antechamber” gives way with an ellipsis there; the middle title wraps onto two lines if it must.</li><li>The name is read aloud only as part of the story, as today.</li></ul><hr><h2>Today, for comparison</h2><div class=row><figure><div class="stage lap " style=""><div class=plate><small>You are in</small><b>Elior's Cottage</b><span class="chip c">Grandmother</span><span class=chip>Lantern</span><span class=chip>Bread</span></div><div class=mark style="left:71.3%;top:41.4%"></div><div class="name c" style="left:71.3%;top:41.4%">Grandmother</div><div class=mark style="left:28.000000000000004%;top:52.0%"></div><div class="name " style="left:28.000000000000004%;top:52.0%">Hearth</div><div class=mark style="left:82.0%;top:52.300000000000004%"></div><div class="name i" style="left:82.0%;top:52.300000000000004%">Lantern</div><div class=mark style="left:46.0%;top:32.5%"></div><div class="name " style="left:46.0%;top:32.5%">Shelves</div><div class=mark style="left:53.5%;top:30.0%"></div><div class="name " style="left:53.5%;top:30.0%">Books</div><div class=mark style="left:43.0%;top:66.0%"></div><div class="name " style="left:43.0%;top:66.0%">Table</div><div class=mark style="left:53.0%;top:60.0%"></div><div class="name i" style="left:53.0%;top:60.0%">Bread</div><div class=mark style="left:93.0%;top:80.0%"></div><div class="name x" style="left:80%;top:80.0%">→ Village Square</div><div class=btns><span>☰</span><span>⌖</span><span>◷</span></div><div class=actions>Actions ▴</div></div><figcaption><b>Today.</b> The card lists Grandmother, the lantern and the bread as buttons, a second time over the marks on the picture.</figcaption></figure></div>
4 changes: 3 additions & 1 deletion web/e2e/dreamEnding.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ for (const command of ['talk to voice', 'say who are you to voice']) {

const middle = page.getByTestId('arrival-on-picture')
await expect(middle).toContainText('A modest cottage')
await expect(middle).not.toContainText('You are in')
await expect(middle.locator('.lq-arrival-text')).not.toContainText(
'You are in',
)
await expect(middle).not.toContainText('The whisper comes once more')
await expect(middle).not.toContainText('let them rest')

Expand Down
2 changes: 1 addition & 1 deletion web/e2e/lantern.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -562,8 +562,8 @@ test.describe('Lantern UI (the default)', () => {
})

const plate = page.getByRole('region', { name: 'Current location' })
await expect(plate).toContainText('You are in')
await expect(plate).toContainText('Village Square')
await expect(plate).not.toContainText('You are in')
for (const name of [/Quests · 0/, /Story so far/])
await expect(page.getByRole('button', { name })).toBeVisible()
// The Bag and Spells are the belt of pictures at the foot of the picture.
Expand Down
20 changes: 12 additions & 8 deletions web/e2e/phoneSideways.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ for (const size of [
await expect(page.locator('.lq-nav-bottom')).toHaveCount(0)
await expect(page.getByRole('button', { name: 'Actions ▴' })).toBeVisible()
await expect(page.getByRole('button', { name: 'Hint' })).toBeVisible()
// One compact row, however many chips: it never covers the words.
// One compact row: it never covers the words.
const plate = await page.locator('.lq-stage-plate').boundingBox()
expect(plate?.height ?? 999).toBeLessThan(60)
// And it ends before the first button on the right.
Expand Down Expand Up @@ -126,19 +126,23 @@ for (const size of [
})
}

test('a name too long for the plate gives way so the chips keep a share', async ({
test('a name too long for the corner stays one row and ends before the buttons', async ({
page,
}): Promise<void> => {
await page.setViewportSize({ width: 568, height: 320 })
await start(page)
await lightDots(page, 'Submerged Antechamber')
const name = page.locator('.lq-stage-plate .lq-room-name')
await expect(name).toHaveText('Submerged Antechamber')
await expect
.poll(() =>
page
.locator('.lq-stage-plate .lq-here')
.evaluate((el) => el.getBoundingClientRect().width),
)
.toBeGreaterThanOrEqual(48)
.poll(async (): Promise<number> => {
const plate = await page.locator('.lq-stage-plate').boundingBox()
const buttons = await page.locator('.lq-stage-buttons').boundingBox()
return (buttons?.x ?? 0) - ((plate?.x ?? 0) + (plate?.width ?? 999))
})
.toBeGreaterThanOrEqual(0)
const row = await name.boundingBox()
expect(row?.height ?? 999).toBeLessThan(30)
})

test('a tablet held upright keeps the tall stage, with no turn page', async ({
Expand Down
21 changes: 15 additions & 6 deletions web/e2e/questNews.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,11 @@ test.describe('quest news told in the middle of the picture', () => {
})
}

// Heights at which a page holds the short need alone, under the one-row
// room plate (about 94 px lower than under the old room card).
for (const [width, height] of [
[850, 840],
[390, 720],
[850, 740],
[390, 620],
]) {
test(`keeps one left edge for a checklist split over pages at ${width} wide`, async ({
page,
Expand Down Expand Up @@ -189,10 +191,17 @@ test.describe('quest news told in the middle of the picture', () => {
if ((await list.first().locator('li').count()) === 1) alone = first
const bounds = await list.first().boundingBox({ timeout: 1500 })
lefts[first] = Math.round(bounds?.x ?? -1)
// The last click ends the news and takes the card away.
await middle(page)
.click({ position: { x: 20, y: 20 }, timeout: 1500 })
.catch(() => undefined)
// Enter, with no control focused, goes on a page; the last ends the
// news and takes the card away.
await page.evaluate(() =>
(document.activeElement as HTMLElement | null)?.blur(),
)
await page.keyboard.press('Enter')
await expect
.poll(async () =>
(await list.count()) === 0 ? '' : list.first().innerText(),
)
.not.toBe(first)
}
expect(Object.keys(lefts).length).toBeGreaterThan(1)
// A page holds the short need alone: the case that shrank the line.
Expand Down
Loading
Loading