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',