@semiont/react-ui 0.5.24 → 0.5.26
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.
- package/dist/PdfAnnotationCanvas.client-6ZSF6ZTJ.js +677 -0
- package/dist/PdfAnnotationCanvas.client-6ZSF6ZTJ.js.map +1 -0
- package/dist/PdfAnnotationCanvas.client-JCJMXWWJ.css +180 -0
- package/dist/PdfAnnotationCanvas.client-JCJMXWWJ.css.map +1 -0
- package/dist/{ar-JRTKCMTF.js → ar-YTUUGK2O.js} +32 -6
- package/dist/ar-YTUUGK2O.js.map +1 -0
- package/dist/{bn-LO6EDSCC.js → bn-ASGNGPMD.js} +32 -6
- package/dist/bn-ASGNGPMD.js.map +1 -0
- package/dist/{chunk-4QMTEPQ3.js → chunk-BTUG3VJJ.js} +18 -298
- package/dist/chunk-BTUG3VJJ.js.map +1 -0
- package/dist/{chunk-5Y5JDCKC.js → chunk-L3WKRWP3.js} +32 -6
- package/dist/chunk-L3WKRWP3.js.map +1 -0
- package/dist/chunk-OBQOZCJE.js +287 -0
- package/dist/chunk-OBQOZCJE.js.map +1 -0
- package/dist/{cs-LCHHOR4O.js → cs-WMYVAOZY.js} +32 -6
- package/dist/cs-WMYVAOZY.js.map +1 -0
- package/dist/{da-TARGPMJR.js → da-FSHHN4D6.js} +32 -6
- package/dist/da-FSHHN4D6.js.map +1 -0
- package/dist/{de-SBH4QBPJ.js → de-FJFO3TPT.js} +32 -6
- package/dist/de-FJFO3TPT.js.map +1 -0
- package/dist/{el-2D2OLGWB.js → el-XOH4QIAG.js} +32 -6
- package/dist/el-XOH4QIAG.js.map +1 -0
- package/dist/{en-QFIEG3CA.js → en-3FCBW3YD.js} +6 -2
- package/dist/{es-U6YK2LX6.js → es-HDX4QI7E.js} +32 -6
- package/dist/es-HDX4QI7E.js.map +1 -0
- package/dist/{fa-S7SZE37M.js → fa-DJ6WTFTJ.js} +32 -6
- package/dist/fa-DJ6WTFTJ.js.map +1 -0
- package/dist/{fi-CBL3NIXN.js → fi-4S7H7W7U.js} +32 -6
- package/dist/fi-4S7H7W7U.js.map +1 -0
- package/dist/{fr-EABUHV6S.js → fr-6UJSYHXC.js} +32 -6
- package/dist/fr-6UJSYHXC.js.map +1 -0
- package/dist/{he-THWD244Y.js → he-ZZJOHJX2.js} +32 -6
- package/dist/he-ZZJOHJX2.js.map +1 -0
- package/dist/{hi-GHGFZJ44.js → hi-HZJTAW7U.js} +32 -6
- package/dist/hi-HZJTAW7U.js.map +1 -0
- package/dist/{id-POZOUMDW.js → id-E3I6ILEM.js} +32 -6
- package/dist/id-E3I6ILEM.js.map +1 -0
- package/dist/index.css +140 -94
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +85 -24
- package/dist/index.js +1782 -1697
- package/dist/index.js.map +1 -1
- package/dist/{it-KDYJADSH.js → it-ULAPAD7U.js} +32 -6
- package/dist/it-ULAPAD7U.js.map +1 -0
- package/dist/{ja-Z5Z6ZF3P.js → ja-SAHZQ4CZ.js} +32 -6
- package/dist/ja-SAHZQ4CZ.js.map +1 -0
- package/dist/{ko-7X3XUXRN.js → ko-DFU2ADQ4.js} +32 -6
- package/dist/ko-DFU2ADQ4.js.map +1 -0
- package/dist/{ms-BTWEIW43.js → ms-NWL76XNT.js} +32 -6
- package/dist/ms-NWL76XNT.js.map +1 -0
- package/dist/{nl-IS2CGF5I.js → nl-ATCM76WN.js} +32 -6
- package/dist/nl-ATCM76WN.js.map +1 -0
- package/dist/{no-AAMEVA26.js → no-UIAQ5RJF.js} +32 -6
- package/dist/no-UIAQ5RJF.js.map +1 -0
- package/dist/{pl-PVXUQYFT.js → pl-NOECQDXD.js} +32 -6
- package/dist/pl-NOECQDXD.js.map +1 -0
- package/dist/{pt-3ITQ76TL.js → pt-SVWIIUPC.js} +32 -6
- package/dist/pt-SVWIIUPC.js.map +1 -0
- package/dist/{ro-FRB4IIVS.js → ro-NR2DRTZG.js} +32 -6
- package/dist/ro-NR2DRTZG.js.map +1 -0
- package/dist/{sv-DS3RO24M.js → sv-STVJ37JP.js} +32 -6
- package/dist/sv-STVJ37JP.js.map +1 -0
- package/dist/test-utils.js +5 -3
- package/dist/test-utils.js.map +1 -1
- package/dist/{th-AX66MWWB.js → th-UWQR4K54.js} +32 -6
- package/dist/th-UWQR4K54.js.map +1 -0
- package/dist/{tr-7KTB67TT.js → tr-WNXZSD27.js} +32 -6
- package/dist/tr-WNXZSD27.js.map +1 -0
- package/dist/{uk-SMQS2MAQ.js → uk-SJRMB47Y.js} +32 -6
- package/dist/uk-SJRMB47Y.js.map +1 -0
- package/dist/{vi-D3JXL2EZ.js → vi-TMRQFES2.js} +32 -6
- package/dist/vi-TMRQFES2.js.map +1 -0
- package/dist/{zh-S754AWFS.js → zh-QOL3LYEM.js} +32 -6
- package/dist/zh-QOL3LYEM.js.map +1 -0
- package/package.json +8 -8
- package/src/components/AssistProgress.tsx +42 -30
- package/src/components/EntityFoundLog.tsx +38 -0
- package/src/components/StatusDisplay.css +35 -35
- package/src/components/__tests__/AssistProgress.test.tsx +86 -28
- package/src/components/annotation/annotations.css +1 -1
- package/src/components/modals/SearchModal.css +5 -5
- package/src/components/pdf-annotation/PdfAnnotationCanvas.css +143 -6
- package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +689 -198
- package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +1008 -3
- package/src/components/pdf-annotation/__tests__/estimate-slot-height.test.ts +53 -0
- package/src/components/pdf-annotation/estimate-slot-height.ts +31 -0
- package/src/components/resource/AnnotateView.tsx +14 -7
- package/src/components/resource/BrowseView.tsx +2 -7
- package/src/components/resource/DownloadFileLink.tsx +49 -0
- package/src/components/resource/__tests__/AnnotateView.focus.test.tsx +58 -0
- package/src/components/resource/__tests__/browse-renderers.dispatch.test.tsx +33 -0
- package/src/components/resource/__tests__/download-link.test.tsx +129 -0
- package/src/components/resource/annotate-renderers.tsx +10 -1
- package/src/components/resource/browse-renderers.tsx +10 -1
- package/src/components/resource/panels/AssistSection.css +0 -43
- package/src/components/resource/panels/AssistSection.tsx +11 -2
- package/src/components/resource/panels/AssistShell.tsx +5 -1
- package/src/components/resource/panels/ReferencesPanel.tsx +12 -13
- package/src/components/resource/panels/TaggingPanel.tsx +21 -1
- package/src/components/resource/panels/__tests__/AssistSection.test.tsx +2 -2
- package/src/components/resource/panels/__tests__/AssistShell.test.tsx +9 -4
- package/src/components/resource/panels/__tests__/HighlightPanel.annotationProgress.test.tsx +1 -1
- package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +26 -0
- package/src/features/auth/auth.css +10 -10
- package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +58 -3
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +18 -7
- package/src/styles/core/inputs.css +1 -1
- package/src/styles/features/compose.css +1 -1
- package/src/styles/features/resource-viewer.css +1 -1
- package/src/styles/features/static-pages.css +11 -11
- package/src/styles/motivations/motivation-comment.css +2 -2
- package/src/styles/motivations/motivation-reference.css +2 -2
- package/src/styles/patterns/panels-base.css +4 -4
- package/src/styles/utilities/motion.css +15 -15
- package/src/styles/variables.css +29 -1
- package/translations/ar.json +27 -5
- package/translations/bn.json +27 -5
- package/translations/cs.json +27 -5
- package/translations/da.json +27 -5
- package/translations/de.json +27 -5
- package/translations/el.json +27 -5
- package/translations/en.json +27 -5
- package/translations/es.json +27 -5
- package/translations/fa.json +27 -5
- package/translations/fi.json +27 -5
- package/translations/fr.json +27 -5
- package/translations/he.json +27 -5
- package/translations/hi.json +27 -5
- package/translations/id.json +27 -5
- package/translations/it.json +27 -5
- package/translations/ja.json +27 -5
- package/translations/ko.json +27 -5
- package/translations/ms.json +27 -5
- package/translations/nl.json +27 -5
- package/translations/no.json +27 -5
- package/translations/pl.json +27 -5
- package/translations/pt.json +27 -5
- package/translations/ro.json +27 -5
- package/translations/sv.json +27 -5
- package/translations/th.json +27 -5
- package/translations/tr.json +27 -5
- package/translations/uk.json +27 -5
- package/translations/vi.json +27 -5
- package/translations/zh.json +27 -5
- package/dist/PdfAnnotationCanvas.client-SU5KNT7G.css +0 -98
- package/dist/PdfAnnotationCanvas.client-SU5KNT7G.css.map +0 -1
- package/dist/PdfAnnotationCanvas.client-V7Q3BEEQ.js +0 -427
- package/dist/PdfAnnotationCanvas.client-V7Q3BEEQ.js.map +0 -1
- package/dist/ar-JRTKCMTF.js.map +0 -1
- package/dist/bn-LO6EDSCC.js.map +0 -1
- package/dist/chunk-4QMTEPQ3.js.map +0 -1
- package/dist/chunk-5Y5JDCKC.js.map +0 -1
- package/dist/cs-LCHHOR4O.js.map +0 -1
- package/dist/da-TARGPMJR.js.map +0 -1
- package/dist/de-SBH4QBPJ.js.map +0 -1
- package/dist/el-2D2OLGWB.js.map +0 -1
- package/dist/es-U6YK2LX6.js.map +0 -1
- package/dist/fa-S7SZE37M.js.map +0 -1
- package/dist/fi-CBL3NIXN.js.map +0 -1
- package/dist/fr-EABUHV6S.js.map +0 -1
- package/dist/he-THWD244Y.js.map +0 -1
- package/dist/hi-GHGFZJ44.js.map +0 -1
- package/dist/id-POZOUMDW.js.map +0 -1
- package/dist/it-KDYJADSH.js.map +0 -1
- package/dist/ja-Z5Z6ZF3P.js.map +0 -1
- package/dist/ko-7X3XUXRN.js.map +0 -1
- package/dist/ms-BTWEIW43.js.map +0 -1
- package/dist/nl-IS2CGF5I.js.map +0 -1
- package/dist/no-AAMEVA26.js.map +0 -1
- package/dist/pl-PVXUQYFT.js.map +0 -1
- package/dist/pt-3ITQ76TL.js.map +0 -1
- package/dist/ro-FRB4IIVS.js.map +0 -1
- package/dist/sv-DS3RO24M.js.map +0 -1
- package/dist/th-AX66MWWB.js.map +0 -1
- package/dist/tr-7KTB67TT.js.map +0 -1
- package/dist/uk-SMQS2MAQ.js.map +0 -1
- package/dist/vi-D3JXL2EZ.js.map +0 -1
- package/dist/zh-S754AWFS.js.map +0 -1
- /package/dist/{en-QFIEG3CA.js.map → en-3FCBW3YD.js.map} +0 -0
|
@@ -161,7 +161,7 @@
|
|
|
161
161
|
|
|
162
162
|
.semiont-toolbar-icon-large {
|
|
163
163
|
font-size: 1.875rem; /* 30px - was 2xl/1.5rem */
|
|
164
|
-
font-weight: var(--semiont-font-
|
|
164
|
+
font-weight: var(--semiont-font-bold);
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
/* Dark mode for Toolbar - Fixed to respect data-theme attribute */
|
|
@@ -206,7 +206,7 @@
|
|
|
206
206
|
color: var(--semiont-color-primary-700);
|
|
207
207
|
border: 1px solid var(--semiont-color-primary-200);
|
|
208
208
|
border-radius: var(--semiont-radius-base);
|
|
209
|
-
font-size: var(--semiont-
|
|
209
|
+
font-size: var(--semiont-text-xs);
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
/* Editing affordances (RESOURCE-TAGS-INLINE-EDITING) */
|
|
@@ -217,7 +217,7 @@
|
|
|
217
217
|
border: none;
|
|
218
218
|
background: none;
|
|
219
219
|
color: inherit;
|
|
220
|
-
font-size: var(--semiont-
|
|
220
|
+
font-size: var(--semiont-text-xs);
|
|
221
221
|
cursor: pointer;
|
|
222
222
|
opacity: 0.7;
|
|
223
223
|
}
|
|
@@ -279,8 +279,8 @@
|
|
|
279
279
|
}
|
|
280
280
|
|
|
281
281
|
.semiont-error-boundary-title {
|
|
282
|
-
font-size: var(--semiont-
|
|
283
|
-
font-weight: var(--semiont-font-
|
|
282
|
+
font-size: var(--semiont-text-2xl);
|
|
283
|
+
font-weight: var(--semiont-font-bold);
|
|
284
284
|
color: var(--semiont-color-neutral-900);
|
|
285
285
|
margin-bottom: var(--semiont-spacing-xs);
|
|
286
286
|
}
|
|
@@ -290,30 +290,30 @@
|
|
|
290
290
|
}
|
|
291
291
|
|
|
292
292
|
.semiont-error-boundary-details {
|
|
293
|
-
background-color: var(--semiont-color-
|
|
294
|
-
border: 1px solid var(--semiont-color-
|
|
293
|
+
background-color: var(--semiont-color-red-50);
|
|
294
|
+
border: 1px solid var(--semiont-color-red-200);
|
|
295
295
|
border-radius: var(--semiont-radius-md);
|
|
296
296
|
padding: var(--semiont-spacing-md);
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
.semiont-error-boundary-details-title {
|
|
300
|
-
font-size: var(--semiont-
|
|
301
|
-
font-weight: var(--semiont-font-
|
|
302
|
-
color: var(--semiont-color-
|
|
300
|
+
font-size: var(--semiont-text-sm);
|
|
301
|
+
font-weight: var(--semiont-font-semibold);
|
|
302
|
+
color: var(--semiont-color-red-800);
|
|
303
303
|
margin-bottom: calc(var(--semiont-spacing-xs) / 2);
|
|
304
304
|
}
|
|
305
305
|
|
|
306
306
|
.semiont-error-boundary-details-message {
|
|
307
|
-
font-size: var(--semiont-
|
|
308
|
-
color: var(--semiont-color-
|
|
307
|
+
font-size: var(--semiont-text-xs);
|
|
308
|
+
color: var(--semiont-color-red-700);
|
|
309
309
|
font-family: monospace;
|
|
310
310
|
word-break: break-all;
|
|
311
311
|
}
|
|
312
312
|
|
|
313
313
|
.semiont-error-boundary-stack {
|
|
314
314
|
margin-top: var(--semiont-spacing-xs);
|
|
315
|
-
font-size: var(--semiont-
|
|
316
|
-
color: var(--semiont-color-
|
|
315
|
+
font-size: var(--semiont-text-xs);
|
|
316
|
+
color: var(--semiont-color-red-600);
|
|
317
317
|
overflow-x: auto;
|
|
318
318
|
}
|
|
319
319
|
|
|
@@ -326,8 +326,8 @@
|
|
|
326
326
|
/* AsyncErrorBoundary Component */
|
|
327
327
|
.semiont-async-error-boundary {
|
|
328
328
|
padding: var(--semiont-spacing-md);
|
|
329
|
-
background-color: var(--semiont-color-
|
|
330
|
-
border: 1px solid var(--semiont-color-
|
|
329
|
+
background-color: var(--semiont-color-amber-50);
|
|
330
|
+
border: 1px solid var(--semiont-color-amber-200);
|
|
331
331
|
border-radius: var(--semiont-radius-md);
|
|
332
332
|
}
|
|
333
333
|
|
|
@@ -343,7 +343,7 @@
|
|
|
343
343
|
.semiont-icon-warning {
|
|
344
344
|
height: 1.25rem;
|
|
345
345
|
width: 1.25rem;
|
|
346
|
-
color: var(--semiont-color-
|
|
346
|
+
color: var(--semiont-color-amber-400);
|
|
347
347
|
}
|
|
348
348
|
|
|
349
349
|
.semiont-async-error-details {
|
|
@@ -352,21 +352,21 @@
|
|
|
352
352
|
}
|
|
353
353
|
|
|
354
354
|
.semiont-async-error-title {
|
|
355
|
-
font-size: var(--semiont-
|
|
356
|
-
font-weight: var(--semiont-font-
|
|
357
|
-
color: var(--semiont-color-
|
|
355
|
+
font-size: var(--semiont-text-sm);
|
|
356
|
+
font-weight: var(--semiont-font-medium);
|
|
357
|
+
color: var(--semiont-color-amber-800);
|
|
358
358
|
}
|
|
359
359
|
|
|
360
360
|
.semiont-async-error-message {
|
|
361
361
|
margin-top: calc(var(--semiont-spacing-xs) / 2);
|
|
362
|
-
font-size: var(--semiont-
|
|
363
|
-
color: var(--semiont-color-
|
|
362
|
+
font-size: var(--semiont-text-sm);
|
|
363
|
+
color: var(--semiont-color-amber-700);
|
|
364
364
|
}
|
|
365
365
|
|
|
366
366
|
.semiont-async-error-retry {
|
|
367
367
|
margin-top: var(--semiont-spacing-xs);
|
|
368
|
-
font-size: var(--semiont-
|
|
369
|
-
color: var(--semiont-color-
|
|
368
|
+
font-size: var(--semiont-text-sm);
|
|
369
|
+
color: var(--semiont-color-amber-600);
|
|
370
370
|
text-decoration: underline;
|
|
371
371
|
background: none;
|
|
372
372
|
border: none;
|
|
@@ -375,7 +375,7 @@
|
|
|
375
375
|
}
|
|
376
376
|
|
|
377
377
|
.semiont-async-error-retry:hover {
|
|
378
|
-
color: var(--semiont-color-
|
|
378
|
+
color: var(--semiont-color-amber-500);
|
|
379
379
|
}
|
|
380
380
|
|
|
381
381
|
/* Dark mode for ErrorBoundary */
|
|
@@ -393,43 +393,43 @@
|
|
|
393
393
|
|
|
394
394
|
[data-theme="dark"] .semiont-error-boundary-details {
|
|
395
395
|
background-color: rgba(239, 68, 68, 0.2);
|
|
396
|
-
border-color: var(--semiont-color-
|
|
396
|
+
border-color: var(--semiont-color-red-800);
|
|
397
397
|
}
|
|
398
398
|
|
|
399
399
|
[data-theme="dark"] .semiont-error-boundary-details-title {
|
|
400
|
-
color: var(--semiont-color-
|
|
400
|
+
color: var(--semiont-color-red-300);
|
|
401
401
|
}
|
|
402
402
|
|
|
403
403
|
[data-theme="dark"] .semiont-error-boundary-details-message {
|
|
404
|
-
color: var(--semiont-color-
|
|
404
|
+
color: var(--semiont-color-red-400);
|
|
405
405
|
}
|
|
406
406
|
|
|
407
407
|
[data-theme="dark"] .semiont-error-boundary-stack {
|
|
408
|
-
color: var(--semiont-color-
|
|
408
|
+
color: var(--semiont-color-red-500);
|
|
409
409
|
}
|
|
410
410
|
|
|
411
411
|
[data-theme="dark"] .semiont-async-error-boundary {
|
|
412
412
|
background-color: rgba(245, 158, 11, 0.2);
|
|
413
|
-
border-color: var(--semiont-color-
|
|
413
|
+
border-color: var(--semiont-color-amber-800);
|
|
414
414
|
}
|
|
415
415
|
|
|
416
416
|
[data-theme="dark"] .semiont-icon-warning {
|
|
417
|
-
color: var(--semiont-color-
|
|
417
|
+
color: var(--semiont-color-amber-400);
|
|
418
418
|
}
|
|
419
419
|
|
|
420
420
|
[data-theme="dark"] .semiont-async-error-title {
|
|
421
|
-
color: var(--semiont-color-
|
|
421
|
+
color: var(--semiont-color-amber-300);
|
|
422
422
|
}
|
|
423
423
|
|
|
424
424
|
[data-theme="dark"] .semiont-async-error-message {
|
|
425
|
-
color: var(--semiont-color-
|
|
425
|
+
color: var(--semiont-color-amber-400);
|
|
426
426
|
}
|
|
427
427
|
|
|
428
428
|
[data-theme="dark"] .semiont-async-error-retry {
|
|
429
|
-
color: var(--semiont-color-
|
|
429
|
+
color: var(--semiont-color-amber-400);
|
|
430
430
|
}
|
|
431
431
|
|
|
432
432
|
[data-theme="dark"] .semiont-async-error-retry:hover {
|
|
433
|
-
color: var(--semiont-color-
|
|
433
|
+
color: var(--semiont-color-amber-300);
|
|
434
434
|
}
|
|
435
435
|
|
|
@@ -7,13 +7,19 @@
|
|
|
7
7
|
* cancel/dismiss arrive as callbacks the caller wires (job.cancelRequest /
|
|
8
8
|
* mark.dismissProgress). Feature blocks are data-presence-driven so each call
|
|
9
9
|
* site keeps its current visuals by passing what it always had.
|
|
10
|
+
*
|
|
11
|
+
* i18n contract (ASSIST-SURFACE-WARTS Lane A): every string this component
|
|
12
|
+
* renders comes from `translations`. There are NO English fallbacks — a
|
|
13
|
+
* missing key must be a type error at the call site, not a silent English
|
|
14
|
+
* leak in a Japanese UI. Tests use key-echo strings ('tr.complete') so an
|
|
15
|
+
* assertion failing means "rendered something other than what was passed".
|
|
10
16
|
*/
|
|
11
17
|
import { describe, it, expect, vi } from 'vitest';
|
|
12
18
|
import { render, screen } from '@testing-library/react';
|
|
13
19
|
import userEvent from '@testing-library/user-event';
|
|
14
20
|
import '@testing-library/jest-dom';
|
|
15
21
|
import type { components } from '@semiont/core';
|
|
16
|
-
import { AssistProgress } from '../AssistProgress';
|
|
22
|
+
import { AssistProgress, type AssistProgressTranslations } from '../AssistProgress';
|
|
17
23
|
|
|
18
24
|
type JobProgress = components['schemas']['JobProgress'];
|
|
19
25
|
|
|
@@ -21,13 +27,26 @@ const running = (over: Partial<JobProgress> = {}): JobProgress => ({
|
|
|
21
27
|
stage: 'analyzing', percentage: 40, message: 'working on it', ...over,
|
|
22
28
|
});
|
|
23
29
|
|
|
30
|
+
/** Every required key, echoing its own name; override per test. */
|
|
31
|
+
const T = (over: Partial<AssistProgressTranslations> = {}): AssistProgressTranslations => ({
|
|
32
|
+
cancel: 'tr.cancel',
|
|
33
|
+
inProgress: 'tr.inProgress',
|
|
34
|
+
complete: 'tr.complete',
|
|
35
|
+
failed: 'tr.failed',
|
|
36
|
+
close: 'tr.close',
|
|
37
|
+
paramsTitle: 'tr.paramsTitle',
|
|
38
|
+
processing: (label: string) => `tr.processing(${label})`,
|
|
39
|
+
...over,
|
|
40
|
+
});
|
|
41
|
+
|
|
24
42
|
describe('AssistProgress', () => {
|
|
25
43
|
it('renders provider-free: message, params, and data hooks', () => {
|
|
26
44
|
const { container } = render(
|
|
27
45
|
<AssistProgress
|
|
28
46
|
progress={running({ requestParams: [{ label: 'Density', value: '5' }] })}
|
|
29
47
|
dataType="comment"
|
|
30
|
-
|
|
48
|
+
translations={T()}
|
|
49
|
+
/>,
|
|
31
50
|
);
|
|
32
51
|
expect(screen.getByText('working on it')).toBeInTheDocument();
|
|
33
52
|
expect(screen.getByText(/Density/)).toBeInTheDocument();
|
|
@@ -37,12 +56,24 @@ describe('AssistProgress', () => {
|
|
|
37
56
|
expect(root).toHaveAttribute('data-status', 'analyzing');
|
|
38
57
|
});
|
|
39
58
|
|
|
59
|
+
it('renders the request-parameters title from translations, never a literal', () => {
|
|
60
|
+
render(
|
|
61
|
+
<AssistProgress
|
|
62
|
+
progress={running({ requestParams: [{ label: 'Density', value: '5' }] })}
|
|
63
|
+
dataType="comment"
|
|
64
|
+
translations={T()}
|
|
65
|
+
/>,
|
|
66
|
+
);
|
|
67
|
+
expect(screen.getByText('tr.paramsTitle')).toBeInTheDocument();
|
|
68
|
+
expect(screen.queryByText('Request Parameters:')).not.toBeInTheDocument();
|
|
69
|
+
});
|
|
70
|
+
|
|
40
71
|
it('renders a title header only when given one', () => {
|
|
41
72
|
const { rerender } = render(
|
|
42
|
-
<AssistProgress progress={running()} dataType="reference" translations={{ title: 'Annotating Entity References' }} />,
|
|
73
|
+
<AssistProgress progress={running()} dataType="reference" translations={T({ title: 'Annotating Entity References' })} />,
|
|
43
74
|
);
|
|
44
75
|
expect(screen.getByText('Annotating Entity References')).toBeInTheDocument();
|
|
45
|
-
rerender(<AssistProgress progress={running()} dataType="comment" />);
|
|
76
|
+
rerender(<AssistProgress progress={running()} dataType="comment" translations={T()} />);
|
|
46
77
|
expect(screen.queryByText('Annotating Entity References')).not.toBeInTheDocument();
|
|
47
78
|
});
|
|
48
79
|
|
|
@@ -50,7 +81,7 @@ describe('AssistProgress', () => {
|
|
|
50
81
|
// Cancel lives in the title header — both flows that offer cancel
|
|
51
82
|
// (reference detection, generation) render the titled profile.
|
|
52
83
|
const onCancel = vi.fn();
|
|
53
|
-
const tr = { title: 'Generating Resource', cancel: 'Cancel Job' };
|
|
84
|
+
const tr = T({ title: 'Generating Resource', cancel: 'Cancel Job' });
|
|
54
85
|
const { rerender } = render(
|
|
55
86
|
<AssistProgress progress={running()} dataType="generation" onCancel={onCancel} translations={tr} />,
|
|
56
87
|
);
|
|
@@ -68,13 +99,20 @@ describe('AssistProgress', () => {
|
|
|
68
99
|
expect(screen.queryByTitle('Cancel Job')).not.toBeInTheDocument();
|
|
69
100
|
});
|
|
70
101
|
|
|
71
|
-
it('stage branching:
|
|
102
|
+
it('stage branching: both terminal stages prefer the job message, then the translated copy', () => {
|
|
103
|
+
// The job composes detail no static string can ("Created 14 highlights"),
|
|
104
|
+
// so it wins on BOTH terminal stages; the translated copy covers the
|
|
105
|
+
// required-but-empty message case (pinned below).
|
|
72
106
|
const { rerender } = render(
|
|
73
|
-
<AssistProgress progress={running({ stage: 'complete' })} dataType="reference" translations={{ complete: 'All done!' }} />,
|
|
107
|
+
<AssistProgress progress={running({ stage: 'complete' })} dataType="reference" translations={T({ complete: 'All done!' })} />,
|
|
108
|
+
);
|
|
109
|
+
expect(screen.getByText('working on it')).toBeInTheDocument();
|
|
110
|
+
rerender(
|
|
111
|
+
<AssistProgress progress={running({ stage: 'complete', message: '' })} dataType="reference" translations={T({ complete: 'All done!' })} />,
|
|
74
112
|
);
|
|
75
113
|
expect(screen.getByText('All done!')).toBeInTheDocument();
|
|
76
114
|
rerender(
|
|
77
|
-
<AssistProgress progress={running({ stage: 'error', message: 'it broke' })} dataType="reference" translations={{ failed: 'Failed' }} />,
|
|
115
|
+
<AssistProgress progress={running({ stage: 'error', message: 'it broke' })} dataType="reference" translations={T({ failed: 'Failed' })} />,
|
|
78
116
|
);
|
|
79
117
|
expect(screen.getByText('it broke')).toBeInTheDocument();
|
|
80
118
|
});
|
|
@@ -83,7 +121,7 @@ describe('AssistProgress', () => {
|
|
|
83
121
|
render(
|
|
84
122
|
<AssistProgress
|
|
85
123
|
progress={running({ completedEntityTypes: [{ entityType: 'Person', foundCount: 3 }] })}
|
|
86
|
-
dataType="reference" translations={{ found: (n) => `Found ${n}` }}
|
|
124
|
+
dataType="reference" translations={T({ found: (n) => `Found ${n}` })}
|
|
87
125
|
/>,
|
|
88
126
|
);
|
|
89
127
|
expect(screen.getByText('Person:')).toBeInTheDocument();
|
|
@@ -94,55 +132,75 @@ describe('AssistProgress', () => {
|
|
|
94
132
|
const { rerender } = render(
|
|
95
133
|
<AssistProgress
|
|
96
134
|
progress={running({ currentEntityType: 'Location' })}
|
|
97
|
-
dataType="reference" translations={{ current: (l) => `
|
|
135
|
+
dataType="reference" translations={T({ current: (l) => `Current: ${l}` })}
|
|
98
136
|
/>,
|
|
99
137
|
);
|
|
100
|
-
expect(screen.getByText('
|
|
138
|
+
expect(screen.getByText('Current: Location')).toBeInTheDocument();
|
|
101
139
|
rerender(
|
|
102
140
|
<AssistProgress
|
|
103
141
|
progress={running({ currentCategory: 'Rule', processedCategories: 2, totalCategories: 5 })}
|
|
104
|
-
dataType="tag"
|
|
142
|
+
dataType="tag" translations={T()}
|
|
143
|
+
/>,
|
|
105
144
|
);
|
|
106
145
|
expect(screen.getByText(/Rule/)).toBeInTheDocument();
|
|
107
146
|
expect(screen.getByText(/2\/5/)).toBeInTheDocument();
|
|
108
147
|
});
|
|
109
148
|
|
|
149
|
+
it('falls back to the generic processing formatter — never the English literal', () => {
|
|
150
|
+
// The reference flow passes its own `current` wording; every other flow
|
|
151
|
+
// (tag categories, and any flow without one) uses `processing`. Neither
|
|
152
|
+
// path may render a hardcoded "Processing: ".
|
|
153
|
+
const { rerender } = render(
|
|
154
|
+
<AssistProgress progress={running({ currentEntityType: 'Location' })} dataType="reference" translations={T()} />,
|
|
155
|
+
);
|
|
156
|
+
expect(screen.getByText('tr.processing(Location)')).toBeInTheDocument();
|
|
157
|
+
rerender(
|
|
158
|
+
<AssistProgress progress={running({ currentCategory: 'Rule' })} dataType="tag" translations={T()} />,
|
|
159
|
+
);
|
|
160
|
+
expect(screen.getByText(/tr\.processing\(Rule\)/)).toBeInTheDocument();
|
|
161
|
+
expect(screen.queryByText(/^Processing: /)).not.toBeInTheDocument();
|
|
162
|
+
});
|
|
163
|
+
|
|
110
164
|
it('renders the percentage bar only when opted in', () => {
|
|
111
165
|
const { container, rerender } = render(
|
|
112
|
-
<AssistProgress progress={running({ percentage: 40 })} dataType="tag" showPercentBar />,
|
|
166
|
+
<AssistProgress progress={running({ percentage: 40 })} dataType="tag" showPercentBar translations={T()} />,
|
|
113
167
|
);
|
|
114
168
|
const fill = container.querySelector('.semiont-progress-bar__fill');
|
|
115
169
|
expect(fill).toBeInTheDocument();
|
|
116
170
|
expect(fill).toHaveStyle({ width: '40%' });
|
|
117
171
|
rerender(
|
|
118
|
-
<AssistProgress progress={running({ percentage: 40 })} dataType="reference" />,
|
|
172
|
+
<AssistProgress progress={running({ percentage: 40 })} dataType="reference" translations={T()} />,
|
|
119
173
|
);
|
|
120
174
|
expect(container.querySelector('.semiont-progress-bar__fill')).not.toBeInTheDocument();
|
|
121
175
|
});
|
|
122
176
|
|
|
123
|
-
it('cancel and dismiss
|
|
124
|
-
// ✕ / × glyphs alone are meaningless to screen readers
|
|
125
|
-
//
|
|
177
|
+
it('cancel and dismiss take their accessible names from translations', () => {
|
|
178
|
+
// ✕ / × glyphs alone are meaningless to screen readers, and an English
|
|
179
|
+
// fallback is meaningless to a non-English reader. The labels are
|
|
180
|
+
// required keys, so there is nothing to fall back TO.
|
|
126
181
|
render(
|
|
127
182
|
<AssistProgress progress={running()} dataType="generation"
|
|
128
183
|
onCancel={vi.fn()} onDismiss={vi.fn()}
|
|
129
|
-
translations={{ title: 'Generating' }} />,
|
|
184
|
+
translations={T({ title: 'Generating' })} />,
|
|
130
185
|
);
|
|
131
|
-
expect(screen.getByLabelText('
|
|
132
|
-
expect(screen.getByLabelText('
|
|
186
|
+
expect(screen.getByLabelText('tr.cancel')).toBeInTheDocument();
|
|
187
|
+
expect(screen.getByLabelText('tr.close')).toBeInTheDocument();
|
|
188
|
+
expect(screen.queryByLabelText('Cancel')).not.toBeInTheDocument();
|
|
189
|
+
expect(screen.queryByLabelText('Dismiss')).not.toBeInTheDocument();
|
|
133
190
|
});
|
|
134
191
|
|
|
135
|
-
it('terminal stages never render a blank status line', () => {
|
|
192
|
+
it('terminal stages never render a blank status line, and never an English one', () => {
|
|
136
193
|
// JobProgress.message is required but can be '' — a terminal display with
|
|
137
|
-
// no text at all is worse than a generic word
|
|
194
|
+
// no text at all is worse than a generic word, but that word must be the
|
|
195
|
+
// caller's translated one.
|
|
138
196
|
const { rerender } = render(
|
|
139
|
-
<AssistProgress progress={running({ stage: 'complete', message: '' })} dataType="reference" />,
|
|
197
|
+
<AssistProgress progress={running({ stage: 'complete', message: '' })} dataType="reference" translations={T()} />,
|
|
140
198
|
);
|
|
141
|
-
expect(screen.getByText('
|
|
199
|
+
expect(screen.getByText('tr.complete')).toBeInTheDocument();
|
|
142
200
|
rerender(
|
|
143
|
-
<AssistProgress progress={running({ stage: 'error', message: '' })} dataType="reference" />,
|
|
201
|
+
<AssistProgress progress={running({ stage: 'error', message: '' })} dataType="reference" translations={T()} />,
|
|
144
202
|
);
|
|
145
|
-
expect(screen.getByText('
|
|
203
|
+
expect(screen.getByText('tr.failed')).toBeInTheDocument();
|
|
146
204
|
});
|
|
147
205
|
|
|
148
206
|
it('renders dismiss whenever the caller offers it (WHEN is the caller\'s policy)', async () => {
|
|
@@ -151,12 +209,12 @@ describe('AssistProgress', () => {
|
|
|
151
209
|
// "callback present → affordance rendered".
|
|
152
210
|
const onDismiss = vi.fn();
|
|
153
211
|
const { rerender } = render(
|
|
154
|
-
<AssistProgress progress={running()} dataType="highlight" translations={{ close: 'Close' }} />,
|
|
212
|
+
<AssistProgress progress={running()} dataType="highlight" translations={T({ close: 'Close' })} />,
|
|
155
213
|
);
|
|
156
214
|
expect(screen.queryByLabelText('Close')).not.toBeInTheDocument();
|
|
157
215
|
rerender(
|
|
158
216
|
<AssistProgress progress={running()} dataType="highlight"
|
|
159
|
-
onDismiss={onDismiss} translations={{ close: 'Close' }} />,
|
|
217
|
+
onDismiss={onDismiss} translations={T({ close: 'Close' })} />,
|
|
160
218
|
);
|
|
161
219
|
await userEvent.click(screen.getByLabelText('Close'));
|
|
162
220
|
expect(onDismiss).toHaveBeenCalledOnce();
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
* or a position-fallback. The hover tooltip (added by
|
|
46
46
|
* `getAnnotationDecorationMeta`) names the strategy. */
|
|
47
47
|
.annotation-low-confidence {
|
|
48
|
-
text-decoration: underline dotted var(--semiont-color-
|
|
48
|
+
text-decoration: underline dotted var(--semiont-color-amber-500);
|
|
49
49
|
text-decoration-thickness: 2px;
|
|
50
50
|
text-underline-offset: 2px;
|
|
51
51
|
}
|
|
@@ -211,11 +211,11 @@
|
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
.semiont-search-modal__result-icon--entity {
|
|
214
|
-
color: var(--semiont-color-
|
|
214
|
+
color: var(--semiont-color-green-500);
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
[data-theme="dark"] .semiont-search-modal__result-icon--entity {
|
|
218
|
-
color: var(--semiont-color-
|
|
218
|
+
color: var(--semiont-color-green-400);
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
/* Result content */
|
|
@@ -244,13 +244,13 @@
|
|
|
244
244
|
font-size: 0.75rem;
|
|
245
245
|
font-weight: 500;
|
|
246
246
|
border-radius: 9999px;
|
|
247
|
-
background-color: var(--semiont-color-
|
|
248
|
-
color: var(--semiont-color-
|
|
247
|
+
background-color: var(--semiont-color-green-100);
|
|
248
|
+
color: var(--semiont-color-green-700);
|
|
249
249
|
}
|
|
250
250
|
|
|
251
251
|
[data-theme="dark"] .semiont-search-modal__result-badge {
|
|
252
252
|
background-color: rgba(34, 197, 94, 0.3);
|
|
253
|
-
color: var(--semiont-color-
|
|
253
|
+
color: var(--semiont-color-green-300);
|
|
254
254
|
}
|
|
255
255
|
|
|
256
256
|
.semiont-search-modal__result-description {
|
|
@@ -20,14 +20,151 @@
|
|
|
20
20
|
|
|
21
21
|
/* stylelint-disable semiont/accessibility -- Error indicator added by semantic-indicators.css */
|
|
22
22
|
.semiont-pdf-annotation-canvas__error {
|
|
23
|
-
color: var(--
|
|
23
|
+
color: var(--semiont-color-error);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
[data-theme="dark"] .semiont-pdf-annotation-canvas__error {
|
|
27
|
-
color: var(--
|
|
27
|
+
color: var(--semiont-color-error);
|
|
28
28
|
}
|
|
29
29
|
/* stylelint-enable semiont/accessibility */
|
|
30
30
|
|
|
31
|
+
/*
|
|
32
|
+
* The scrolling column (PDF-CONTINUOUS-SCROLL S1). Every page owns a slot so
|
|
33
|
+
* the scrollbar reflects the whole document; only pages near the viewport are
|
|
34
|
+
* mounted, and an unmounted slot holds its space with an estimated height set
|
|
35
|
+
* inline. `max-height` is what makes this a scroll container at all — without
|
|
36
|
+
* it the column grows to its content and the page scrolls instead, which
|
|
37
|
+
* still works but defeats the windowing.
|
|
38
|
+
*/
|
|
39
|
+
/*
|
|
40
|
+
* Viewport = the pages plus their strip, side by side. The strip LEADS — it
|
|
41
|
+
* hugs the content's leading edge, where a page rail conventionally lives
|
|
42
|
+
* (Preview, Acrobat), so the reader's eye finds it before the page.
|
|
43
|
+
*/
|
|
44
|
+
.semiont-pdf-annotation-canvas__viewport {
|
|
45
|
+
display: flex;
|
|
46
|
+
gap: 0.5rem;
|
|
47
|
+
align-items: stretch;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.semiont-pdf-annotation-canvas__viewport[data-axis="horizontal"] {
|
|
51
|
+
flex-direction: column;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.semiont-pdf-annotation-canvas__column {
|
|
55
|
+
display: flex;
|
|
56
|
+
flex: 1 1 auto;
|
|
57
|
+
gap: 1rem;
|
|
58
|
+
align-items: center;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/*
|
|
62
|
+
* Both blocks below key off ONE attribute, so the strip can never end up
|
|
63
|
+
* running across the direction the document scrolls.
|
|
64
|
+
*/
|
|
65
|
+
/*
|
|
66
|
+
* The column does NOT scroll. The pane the viewer sits in already does
|
|
67
|
+
* (`flex: 1; min-height: 0`), and giving the column a viewport-relative
|
|
68
|
+
* height of its own produced a scroller inside a scroller — two bars along
|
|
69
|
+
* one axis, the inner one redundant with the page rail beside it.
|
|
70
|
+
*/
|
|
71
|
+
.semiont-pdf-annotation-canvas__column[data-axis="vertical"] {
|
|
72
|
+
flex-direction: column;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.semiont-pdf-annotation-canvas__column[data-axis="horizontal"] {
|
|
76
|
+
flex-direction: row;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.semiont-pdf-annotation-canvas__slot {
|
|
80
|
+
display: flex;
|
|
81
|
+
justify-content: center;
|
|
82
|
+
width: 100%;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/*
|
|
86
|
+
* The page strip: the document's shape in miniature. Rectangles only — no
|
|
87
|
+
* rasterization — so a 400-page scan costs 400 empty divs, not 400 renders.
|
|
88
|
+
*/
|
|
89
|
+
/*
|
|
90
|
+
* The strip scrolls — a 70-page document cannot fit its numbered rectangles in
|
|
91
|
+
* one screen — but it must not show a scrollbar. Two scrollbars side by side
|
|
92
|
+
* read as two controls for one thing, and the strip's own moves the strip
|
|
93
|
+
* rather than the document, so it looks broken. Position is carried by the
|
|
94
|
+
* highlighted page, and the strip follows the reader on its own.
|
|
95
|
+
*/
|
|
96
|
+
.semiont-pdf-annotation-canvas__strip {
|
|
97
|
+
display: flex;
|
|
98
|
+
flex: 0 0 auto;
|
|
99
|
+
gap: 2px;
|
|
100
|
+
padding: 0.5rem;
|
|
101
|
+
scrollbar-width: none; /* standard; Firefox + recent Chromium */
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* WebKit/Safari, which has no `scrollbar-width` support to rely on. */
|
|
105
|
+
.semiont-pdf-annotation-canvas__strip::-webkit-scrollbar {
|
|
106
|
+
display: none;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/*
|
|
110
|
+
* The rail hugs the leading edge of the content and stays put while the
|
|
111
|
+
* document scrolls past it — `sticky` rather than a fixed-height box, so it
|
|
112
|
+
* follows the reader without claiming a second scroll region in the layout.
|
|
113
|
+
*/
|
|
114
|
+
.semiont-pdf-annotation-canvas__strip[data-axis="vertical"] {
|
|
115
|
+
position: sticky;
|
|
116
|
+
top: 0;
|
|
117
|
+
flex-direction: column;
|
|
118
|
+
align-self: flex-start;
|
|
119
|
+
align-items: center;
|
|
120
|
+
max-height: 100vh;
|
|
121
|
+
overflow-y: auto;
|
|
122
|
+
border-right: 1px solid var(--semiont-border-secondary);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.semiont-pdf-annotation-canvas__strip[data-axis="horizontal"] {
|
|
126
|
+
position: sticky;
|
|
127
|
+
top: 0;
|
|
128
|
+
flex-direction: row;
|
|
129
|
+
align-items: flex-end;
|
|
130
|
+
max-width: 100%;
|
|
131
|
+
overflow-x: auto;
|
|
132
|
+
border-bottom: 1px solid var(--semiont-border-secondary);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.semiont-pdf-annotation-canvas__strip-page {
|
|
136
|
+
display: flex;
|
|
137
|
+
flex: 0 0 auto;
|
|
138
|
+
align-items: center;
|
|
139
|
+
justify-content: center;
|
|
140
|
+
width: 30px;
|
|
141
|
+
padding: 0;
|
|
142
|
+
border: 1px solid var(--semiont-border-primary);
|
|
143
|
+
border-radius: 1px;
|
|
144
|
+
background: var(--semiont-bg-secondary);
|
|
145
|
+
color: var(--semiont-text-tertiary);
|
|
146
|
+
font-size: var(--semiont-text-2xs);
|
|
147
|
+
line-height: 1;
|
|
148
|
+
cursor: pointer;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.semiont-pdf-annotation-canvas__strip-page:hover {
|
|
152
|
+
background: var(--semiont-bg-hover);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/* The reader's position, legible at a glance across a long strip. */
|
|
156
|
+
.semiont-pdf-annotation-canvas__strip-page[aria-current="page"] {
|
|
157
|
+
border-color: var(--semiont-color-primary-600);
|
|
158
|
+
background: var(--semiont-color-primary-500);
|
|
159
|
+
color: var(--semiont-color-neutral-0);
|
|
160
|
+
font-weight: var(--semiont-font-semibold);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.semiont-pdf-annotation-canvas__strip-page:focus-visible {
|
|
164
|
+
outline: 2px solid var(--semiont-focus-ring);
|
|
165
|
+
outline-offset: 1px;
|
|
166
|
+
}
|
|
167
|
+
|
|
31
168
|
.semiont-pdf-annotation-canvas__container {
|
|
32
169
|
position: relative;
|
|
33
170
|
display: inline-block;
|
|
@@ -83,12 +220,12 @@
|
|
|
83
220
|
justify-content: center;
|
|
84
221
|
gap: 1rem;
|
|
85
222
|
padding: 1rem;
|
|
86
|
-
border-top: 1px solid var(--border-
|
|
223
|
+
border-top: 1px solid var(--semiont-border-primary);
|
|
87
224
|
}
|
|
88
225
|
|
|
89
226
|
.semiont-pdf-annotation-canvas__button {
|
|
90
227
|
padding: 0.5rem 1rem;
|
|
91
|
-
border: 1px solid var(--border-
|
|
228
|
+
border: 1px solid var(--semiont-border-primary);
|
|
92
229
|
border-radius: 4px;
|
|
93
230
|
background: transparent;
|
|
94
231
|
cursor: pointer;
|
|
@@ -101,11 +238,11 @@
|
|
|
101
238
|
}
|
|
102
239
|
|
|
103
240
|
.semiont-pdf-annotation-canvas__button:hover:not(:disabled) {
|
|
104
|
-
background-color: var(--hover
|
|
241
|
+
background-color: var(--semiont-bg-hover);
|
|
105
242
|
}
|
|
106
243
|
|
|
107
244
|
.semiont-pdf-annotation-canvas__button:focus-visible {
|
|
108
|
-
outline: 2px solid var(--focus-
|
|
245
|
+
outline: 2px solid var(--semiont-focus-ring);
|
|
109
246
|
outline-offset: 2px;
|
|
110
247
|
}
|
|
111
248
|
|