@semiont/react-ui 0.5.26 → 0.5.27

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 (249) hide show
  1. package/README.md +28 -4
  2. package/dist/{PdfAnnotationCanvas.client-6ZSF6ZTJ.js → PdfAnnotationCanvas.client-EVUPTUWZ.js} +5 -5
  3. package/dist/PdfAnnotationCanvas.client-EVUPTUWZ.js.map +1 -0
  4. package/dist/{PdfAnnotationCanvas.client-JCJMXWWJ.css → PdfAnnotationCanvas.client-TGDPPGXJ.css} +6 -1
  5. package/dist/PdfAnnotationCanvas.client-TGDPPGXJ.css.map +1 -0
  6. package/dist/{ar-YTUUGK2O.js → ar-OU3DQGC6.js} +53 -7
  7. package/dist/ar-OU3DQGC6.js.map +1 -0
  8. package/dist/{bn-ASGNGPMD.js → bn-PHWCR6AZ.js} +53 -7
  9. package/dist/bn-PHWCR6AZ.js.map +1 -0
  10. package/dist/{chunk-OBQOZCJE.js → chunk-5VDTWRXJ.js} +31 -31
  11. package/dist/{chunk-L3WKRWP3.js → chunk-PCRRBU7H.js} +53 -7
  12. package/dist/chunk-PCRRBU7H.js.map +1 -0
  13. package/dist/{cs-WMYVAOZY.js → cs-NEXYZ62I.js} +53 -7
  14. package/dist/cs-NEXYZ62I.js.map +1 -0
  15. package/dist/{da-FSHHN4D6.js → da-FAT43YCU.js} +53 -7
  16. package/dist/da-FAT43YCU.js.map +1 -0
  17. package/dist/{de-FJFO3TPT.js → de-KRQ3WBCU.js} +53 -7
  18. package/dist/de-KRQ3WBCU.js.map +1 -0
  19. package/dist/{el-XOH4QIAG.js → el-Q4IU3EKC.js} +53 -7
  20. package/dist/el-Q4IU3EKC.js.map +1 -0
  21. package/dist/{en-3FCBW3YD.js → en-2KADE7N4.js} +4 -2
  22. package/dist/{es-HDX4QI7E.js → es-2IO3R2MK.js} +53 -7
  23. package/dist/es-2IO3R2MK.js.map +1 -0
  24. package/dist/{fa-DJ6WTFTJ.js → fa-Q5GOQFXX.js} +53 -7
  25. package/dist/fa-Q5GOQFXX.js.map +1 -0
  26. package/dist/{fi-4S7H7W7U.js → fi-R6NMRWBE.js} +53 -7
  27. package/dist/fi-R6NMRWBE.js.map +1 -0
  28. package/dist/{fr-6UJSYHXC.js → fr-QY4D5E6J.js} +53 -7
  29. package/dist/fr-QY4D5E6J.js.map +1 -0
  30. package/dist/{he-ZZJOHJX2.js → he-X6Y77XHY.js} +53 -7
  31. package/dist/he-X6Y77XHY.js.map +1 -0
  32. package/dist/{hi-HZJTAW7U.js → hi-XL7IOEQM.js} +53 -7
  33. package/dist/hi-XL7IOEQM.js.map +1 -0
  34. package/dist/{id-E3I6ILEM.js → id-QZ4ZZS67.js} +53 -7
  35. package/dist/id-QZ4ZZS67.js.map +1 -0
  36. package/dist/index.css +105 -454
  37. package/dist/index.css.map +1 -1
  38. package/dist/index.d.ts +155 -54
  39. package/dist/index.js +1798 -1637
  40. package/dist/index.js.map +1 -1
  41. package/dist/integrations/css-modules-helper.js +1 -3
  42. package/dist/integrations/css-modules-helper.js.map +1 -1
  43. package/dist/{it-ULAPAD7U.js → it-ME2Q7OB6.js} +53 -7
  44. package/dist/it-ME2Q7OB6.js.map +1 -0
  45. package/dist/{ja-SAHZQ4CZ.js → ja-HX7QASRI.js} +53 -7
  46. package/dist/ja-HX7QASRI.js.map +1 -0
  47. package/dist/{ko-DFU2ADQ4.js → ko-EQO5AHAV.js} +53 -7
  48. package/dist/ko-EQO5AHAV.js.map +1 -0
  49. package/dist/{ms-NWL76XNT.js → ms-UILFSF3I.js} +53 -7
  50. package/dist/ms-UILFSF3I.js.map +1 -0
  51. package/dist/{nl-ATCM76WN.js → nl-NNI45PMK.js} +53 -7
  52. package/dist/nl-NNI45PMK.js.map +1 -0
  53. package/dist/{no-UIAQ5RJF.js → no-MAL5F6P6.js} +53 -7
  54. package/dist/no-MAL5F6P6.js.map +1 -0
  55. package/dist/{pl-NOECQDXD.js → pl-4RMY6JAJ.js} +53 -7
  56. package/dist/pl-4RMY6JAJ.js.map +1 -0
  57. package/dist/{pt-SVWIIUPC.js → pt-VQH2X3QQ.js} +53 -7
  58. package/dist/pt-VQH2X3QQ.js.map +1 -0
  59. package/dist/{ro-NR2DRTZG.js → ro-XXCQO2OG.js} +53 -7
  60. package/dist/ro-XXCQO2OG.js.map +1 -0
  61. package/dist/{sv-STVJ37JP.js → sv-7Z6EJ6U4.js} +53 -7
  62. package/dist/sv-7Z6EJ6U4.js.map +1 -0
  63. package/dist/test-utils.css +1 -1
  64. package/dist/test-utils.css.map +1 -1
  65. package/dist/test-utils.d.ts +1 -1
  66. package/dist/test-utils.js +2 -2
  67. package/dist/test-utils.js.map +1 -1
  68. package/dist/{th-UWQR4K54.js → th-L3DXXYVQ.js} +53 -7
  69. package/dist/th-L3DXXYVQ.js.map +1 -0
  70. package/dist/{tr-WNXZSD27.js → tr-P3DFVLFJ.js} +53 -7
  71. package/dist/tr-P3DFVLFJ.js.map +1 -0
  72. package/dist/{uk-SJRMB47Y.js → uk-7C5D3PXS.js} +53 -7
  73. package/dist/uk-7C5D3PXS.js.map +1 -0
  74. package/dist/{vi-TMRQFES2.js → vi-ZLTLEO7P.js} +53 -7
  75. package/dist/vi-ZLTLEO7P.js.map +1 -0
  76. package/dist/{zh-QOL3LYEM.js → zh-5FHVWDIN.js} +53 -7
  77. package/dist/zh-5FHVWDIN.js.map +1 -0
  78. package/package.json +7 -7
  79. package/src/components/AssistProgress.tsx +144 -134
  80. package/src/components/{EntityFoundLog.tsx → ItemFoundLog.tsx} +12 -7
  81. package/src/components/ProtectedErrorBoundary.css +3 -1
  82. package/src/components/StatusDisplay.css +1 -1
  83. package/src/components/Toast.css +3 -1
  84. package/src/components/__tests__/AssistProgress.test.tsx +216 -148
  85. package/src/components/annotation/annotation-entries.css +0 -4
  86. package/src/components/annotation/references.css +0 -4
  87. package/src/components/image-annotation/AnnotationOverlay.tsx +6 -6
  88. package/src/components/image-annotation/SvgDrawingCanvas.tsx +1 -1
  89. package/src/components/image-annotation/__tests__/AnnotationOverlay.click.test.tsx +7 -9
  90. package/src/components/image-annotation/__tests__/SvgDrawingCanvas.click.test.tsx +1 -2
  91. package/src/components/layout/Header.css +0 -4
  92. package/src/components/layout/LeftSidebar.css +0 -4
  93. package/src/components/modals/ConfigureGenerationStep.tsx +109 -48
  94. package/src/components/modals/ConfigureSearchStep.tsx +23 -40
  95. package/src/components/modals/ReferenceWizardModal.tsx +55 -16
  96. package/src/components/modals/ResourceGenerateModal.tsx +24 -8
  97. package/src/components/modals/ResourceSearchModal.tsx +1 -1
  98. package/src/components/modals/SearchModal.css +0 -12
  99. package/src/components/modals/SearchModal.tsx +1 -1
  100. package/src/components/modals/SearchResultsStep.tsx +2 -20
  101. package/src/components/modals/WizardFooter.tsx +63 -0
  102. package/src/components/modals/__tests__/ConfigureGenerationStep.test.tsx +255 -0
  103. package/src/components/modals/__tests__/ConfigureSearchStep.test.tsx +72 -0
  104. package/src/components/modals/__tests__/ReferenceWizardModal.test.tsx +256 -0
  105. package/src/components/modals/__tests__/ResourceGenerateModal.test.tsx +1 -0
  106. package/src/components/modals/__tests__/ResourceSearchModal.test.tsx +6 -2
  107. package/src/components/modals/__tests__/SearchModal.search-wiring.test.tsx +6 -2
  108. package/src/components/modals/__tests__/WizardFooter.test.tsx +235 -0
  109. package/src/components/modals/modals.css +16 -42
  110. package/src/components/navigation/NavigationTabs.css +0 -20
  111. package/src/components/pdf-annotation/PdfAnnotationCanvas.css +21 -2
  112. package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +2 -2
  113. package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +3 -5
  114. package/src/components/resource/AnnotateView.tsx +16 -5
  115. package/src/components/resource/AnnotationHistory.tsx +2 -0
  116. package/src/components/resource/BrowseView.tsx +15 -4
  117. package/src/components/resource/ResourceViewer.tsx +23 -24
  118. package/src/components/resource/__tests__/AnnotateView.focus.test.tsx +50 -0
  119. package/src/components/resource/__tests__/AnnotationHistory.test.tsx +2 -0
  120. package/src/components/resource/__tests__/BrowseView.test.tsx +29 -3
  121. package/src/components/resource/__tests__/ResourceViewer.embeddable.test.tsx +47 -3
  122. package/src/components/resource/event-formatting.ts +0 -3
  123. package/src/components/resource/panels/AssessmentEntry.tsx +1 -1
  124. package/src/components/resource/panels/AssessmentPanel.css +5 -6
  125. package/src/components/resource/panels/AssistSection.css +68 -271
  126. package/src/components/resource/panels/AssistSection.tsx +4 -4
  127. package/src/components/resource/panels/AssistShell.tsx +12 -4
  128. package/src/components/resource/panels/CollaborationPanel.css +47 -22
  129. package/src/components/resource/panels/CollaborationPanel.tsx +93 -2
  130. package/src/components/resource/panels/CommentEntry.tsx +1 -1
  131. package/src/components/resource/panels/CommentsPanel.css +0 -16
  132. package/src/components/resource/panels/HighlightEntry.tsx +1 -1
  133. package/src/components/resource/panels/HighlightPanel.css +0 -7
  134. package/src/components/resource/panels/JsonLdPanel.css +0 -4
  135. package/src/components/resource/panels/ReferenceEntry.tsx +1 -1
  136. package/src/components/resource/panels/ReferencesPanel.css +0 -12
  137. package/src/components/resource/panels/ReferencesPanel.tsx +12 -20
  138. package/src/components/resource/panels/ResourceInfoPanel.css +0 -32
  139. package/src/components/resource/panels/ResourceInfoPanel.tsx +1 -1
  140. package/src/components/resource/panels/StatisticsPanel.css +0 -20
  141. package/src/components/resource/panels/TagEntry.tsx +1 -1
  142. package/src/components/resource/panels/TaggingPanel.css +0 -4
  143. package/src/components/resource/panels/TaggingPanel.tsx +2 -10
  144. package/src/components/resource/panels/UnifiedAnnotationsPanel.css +0 -4
  145. package/src/components/resource/panels/__tests__/AssessmentEntry.test.tsx +0 -1
  146. package/src/components/resource/panels/__tests__/AssistSection.test.tsx +48 -50
  147. package/src/components/resource/panels/__tests__/AssistShell.test.tsx +11 -6
  148. package/src/components/resource/panels/__tests__/CollaborationPanel.test.tsx +89 -0
  149. package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +1 -2
  150. package/src/components/resource/panels/__tests__/HighlightEntry.test.tsx +0 -1
  151. package/src/components/resource/panels/__tests__/HighlightPanel.annotationProgress.test.tsx +44 -36
  152. package/src/components/resource/panels/__tests__/ReferenceEntry.test.tsx +0 -1
  153. package/src/components/resource/panels/__tests__/ReferencesPanel.headless.test.tsx +1 -1
  154. package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +34 -53
  155. package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +37 -1
  156. package/src/components/resource/panels/__tests__/TagEntry.test.tsx +0 -1
  157. package/src/components/settings/SettingsPanel.css +0 -4
  158. package/src/features/auth/auth.css +2 -26
  159. package/src/features/resource-compose/state/__tests__/compose-page-state-unit.test.ts +25 -1
  160. package/src/features/resource-compose/state/compose-page-state-unit.ts +11 -2
  161. package/src/features/resource-discovery/__tests__/ResourceDiscoveryPage.test.tsx +55 -0
  162. package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +29 -0
  163. package/src/features/resource-discovery/state/__tests__/discover-state-unit.test.ts +53 -3
  164. package/src/features/resource-discovery/state/discover-state-unit.ts +29 -8
  165. package/src/features/resource-viewer/components/ResourceViewerPage.tsx +41 -20
  166. package/src/features/resource-viewer/hooks/__tests__/useResourceViewedReport.test.tsx +63 -0
  167. package/src/features/resource-viewer/hooks/useResourceViewedReport.ts +33 -0
  168. package/src/features/resource-viewer/state/__tests__/resource-viewer-page-state-unit.test.ts +2 -2
  169. package/src/features/resource-viewer/state/resource-viewer-page-state-unit.ts +1 -1
  170. package/src/styles/core/buttons.css +3 -1
  171. package/src/styles/core/forms.css +0 -21
  172. package/src/styles/core/indicators.css +0 -4
  173. package/src/styles/core/inputs.css +0 -4
  174. package/src/styles/core/progress.css +2 -34
  175. package/src/styles/core/sliders.css +0 -4
  176. package/src/styles/core/toggles.css +0 -4
  177. package/src/styles/features/resource-discovery.css +13 -0
  178. package/src/styles/features/resource-viewer.css +0 -20
  179. package/src/styles/motivations/motivation-assessment.css +2 -2
  180. package/src/styles/panels/history-panel.css +0 -40
  181. package/src/styles/panels/user-panel.css +0 -3
  182. package/src/styles/patterns/panel-helpers.css +0 -27
  183. package/src/styles/patterns/panel-sections.css +0 -59
  184. package/src/styles/patterns/panels-base.css +0 -32
  185. package/translations/ar.json +49 -5
  186. package/translations/bn.json +49 -5
  187. package/translations/cs.json +49 -5
  188. package/translations/da.json +49 -5
  189. package/translations/de.json +49 -5
  190. package/translations/el.json +49 -5
  191. package/translations/en.json +49 -5
  192. package/translations/es.json +49 -5
  193. package/translations/fa.json +49 -5
  194. package/translations/fi.json +49 -5
  195. package/translations/fr.json +49 -5
  196. package/translations/he.json +49 -5
  197. package/translations/hi.json +49 -5
  198. package/translations/id.json +49 -5
  199. package/translations/it.json +49 -5
  200. package/translations/ja.json +49 -5
  201. package/translations/ko.json +49 -5
  202. package/translations/ms.json +49 -5
  203. package/translations/nl.json +49 -5
  204. package/translations/no.json +49 -5
  205. package/translations/pl.json +49 -5
  206. package/translations/pt.json +49 -5
  207. package/translations/ro.json +49 -5
  208. package/translations/sv.json +49 -5
  209. package/translations/th.json +49 -5
  210. package/translations/tr.json +49 -5
  211. package/translations/uk.json +49 -5
  212. package/translations/vi.json +49 -5
  213. package/translations/zh.json +49 -5
  214. package/dist/PdfAnnotationCanvas.client-6ZSF6ZTJ.js.map +0 -1
  215. package/dist/PdfAnnotationCanvas.client-JCJMXWWJ.css.map +0 -1
  216. package/dist/ar-YTUUGK2O.js.map +0 -1
  217. package/dist/bn-ASGNGPMD.js.map +0 -1
  218. package/dist/chunk-L3WKRWP3.js.map +0 -1
  219. package/dist/chunk-XUDKYAVC.js +0 -21
  220. package/dist/chunk-XUDKYAVC.js.map +0 -1
  221. package/dist/cs-WMYVAOZY.js.map +0 -1
  222. package/dist/da-FSHHN4D6.js.map +0 -1
  223. package/dist/de-FJFO3TPT.js.map +0 -1
  224. package/dist/el-XOH4QIAG.js.map +0 -1
  225. package/dist/es-HDX4QI7E.js.map +0 -1
  226. package/dist/fa-DJ6WTFTJ.js.map +0 -1
  227. package/dist/fi-4S7H7W7U.js.map +0 -1
  228. package/dist/fr-6UJSYHXC.js.map +0 -1
  229. package/dist/he-ZZJOHJX2.js.map +0 -1
  230. package/dist/hi-HZJTAW7U.js.map +0 -1
  231. package/dist/id-E3I6ILEM.js.map +0 -1
  232. package/dist/it-ULAPAD7U.js.map +0 -1
  233. package/dist/ja-SAHZQ4CZ.js.map +0 -1
  234. package/dist/ko-DFU2ADQ4.js.map +0 -1
  235. package/dist/ms-NWL76XNT.js.map +0 -1
  236. package/dist/nl-ATCM76WN.js.map +0 -1
  237. package/dist/no-UIAQ5RJF.js.map +0 -1
  238. package/dist/pl-NOECQDXD.js.map +0 -1
  239. package/dist/pt-SVWIIUPC.js.map +0 -1
  240. package/dist/ro-NR2DRTZG.js.map +0 -1
  241. package/dist/sv-STVJ37JP.js.map +0 -1
  242. package/dist/th-UWQR4K54.js.map +0 -1
  243. package/dist/tr-WNXZSD27.js.map +0 -1
  244. package/dist/uk-SJRMB47Y.js.map +0 -1
  245. package/dist/vi-TMRQFES2.js.map +0 -1
  246. package/dist/zh-QOL3LYEM.js.map +0 -1
  247. package/src/integrations/tailwind-plugin.cjs +0 -224
  248. /package/dist/{chunk-OBQOZCJE.js.map → chunk-5VDTWRXJ.js.map} +0 -0
  249. /package/dist/{en-3FCBW3YD.js.map → en-2KADE7N4.js.map} +0 -0
@@ -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();
@@ -22,7 +22,7 @@ import type { Annotation, AnnotationId } from '@semiont/core';
22
22
 
23
23
  // Mock translations - simulates useTranslations('HighlightPanel')
24
24
  // The mock receives keys like 'title', 'noHighlights', etc. and returns translated strings
25
- const mockT = vi.fn((key: string) => {
25
+ const mockT = vi.fn((key: string, params?: Record<string, unknown>) => {
26
26
  const translations: Record<string, string> = {
27
27
  title: 'Highlights',
28
28
  noHighlights: 'No highlights yet',
@@ -34,8 +34,31 @@ const mockT = vi.fn((key: string) => {
34
34
  densitySparse: 'Sparse',
35
35
  densityDense: 'Dense',
36
36
  annotate: 'Annotate',
37
+ annotating: 'Annotating...',
37
38
  };
38
- return translations[key] || key;
39
+ // P3: the coded status line. Interpolates `{{var}}` like production —
40
+ // a mock that ignored params would let copy that cannot interpolate in the
41
+ // app still pass here.
42
+ Object.assign(translations, {
43
+ codeLoading: 'Loading…',
44
+ codeAnalyzing: 'Marking…',
45
+ codeDetectingEntities: 'Marking…',
46
+ codeCompleteCreated: 'Created {{count}} {{kind}}',
47
+ kindHighlight: 'highlights',
48
+ subject: '{{kind}}: {{label}}',
49
+ subjectWithPosition: '{{kind}}: {{label}} ({{done}} of {{total}})',
50
+ subjectKindEntityType: 'Entity type',
51
+ subjectKindCategory: 'Category',
52
+ // CLEAN-PROGRESS D3: the widget's own strings now come from the
53
+ // AssistProgress namespace, not from each panel's.
54
+ cancel: 'Cancel',
55
+ inProgress: 'Annotating...',
56
+ });
57
+ let out = translations[key] || key;
58
+ for (const [k, v] of Object.entries((params ?? {}) as Record<string, unknown>)) {
59
+ out = out.replace(`{{${k}}}`, String(v));
60
+ }
61
+ return out;
39
62
  });
40
63
 
41
64
  vi.mock('../../../../contexts/TranslationContext', () => ({
@@ -85,16 +108,14 @@ describe('HighlightPanel + AssistSection Integration', () => {
85
108
  pendingAnnotation={null}
86
109
  isAssisting={true}
87
110
  progress={{
88
- stage: 'analyzing',
89
111
  percentage: 30,
90
- message: 'Analyzing text for highlights...',
91
112
  }}
92
113
  annotateMode={true}
93
114
  />
94
115
  );
95
116
 
96
117
  // Verify AssistSection received and rendered the progress
97
- expect(screen.getByText('Analyzing text for highlights...')).toBeInTheDocument();
118
+ expect(screen.getByText('Annotating...')).toBeInTheDocument();
98
119
  });
99
120
 
100
121
  it('should pass null progress to AssistSection', () => {
@@ -136,16 +157,15 @@ describe('HighlightPanel + AssistSection Integration', () => {
136
157
  pendingAnnotation={null}
137
158
  isAssisting={false}
138
159
  progress={{
139
- stage: 'complete',
160
+ message: { code: 'complete-created', count: 3, kind: 'highlight' },
140
161
  percentage: 100,
141
- message: 'Complete! Created 14 highlights',
142
162
  }}
143
163
  annotateMode={true}
144
164
  />
145
165
  );
146
166
 
147
167
  // Progress should still be visible
148
- expect(screen.getByText('Complete! Created 14 highlights')).toBeInTheDocument();
168
+ expect(screen.getByText('Created 3 highlights')).toBeInTheDocument();
149
169
  // Form should NOT be visible
150
170
  expect(screen.queryByPlaceholderText('Enter custom instructions...')).not.toBeInTheDocument();
151
171
  });
@@ -157,19 +177,17 @@ describe('HighlightPanel + AssistSection Integration', () => {
157
177
  pendingAnnotation={null}
158
178
  isAssisting={true}
159
179
  progress={{
160
- stage: 'analyzing',
161
180
  percentage: 0,
162
- message: 'Analyzing...',
163
181
  requestParams: [
164
- { label: 'Instructions', value: 'Find important points' },
165
- { label: 'Density', value: '5' },
182
+ { label: 'instructions', value: 'Find important points' },
183
+ { label: 'density', value: '5' },
166
184
  ],
167
185
  }}
168
186
  annotateMode={true}
169
187
  />
170
188
  );
171
189
 
172
- expect(screen.getByText('paramsTitle')).toBeInTheDocument();
190
+ expect(screen.getByTestId('semiont-assist-params')).toBeInTheDocument();
173
191
  expect(screen.getByText('Find important points')).toBeInTheDocument();
174
192
  expect(screen.getByText('5')).toBeInTheDocument();
175
193
  });
@@ -211,15 +229,13 @@ describe('HighlightPanel + AssistSection Integration', () => {
211
229
  pendingAnnotation={null}
212
230
  isAssisting={true}
213
231
  progress={{
214
- stage: 'analyzing',
215
232
  percentage: 0,
216
- message: 'Analyzing...',
217
233
  }}
218
234
  annotateMode={true}
219
235
  />
220
236
  );
221
237
 
222
- expect(screen.getByText('Analyzing...')).toBeInTheDocument();
238
+ expect(screen.getByText('Annotating...')).toBeInTheDocument();
223
239
 
224
240
  // Switch to browse mode
225
241
  rerender(
@@ -228,16 +244,14 @@ describe('HighlightPanel + AssistSection Integration', () => {
228
244
  pendingAnnotation={null}
229
245
  isAssisting={true}
230
246
  progress={{
231
- stage: 'analyzing',
232
247
  percentage: 0,
233
- message: 'Analyzing...',
234
248
  }}
235
249
  annotateMode={false}
236
250
  />
237
251
  );
238
252
 
239
253
  // Progress should be hidden
240
- expect(screen.queryByText('Analyzing...')).not.toBeInTheDocument();
254
+ expect(screen.queryByText('Annotating...')).not.toBeInTheDocument();
241
255
  });
242
256
  });
243
257
 
@@ -264,16 +278,15 @@ describe('HighlightPanel + AssistSection Integration', () => {
264
278
  pendingAnnotation={null}
265
279
  isAssisting={false}
266
280
  progress={{
267
- stage: 'complete',
281
+ message: { code: 'complete-created', count: 3, kind: 'highlight' },
268
282
  percentage: 100,
269
- message: 'Done!',
270
283
  }}
271
284
  annotateMode={true}
272
285
  />
273
286
  );
274
287
 
275
288
  // Progress should be visible
276
- expect(screen.getByText('Done!')).toBeInTheDocument();
289
+ expect(screen.getByText('Created 3 highlights')).toBeInTheDocument();
277
290
  // Form should be hidden
278
291
  expect(screen.queryByPlaceholderText('Enter custom instructions...')).not.toBeInTheDocument();
279
292
  });
@@ -285,15 +298,15 @@ describe('HighlightPanel + AssistSection Integration', () => {
285
298
  pendingAnnotation={null}
286
299
  isAssisting={true}
287
300
  progress={{
288
- stage: 'started',
289
301
  percentage: 0,
290
- message: 'Starting...',
291
302
  }}
292
303
  annotateMode={true}
293
304
  />
294
305
  );
295
306
 
296
- expect(screen.getByText('Starting...')).toBeInTheDocument();
307
+ // Stage is the observable across non-terminal updates now that the
308
+ // status text is a single translated string (the wire carries codes).
309
+ expect(document.querySelector('.semiont-assist-progress')).toHaveAttribute('data-ended', 'false');
297
310
 
298
311
  // Update to analyzing
299
312
  rerender(
@@ -302,16 +315,14 @@ describe('HighlightPanel + AssistSection Integration', () => {
302
315
  pendingAnnotation={null}
303
316
  isAssisting={true}
304
317
  progress={{
305
- stage: 'analyzing',
306
318
  percentage: 50,
307
- message: 'Analyzing...',
308
319
  }}
309
320
  annotateMode={true}
310
321
  />
311
322
  );
312
323
 
313
- expect(screen.queryByText('Starting...')).not.toBeInTheDocument();
314
- expect(screen.getByText('Analyzing...')).toBeInTheDocument();
324
+ expect(document.querySelector('.semiont-assist-progress')).toHaveAttribute('data-ended', 'false');
325
+ expect(screen.getByText('Annotating...')).toBeInTheDocument();
315
326
 
316
327
  // Update to complete
317
328
  rerender(
@@ -320,16 +331,15 @@ describe('HighlightPanel + AssistSection Integration', () => {
320
331
  pendingAnnotation={null}
321
332
  isAssisting={false}
322
333
  progress={{
323
- stage: 'complete',
334
+ message: { code: 'complete-created', count: 3, kind: 'highlight' },
324
335
  percentage: 100,
325
- message: 'Complete!',
326
336
  }}
327
337
  annotateMode={true}
328
338
  />
329
339
  );
330
340
 
331
- expect(screen.queryByText('Analyzing...')).not.toBeInTheDocument();
332
- expect(screen.getByText('Complete!')).toBeInTheDocument();
341
+ expect(screen.queryByText('Annotating...')).not.toBeInTheDocument();
342
+ expect(screen.getByText('Created 3 highlights')).toBeInTheDocument();
333
343
  });
334
344
  });
335
345
 
@@ -341,16 +351,14 @@ describe('HighlightPanel + AssistSection Integration', () => {
341
351
  pendingAnnotation={null}
342
352
  isAssisting={true}
343
353
  progress={{
344
- stage: 'analyzing',
345
354
  percentage: 0,
346
- message: 'Analyzing...',
347
355
  }}
348
356
  annotateMode={true}
349
357
  />
350
358
  );
351
359
 
352
360
  // Both progress and highlights should be visible
353
- expect(screen.getByText('Analyzing...')).toBeInTheDocument();
361
+ expect(screen.getByText('Annotating...')).toBeInTheDocument();
354
362
  expect(screen.getByText('Highlights')).toBeInTheDocument();
355
363
  });
356
364
 
@@ -236,7 +236,6 @@ describe('ReferenceEntry', () => {
236
236
 
237
237
  expect(clickHandler).toHaveBeenCalledWith({
238
238
  annotationId: 'ref-1',
239
- motivation: 'linking',
240
239
  });
241
240
 
242
241
  subscription.unsubscribe();
@@ -82,7 +82,7 @@ describe('ReferencesPanel — headless (session prop, no providers)', () => {
82
82
  expect(entryText).toBeInTheDocument();
83
83
 
84
84
  fireEvent.click(entryText);
85
- expect(client.browse.click).toHaveBeenCalledWith('ref-1', 'linking');
85
+ expect(client.browse.click).toHaveBeenCalledWith('ref-1');
86
86
  });
87
87
 
88
88
  describe('incoming references — terminal load failure', () => {
@@ -66,6 +66,12 @@ vi.mock('../../../../contexts/TranslationContext', () => ({
66
66
  includeDescriptiveReferences: 'Include descriptive references',
67
67
  descriptiveReferencesTooltip: 'Also find phrases like \'the CEO\', \'the tech giant\', \'the physicist\' (in addition to names)',
68
68
  cancel: 'Cancel',
69
+ annotating: 'Annotating...',
70
+ // CLEAN-PROGRESS D3: the widget's own strings come from the
71
+ // AssistProgress namespace now, not from this panel's.
72
+ inProgress: 'Annotating...',
73
+ complete: 'Annotation complete!',
74
+ failed: 'Annotation failed',
69
75
  };
70
76
  let result = translations[key] || key;
71
77
  // Replace {count} with actual count value if provided
@@ -325,7 +331,7 @@ describe('ReferencesPanel Component', () => {
325
331
  <ReferencesPanel
326
332
  {...panelProps()}
327
333
  isAssisting={true}
328
- progress={{ stage: 'analyzing', percentage: 0, message: 'Detecting references...', completedEntityTypes: [] }}
334
+ progress={{ percentage: 0, completedItems: [] }}
329
335
  />
330
336
  );
331
337
 
@@ -335,10 +341,8 @@ describe('ReferencesPanel Component', () => {
335
341
  {...panelProps()}
336
342
  isAssisting={false}
337
343
  progress={{
338
- stage: 'complete',
339
344
  percentage: 100,
340
- message: 'Annotation complete',
341
- completedEntityTypes: [{ entityType: 'Person', foundCount: 5 }],
345
+ completedItems: [{ value: 'Person', foundCount: 5 }],
342
346
  }}
343
347
  />
344
348
  );
@@ -378,21 +382,19 @@ describe('ReferencesPanel Component', () => {
378
382
  <ReferencesPanel
379
383
  {...panelProps()}
380
384
  isAssisting={true}
381
- progress={{ stage: 'analyzing', percentage: 0, message: 'Detecting references...', completedEntityTypes: [] }}
385
+ progress={{ percentage: 0, completedItems: [] }}
382
386
  />
383
387
  );
384
388
 
385
- expect(screen.getByText('Detecting references...')).toBeInTheDocument();
389
+ expect(screen.getByText('Annotating...')).toBeInTheDocument();
386
390
  });
387
391
 
388
392
  it('should pass progress data to widget', () => {
389
393
  const progress = {
390
- stage: 'complete',
391
394
  percentage: 100,
392
- message: 'Annotation complete',
393
- completedEntityTypes: [
394
- { entityType: 'Person', foundCount: 5 },
395
- { entityType: 'Organization', foundCount: 3 },
395
+ completedItems: [
396
+ { value: 'Person', foundCount: 5 },
397
+ { value: 'Organization', foundCount: 3 },
396
398
  ],
397
399
  };
398
400
 
@@ -419,10 +421,8 @@ describe('ReferencesPanel Component', () => {
419
421
  {...panelProps()}
420
422
  isAssisting={false}
421
423
  progress={{
422
- stage: 'complete',
423
424
  percentage: 100,
424
- message: 'Annotation complete',
425
- completedEntityTypes: [{ entityType: 'Person', foundCount: 5 }],
425
+ completedItems: [{ value: 'Person', foundCount: 5 }],
426
426
  }}
427
427
  />
428
428
  );
@@ -440,7 +440,7 @@ describe('ReferencesPanel Component', () => {
440
440
  <ReferencesPanel
441
441
  {...panelProps()}
442
442
  isAssisting={true}
443
- progress={{ stage: 'analyzing', percentage: 0, message: 'Detecting references...', completedEntityTypes: [] }}
443
+ progress={{ percentage: 0, completedItems: [] }}
444
444
  />
445
445
  );
446
446
 
@@ -453,13 +453,12 @@ describe('ReferencesPanel Component', () => {
453
453
  <ReferencesPanel
454
454
  {...panelProps()}
455
455
  isAssisting={true}
456
- progress={{ stage: 'analyzing', percentage: 0, message: 'Detecting references...', completedEntityTypes: [] }}
456
+ progress={{ percentage: 0, completedItems: [] }}
457
457
  />
458
458
  );
459
459
 
460
- // Real AssistProgress titles the cancel button with t('cancelAnnotation')
461
- // (the mock translator echoes unknown keys).
462
- const cancelButton = screen.getByTitle('cancelAnnotation');
460
+ // The control's label comes from the AssistProgress namespace's `cancel`.
461
+ const cancelButton = screen.getByTitle('Cancel');
463
462
  expect(cancelButton).toBeInTheDocument();
464
463
  });
465
464
  });
@@ -471,12 +470,10 @@ describe('ReferencesPanel Component', () => {
471
470
  {...panelProps()}
472
471
  isAssisting={false}
473
472
  progress={{
474
- stage: 'complete',
475
473
  percentage: 100,
476
- message: 'Annotation complete',
477
- completedEntityTypes: [
478
- { entityType: 'Person', foundCount: 5 },
479
- { entityType: 'Organization', foundCount: 3 },
474
+ completedItems: [
475
+ { value: 'Person', foundCount: 5 },
476
+ { value: 'Organization', foundCount: 3 },
480
477
  ],
481
478
  }}
482
479
  />
@@ -501,10 +498,8 @@ describe('ReferencesPanel Component', () => {
501
498
  {...panelProps()}
502
499
  isAssisting={false}
503
500
  progress={{
504
- stage: 'complete',
505
501
  percentage: 100,
506
- message: 'Annotation complete',
507
- completedEntityTypes: [{ entityType: 'Person', foundCount: 5 }],
502
+ completedItems: [{ value: 'Person', foundCount: 5 }],
508
503
  }}
509
504
  />
510
505
  );
@@ -521,10 +516,8 @@ describe('ReferencesPanel Component', () => {
521
516
  {...panelProps()}
522
517
  isAssisting={false}
523
518
  progress={{
524
- stage: 'complete',
525
519
  percentage: 100,
526
- message: 'Annotation complete',
527
- completedEntityTypes: [{ entityType: 'Person', foundCount: 5 }],
520
+ completedItems: [{ value: 'Person', foundCount: 5 }],
528
521
  }}
529
522
  />
530
523
  );
@@ -541,10 +534,8 @@ describe('ReferencesPanel Component', () => {
541
534
  {...panelProps()}
542
535
  isAssisting={false}
543
536
  progress={{
544
- stage: 'complete',
545
537
  percentage: 100,
546
- message: 'Annotation complete',
547
- completedEntityTypes: [{ entityType: 'Person', foundCount: 5 }],
538
+ completedItems: [{ value: 'Person', foundCount: 5 }],
548
539
  }}
549
540
  />
550
541
  );
@@ -564,10 +555,8 @@ describe('ReferencesPanel Component', () => {
564
555
  {...panelProps()}
565
556
  isAssisting={false}
566
557
  progress={{
567
- stage: 'complete',
568
558
  percentage: 100,
569
- message: 'Annotation complete',
570
- completedEntityTypes: [{ entityType: 'Person', foundCount: 5 }],
559
+ completedItems: [{ value: 'Person', foundCount: 5 }],
571
560
  }}
572
561
  />
573
562
  );
@@ -587,10 +576,8 @@ describe('ReferencesPanel Component', () => {
587
576
  {...panelProps()}
588
577
  isAssisting={false}
589
578
  progress={{
590
- stage: 'complete',
591
579
  percentage: 100,
592
- message: 'Annotation complete',
593
- completedEntityTypes: [],
580
+ completedItems: [],
594
581
  }}
595
582
  />
596
583
  );
@@ -615,12 +602,12 @@ describe('ReferencesPanel Component', () => {
615
602
  <ReferencesPanel
616
603
  {...panelProps()}
617
604
  isAssisting={true}
618
- progress={{ stage: 'analyzing', percentage: 0, message: 'Detecting references...', completedEntityTypes: [] }}
605
+ progress={{ percentage: 0, completedItems: [] }}
619
606
  />
620
607
  );
621
608
 
622
609
  // Detecting state
623
- expect(screen.getByText('Detecting references...')).toBeInTheDocument();
610
+ expect(screen.getByText('Annotating...')).toBeInTheDocument();
624
611
  expect(screen.queryByText('Select entity types')).not.toBeInTheDocument();
625
612
  });
626
613
 
@@ -629,12 +616,12 @@ describe('ReferencesPanel Component', () => {
629
616
  <ReferencesPanel
630
617
  {...panelProps()}
631
618
  isAssisting={true}
632
- progress={{ stage: 'analyzing', percentage: 0, message: 'Detecting references...', completedEntityTypes: [] }}
619
+ progress={{ percentage: 0, completedItems: [] }}
633
620
  />
634
621
  );
635
622
 
636
623
  // Detecting
637
- expect(screen.getByText('Detecting references...')).toBeInTheDocument();
624
+ expect(screen.getByText('Annotating...')).toBeInTheDocument();
638
625
 
639
626
  // Complete - first trigger useEffect to copy to lastDetectionLog
640
627
  rerender(
@@ -642,10 +629,8 @@ describe('ReferencesPanel Component', () => {
642
629
  {...panelProps()}
643
630
  isAssisting={false}
644
631
  progress={{
645
- stage: 'complete',
646
632
  percentage: 100,
647
- message: 'Annotation complete',
648
- completedEntityTypes: [{ entityType: 'Person', foundCount: 5 }],
633
+ completedItems: [{ value: 'Person', foundCount: 5 }],
649
634
  }}
650
635
  />
651
636
  );
@@ -655,7 +640,7 @@ describe('ReferencesPanel Component', () => {
655
640
  <ReferencesPanel {...panelProps()} isAssisting={false} progress={null} />
656
641
  );
657
642
 
658
- expect(screen.queryByText('Annotation complete')).not.toBeInTheDocument();
643
+ expect(screen.queryByText('Annotation complete!')).not.toBeInTheDocument();
659
644
  // Both log and selection UI should be visible
660
645
  expect(screen.getByText('Person:')).toBeInTheDocument();
661
646
  expect(screen.getByText('Select entity types')).toBeInTheDocument();
@@ -667,10 +652,8 @@ describe('ReferencesPanel Component', () => {
667
652
  {...panelProps()}
668
653
  isAssisting={false}
669
654
  progress={{
670
- stage: 'complete',
671
655
  percentage: 100,
672
- message: 'Annotation complete',
673
- completedEntityTypes: [{ entityType: 'Person', foundCount: 5 }],
656
+ completedItems: [{ value: 'Person', foundCount: 5 }],
674
657
  }}
675
658
  />
676
659
  );
@@ -761,10 +744,8 @@ describe('ReferencesPanel Component', () => {
761
744
  {...panelProps()}
762
745
  isAssisting={false}
763
746
  progress={{
764
- stage: 'complete',
765
747
  percentage: 100,
766
- message: 'Annotation complete',
767
- completedEntityTypes: [{ entityType: 'Person', foundCount: 0 }],
748
+ completedItems: [{ value: 'Person', foundCount: 0 }],
768
749
  }}
769
750
  />
770
751
  );