From 11e59292f63b117ac84733150b923d220f012794 Mon Sep 17 00:00:00 2001 From: Hanrim <148833226+MerciHanrim@users.noreply.github.com> Date: Mon, 5 Oct 2026 11:17:03 +0900 Subject: [PATCH] fix(toolbar): the temporary-session chip is drawn as the menu buttons beside it (v0.17.2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Temporary session chip in the toolbar was a 20 px pill with 10 px text next to 28 px menu buttons, and its grey ink on the overlay colour read as a status badge. It is a control that opens a menu, so it is now drawn as the menu buttons are: 28 px tall, the 8 px control radius, 12 px text and 12 px of side padding, centred on the same line, the primary ink on the raised face, the menu buttons' hover boundary and their focus. It keeps its orange (warning) border at rest, its label, its accessible name and its menu; no icon and no new colour are added, and nothing in how sessions switch or save changes (CSS only; no component or store code is touched). - src/index.css: `.session-chip__btn` takes `--text-primary` and `--surface-raised`, and joins `.btn` in the focus block (the boundary takes `--line-focus` with the `--focus-halo`, and the forced-colours outline); its hover rule already used `--line-control-hover`. The revision chip keeps its own pill. - Contrast, measured in light and dark: text on face 15.59:1 and 10.88:1, the same as the menu buttons; the orange boundary 4.74:1 on the face and the toolbar in light, 4.89:1 on the face and 5.78:1 on the toolbar in dark. Hovered, opened or focused, the boundary is the menu buttons' hover or focus colour, as it is for them. - Measured in a real browser in all 18 languages: at 1280 and 721 px (the narrowest desktop width) the chip is 28 px tall at the same top as the Templates button, radius 8 px, 12 px text, one line, nothing clipped, no horizontal scroll; widths 74 px (zh-Hans) to 146 px (it). The phone layout (390 and 320 px) has no toolbar chip; its reminder in the More sheet measures exactly as before in every language. - Because the chip is wider, the toolbar's overflow sends one or two more groups to More at some widths between 800 and 1024 px (for example en at 800 px: 3 inline groups -> 2); the toolbar keeps its height. - e2e/storage-sessions.spec.ts pins it: the chip's height, radius, text size, ink and face equal the Templates button's, the same centre line, one line of text, no icon, the warning colour as the border (not the button's), the accessible name "Temporary session", and the keyboard path (Enter opens, ArrowDown enters, Escape closes and returns a visible focus with the focus boundary and halo). - Declared user-facing: version 0.17.2 and release note release:0.17.2 with three lines in 18 languages (16 without native review). The per-language copy tests move their pinned counts by the three keys, as each release does; pt-PT words two of the lines in its own way, ru spells скруглёнными with ё, vi keeps `menu` as in earlier lines. --- .changes/session-chip-size.json | 1 + e2e/storage-sessions.spec.ts | 74 ++++++++++++++++++++++++++++++++ e2e/whats-new.spec.ts | 2 +- package-lock.json | 4 +- package.json | 2 +- src/i18n/itCopy.test.ts | 6 +-- src/i18n/locales/ar/ui.ts | 3 ++ src/i18n/locales/de/ui.ts | 3 ++ src/i18n/locales/en/ui.ts | 3 ++ src/i18n/locales/es-419/ui.ts | 3 ++ src/i18n/locales/es-ES/ui.ts | 3 ++ src/i18n/locales/fr/ui.ts | 3 ++ src/i18n/locales/it/ui.ts | 3 ++ src/i18n/locales/ja/ui.ts | 3 ++ src/i18n/locales/ko/ui.ts | 3 ++ src/i18n/locales/nl/ui.ts | 3 ++ src/i18n/locales/pt-BR/ui.ts | 3 ++ src/i18n/locales/pt-PT/ui.ts | 3 ++ src/i18n/locales/ru/ui.ts | 3 ++ src/i18n/locales/th/ui.ts | 3 ++ src/i18n/locales/tr/ui.ts | 3 ++ src/i18n/locales/vi/ui.ts | 3 ++ src/i18n/locales/zh-Hans/ui.ts | 3 ++ src/i18n/locales/zh-Hant/ui.ts | 3 ++ src/i18n/nlCopy.test.ts | 6 +-- src/i18n/ptPtCopy.test.ts | 10 +++-- src/i18n/ruCopy.test.ts | 3 +- src/i18n/thCopy.test.ts | 4 +- src/i18n/trCopy.test.ts | 2 +- src/i18n/viCopy.test.ts | 9 ++-- src/index.css | 19 ++++++-- src/releaseNotes/releaseNotes.ts | 8 ++++ 32 files changed, 181 insertions(+), 23 deletions(-) create mode 100644 .changes/session-chip-size.json diff --git a/.changes/session-chip-size.json b/.changes/session-chip-size.json new file mode 100644 index 00000000..0efde5ab --- /dev/null +++ b/.changes/session-chip-size.json @@ -0,0 +1 @@ +{ "type": "user-facing", "releaseNoteId": "release:0.17.2" } diff --git a/e2e/storage-sessions.spec.ts b/e2e/storage-sessions.spec.ts index 14f87a6e..c11b1837 100644 --- a/e2e/storage-sessions.spec.ts +++ b/e2e/storage-sessions.spec.ts @@ -155,6 +155,80 @@ test.describe('switching sessions', () => { expect(await storedWorkNodes(page)).toBe(2) }) + // the chip is a control in the toolbar row: drawn as the menu button beside + // it (28 px, the 8 px control radius, 12 px text, its ink on its raised + // face), centred on the same line, one line of text, no icon; the warning + // border still marks the session, and its name, keyboard path and focus are + // those of a menu button + test('the chip is drawn as the menu buttons beside it, keeps its warning border and name, and works from the keyboard', async ({ page }) => { + await openApp(page) + await openStorageArea(page) + await confirmAction(page, 'to-temporary') + await expect(chip(page)).toBeVisible() + const m = await page.evaluate(() => { + const el = document.querySelector('.toolbar [data-session-chip="temporary"]')! + const btn = document.querySelector('.toolbar__actions-core > .menu > .btn')! + const c = el.getBoundingClientRect() + const b = btn.getBoundingClientRect() + const cs = getComputedStyle(el) + const probe = document.createElement('span') + probe.style.color = 'var(--warning)' + document.body.append(probe) + const warning = getComputedStyle(probe).color + probe.remove() + const range = document.createRange() + range.selectNodeContents(el) + return { + h: c.height, + btnH: b.height, + offCentre: Math.abs(c.top + c.height / 2 - (b.top + b.height / 2)), + radius: cs.borderTopLeftRadius, + btnRadius: getComputedStyle(btn).borderTopLeftRadius, + fontSize: cs.fontSize, + btnFontSize: getComputedStyle(btn).fontSize, + border: cs.borderTopColor, + warning, + color: cs.color, + btnColor: getComputedStyle(btn).color, + background: cs.backgroundColor, + btnBackground: getComputedStyle(btn).backgroundColor, + btnBorder: getComputedStyle(btn).borderTopColor, + lines: new Set([...range.getClientRects()].map((r) => Math.round(r.top))).size, + icons: el.querySelectorAll('svg, img, .icon').length, + clipped: el.scrollWidth > el.clientWidth, + } + }) + expect(m).toMatchObject({ h: 28, btnH: 28, radius: '8px', btnRadius: '8px', fontSize: '12px', btnFontSize: '12px', lines: 1, icons: 0, clipped: false }) + expect(m.offCentre).toBeLessThan(0.5) + expect(m.color).toBe(m.btnColor) + expect(m.background).toBe(m.btnBackground) + expect(m.border).toBe(m.warning) + expect(m.border).not.toBe(m.btnBorder) // the session still shows + await expect(chip(page)).toHaveAccessibleName('Temporary session') + // keyboard: Enter opens, ArrowDown enters the menu, Escape closes it and focus comes back + await chip(page).focus() + await page.keyboard.press('Enter') + await expect(chip(page)).toHaveAttribute('aria-expanded', 'true') + await page.keyboard.press('ArrowDown') + await expect(page.locator('.session-chip__pop').getByRole('menuitem').first()).toBeFocused() + await page.keyboard.press('Escape') + await expect(page.locator('.session-chip__pop')).toHaveCount(0) + await expect(chip(page)).toBeFocused() + // the menu buttons' focus: the boundary takes the focus colour, with the halo + const focus = await chip(page).evaluate((el) => { + const probe = document.createElement('span') + probe.style.color = 'var(--line-focus)' + document.body.append(probe) + const lineFocus = getComputedStyle(probe).color + probe.remove() + const cs = getComputedStyle(el) + return { visible: el.matches(':focus-visible'), border: cs.borderTopColor, lineFocus, halo: cs.boxShadow } + }) + expect(focus.visible).toBe(true) + expect(focus.border).toBe(focus.lineFocus) + expect(focus.halo).toMatch(/0px 0px 0px 3px/) + }) + test('a temporary session with work warns before the page is closed; a personal browser does not', async ({ page, context }) => { // personal: no dialog, the page closes const p2 = await context.newPage() diff --git a/e2e/whats-new.spec.ts b/e2e/whats-new.spec.ts index bd2cb50d..9dbd941f 100644 --- a/e2e/whats-new.spec.ts +++ b/e2e/whats-new.spec.ts @@ -239,7 +239,7 @@ test.describe('closing the notice and opening the panel are different things', ( expect(text).not.toContain('text unavailable') } } - expect(RELEASE_NOTES.map((n) => n.version)).toEqual(['0.17.1', '0.17.0', '0.16.0', '0.15.3', '0.15.2', '0.15.1', '0.15.0', '0.14.0']) + expect(RELEASE_NOTES.map((n) => n.version)).toEqual(['0.17.2', '0.17.1', '0.17.0', '0.16.0', '0.15.3', '0.15.2', '0.15.1', '0.15.0', '0.14.0']) await page.keyboard.press('Escape') await expect(panel).toHaveCount(0) diff --git a/package-lock.json b/package-lock.json index 6d1b83ec..cd8e4ded 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "loop-studio", - "version": "0.17.1", + "version": "0.17.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "loop-studio", - "version": "0.17.1", + "version": "0.17.2", "dependencies": { "@fontsource/ibm-plex-mono": "^5.3.0", "@fontsource/ibm-plex-sans": "^5.3.0", diff --git a/package.json b/package.json index 656a25a3..e946b1e8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "loop-studio", "private": true, - "version": "0.17.1", + "version": "0.17.2", "type": "module", "engines": { "node": ">=22.12.0" diff --git a/src/i18n/itCopy.test.ts b/src/i18n/itCopy.test.ts index 15ed4b84..f322c8c8 100644 --- a/src/i18n/itCopy.test.ts +++ b/src/i18n/itCopy.test.ts @@ -199,15 +199,15 @@ const IDENTICAL_TO_EN: ReadonlyArray = [ // ------------------------------------------------------------------ shape describe('it copy — the surfaces exist and are complete', () => { it('has exactly the base key set', () => { - expect(KEYS).toHaveLength(981) // issue #301 decision 1: +4 (share.unavailable and the three 0.17.1 release-note lines) + expect(KEYS).toHaveLength(984) // issue #301 decision 1: +4 (share.unavailable and the three 0.17.1 release-note lines); the session chip: +3 (the three 0.17.2 release-note lines) expect(Object.keys(IT).sort()).toEqual([...KEYS].sort()) }) it('covers all four runtime surfaces, at the measured sizes', () => { const per: Record = {} for (const r of RUNTIME) per[r.surface.split('/')[0]!] = (per[r.surface.split('/')[0]!] ?? 0) + 1 - expect(per).toEqual({ catalog: 981, template: 196, frame: 7, module: 19 }) - expect(RUNTIME).toHaveLength(1203) + expect(per).toEqual({ catalog: 984, template: 196, frame: 7, module: 19 }) + expect(RUNTIME).toHaveLength(1206) }) it('EVERY row has a non-empty ENGLISH side — the vacuity guard', () => { diff --git a/src/i18n/locales/ar/ui.ts b/src/i18n/locales/ar/ui.ts index 711c6ee7..b40db819 100644 --- a/src/i18n/locales/ar/ui.ts +++ b/src/i18n/locales/ar/ui.ts @@ -559,6 +559,9 @@ const ui = { 'whatsNew.v0171.compression': 'تستخدم روابط المشاركة الآن الضغط المدمج في المتصفح.', 'whatsNew.v0171.unavailable': 'إذا لم تكن هذه الميزة متاحة، يُبقي Loop Studio المخطط الحالي كما هو ويوضح أن المطلوب متصفح حديث.', 'whatsNew.v0171.compatible': 'تظل روابط المشاركة الحالية متوافقة، ولم يتغير تنسيق الرابط.', + 'whatsNew.v0172.size': 'أصبح زر الجلسة المؤقتة أعلى النافذة بنفس ارتفاع أزرار القوائم المجاورة له وبزوايا مستديرة مثلها.', + 'whatsNew.v0172.text': 'صار نص الزر أكبر وأسهل في القراءة.', + 'whatsNew.v0172.same': 'يحتفظ الزر بإطاره البرتقالي ويفتح القائمة نفسها، ولم تتغير طريقة عمل الجلسة المؤقتة.', } as const export default ui diff --git a/src/i18n/locales/de/ui.ts b/src/i18n/locales/de/ui.ts index 740862ca..57029cde 100644 --- a/src/i18n/locales/de/ui.ts +++ b/src/i18n/locales/de/ui.ts @@ -594,6 +594,9 @@ const ui = { 'whatsNew.v0171.compression': 'Links zum Teilen nutzen jetzt die eingebaute Komprimierung des Browsers.', 'whatsNew.v0171.unavailable': 'Ist diese Funktion nicht verfügbar, lässt Loop Studio das aktuelle Diagramm unverändert und weist darauf hin, dass ein aktueller Browser nötig ist.', 'whatsNew.v0171.compatible': 'Bestehende Links zum Teilen bleiben kompatibel; das Linkformat hat sich nicht geändert.', + 'whatsNew.v0172.size': 'Die Schaltfläche für die temporäre Sitzung oben im Fenster hat jetzt dieselbe Höhe und dieselben abgerundeten Ecken wie die Menüschaltflächen daneben.', + 'whatsNew.v0172.text': 'Ihre Beschriftung ist größer und besser lesbar.', + 'whatsNew.v0172.same': 'Sie behält ihren orangefarbenen Rahmen und öffnet dasselbe Menü; an der Funktionsweise einer temporären Sitzung hat sich nichts geändert.', } satisfies Record export default ui diff --git a/src/i18n/locales/en/ui.ts b/src/i18n/locales/en/ui.ts index 87b546e7..87de1300 100644 --- a/src/i18n/locales/en/ui.ts +++ b/src/i18n/locales/en/ui.ts @@ -503,6 +503,9 @@ const ui = { 'whatsNew.v0171.compression': 'Share links now use the browser’s built-in compression.', 'whatsNew.v0171.unavailable': 'If that feature is unavailable, Loop Studio keeps your current diagram unchanged and explains that a current browser is required.', 'whatsNew.v0171.compatible': 'Existing share links remain compatible; the link format has not changed.', + 'whatsNew.v0172.size': 'The Temporary session button at the top of the window now has the same height and rounded corners as the menu buttons beside it.', + 'whatsNew.v0172.text': 'Its label is larger and easier to read.', + 'whatsNew.v0172.same': 'It keeps its orange border and opens the same menu; how a temporary session works has not changed.', } as const export type UiKey = keyof typeof ui diff --git a/src/i18n/locales/es-419/ui.ts b/src/i18n/locales/es-419/ui.ts index fcdcf619..0203532c 100644 --- a/src/i18n/locales/es-419/ui.ts +++ b/src/i18n/locales/es-419/ui.ts @@ -517,6 +517,9 @@ const ui = { 'whatsNew.v0171.compression': 'Los enlaces para compartir ahora usan la compresión integrada del navegador.', 'whatsNew.v0171.unavailable': 'Si esa función no está disponible, Loop Studio deja el diagrama actual sin cambios y explica que se necesita un navegador actual.', 'whatsNew.v0171.compatible': 'Los enlaces para compartir existentes siguen siendo compatibles; el formato del enlace no ha cambiado.', + 'whatsNew.v0172.size': 'El botón de sesión temporal, en la parte superior de la ventana, ahora tiene la misma altura y las mismas esquinas redondeadas que los botones de menú que lo acompañan.', + 'whatsNew.v0172.text': 'Su texto es más grande y más fácil de leer.', + 'whatsNew.v0172.same': 'Conserva su borde naranja y abre el mismo menú; el funcionamiento de una sesión temporal no ha cambiado.', } as const export type UiKey = keyof typeof ui diff --git a/src/i18n/locales/es-ES/ui.ts b/src/i18n/locales/es-ES/ui.ts index 3f7fecd9..3707bcec 100644 --- a/src/i18n/locales/es-ES/ui.ts +++ b/src/i18n/locales/es-ES/ui.ts @@ -531,6 +531,9 @@ const ui = { 'whatsNew.v0171.compression': 'Los enlaces para compartir ahora usan la compresión integrada del navegador.', 'whatsNew.v0171.unavailable': 'Si esa función no está disponible, Loop Studio deja el diagrama actual sin cambios y explica que se necesita un navegador actual.', 'whatsNew.v0171.compatible': 'Los enlaces para compartir existentes siguen siendo compatibles; el formato del enlace no ha cambiado.', + 'whatsNew.v0172.size': 'El botón de sesión temporal, en la parte superior de la ventana, ahora tiene la misma altura y las mismas esquinas redondeadas que los botones de menú que lo acompañan.', + 'whatsNew.v0172.text': 'Su texto es más grande y más fácil de leer.', + 'whatsNew.v0172.same': 'Conserva su borde naranja y abre el mismo menú; el funcionamiento de una sesión temporal no ha cambiado.', } as const export type UiKey = keyof typeof ui diff --git a/src/i18n/locales/fr/ui.ts b/src/i18n/locales/fr/ui.ts index e00ea0f7..3b2d9b68 100644 --- a/src/i18n/locales/fr/ui.ts +++ b/src/i18n/locales/fr/ui.ts @@ -588,6 +588,9 @@ const ui = { 'whatsNew.v0171.compression': 'Les liens de partage utilisent désormais la compression intégrée du navigateur.', 'whatsNew.v0171.unavailable': 'Si cette fonction n’est pas disponible, Loop Studio laisse le diagramme actuel inchangé et explique qu’un navigateur récent est nécessaire.', 'whatsNew.v0171.compatible': 'Les liens de partage existants restent compatibles et leur format n’a pas changé.', + 'whatsNew.v0172.size': 'Le bouton Session temporaire, en haut de la fenêtre, a désormais la même hauteur et les mêmes coins arrondis que les boutons de menu voisins.', + 'whatsNew.v0172.text': 'Son libellé est plus grand et plus facile à lire.', + 'whatsNew.v0172.same': 'Il garde sa bordure orange et ouvre le même menu, et le fonctionnement d’une session temporaire n’a pas changé.', } satisfies Record export default ui diff --git a/src/i18n/locales/it/ui.ts b/src/i18n/locales/it/ui.ts index 59b00817..d548e247 100644 --- a/src/i18n/locales/it/ui.ts +++ b/src/i18n/locales/it/ui.ts @@ -533,6 +533,9 @@ const ui = { 'whatsNew.v0171.compression': 'I link di condivisione ora usano la compressione integrata del browser.', 'whatsNew.v0171.unavailable': 'Se questa funzione non è disponibile, Loop Studio lascia invariato il diagramma attuale e spiega che serve un browser aggiornato.', 'whatsNew.v0171.compatible': 'I link di condivisione esistenti restano compatibili; il formato dei link non è cambiato.', + 'whatsNew.v0172.size': 'Il pulsante della sessione temporanea, in alto nella finestra, ha ora la stessa altezza e gli stessi angoli arrotondati dei pulsanti di menu accanto.', + 'whatsNew.v0172.text': 'La sua scritta è più grande e più facile da leggere.', + 'whatsNew.v0172.same': 'Mantiene il bordo arancione e apre lo stesso menu; il funzionamento di una sessione temporanea non è cambiato.', } satisfies Record export default ui diff --git a/src/i18n/locales/ja/ui.ts b/src/i18n/locales/ja/ui.ts index 3a946960..087ad535 100644 --- a/src/i18n/locales/ja/ui.ts +++ b/src/i18n/locales/ja/ui.ts @@ -538,6 +538,9 @@ const ui = { 'whatsNew.v0171.compression': '共有リンクはブラウザーに組み込まれた圧縮機能を使うようになりました。', 'whatsNew.v0171.unavailable': 'この機能を使えない場合は、現在のダイアグラムをそのまま保ち、最新のブラウザーが必要であることを案内します。', 'whatsNew.v0171.compatible': '既存の共有リンクとの互換性は保たれ、リンクの形式も変わっていません。', + 'whatsNew.v0172.size': '画面上部の一時セッションのボタンが、隣のメニューボタンと同じ高さと角の形になりました。', + 'whatsNew.v0172.text': 'ボタンの文字が大きくなり、読みやすくなりました。', + 'whatsNew.v0172.same': 'オレンジ色の枠と開くメニューはそのままで、一時セッションの動作も変わっていません。', } satisfies Record export default ui diff --git a/src/i18n/locales/ko/ui.ts b/src/i18n/locales/ko/ui.ts index 66abcf52..8e558e8b 100644 --- a/src/i18n/locales/ko/ui.ts +++ b/src/i18n/locales/ko/ui.ts @@ -506,6 +506,9 @@ const ui = { 'whatsNew.v0171.compression': '공유 링크는 이제 브라우저의 기본 압축 기능을 사용합니다.', 'whatsNew.v0171.unavailable': '이 기능을 사용할 수 없으면 현재 다이어그램을 그대로 유지하고 최신 브라우저가 필요하다고 안내합니다.', 'whatsNew.v0171.compatible': '기존 공유 링크와의 호환성은 유지되며 링크 형식도 바뀌지 않았습니다.', + 'whatsNew.v0172.size': '화면 위쪽의 임시 세션 버튼이 옆의 메뉴 버튼과 같은 높이와 모서리 모양이 되었습니다.', + 'whatsNew.v0172.text': '버튼 글자가 커져 더 읽기 쉬워졌습니다.', + 'whatsNew.v0172.same': '주황색 테두리와 버튼을 누르면 열리는 메뉴는 그대로이며, 임시 세션이 작동하는 방식도 바뀌지 않았습니다.', } satisfies Record export default ui diff --git a/src/i18n/locales/nl/ui.ts b/src/i18n/locales/nl/ui.ts index b9fe7877..066f01d2 100644 --- a/src/i18n/locales/nl/ui.ts +++ b/src/i18n/locales/nl/ui.ts @@ -514,6 +514,9 @@ const ui = { 'whatsNew.v0171.compression': 'Deellinks gebruiken nu de ingebouwde compressie van de browser.', 'whatsNew.v0171.unavailable': 'Is die functie niet beschikbaar, dan laat Loop Studio het huidige diagram ongewijzigd en legt uit dat een actuele browser nodig is.', 'whatsNew.v0171.compatible': 'Bestaande deellinks blijven compatibel; de opbouw van de link is niet veranderd.', + 'whatsNew.v0172.size': 'De knop voor de tijdelijke sessie bovenaan het venster heeft nu dezelfde hoogte en dezelfde afgeronde hoeken als de menuknoppen ernaast.', + 'whatsNew.v0172.text': 'Het opschrift is groter en beter leesbaar.', + 'whatsNew.v0172.same': 'De knop houdt zijn oranje rand en opent hetzelfde menu; hoe een tijdelijke sessie werkt, is niet veranderd.', } as const export default ui diff --git a/src/i18n/locales/pt-BR/ui.ts b/src/i18n/locales/pt-BR/ui.ts index 4a938ed4..992bbbd5 100644 --- a/src/i18n/locales/pt-BR/ui.ts +++ b/src/i18n/locales/pt-BR/ui.ts @@ -519,6 +519,9 @@ const ui = { 'whatsNew.v0171.compression': 'Os links de compartilhamento agora usam a compressão integrada do navegador.', 'whatsNew.v0171.unavailable': 'Se esse recurso não estiver disponível, o Loop Studio mantém o diagrama atual sem alterações e explica que é preciso um navegador atual.', 'whatsNew.v0171.compatible': 'Os links de compartilhamento existentes continuam compatíveis; o formato do link não mudou.', + 'whatsNew.v0172.size': 'O botão de sessão temporária, no topo da janela, agora tem a mesma altura e os mesmos cantos arredondados que os botões de menu ao lado.', + 'whatsNew.v0172.text': 'O texto do botão está maior e mais fácil de ler.', + 'whatsNew.v0172.same': 'Ele mantém a borda laranja e abre o mesmo menu; o funcionamento de uma sessão temporária não mudou.', } as const export type UiKey = keyof typeof ui diff --git a/src/i18n/locales/pt-PT/ui.ts b/src/i18n/locales/pt-PT/ui.ts index 3d1d5dcd..6e1d355e 100644 --- a/src/i18n/locales/pt-PT/ui.ts +++ b/src/i18n/locales/pt-PT/ui.ts @@ -533,6 +533,9 @@ const ui = { 'whatsNew.v0171.compression': 'As ligações de partilha passam a usar a compressão integrada do navegador.', 'whatsNew.v0171.unavailable': 'Se essa funcionalidade não estiver disponível, o Loop Studio mantém o diagrama atual sem alterações e explica que é necessário um navegador atual.', 'whatsNew.v0171.compatible': 'As ligações de partilha existentes continuam compatíveis; o formato da ligação não mudou.', + 'whatsNew.v0172.size': 'O botão de sessão temporária, no topo da janela, tem agora a mesma altura e os mesmos cantos arredondados que os botões de menu ao lado.', + 'whatsNew.v0172.text': 'O texto do botão está maior e mais fácil de ler.', + 'whatsNew.v0172.same': 'Mantém o contorno cor de laranja e abre o mesmo menu; o funcionamento de uma sessão temporária não mudou.', } as const export type UiKey = keyof typeof ui diff --git a/src/i18n/locales/ru/ui.ts b/src/i18n/locales/ru/ui.ts index c0ba3e34..a9bed2e9 100644 --- a/src/i18n/locales/ru/ui.ts +++ b/src/i18n/locales/ru/ui.ts @@ -587,6 +587,9 @@ const ui = { 'whatsNew.v0171.compression': 'Ссылки для доступа теперь используют встроенное сжатие браузера.', 'whatsNew.v0171.unavailable': 'Если эта функция недоступна, Loop Studio оставляет текущую диаграмму без изменений и сообщает, что нужен современный браузер.', 'whatsNew.v0171.compatible': 'Существующие ссылки для доступа остаются совместимыми, формат ссылки не изменился.', + 'whatsNew.v0172.size': 'Кнопка временного сеанса в верхней части окна теперь такой же высоты и с такими же скруглёнными углами, как соседние кнопки меню.', + 'whatsNew.v0172.text': 'Надпись на ней стала крупнее и легче читается.', + 'whatsNew.v0172.same': 'Оранжевая рамка и открывающееся меню остались прежними; работа временного сеанса не изменилась.', } as const export type UiKey = keyof typeof ui diff --git a/src/i18n/locales/th/ui.ts b/src/i18n/locales/th/ui.ts index b63776a0..443d330f 100644 --- a/src/i18n/locales/th/ui.ts +++ b/src/i18n/locales/th/ui.ts @@ -579,6 +579,9 @@ const ui = { 'whatsNew.v0171.compression': 'ลิงก์แชร์ใช้การบีบอัดที่มีในเบราว์เซอร์แล้ว', 'whatsNew.v0171.unavailable': 'หากใช้ฟีเจอร์นี้ไม่ได้ ไดอะแกรมปัจจุบันจะยังคงเหมือนเดิม และจะแจ้งว่าต้องใช้เบราว์เซอร์รุ่นปัจจุบัน', 'whatsNew.v0171.compatible': 'ลิงก์แชร์ที่มีอยู่ยังใช้งานร่วมกันได้ และรูปแบบลิงก์ไม่ได้เปลี่ยน', + 'whatsNew.v0172.size': 'ปุ่มเซสชันชั่วคราวที่ด้านบนของหน้าต่างตอนนี้มีความสูงและมุมโค้งเท่ากับปุ่มเมนูที่อยู่ข้าง ๆ', + 'whatsNew.v0172.text': 'ข้อความบนปุ่มใหญ่ขึ้นและอ่านง่ายขึ้น', + 'whatsNew.v0172.same': 'ปุ่มยังคงมีขอบสีส้มและเปิดเมนูเดิม ส่วนการทำงานของเซสชันชั่วคราวไม่ได้เปลี่ยน', } as const export type UiKey = keyof typeof ui diff --git a/src/i18n/locales/tr/ui.ts b/src/i18n/locales/tr/ui.ts index d6af50d2..ed9ee079 100644 --- a/src/i18n/locales/tr/ui.ts +++ b/src/i18n/locales/tr/ui.ts @@ -573,6 +573,9 @@ const ui = { 'whatsNew.v0171.compression': 'Paylaşım bağlantıları artık tarayıcının yerleşik sıkıştırmasını kullanıyor.', 'whatsNew.v0171.unavailable': 'Bu özellik kullanılamıyorsa Loop Studio mevcut diyagramı değiştirmeden bırakır ve güncel bir tarayıcı gerektiğini açıklar.', 'whatsNew.v0171.compatible': 'Mevcut paylaşım bağlantıları uyumlu kalıyor; bağlantı biçimi değişmedi.', + 'whatsNew.v0172.size': 'Pencerenin üst kısmındaki geçici oturum düğmesi artık yanındaki menü düğmeleriyle aynı yükseklikte ve aynı yuvarlak köşelere sahip.', + 'whatsNew.v0172.text': 'Düğmenin yazısı daha büyük ve daha kolay okunuyor.', + 'whatsNew.v0172.same': 'Turuncu kenarlığını koruyor ve aynı menüyü açıyor; geçici oturumun çalışma biçimi değişmedi.', } as const export type UiKey = keyof typeof ui diff --git a/src/i18n/locales/vi/ui.ts b/src/i18n/locales/vi/ui.ts index df6fe895..e367034d 100644 --- a/src/i18n/locales/vi/ui.ts +++ b/src/i18n/locales/vi/ui.ts @@ -519,6 +519,9 @@ const ui = { 'whatsNew.v0171.compression': 'Liên kết chia sẻ giờ dùng tính năng nén có sẵn của trình duyệt.', 'whatsNew.v0171.unavailable': 'Nếu tính năng này không dùng được, Loop Studio giữ nguyên sơ đồ hiện tại và cho biết cần một trình duyệt hiện hành.', 'whatsNew.v0171.compatible': 'Các liên kết chia sẻ hiện có vẫn tương thích; định dạng liên kết không thay đổi.', + 'whatsNew.v0172.size': 'Nút phiên tạm thời ở đầu cửa sổ giờ có cùng chiều cao và góc bo tròn như các nút menu bên cạnh.', + 'whatsNew.v0172.text': 'Chữ trên nút lớn hơn và dễ đọc hơn.', + 'whatsNew.v0172.same': 'Nút vẫn giữ viền màu cam và mở cùng một menu; cách phiên tạm thời hoạt động không thay đổi.', } satisfies Record export default ui diff --git a/src/i18n/locales/zh-Hans/ui.ts b/src/i18n/locales/zh-Hans/ui.ts index d42fd6ed..79d77fec 100644 --- a/src/i18n/locales/zh-Hans/ui.ts +++ b/src/i18n/locales/zh-Hans/ui.ts @@ -511,6 +511,9 @@ const ui = { 'whatsNew.v0171.compression': '分享链接现在使用浏览器内置的压缩功能。', 'whatsNew.v0171.unavailable': '如果无法使用此功能,当前图表将保持不变,并提示需要较新的浏览器。', 'whatsNew.v0171.compatible': '现有的分享链接仍然兼容,链接格式也没有改变。', + 'whatsNew.v0172.size': '窗口顶部的临时会话按钮现在与旁边的菜单按钮高度相同、圆角一致。', + 'whatsNew.v0172.text': '按钮文字更大,更易阅读。', + 'whatsNew.v0172.same': '它仍保留橙色边框,打开的菜单也相同;临时会话的工作方式没有改变。', } satisfies Record export default ui diff --git a/src/i18n/locales/zh-Hant/ui.ts b/src/i18n/locales/zh-Hant/ui.ts index c9386292..1525957c 100644 --- a/src/i18n/locales/zh-Hant/ui.ts +++ b/src/i18n/locales/zh-Hant/ui.ts @@ -537,6 +537,9 @@ const ui = { 'whatsNew.v0171.compression': '分享連結現在使用瀏覽器內建的壓縮功能。', 'whatsNew.v0171.unavailable': '如果無法使用此功能,目前的圖表會保持不變,並提示需要較新的瀏覽器。', 'whatsNew.v0171.compatible': '現有的分享連結仍然相容,連結格式也沒有改變。', + 'whatsNew.v0172.size': '視窗頂端的臨時工作階段按鈕現在與旁邊的選單按鈕高度相同、圓角一致。', + 'whatsNew.v0172.text': '按鈕文字更大,更容易閱讀。', + 'whatsNew.v0172.same': '它仍保留橘色邊框,開啟的選單也相同;臨時工作階段的運作方式沒有改變。', } satisfies Record export default ui diff --git a/src/i18n/nlCopy.test.ts b/src/i18n/nlCopy.test.ts index a7b246c9..88453d43 100644 --- a/src/i18n/nlCopy.test.ts +++ b/src/i18n/nlCopy.test.ts @@ -255,15 +255,15 @@ const IDENTICAL_TO_EN: ReadonlyArray = [ // ------------------------------------------------------------------ shape describe('nl copy — the surfaces exist and are complete', () => { it('has exactly the base key set', () => { - expect(KEYS).toHaveLength(981) // issue #301 decision 1: +4 (share.unavailable and the three 0.17.1 release-note lines) + expect(KEYS).toHaveLength(984) // issue #301 decision 1: +4 (share.unavailable and the three 0.17.1 release-note lines); the session chip: +3 (the three 0.17.2 release-note lines) expect(Object.keys(NL).sort()).toEqual([...KEYS].sort()) }) it('covers all four runtime surfaces, at the measured sizes', () => { const per: Record = {} for (const r of RUNTIME) per[r.surface.split('/')[0]!] = (per[r.surface.split('/')[0]!] ?? 0) + 1 - expect(per).toEqual({ catalog: 981, template: 196, frame: 7, module: 19 }) - expect(RUNTIME).toHaveLength(1203) + expect(per).toEqual({ catalog: 984, template: 196, frame: 7, module: 19 }) + expect(RUNTIME).toHaveLength(1206) }) it('EVERY row has a non-empty ENGLISH side — the vacuity guard', () => { diff --git a/src/i18n/ptPtCopy.test.ts b/src/i18n/ptPtCopy.test.ts index 08922a45..4ec921e6 100644 --- a/src/i18n/ptPtCopy.test.ts +++ b/src/i18n/ptPtCopy.test.ts @@ -58,7 +58,7 @@ describe('pt-PT copy — the region audit over pt-BR', () => { // ---------------------------------------------------------------- shape it('has exactly the same key set as pt-BR', () => { expect(Object.keys(ptValues).sort()).toEqual(KEYS.slice().sort()) - expect(KEYS).toHaveLength(981) // issue #301 decision 1: +4 (share.unavailable and the three 0.17.1 release-note lines) + expect(KEYS).toHaveLength(984) // issue #301 decision 1: +4 (share.unavailable and the three 0.17.1 release-note lines); the session chip: +3 (the three 0.17.2 release-note lines) }) it('differs from pt-BR on exactly the audited keys', () => { @@ -114,7 +114,11 @@ describe('pt-PT copy — the region audit over pt-BR', () => { // lines - `ligações de partilha` not `links de compartilhamento`, `num` // not `em um`, `passam a usar` not `agora usam`, `funcionalidade` not // `recurso`, `necessário` not `preciso`, `ligação` not `link`. - expect(DELTA).toHaveLength(259) + // The session chip (0.17.2) moved it from 259 to 261: two of its three + // release-note lines - `tem agora` not `agora tem`, `contorno cor de + // laranja` not `borda laranja`, no subject pronoun `Ele`; the third line + // reads the same in both. + expect(DELTA).toHaveLength(261) // and it is a real audit, not a rewrite — most of the catalog agrees. // // The password strings of issue #300 are counted apart. The word for a @@ -158,7 +162,7 @@ describe('pt-PT copy — the region audit over pt-BR', () => { // outside them, the quarter bound stands as it always did const rest = KEYS.filter((k) => !isPasswordKey(k)) const restDelta = DELTA.filter((k) => !isPasswordKey(k)) - expect(restDelta).toHaveLength(231) + expect(restDelta).toHaveLength(233) expect(restDelta.length).toBeLessThan(rest.length / 4) }) diff --git a/src/i18n/ruCopy.test.ts b/src/i18n/ruCopy.test.ts index 46d1fb44..baafeec4 100644 --- a/src/i18n/ruCopy.test.ts +++ b/src/i18n/ruCopy.test.ts @@ -43,7 +43,7 @@ describe('ru copy — the first Cyrillic catalog', () => { // ------------------------------------------------------------------ shape it('has exactly the base key set', () => { expect(Object.keys(ruValues).sort()).toEqual(KEYS.slice().sort()) - expect(KEYS).toHaveLength(981) // issue #301 decision 1: +4 (share.unavailable and the three 0.17.1 release-note lines) + expect(KEYS).toHaveLength(984) // issue #301 decision 1: +4 (share.unavailable and the three 0.17.1 release-note lines); the session chip: +3 (the three 0.17.2 release-note lines) }) // ----------------------------------------------------------------- script @@ -246,6 +246,7 @@ describe('ru copy — the first Cyrillic catalog', () => { 'разделённые', 'своё', 'скруглённые', + 'скруглёнными', 'создаёт', // issue #302: the 0.15.2 release-note lines, `сохранённая тема` and // `сохранённое значение темы` diff --git a/src/i18n/thCopy.test.ts b/src/i18n/thCopy.test.ts index abb3dc57..dfb69f5e 100644 --- a/src/i18n/thCopy.test.ts +++ b/src/i18n/thCopy.test.ts @@ -88,11 +88,11 @@ const englishFor = (surface: string, id: string): string => { describe('th copy — the first no-space script', () => { it('has exactly the base key set', () => { expect(Object.keys(TH).sort()).toEqual(KEYS.slice().sort()) - expect(KEYS).toHaveLength(981) // issue #301 decision 1: +4 (share.unavailable and the three 0.17.1 release-note lines) + expect(KEYS).toHaveLength(984) // issue #301 decision 1: +4 (share.unavailable and the three 0.17.1 release-note lines); the session chip: +3 (the three 0.17.2 release-note lines) }) it('covers all three runtime surfaces', () => { - expect(RUNTIME.filter(([s]) => s === 'catalog')).toHaveLength(981) + expect(RUNTIME.filter(([s]) => s === 'catalog')).toHaveLength(984) expect(RUNTIME.filter(([s]) => s.startsWith('template/'))).toHaveLength(196) expect(RUNTIME.filter(([s]) => s.startsWith('frame/'))).toHaveLength(7) expect(RUNTIME.filter(([s]) => s.startsWith('module/'))).toHaveLength(19) diff --git a/src/i18n/trCopy.test.ts b/src/i18n/trCopy.test.ts index d6776ff4..512a4340 100644 --- a/src/i18n/trCopy.test.ts +++ b/src/i18n/trCopy.test.ts @@ -30,7 +30,7 @@ const KEYS = Object.keys(EN) describe('tr copy — the first Turkish catalog', () => { it('has exactly the base key set', () => { expect(Object.keys(TR).sort()).toEqual(KEYS.slice().sort()) - expect(KEYS).toHaveLength(981) // issue #300: +33; issue #301 decision 1: +4 (share.unavailable and the three 0.17.1 release-note lines) + expect(KEYS).toHaveLength(984) // issue #300: +33; issue #301 decision 1: +4 (share.unavailable and the three 0.17.1 release-note lines); the session chip: +3 (the three 0.17.2 release-note lines) }) }) diff --git a/src/i18n/viCopy.test.ts b/src/i18n/viCopy.test.ts index b4c5260d..aac42a17 100644 --- a/src/i18n/viCopy.test.ts +++ b/src/i18n/viCopy.test.ts @@ -132,11 +132,11 @@ const englishFor = (surface: string, id: string): string => { describe('vi copy — the first locale English hides inside', () => { it('has exactly the base key set', () => { expect(Object.keys(VI).sort()).toEqual(KEYS.slice().sort()) - expect(KEYS).toHaveLength(981) // issue #301 decision 1: +4 (share.unavailable and the three 0.17.1 release-note lines) + expect(KEYS).toHaveLength(984) // issue #301 decision 1: +4 (share.unavailable and the three 0.17.1 release-note lines); the session chip: +3 (the three 0.17.2 release-note lines) }) it('covers all four runtime surfaces', () => { - expect(RUNTIME.filter(([s]) => s === 'catalog')).toHaveLength(981) + expect(RUNTIME.filter(([s]) => s === 'catalog')).toHaveLength(984) expect(RUNTIME.filter(([s]) => s.startsWith('template/'))).toHaveLength(196) expect(RUNTIME.filter(([s]) => s.startsWith('frame/'))).toHaveLength(7) expect(RUNTIME.filter(([s]) => s.startsWith('module/'))).toHaveLength(19) @@ -538,6 +538,9 @@ const ASCII_KEYS = [ 'catalog:whatsNew.v0170.limits', // issue #301 decision 1: `Loop Studio` in a 0.17.1 release-note line 'catalog:whatsNew.v0171.unavailable', + // the session chip (0.17.2): `menu` in two release-note lines + 'catalog:whatsNew.v0172.size', + 'catalog:whatsNew.v0172.same', // the storage gate and the Storage and privacy area (issue #297) 'catalog:gate.lead', 'catalog:gate.portable.note', @@ -559,7 +562,7 @@ describe('a kept English word is declared, twice over — by key and by word', ( ([s, id]) => s + ':' + id, ) expect(actual.slice().sort()).toEqual(ASCII_KEYS.slice().sort()) - expect(ASCII_KEYS).toHaveLength(184) + expect(ASCII_KEYS).toHaveLength(186) }) it('the kept vocabulary is exactly the declared one', () => { diff --git a/src/index.css b/src/index.css index 7b424f0e..609a25c3 100644 --- a/src/index.css +++ b/src/index.css @@ -6396,6 +6396,7 @@ input[type='number']::-webkit-inner-spin-button { beat its hover and its on / selected state, by specificity or by order. The graph is not here — node and frame focus read `--state-focus`. */ .btn:not(.btn--ghost):focus-visible:not(:disabled), +.session-chip__btn:focus-visible, .pb-btn:focus-visible:not(:disabled), .field input:focus-visible:not(:disabled), .field select:focus-visible:not(:disabled), @@ -6431,6 +6432,7 @@ input[type='number']::-webkit-inner-spin-button { inside its own box for the same reason its halo was dropped. */ @media (forced-colors: active) { .btn:not(.btn--ghost):focus-visible:not(:disabled), + .session-chip__btn:focus-visible, .pb-btn:focus-visible:not(:disabled), .field input:focus-visible:not(:disabled), .field select:focus-visible:not(:disabled), @@ -6705,19 +6707,30 @@ input[type='number']::-webkit-inner-spin-button { } /* ── issue #297 — the temporary-session chip (toolbar) ──────────────────── - A `.rev-chip` that is a button: the same box as the revision chip beside - it, a solid boundary when colours are forced, and a small menu with the - ways out (export, save here after all, the Storage area). */ + A `.rev-chip` that is a button, with a solid boundary when colours are + forced and a small menu with the ways out (export, save here after all, the + Storage area). It is a control, so it is drawn as the toolbar's menu + buttons (`.btn`) are: 28 px tall, the control radius, 12 px text, 12 px of + side padding, their ink on their raised face, their hover boundary and + their focus (it is listed with `.btn` in the focus block at the end of this + file). The warning border is what still marks the session. */ .session-chip { position: relative; display: inline-flex; } .session-chip__btn { cursor: pointer; + height: 28px; + padding: 0 12px; + border-radius: var(--control-radius); font-family: var(--font-sans); + font-size: 12px; letter-spacing: 0; font-weight: 600; + color: var(--text-primary); + background: var(--surface-raised); border-color: var(--warning); + flex-shrink: 0; } .session-chip__btn:hover, .session-chip__btn[aria-expanded='true'] { diff --git a/src/releaseNotes/releaseNotes.ts b/src/releaseNotes/releaseNotes.ts index cc3e3a57..902eb9ab 100644 --- a/src/releaseNotes/releaseNotes.ts +++ b/src/releaseNotes/releaseNotes.ts @@ -38,6 +38,14 @@ export const releaseNoteIdFor = (version: string): ReleaseNoteId => `release:${v * mechanics, no test counts, no pull-request numbers. */ export const RELEASE_NOTES: readonly ReleaseNote[] = [ + { + // the temporary-session chip takes the box of the toolbar's menu buttons; + // the date is the day it is deployed, set right before the merge + id: 'release:0.17.2', + version: '0.17.2', + date: '2026-10-05', + items: ['whatsNew.v0172.size', 'whatsNew.v0172.text', 'whatsNew.v0172.same'], + }, { // issue #301 decision 1 - share links use only the browser's own compression id: 'release:0.17.1',