@semiont/react-ui 0.5.26 → 0.5.28

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.
Files changed (242) hide show
  1. package/README.md +28 -4
  2. package/dist/{PdfAnnotationCanvas.client-6ZSF6ZTJ.js → PdfAnnotationCanvas.client-C2IGG4SW.js} +33 -6
  3. package/dist/{PdfAnnotationCanvas.client-JCJMXWWJ.css → PdfAnnotationCanvas.client-TGDPPGXJ.css} +6 -1
  4. package/dist/{ar-YTUUGK2O.js → ar-FABPTJVB.js} +88 -26
  5. package/dist/{bn-ASGNGPMD.js → bn-LKP6BDMH.js} +88 -26
  6. package/dist/{chunk-L3WKRWP3.js → chunk-2GOHTTHW.js} +88 -26
  7. package/dist/{chunk-OBQOZCJE.js → chunk-2YHEE63S.js} +31 -31
  8. package/dist/{cs-WMYVAOZY.js → cs-YQYNWHRG.js} +88 -26
  9. package/dist/{da-FSHHN4D6.js → da-QHZOYKRY.js} +88 -26
  10. package/dist/{de-FJFO3TPT.js → de-GKA7RD7J.js} +88 -26
  11. package/dist/{el-XOH4QIAG.js → el-45T257AL.js} +88 -26
  12. package/dist/{en-3FCBW3YD.js → en-RVW5EQ66.js} +4 -2
  13. package/dist/{es-HDX4QI7E.js → es-AVER7RU4.js} +88 -26
  14. package/dist/{fa-DJ6WTFTJ.js → fa-K3VPK3O2.js} +88 -26
  15. package/dist/{fi-4S7H7W7U.js → fi-5NEKF7EB.js} +88 -26
  16. package/dist/{fr-6UJSYHXC.js → fr-FUMNDLBZ.js} +88 -26
  17. package/dist/{he-ZZJOHJX2.js → he-3JGNA3PR.js} +88 -26
  18. package/dist/{hi-HZJTAW7U.js → hi-IJY375DF.js} +88 -26
  19. package/dist/{id-E3I6ILEM.js → id-LWHKJ7ZD.js} +88 -26
  20. package/dist/index.css +380 -524
  21. package/dist/index.d.ts +318 -192
  22. package/dist/index.js +2960 -2421
  23. package/dist/integrations/css-modules-helper.js +1 -3
  24. package/dist/{it-ULAPAD7U.js → it-EBVOV4RW.js} +88 -26
  25. package/dist/{ja-SAHZQ4CZ.js → ja-KWRIX2NF.js} +88 -26
  26. package/dist/{ko-DFU2ADQ4.js → ko-L5CJMFDS.js} +88 -26
  27. package/dist/{ms-NWL76XNT.js → ms-LSNMJDR2.js} +88 -26
  28. package/dist/{nl-ATCM76WN.js → nl-ZB32MB7U.js} +88 -26
  29. package/dist/{no-UIAQ5RJF.js → no-4GTNY7M5.js} +88 -26
  30. package/dist/{pl-NOECQDXD.js → pl-LUHBLTHB.js} +88 -26
  31. package/dist/{pt-SVWIIUPC.js → pt-C4H3UNQ6.js} +88 -26
  32. package/dist/{ro-NR2DRTZG.js → ro-GGITS5V4.js} +88 -26
  33. package/dist/{sv-STVJ37JP.js → sv-T56RTLBP.js} +88 -26
  34. package/dist/test-utils.css +1 -1
  35. package/dist/test-utils.d.ts +1 -1
  36. package/dist/test-utils.js +2 -2
  37. package/dist/{th-UWQR4K54.js → th-22X2IWCB.js} +88 -26
  38. package/dist/{tr-WNXZSD27.js → tr-77TFG62H.js} +88 -26
  39. package/dist/{uk-SJRMB47Y.js → uk-MIIL6NXT.js} +88 -26
  40. package/dist/{vi-TMRQFES2.js → vi-7JYOIGNV.js} +88 -26
  41. package/dist/{zh-QOL3LYEM.js → zh-ID6DRYU4.js} +88 -26
  42. package/package.json +11 -10
  43. package/src/components/AssistProgress.tsx +178 -134
  44. package/src/components/CodeMirrorRenderer.tsx +8 -8
  45. package/src/components/{EntityFoundLog.tsx → ItemFoundLog.tsx} +12 -7
  46. package/src/components/ProtectedErrorBoundary.css +3 -1
  47. package/src/components/StatusDisplay.css +1 -1
  48. package/src/components/Toast.css +3 -1
  49. package/src/components/__tests__/AssistProgress.test.tsx +268 -141
  50. package/src/components/annotation/annotation-entries.css +0 -4
  51. package/src/components/annotation/references.css +0 -4
  52. package/src/components/image-annotation/AnnotationOverlay.tsx +6 -6
  53. package/src/components/image-annotation/SvgDrawingCanvas.tsx +1 -1
  54. package/src/components/image-annotation/__tests__/AnnotationOverlay.click.test.tsx +7 -9
  55. package/src/components/image-annotation/__tests__/SvgDrawingCanvas.click.test.tsx +1 -2
  56. package/src/components/layout/Header.css +0 -4
  57. package/src/components/layout/LeftSidebar.css +0 -4
  58. package/src/components/modals/ComposeStep.tsx +218 -0
  59. package/src/components/modals/ConfigureGatherStep.tsx +10 -18
  60. package/src/components/modals/ConfigureGenerationStep.tsx +131 -54
  61. package/src/components/modals/ConfigureSearchStep.tsx +42 -40
  62. package/src/components/modals/ContextSummary.tsx +185 -72
  63. package/src/components/modals/CorpusPane.tsx +75 -0
  64. package/src/components/modals/DiscardPrompt.tsx +38 -0
  65. package/src/components/modals/GatherContextStep.tsx +237 -107
  66. package/src/components/modals/ReferenceWizardModal.tsx +287 -60
  67. package/src/components/modals/ResourceGenerateModal.tsx +233 -109
  68. package/src/components/modals/SearchModal.css +335 -111
  69. package/src/components/modals/SearchModal.tsx +1 -1
  70. package/src/components/modals/SearchResultsStep.tsx +63 -88
  71. package/src/components/modals/WizardFooter.tsx +69 -0
  72. package/src/components/modals/__tests__/ComposeStep.test.tsx +152 -0
  73. package/src/components/modals/__tests__/ConfigureGatherStep.test.tsx +14 -26
  74. package/src/components/modals/__tests__/ConfigureGenerationStep.test.tsx +290 -0
  75. package/src/components/modals/__tests__/ConfigureSearchStep.test.tsx +95 -0
  76. package/src/components/modals/__tests__/GatherContextStep.resource.test.tsx +463 -25
  77. package/src/components/modals/__tests__/ReferenceWizardModal.test.tsx +635 -0
  78. package/src/components/modals/__tests__/ResourceGenerateModal.test.tsx +277 -85
  79. package/src/components/modals/__tests__/SearchModal.search-wiring.test.tsx +6 -2
  80. package/src/components/modals/__tests__/SearchResultsStep.test.tsx +61 -0
  81. package/src/components/modals/__tests__/WizardFooter.test.tsx +344 -0
  82. package/src/components/modals/modals.css +16 -42
  83. package/src/components/navigation/NavigationTabs.css +0 -20
  84. package/src/components/pdf-annotation/PdfAnnotationCanvas.css +21 -2
  85. package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +36 -3
  86. package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +41 -6
  87. package/src/components/resource/AnnotateView.tsx +19 -8
  88. package/src/components/resource/AnnotationHistory.tsx +2 -0
  89. package/src/components/resource/BrowseView.tsx +20 -9
  90. package/src/components/resource/ResourceViewer.tsx +27 -28
  91. package/src/components/resource/__tests__/AnnotateView.embeddable.test.tsx +1 -1
  92. package/src/components/resource/__tests__/AnnotateView.focus.test.tsx +50 -0
  93. package/src/components/resource/__tests__/AnnotationHistory.test.tsx +2 -0
  94. package/src/components/resource/__tests__/BrowseView.embeddable.test.tsx +1 -1
  95. package/src/components/resource/__tests__/BrowseView.test.tsx +38 -12
  96. package/src/components/resource/__tests__/ResourceViewer.embeddable.test.tsx +47 -3
  97. package/src/components/resource/annotate-renderers.tsx +3 -3
  98. package/src/components/resource/event-formatting.ts +0 -3
  99. package/src/components/resource/panels/AssessmentEntry.tsx +1 -1
  100. package/src/components/resource/panels/AssessmentPanel.css +5 -6
  101. package/src/components/resource/panels/AssistSection.css +95 -263
  102. package/src/components/resource/panels/AssistSection.tsx +4 -4
  103. package/src/components/resource/panels/AssistShell.tsx +12 -4
  104. package/src/components/resource/panels/CollaborationPanel.css +47 -22
  105. package/src/components/resource/panels/CollaborationPanel.tsx +93 -2
  106. package/src/components/resource/panels/CommentEntry.tsx +1 -1
  107. package/src/components/resource/panels/CommentsPanel.css +0 -16
  108. package/src/components/resource/panels/HighlightEntry.tsx +1 -1
  109. package/src/components/resource/panels/HighlightPanel.css +0 -7
  110. package/src/components/resource/panels/JsonLdPanel.css +0 -4
  111. package/src/components/resource/panels/ReferenceEntry.tsx +10 -3
  112. package/src/components/resource/panels/ReferencesPanel.css +0 -12
  113. package/src/components/resource/panels/ReferencesPanel.tsx +16 -20
  114. package/src/components/resource/panels/ResourceInfoPanel.css +0 -32
  115. package/src/components/resource/panels/ResourceInfoPanel.tsx +89 -20
  116. package/src/components/resource/panels/StatisticsPanel.css +0 -20
  117. package/src/components/resource/panels/TagEntry.tsx +1 -1
  118. package/src/components/resource/panels/TaggingPanel.css +0 -4
  119. package/src/components/resource/panels/TaggingPanel.tsx +2 -10
  120. package/src/components/resource/panels/UnifiedAnnotationsPanel.css +0 -4
  121. package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +7 -2
  122. package/src/components/resource/panels/__tests__/AssessmentEntry.test.tsx +0 -1
  123. package/src/components/resource/panels/__tests__/AssistSection.test.tsx +48 -50
  124. package/src/components/resource/panels/__tests__/AssistShell.test.tsx +11 -6
  125. package/src/components/resource/panels/__tests__/CollaborationPanel.test.tsx +89 -0
  126. package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +1 -2
  127. package/src/components/resource/panels/__tests__/HighlightEntry.test.tsx +0 -1
  128. package/src/components/resource/panels/__tests__/HighlightPanel.annotationProgress.test.tsx +44 -36
  129. package/src/components/resource/panels/__tests__/ReferenceEntry.test.tsx +41 -1
  130. package/src/components/resource/panels/__tests__/ReferencesPanel.headless.test.tsx +1 -1
  131. package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +67 -54
  132. package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +185 -7
  133. package/src/components/resource/panels/__tests__/TagEntry.test.tsx +0 -1
  134. package/src/components/settings/SettingsPanel.css +0 -4
  135. package/src/features/auth/auth.css +2 -26
  136. package/src/features/resource-compose/__tests__/ResourceComposePage.test.tsx +0 -65
  137. package/src/features/resource-compose/components/ResourceComposePage.tsx +17 -130
  138. package/src/features/resource-compose/state/__tests__/compose-page-state-unit.test.ts +1 -76
  139. package/src/features/resource-compose/state/compose-page-state-unit.ts +5 -48
  140. package/src/features/resource-discovery/__tests__/ResourceDiscoveryPage.test.tsx +55 -0
  141. package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +29 -0
  142. package/src/features/resource-discovery/state/__tests__/discover-state-unit.test.ts +53 -3
  143. package/src/features/resource-discovery/state/discover-state-unit.ts +29 -8
  144. package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +71 -15
  145. package/src/features/resource-viewer/components/ResourceViewerPage.tsx +166 -71
  146. package/src/features/resource-viewer/hooks/__tests__/useResourceViewedReport.test.tsx +63 -0
  147. package/src/features/resource-viewer/hooks/useResourceViewedReport.ts +33 -0
  148. package/src/features/resource-viewer/state/__tests__/resource-viewer-page-state-unit.test.ts +2 -2
  149. package/src/features/resource-viewer/state/resource-viewer-page-state-unit.ts +1 -1
  150. package/src/styles/core/buttons.css +3 -1
  151. package/src/styles/core/forms.css +35 -29
  152. package/src/styles/core/indicators.css +0 -4
  153. package/src/styles/core/inputs.css +0 -4
  154. package/src/styles/core/progress.css +2 -34
  155. package/src/styles/core/sliders.css +0 -4
  156. package/src/styles/core/toggles.css +0 -4
  157. package/src/styles/features/resource-discovery.css +13 -0
  158. package/src/styles/features/resource-viewer.css +0 -42
  159. package/src/styles/features/resource.css +0 -4
  160. package/src/styles/motivations/motivation-assessment.css +2 -2
  161. package/src/styles/panels/history-panel.css +0 -40
  162. package/src/styles/panels/user-panel.css +0 -3
  163. package/src/styles/patterns/panel-helpers.css +0 -27
  164. package/src/styles/patterns/panel-sections.css +0 -59
  165. package/src/styles/patterns/panels-base.css +0 -32
  166. package/translations/ar.json +84 -24
  167. package/translations/bn.json +84 -24
  168. package/translations/cs.json +84 -24
  169. package/translations/da.json +84 -24
  170. package/translations/de.json +84 -24
  171. package/translations/el.json +84 -24
  172. package/translations/en.json +84 -24
  173. package/translations/es.json +84 -24
  174. package/translations/fa.json +84 -24
  175. package/translations/fi.json +84 -24
  176. package/translations/fr.json +84 -24
  177. package/translations/he.json +84 -24
  178. package/translations/hi.json +84 -24
  179. package/translations/id.json +84 -24
  180. package/translations/it.json +84 -24
  181. package/translations/ja.json +84 -24
  182. package/translations/ko.json +84 -24
  183. package/translations/ms.json +84 -24
  184. package/translations/nl.json +84 -24
  185. package/translations/no.json +84 -24
  186. package/translations/pl.json +84 -24
  187. package/translations/pt.json +84 -24
  188. package/translations/ro.json +84 -24
  189. package/translations/sv.json +84 -24
  190. package/translations/th.json +84 -24
  191. package/translations/tr.json +84 -24
  192. package/translations/uk.json +84 -24
  193. package/translations/vi.json +84 -24
  194. package/translations/zh.json +84 -24
  195. package/dist/PdfAnnotationCanvas.client-6ZSF6ZTJ.js.map +0 -1
  196. package/dist/PdfAnnotationCanvas.client-JCJMXWWJ.css.map +0 -1
  197. package/dist/ar-YTUUGK2O.js.map +0 -1
  198. package/dist/bn-ASGNGPMD.js.map +0 -1
  199. package/dist/chunk-BTUG3VJJ.js.map +0 -1
  200. package/dist/chunk-L3WKRWP3.js.map +0 -1
  201. package/dist/chunk-O23TLLXW.js.map +0 -1
  202. package/dist/chunk-OBQOZCJE.js.map +0 -1
  203. package/dist/chunk-QT7LYM72.js.map +0 -1
  204. package/dist/chunk-VXASXU4K.js.map +0 -1
  205. package/dist/chunk-XUDKYAVC.js +0 -21
  206. package/dist/chunk-XUDKYAVC.js.map +0 -1
  207. package/dist/cs-WMYVAOZY.js.map +0 -1
  208. package/dist/da-FSHHN4D6.js.map +0 -1
  209. package/dist/de-FJFO3TPT.js.map +0 -1
  210. package/dist/el-XOH4QIAG.js.map +0 -1
  211. package/dist/en-3FCBW3YD.js.map +0 -1
  212. package/dist/es-HDX4QI7E.js.map +0 -1
  213. package/dist/fa-DJ6WTFTJ.js.map +0 -1
  214. package/dist/fi-4S7H7W7U.js.map +0 -1
  215. package/dist/fr-6UJSYHXC.js.map +0 -1
  216. package/dist/he-ZZJOHJX2.js.map +0 -1
  217. package/dist/hi-HZJTAW7U.js.map +0 -1
  218. package/dist/id-E3I6ILEM.js.map +0 -1
  219. package/dist/index.css.map +0 -1
  220. package/dist/index.js.map +0 -1
  221. package/dist/integrations/css-modules-helper.js.map +0 -1
  222. package/dist/integrations/styled-components-theme.js.map +0 -1
  223. package/dist/it-ULAPAD7U.js.map +0 -1
  224. package/dist/ja-SAHZQ4CZ.js.map +0 -1
  225. package/dist/ko-DFU2ADQ4.js.map +0 -1
  226. package/dist/ms-NWL76XNT.js.map +0 -1
  227. package/dist/nl-ATCM76WN.js.map +0 -1
  228. package/dist/no-UIAQ5RJF.js.map +0 -1
  229. package/dist/pl-NOECQDXD.js.map +0 -1
  230. package/dist/pt-SVWIIUPC.js.map +0 -1
  231. package/dist/ro-NR2DRTZG.js.map +0 -1
  232. package/dist/sv-STVJ37JP.js.map +0 -1
  233. package/dist/test-utils.css.map +0 -1
  234. package/dist/test-utils.js.map +0 -1
  235. package/dist/th-UWQR4K54.js.map +0 -1
  236. package/dist/tr-WNXZSD27.js.map +0 -1
  237. package/dist/uk-SJRMB47Y.js.map +0 -1
  238. package/dist/vi-TMRQFES2.js.map +0 -1
  239. package/dist/zh-QOL3LYEM.js.map +0 -1
  240. package/src/components/modals/ResourceSearchModal.tsx +0 -224
  241. package/src/components/modals/__tests__/ResourceSearchModal.test.tsx +0 -159
  242. package/src/integrations/tailwind-plugin.cjs +0 -224
@@ -57,10 +57,6 @@
57
57
  color: var(--semiont-text-secondary);
58
58
  }
59
59
 
60
- [data-theme="dark"] .semiont-statistics-panel__label {
61
- color: var(--semiont-text-secondary);
62
- }
63
-
64
60
  .semiont-statistics-panel__value {
65
61
  font-size: var(--semiont-text-lg);
66
62
  font-weight: var(--semiont-font-semibold);
@@ -68,10 +64,6 @@
68
64
  margin-left: auto;
69
65
  }
70
66
 
71
- [data-theme="dark"] .semiont-statistics-panel__value {
72
- color: var(--semiont-text-primary);
73
- }
74
-
75
67
  /* Sub-items (indented) */
76
68
  .semiont-statistics-panel__subitems {
77
69
  width: 100%;
@@ -93,20 +85,12 @@
93
85
  color: var(--semiont-text-tertiary);
94
86
  }
95
87
 
96
- [data-theme="dark"] .semiont-statistics-panel__sublabel {
97
- color: var(--semiont-text-tertiary);
98
- }
99
-
100
88
  .semiont-statistics-panel__subvalue {
101
89
  font-size: var(--semiont-text-sm);
102
90
  font-weight: var(--semiont-font-medium);
103
91
  color: var(--semiont-text-secondary);
104
92
  }
105
93
 
106
- [data-theme="dark"] .semiont-statistics-panel__subvalue {
107
- color: var(--semiont-text-secondary);
108
- }
109
-
110
94
  /* Entity Types */
111
95
  .semiont-statistics-panel__entity-types {
112
96
  display: flex;
@@ -140,8 +124,4 @@
140
124
  flex: 1;
141
125
  }
142
126
 
143
- [data-theme="dark"] .semiont-statistics-panel__entity-name {
144
- color: var(--semiont-text-primary);
145
- }
146
-
147
127
  /* Tags use .semiont-tag from core/tags.css */
@@ -52,7 +52,7 @@ export function TagEntry({
52
52
  <div
53
53
  ref={ref}
54
54
  onClick={() => {
55
- session?.client.browse.click(tag.id, tag.motivation);
55
+ session?.client.browse.click(tag.id);
56
56
  }}
57
57
  {...hoverProps}
58
58
  className={`semiont-annotation-entry${isHovered ? ' semiont-annotation-pulse' : ''}`}
@@ -30,10 +30,6 @@
30
30
  letter-spacing: 0.05em;
31
31
  }
32
32
 
33
- [data-theme="dark"] .semiont-tagging-panel__label {
34
- color: var(--semiont-text-secondary);
35
- }
36
-
37
33
  .semiont-tagging-panel__input {
38
34
  padding: var(--semiont-panel-padding-sm);
39
35
  background: var(--semiont-bg-secondary);
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
4
+ import { assistProgressTranslations } from '../../../lib/assist-progress-copy';
4
5
  import { isReady } from '@semiont/sdk';
5
6
  import { useTranslations } from '../../../contexts/TranslationContext';
6
7
  import { useObservable } from '../../../hooks/useObservable';
@@ -364,17 +365,8 @@ export function TaggingPanel({
364
365
  isAssisting={isAssisting}
365
366
  progress={progress}
366
367
  progressProps={{
367
- showPercentBar: true,
368
368
  onDismiss: handleDismissProgress,
369
- translations: {
370
- cancel: t('cancel'),
371
- inProgress: t('annotating'),
372
- complete: t('complete'),
373
- failed: t('failed'),
374
- paramsTitle: ta('paramsTitle'),
375
- processing: (label) => ta('processing', { label }),
376
- close: ta('close'),
377
- },
369
+ translations: assistProgressTranslations(ta),
378
370
  }}
379
371
  form={
380
372
  <>
@@ -18,10 +18,6 @@
18
18
  color: var(--semiont-text-primary);
19
19
  }
20
20
 
21
- [data-theme="dark"] .semiont-unified-panel__title {
22
- color: var(--semiont-text-primary);
23
- }
24
-
25
21
  .semiont-unified-panel__tabs {
26
22
  display: flex;
27
23
  gap: 0;
@@ -6,7 +6,7 @@ import type { components, Selector } from '@semiont/core';
6
6
  type JobProgress = components['schemas']['JobProgress'];
7
7
  import type { RouteBuilder, LinkComponentProps } from '../../../contexts/RoutingContext';
8
8
  import type { SemiontSession } from '@semiont/sdk';
9
- import { annotatorKeyForMotivation, type Annotator } from '../../../lib/annotation-registry';
9
+ import { annotatorKeyForMotivation, type Annotator, type AnnotatorKey } from '../../../lib/annotation-registry';
10
10
  import { StatisticsPanel } from './StatisticsPanel';
11
11
  import { HighlightPanel } from './HighlightPanel';
12
12
  import { ReferencesPanel } from './ReferencesPanel';
@@ -17,7 +17,9 @@ import './UnifiedAnnotationsPanel.css';
17
17
 
18
18
  import type { Annotation } from '@semiont/core';
19
19
  type Motivation = components['schemas']['Motivation'];
20
- type TabKey = 'statistics' | 'reference' | 'highlight' | 'assessment' | 'comment' | 'tag';
20
+ // Derived from the annotator registry (the single motivation↔annotator
21
+ // source) — a hand-written union here could drift from it.
22
+ type TabKey = 'statistics' | AnnotatorKey;
21
23
 
22
24
  // Unified pending annotation type
23
25
  interface PendingAnnotation {
@@ -76,6 +78,8 @@ interface UnifiedAnnotationsPanelProps {
76
78
  entityTypesError?: Error | null;
77
79
  referencedByError?: Error | null;
78
80
  onRetryReferencedBy?: () => void;
81
+ /** Annotations currently sparkling (recently created or resolved) — RESOLUTION-SPARKLE D6. */
82
+ sparkleAnnotationIds?: Set<string>;
79
83
 
80
84
  // Resource context — threaded to every per-motivation panel, which stamps it
81
85
  // as `source` on mark:submit (multi-viewer routing).
@@ -328,6 +332,7 @@ export function UnifiedAnnotationsPanel(props: UnifiedAnnotationsPanelProps) {
328
332
  referencedByLoading={props.referencedByLoading}
329
333
  referencedByError={props.referencedByError}
330
334
  onRetryReferencedBy={props.onRetryReferencedBy}
335
+ sparkleAnnotationIds={props.sparkleAnnotationIds}
331
336
  Link={props.Link}
332
337
  routes={props.routes}
333
338
  />
@@ -185,7 +185,6 @@ describe('AssessmentEntry', () => {
185
185
 
186
186
  expect(clickHandler).toHaveBeenCalledWith({
187
187
  annotationId: 'assessment-1',
188
- motivation: 'assessing',
189
188
  });
190
189
 
191
190
  subscription.unsubscribe();
@@ -21,7 +21,7 @@ import type { SemiontSession } from '@semiont/sdk';
21
21
  import { AssistSection } from '../AssistSection';
22
22
 
23
23
  // Mock translations
24
- const mockT = vi.fn((key: string) => {
24
+ const mockT = vi.fn((key: string, params?: Record<string, unknown>) => {
25
25
  const translations: Record<string, string> = {
26
26
  annotateHighlights: 'Annotate Highlights',
27
27
  annotateAssessments: 'Annotate Assessments',
@@ -45,7 +45,29 @@ const mockT = vi.fn((key: string) => {
45
45
  annotate: 'Annotate',
46
46
  annotating: 'Annotating...',
47
47
  };
48
- return translations[key] || key;
48
+ // P3: the coded status line. Interpolates `{{var}}` like production —
49
+ // a mock that ignored params would let copy that cannot interpolate in the
50
+ // app still pass here.
51
+ Object.assign(translations, {
52
+ codeLoading: 'Loading…',
53
+ codeAnalyzing: 'Marking…',
54
+ codeDetectingEntities: 'Marking…',
55
+ codeCompleteCreated: 'Created {{count}} {{kind}}',
56
+ kindHighlight: 'highlights',
57
+ subject: '{{kind}}: {{label}}',
58
+ subjectWithPosition: '{{kind}}: {{label}} ({{done}} of {{total}})',
59
+ subjectKindEntityType: 'Entity type',
60
+ subjectKindCategory: 'Category',
61
+ // CLEAN-PROGRESS D3: the widget's own strings now come from the
62
+ // AssistProgress namespace, not from each panel's.
63
+ cancel: 'Cancel',
64
+ inProgress: 'Annotating...',
65
+ });
66
+ let out = translations[key] || key;
67
+ for (const [k, v] of Object.entries((params ?? {}) as Record<string, unknown>)) {
68
+ out = out.replace(`{{${k}}}`, String(v));
69
+ }
70
+ return out;
49
71
  });
50
72
 
51
73
  vi.mock('../../../../contexts/TranslationContext', () => ({
@@ -71,41 +93,37 @@ describe('AssistSection', () => {
71
93
  });
72
94
 
73
95
  describe('Progress Display', () => {
74
- it('should render progress message when progress prop provided', () => {
96
+ it('should render the translated status line when progress prop provided', () => {
75
97
  renderWithProviders(
76
98
  <AssistSection
77
99
  session={session}
78
100
  annotationType="highlight"
79
101
  isAssisting={true}
80
102
  progress={{
81
- stage: 'analyzing',
82
103
  percentage: 30,
83
- message: 'Analyzing text for highlights...',
84
104
  }}
85
105
  />
86
106
  );
87
107
 
88
- expect(screen.getByText('Analyzing text for highlights...')).toBeInTheDocument();
108
+ expect(screen.getByText('Annotating...')).toBeInTheDocument();
89
109
  });
90
110
 
91
- it('should render progress message with sparkle icon', () => {
111
+ it('should render the status line with sparkle icon', () => {
92
112
  renderWithProviders(
93
113
  <AssistSection
94
114
  session={session}
95
115
  annotationType="highlight"
96
116
  isAssisting={true}
97
117
  progress={{
98
- stage: 'analyzing',
99
118
  percentage: 0,
100
- message: 'Processing...',
101
119
  }}
102
120
  />
103
121
  );
104
122
 
105
- // Check for icon and message
106
- const progressDiv = screen.getByText('Processing...').closest('.semiont-annotation-progress__message');
123
+ // Check for icon and status text
124
+ const progressDiv = screen.getByText('Annotating...').closest('.semiont-assist-progress__status');
107
125
  expect(progressDiv).toBeInTheDocument();
108
- expect(progressDiv?.querySelector('.semiont-annotation-progress__icon')).toBeInTheDocument();
126
+ expect(progressDiv?.querySelector('.semiont-assist-progress__icon')).toBeInTheDocument();
109
127
  });
110
128
 
111
129
  it('should render request parameters when provided', () => {
@@ -115,22 +133,25 @@ describe('AssistSection', () => {
115
133
  annotationType="highlight"
116
134
  isAssisting={true}
117
135
  progress={{
118
- stage: 'analyzing',
119
136
  percentage: 0,
120
- message: 'Analyzing...',
121
137
  requestParams: [
122
- { label: 'Instructions', value: 'Find important points' },
123
- { label: 'Density', value: '5' },
138
+ { label: 'instructions', value: 'Find important points' },
139
+ { label: 'density', value: '5' },
124
140
  ],
125
141
  }}
126
142
  />
127
143
  );
128
144
 
129
- expect(screen.getByText('paramsTitle')).toBeInTheDocument();
145
+ expect(screen.getByTestId('semiont-assist-params')).toBeInTheDocument();
130
146
  expect(screen.getByText('Find important points')).toBeInTheDocument();
131
- expect(screen.getByText(/Instructions:/)).toBeInTheDocument();
132
147
  expect(screen.getByText('5')).toBeInTheDocument();
133
- expect(screen.getByText(/Density:/)).toBeInTheDocument();
148
+ // The wire carries a CODE; the label the user reads is LOOKED UP. Under
149
+ // the test translator that lookup echoes the key, so seeing the key is
150
+ // the assertion that the label went through translation — and that the
151
+ // raw wire code ('instructions') never reaches the screen.
152
+ expect(screen.getByText(/paramInstructions:/)).toBeInTheDocument();
153
+ expect(screen.getByText(/paramDensity:/)).toBeInTheDocument();
154
+ expect(screen.queryByText(/^instructions:/)).toBeNull();
134
155
  });
135
156
 
136
157
  it('should hide form when progress is present', () => {
@@ -140,9 +161,7 @@ describe('AssistSection', () => {
140
161
  annotationType="highlight"
141
162
  isAssisting={true}
142
163
  progress={{
143
- stage: 'analyzing',
144
164
  percentage: 0,
145
- message: 'Analyzing...',
146
165
  }}
147
166
  />
148
167
  );
@@ -189,15 +208,14 @@ describe('AssistSection', () => {
189
208
  annotationType="highlight"
190
209
  isAssisting={false}
191
210
  progress={{
192
- stage: 'complete',
211
+ message: { code: 'complete-created', count: 3, kind: 'highlight' },
193
212
  percentage: 100,
194
- message: 'Complete! Created 14 highlights',
195
213
  }}
196
214
  />
197
215
  );
198
216
 
199
- // Progress should still be visible
200
- expect(screen.getByText('Complete! Created 14 highlights')).toBeInTheDocument();
217
+ // Progress should still be visible, with the translated terminal copy
218
+ expect(screen.getByText('Created 3 highlights')).toBeInTheDocument();
201
219
  // Form should NOT be visible
202
220
  expect(screen.queryByPlaceholderText('Enter custom instructions...')).not.toBeInTheDocument();
203
221
  });
@@ -455,42 +473,24 @@ describe('AssistSection', () => {
455
473
  });
456
474
 
457
475
  describe('Edge Cases', () => {
458
- it('should handle empty progress message', () => {
476
+ it('should handle progress without a message', () => {
459
477
  renderWithProviders(
460
478
  <AssistSection
461
479
  session={session}
462
480
  annotationType="highlight"
463
481
  isAssisting={true}
464
482
  progress={{
465
- stage: 'analyzing',
466
483
  percentage: 0,
467
- message: '',
468
484
  }}
469
485
  />
470
486
  );
471
487
 
472
- // Should render progress section even with empty message
473
- const progressDiv = document.querySelector('.semiont-annotation-progress');
488
+ // Renders the progress section with the translated status line — the
489
+ // wire carries a code (or nothing), never a sentence to fall back on.
490
+ const progressDiv = document.querySelector('.semiont-assist-progress');
474
491
  expect(progressDiv).toBeInTheDocument();
475
492
  });
476
493
 
477
- it('should handle progress without percentage', () => {
478
- renderWithProviders(
479
- <AssistSection
480
- session={session}
481
- annotationType="highlight"
482
- isAssisting={true}
483
- progress={{
484
- stage: 'analyzing',
485
- percentage: 0,
486
- message: 'Processing...',
487
- }}
488
- />
489
- );
490
-
491
- expect(screen.getByText('Processing...')).toBeInTheDocument();
492
- });
493
-
494
494
  it('should handle progress with empty requestParams array', () => {
495
495
  renderWithProviders(
496
496
  <AssistSection
@@ -498,15 +498,13 @@ describe('AssistSection', () => {
498
498
  annotationType="highlight"
499
499
  isAssisting={true}
500
500
  progress={{
501
- stage: 'analyzing',
502
501
  percentage: 0,
503
- message: 'Processing...',
504
502
  requestParams: [],
505
503
  }}
506
504
  />
507
505
  );
508
506
 
509
- expect(screen.queryByText('paramsTitle')).not.toBeInTheDocument();
507
+ expect(screen.queryByTestId('semiont-assist-params')).not.toBeInTheDocument();
510
508
  });
511
509
  });
512
510
  });
@@ -11,12 +11,17 @@ import userEvent from '@testing-library/user-event';
11
11
  import '@testing-library/jest-dom';
12
12
  import { AssistShell } from '../AssistShell';
13
13
 
14
- const progress = { stage: 'analyzing', percentage: 50, message: 'working' };
14
+ const progress = { percentage: 50 };
15
15
 
16
16
  /** AssistProgress requires a full translation set; the shell just passes it through. */
17
- const TR = { cancel: 'tr.cancel', inProgress: 'tr.inProgress', complete: 'tr.complete',
18
- failed: 'tr.failed', close: 'tr.close', paramsTitle: 'tr.paramsTitle',
19
- processing: (l: string) => `tr.processing(${l})` };
17
+ const TR = {
18
+ cancel: 'tr.cancel',
19
+ close: 'tr.close',
20
+ inProgress: 'tr.inProgress',
21
+ message: () => 'tr.code',
22
+ subject: (c: { kind: string; value: string }) => `tr.subject(${c.kind}:${c.value})`,
23
+ paramLabel: (c: string) => `tr.param(${c})`,
24
+ };
20
25
 
21
26
  describe('AssistShell', () => {
22
27
  beforeEach(() => {
@@ -34,13 +39,13 @@ describe('AssistShell', () => {
34
39
  form={<button type="button">the form</button>} progressProps={{ translations: TR }} />,
35
40
  );
36
41
  expect(screen.queryByText('the form')).not.toBeInTheDocument();
37
- expect(screen.getByText('working')).toBeInTheDocument();
42
+ expect(screen.getByText('tr.inProgress')).toBeInTheDocument();
38
43
  });
39
44
 
40
45
  it('withholds dismiss while assisting, offers it once terminal', async () => {
41
46
  const onDismiss = vi.fn();
42
47
  const props = {
43
- assistType: 'highlight', title: 'Annotate Highlights', progress,
48
+ assistType: 'highlight' as const, title: 'Annotate Highlights', progress,
44
49
  form: <span>form</span>,
45
50
  progressProps: { onDismiss, translations: { ...TR, close: 'Close' } },
46
51
  };
@@ -23,12 +23,18 @@ vi.mock('../../../../contexts/TranslationContext', () => ({
23
23
  reconnecting: 'Reconnecting...',
24
24
  sharing: 'Sharing',
25
25
  collaborationComingSoon: 'Multi-user collaboration coming soon',
26
+ collaborators: 'Collaborators',
27
+ ceilings: '{{contextTokens}} in / {{maxOutputTokens}} out',
28
+ sharedWindow: '{{contextTokens}} window',
26
29
  };
27
30
  let result = translations[key] || key;
28
31
  // Replace {count} with actual count value if provided
29
32
  if (params?.count !== undefined) {
30
33
  result = result.replace('{count}', String(params.count));
31
34
  }
35
+ for (const [k, v] of Object.entries(params ?? {})) {
36
+ result = result.replace(`{{${k}}}`, String(v));
37
+ }
32
38
  return result;
33
39
  }),
34
40
  }));
@@ -52,6 +58,89 @@ describe('CollaborationPanel Component', () => {
52
58
  vi.useRealTimers();
53
59
  });
54
60
 
61
+ // INFERENCE-LIMITS-EXPOSURE P3a. The panel was props-only; it now optionally
62
+ // renders the collaborator roster with each software agent's discovered
63
+ // ceilings. Absence is meaningful and must degrade quietly (D3) — the field
64
+ // is absent whenever discovery could not answer *right now*.
65
+ describe('Collaborators roster', () => {
66
+ const softwareEntry = (over: Record<string, unknown> = {}) => ({
67
+ agent: {
68
+ '@type': 'Software' as const,
69
+ '@id': 'did:web:localhost:agents:anthropic:claude-sonnet-5',
70
+ name: 'Claude Sonnet 5',
71
+ provider: 'anthropic',
72
+ model: 'claude-sonnet-5',
73
+ },
74
+ servesJobTypes: ['generation'],
75
+ limits: { contextTokens: 200_000, maxOutputTokens: 64_000 },
76
+ ...over,
77
+ });
78
+
79
+ it('renders one row per software agent with provider, model and ceilings', () => {
80
+ render(<CollaborationPanel {...defaultProps} collaborators={[softwareEntry() as any]} />);
81
+
82
+ expect(screen.getByText('Collaborators')).toBeInTheDocument();
83
+ const row = screen.getByTestId('semiont-collaborator-row');
84
+ expect(row.textContent).toContain('anthropic');
85
+ expect(row.textContent).toContain('claude-sonnet-5');
86
+ expect(row.textContent).toContain('generation');
87
+ // Distinct in/out ceilings render as a pair.
88
+ expect(row.textContent).toContain('200K in / 64K out');
89
+ });
90
+
91
+ it('renders a single window figure when the provider shares one (Ollama shape)', () => {
92
+ // `maxOutputTokens === contextTokens` is the shared-window sentinel the
93
+ // schema documents; rendering "128K in / 128K out" would be noise.
94
+ const shared = softwareEntry({
95
+ agent: {
96
+ '@type': 'Software' as const,
97
+ name: 'Gemma',
98
+ provider: 'ollama',
99
+ model: 'gemma2:27b',
100
+ },
101
+ limits: { contextTokens: 128_000, maxOutputTokens: 128_000 },
102
+ });
103
+ render(<CollaborationPanel {...defaultProps} collaborators={[shared as any]} />);
104
+
105
+ const row = screen.getByTestId('semiont-collaborator-row');
106
+ expect(row.textContent).toContain('128K window');
107
+ expect(row.textContent).not.toContain('in /');
108
+ });
109
+
110
+ it('renders the row without ceilings when limits are absent (D3)', () => {
111
+ const noLimits = softwareEntry();
112
+ delete (noLimits as Record<string, unknown>).limits;
113
+ render(<CollaborationPanel {...defaultProps} collaborators={[noLimits as any]} />);
114
+
115
+ const row = screen.getByTestId('semiont-collaborator-row');
116
+ expect(row.textContent).toContain('claude-sonnet-5');
117
+ expect(row.textContent).not.toContain('in /');
118
+ expect(row.textContent).not.toContain('window');
119
+ });
120
+
121
+ it('renders exactly as today when the prop is absent (hosts that never wire it)', () => {
122
+ render(<CollaborationPanel {...defaultProps} />);
123
+
124
+ expect(screen.queryByText('Collaborators')).not.toBeInTheDocument();
125
+ expect(screen.queryByTestId('semiont-collaborator-row')).not.toBeInTheDocument();
126
+ // The pre-existing sections are untouched.
127
+ expect(screen.getByText('Connection Status')).toBeInTheDocument();
128
+ });
129
+
130
+ it('lists only software agents — Persons are not inference collaborators', () => {
131
+ const person = { agent: { '@type': 'Person' as const, name: 'Ada' } };
132
+ render(
133
+ <CollaborationPanel
134
+ {...defaultProps}
135
+ collaborators={[softwareEntry() as any, person as any]}
136
+ />,
137
+ );
138
+
139
+ expect(screen.getAllByTestId('semiont-collaborator-row')).toHaveLength(1);
140
+ expect(screen.queryByText('Ada')).not.toBeInTheDocument();
141
+ });
142
+ });
143
+
55
144
  describe('Rendering', () => {
56
145
  it('should render connection status section', () => {
57
146
  render(<CollaborationPanel {...defaultProps} />);
@@ -264,8 +264,7 @@ describe('CommentEntry Component', () => {
264
264
  await userEvent.click(commentDiv);
265
265
 
266
266
  expect(clickHandler).toHaveBeenCalledWith({
267
- annotationId: 'comment-1',
268
- motivation: 'commenting'
267
+ annotationId: 'comment-1'
269
268
  });
270
269
 
271
270
  // Clean up
@@ -147,7 +147,6 @@ describe('HighlightEntry', () => {
147
147
 
148
148
  expect(clickHandler).toHaveBeenCalledWith({
149
149
  annotationId: 'highlight-1',
150
- motivation: 'highlighting',
151
150
  });
152
151
 
153
152
  subscription.unsubscribe();