@cat-factory/app 0.137.1 → 0.138.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4122,7 +4122,6 @@
4122
4122
  "footer": "תרחישים הם התחומים שהבודק בחר לבחון (תרחישי קבלת המפרט שלו). תוצאות וחששות מקובצים תחתם לפי שם."
4123
4123
  },
4124
4124
  "visualConfirm": {
4125
- "ariaLabel": "אישור חזותי",
4126
4125
  "title": "אישור חזותי",
4127
4126
  "titleWithBlock": "אישור חזותי — {title}",
4128
4127
  "subtitle": "סקור את הממשק מול עיצובי הייחוס",
@@ -4445,7 +4445,6 @@
4445
4445
  "footer": "Gli scenari sono le aree che il Tester ha scelto di verificare (i suoi scenari di accettazione della specifica). Esiti e rilievi sono raggruppati sotto di essi per nome."
4446
4446
  },
4447
4447
  "visualConfirm": {
4448
- "ariaLabel": "Conferma visiva",
4449
4448
  "title": "Conferma visiva",
4450
4449
  "titleWithBlock": "Conferma visiva — {title}",
4451
4450
  "subtitle": "Confronta l'interfaccia con i design di riferimento",
@@ -4123,7 +4123,6 @@
4123
4123
  "footer": "シナリオはTesterが検証対象に選んだ領域(スペックの受け入れシナリオ)です。結果と懸念は名前ごとにその下にグループ化されます。"
4124
4124
  },
4125
4125
  "visualConfirm": {
4126
- "ariaLabel": "ビジュアル確認",
4127
4126
  "title": "ビジュアル確認",
4128
4127
  "titleWithBlock": "ビジュアル確認 — {title}",
4129
4128
  "subtitle": "リファレンスデザインと照らしてUIをレビュー",
@@ -4111,7 +4111,6 @@
4111
4111
  }
4112
4112
  },
4113
4113
  "visualConfirm": {
4114
- "ariaLabel": "Potwierdzenie wizualne",
4115
4114
  "title": "Potwierdzenie wizualne",
4116
4115
  "titleWithBlock": "Potwierdzenie wizualne — {title}",
4117
4116
  "subtitle": "Porównaj interfejs z projektami referencyjnymi",
@@ -4123,7 +4123,6 @@
4123
4123
  "footer": "Senaryolar, Tester'ın test etmek için seçtiği alanlardır (spesifikasyon kabul senaryoları). Sonuçlar ve endişeler ada göre bunların altında gruplanır."
4124
4124
  },
4125
4125
  "visualConfirm": {
4126
- "ariaLabel": "Görsel doğrulama",
4127
4126
  "title": "Görsel doğrulama",
4128
4127
  "titleWithBlock": "Görsel doğrulama — {title}",
4129
4128
  "subtitle": "Arayüzü referans tasarımlarla karşılaştırarak inceleyin",
@@ -4111,7 +4111,6 @@
4111
4111
  }
4112
4112
  },
4113
4113
  "visualConfirm": {
4114
- "ariaLabel": "Візуальне підтвердження",
4115
4114
  "title": "Візуальне підтвердження",
4116
4115
  "titleWithBlock": "Візуальне підтвердження — {title}",
4117
4116
  "subtitle": "Порівняйте інтерфейс з еталонними дизайнами",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/app",
3
- "version": "0.137.1",
3
+ "version": "0.138.0",
4
4
  "description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,72 +0,0 @@
1
- import { nextTick, onScopeDispose, watch, type Ref } from 'vue'
2
-
3
- /**
4
- * Lightweight focus management for a modal surface (the screenshot review windows + the
5
- * shared lightbox). While `active`, it:
6
- * · moves focus into the container on open (so keyboard / screen-reader users land inside
7
- * the dialog instead of staying on the background),
8
- * · traps Tab / Shift+Tab within the container's focusable elements, and
9
- * · restores focus to whatever was focused before, on close.
10
- *
11
- * Nested surfaces (a lightbox opened over a review window) hand off cleanly because each
12
- * caller scopes its own `active` — the window passes `open && !lightboxOpen`, so exactly one
13
- * trap is live at a time and they never fight over Tab.
14
- */
15
- export function useFocusTrap(container: Ref<HTMLElement | null>, active: Ref<boolean>): void {
16
- let previouslyFocused: HTMLElement | null = null
17
-
18
- function focusables(): HTMLElement[] {
19
- const root = container.value
20
- if (!root) return []
21
- const nodes = root.querySelectorAll<HTMLElement>(
22
- 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])',
23
- )
24
- // Skip elements that aren't actually rendered (e.g. inside a `v-if`/`hidden` branch).
25
- return Array.from(nodes).filter(
26
- (el) => el.offsetParent !== null || el === document.activeElement,
27
- )
28
- }
29
-
30
- function onKeydown(e: KeyboardEvent): void {
31
- if (!active.value || e.key !== 'Tab') return
32
- const els = focusables()
33
- if (!els.length) {
34
- e.preventDefault()
35
- container.value?.focus()
36
- return
37
- }
38
- const first = els[0]!
39
- const last = els[els.length - 1]!
40
- const current = document.activeElement as HTMLElement | null
41
- const inside = !!container.value?.contains(current)
42
- if (e.shiftKey) {
43
- if (!inside || current === first) {
44
- e.preventDefault()
45
- last.focus()
46
- }
47
- } else if (!inside || current === last) {
48
- e.preventDefault()
49
- first.focus()
50
- }
51
- }
52
-
53
- watch(
54
- active,
55
- (on) => {
56
- if (on) {
57
- previouslyFocused = document.activeElement as HTMLElement | null
58
- window.addEventListener('keydown', onKeydown, true)
59
- void nextTick(() => {
60
- ;(focusables()[0] ?? container.value)?.focus()
61
- })
62
- } else {
63
- window.removeEventListener('keydown', onKeydown, true)
64
- previouslyFocused?.focus?.()
65
- previouslyFocused = null
66
- }
67
- },
68
- { immediate: true },
69
- )
70
-
71
- onScopeDispose(() => window.removeEventListener('keydown', onKeydown, true))
72
- }