@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
@@ -6,6 +6,8 @@
6
6
  */
7
7
 
8
8
  import React, { useState, useEffect, useCallback } from 'react';
9
+ import { assistProgressTranslations } from '../../../lib/assist-progress-copy';
10
+ import { useResourceViewedReport } from '../hooks/useResourceViewedReport';
9
11
  import type { components, ResourceDescriptor, ResourceId, GatheredContext, EventMap } from '@semiont/core';
10
12
  import type { ConnectionState } from '@semiont/core';
11
13
  import { annotationId } from '@semiont/core';
@@ -24,6 +26,7 @@ import type { BrowseMediaRenderers, AnnotateMediaRenderers } from '@semiont/reac
24
26
  import { useObservable } from '@semiont/react-ui';
25
27
  import { useResourceContent } from '../../../hooks/useResourceContent';
26
28
  import { useMediaToken } from '../../../hooks/useMediaToken';
29
+ import { useCollaborators } from '../../../hooks/useCollaborators';
27
30
  import { mediaUrl } from '../../../lib/media-url';
28
31
  import { useToast } from '../../../components/Toast';
29
32
  import { useOutcomeToasts } from '../../../hooks/useOutcomeToasts';
@@ -117,7 +120,7 @@ export interface ResourceViewerPageProps {
117
120
  * @subscribes yield:clone - Clone the current resource
118
121
  * @subscribes beckon:sparkle - Trigger sparkle animation
119
122
  * @subscribes mark:added - Annotation was created (sparkle)
120
- * @subscribes browse:reference-navigate - Navigate to a referenced document
123
+ * @subscribes browse:resource-open - Open a resource in the viewer (local links + the launcher's tour verbs)
121
124
  * @subscribes browse:entity-type-clicked - Navigate filtered by entity type
122
125
  *
123
126
  * Outcome-notification channels (mark:create-error, mark:delete-error,
@@ -147,6 +150,18 @@ export function ResourceViewerPage({
147
150
  const semiont = session?.client;
148
151
  const navigateExternal = useObservableExternalNavigation();
149
152
 
153
+ // The KB's collaborator roster, for the Collaboration panel's software-agent
154
+ // rows. Subscribed unconditionally at the top level (hooks ordering) even
155
+ // though only one panel reads it; the underlying cache is a KB-wide
156
+ // singleton, so this costs one shared subscription, not one per panel open.
157
+ const { collaborators } = useCollaborators(semiont ?? null);
158
+
159
+ // At most one entry serves `generation` — the KB's workers.* config yields one
160
+ // inference config per job type — so `find` is exact, not a first-wins guess.
161
+ const generationAgent = collaborators.find((entry) =>
162
+ entry.servesJobTypes?.includes('generation'),
163
+ );
164
+
150
165
  // ResourceViewer is bring-your-own-session: feed it the active session plus
151
166
  // host-owned navigation (reference follow) and panel control (app-scoped bus).
152
167
  const handleViewerOpenResource = useCallback((id: string) => {
@@ -224,6 +239,7 @@ export function ResourceViewerPage({
224
239
  const panelInitialTab = useObservable(stateUnit?.browse.panelInitialTab$) ?? null;
225
240
  const onScrollCompleted = stateUnit?.browse.onScrollCompleted;
226
241
  const generationProgress = useObservable(stateUnit?.yield.progress$) ?? null;
242
+ const isGenerating = useObservable(stateUnit?.yield.isGenerating$) ?? false;
227
243
  const gatherContext = useObservable(stateUnit?.gather.context$) ?? null;
228
244
  const gatherLoading = useObservable(stateUnit?.gather.loading$) ?? false;
229
245
  const gatherError = useObservable(stateUnit?.gather.error$) ?? null;
@@ -241,7 +257,7 @@ export function ResourceViewerPage({
241
257
 
242
258
  const handleWizardGenerateSubmit = useCallback((referenceId: string, config: GenerationConfig) => {
243
259
  clearNewAnnotationId(annotationId(referenceId));
244
- stateUnit?.yield.generate(referenceId, {
260
+ stateUnit?.yield.generate(config.context, {
245
261
  title: config.title,
246
262
  storageUri: config.storagePath,
247
263
  prompt: config.prompt,
@@ -252,16 +268,15 @@ export function ResourceViewerPage({
252
268
  sourceLanguage: getLanguage(resource),
253
269
  temperature: config.temperature,
254
270
  maxTokens: config.maxTokens,
255
- context: config.context,
256
271
  });
257
272
  }, [stateUnit, clearNewAnnotationId, resource]);
258
273
 
259
274
  // Resource-generate flow (GENERATE-FROM-BUTTON): drive the SAME yield progress$
260
- // the annotation path uses so the full AnnotateReferencesProgressWidget shows —
261
- // NOT a toast. `generateFromResource` is Phase 6 (the @semiont/sdk session);
262
- // this is declared RED until that method lands. Do not re-impl it here.
275
+ // the annotation path uses so the full `AssistProgress` widget shows — NOT a
276
+ // toast. Both paths are one `generate(context, options)` now: the context's
277
+ // focus.kind (resource here, annotation above) decides the shape.
263
278
  const handleResourceGenerateSubmit = useCallback((_resourceId: string, config: GenerationConfig) => {
264
- stateUnit?.yield.generateFromResource({
279
+ stateUnit?.yield.generate(config.context, {
265
280
  title: config.title,
266
281
  storageUri: config.storagePath,
267
282
  ...(config.prompt ? { prompt: config.prompt } : {}),
@@ -269,7 +284,6 @@ export function ResourceViewerPage({
269
284
  sourceLanguage: getLanguage(resource),
270
285
  temperature: config.temperature,
271
286
  maxTokens: config.maxTokens,
272
- context: config.context,
273
287
  });
274
288
  }, [stateUnit, resource]);
275
289
 
@@ -378,7 +392,7 @@ export function ResourceViewerPage({
378
392
  triggerSparkleAnimation(stored.payload.annotation.id);
379
393
  }, [triggerSparkleAnimation]);
380
394
 
381
- const handleReferenceNavigate = useCallback(({ resourceId }: { resourceId: string }) => {
395
+ const handleResourceOpen = useCallback(({ resourceId }: { resourceId: string }) => {
382
396
  if (routes.resourceDetail) {
383
397
  const path = routes.resourceDetail(resourceId);
384
398
  browser.emit('nav:push', { path, reason: 'reference-link' });
@@ -407,7 +421,7 @@ export function ResourceViewerPage({
407
421
  'yield:clone': handleResourceClone,
408
422
  'beckon:sparkle': handleAnnotationSparkle,
409
423
  'mark:added': handleAnnotationAdded,
410
- 'browse:reference-navigate': handleReferenceNavigate,
424
+ 'browse:resource-open': handleResourceOpen,
411
425
  'browse:entity-type-clicked': handleEntityTypeClicked,
412
426
  });
413
427
 
@@ -426,6 +440,11 @@ export function ResourceViewerPage({
426
440
  }
427
441
  }, [contentLoading, content, resource.name, announceResourceLoading, announceResourceLoaded]);
428
442
 
443
+ // Report the arrival on the wire (browse:resource-viewed) — same
444
+ // load-complete condition the announcement uses, so "viewed" means the
445
+ // content is actually on screen (GUIDED-TOUR P5, D6).
446
+ useResourceViewedReport(rUri, !contentLoading && !!content);
447
+
429
448
  // Derived state
430
449
  const documentEntityTypes = resource.entityTypes || [];
431
450
 
@@ -476,17 +495,14 @@ export function ResourceViewerPage({
476
495
  <AssistProgress
477
496
  progress={generationProgress}
478
497
  dataType="generation"
498
+ // The ending is the owner's fact and every owner must state it
499
+ // (CLEAN-PROGRESS D1). This call site used to pass neither
500
+ // `ended` nor `onDismiss`, so a finished generation stayed in its
501
+ // running form and then vanished on a timer.
502
+ ended={!isGenerating}
479
503
  onCancel={() => session?.client.job.cancelRequest('generation')}
480
- translations={{
481
- title: tg('progressTitle'),
482
- cancel: tg('progressCancel'),
483
- inProgress: tg('progressInProgress'),
484
- complete: tg('progressComplete'),
485
- failed: tg('progressFailed'),
486
- paramsTitle: ta('paramsTitle'),
487
- processing: (label) => ta('processing', { label }),
488
- close: ta('close'),
489
- }}
504
+ onDismiss={() => stateUnit?.yield.dismissProgress()}
505
+ translations={assistProgressTranslations(ta)}
490
506
  />
491
507
  )}
492
508
  {/* Scrollable body wrapper - contains document content, header is sibling above */}
@@ -637,6 +653,7 @@ export function ResourceViewerPage({
637
653
  state={streamStatus}
638
654
  eventCount={0}
639
655
  knowledgeBaseName={knowledgeBaseName}
656
+ collaborators={collaborators}
640
657
  />
641
658
  )}
642
659
 
@@ -657,6 +674,7 @@ export function ResourceViewerPage({
657
674
 
658
675
  {/* Reference Resolution Wizard */}
659
676
  <ReferenceWizardModal
677
+ {...(generationAgent ? { generationAgent } : {})}
660
678
  isOpen={wizardOpen}
661
679
  onClose={handleWizardClose}
662
680
  annotationId={wizardAnnotationId}
@@ -703,6 +721,7 @@ export function ResourceViewerPage({
703
721
  creativityCreative: tw('creativityCreative'),
704
722
  maxLength: tw('maxLength'),
705
723
  maxLengthHelp: tw('maxLengthHelp'),
724
+ maxLengthCeiling: tw('maxLengthCeiling'),
706
725
  maxResults: tw('maxResults'),
707
726
  semanticScoring: tw('semanticScoring'),
708
727
  semanticScoringHelp: tw('semanticScoringHelp'),
@@ -711,6 +730,7 @@ export function ResourceViewerPage({
711
730
 
712
731
  {/* Resource-generate flow (GENERATE-FROM-BUTTON) */}
713
732
  <ResourceGenerateModal
733
+ {...(generationAgent ? { generationAgent } : {})}
714
734
  isOpen={generateOpen}
715
735
  onClose={() => setGenerateOpen(false)}
716
736
  resourceId={rUri}
@@ -748,6 +768,7 @@ export function ResourceViewerPage({
748
768
  creativityCreative: tg('creativityCreative'),
749
769
  maxLength: tg('maxLength'),
750
770
  maxLengthHelp: tg('maxLengthHelp'),
771
+ maxLengthCeiling: tg('maxLengthCeiling'),
751
772
  generate: tg('generate'),
752
773
  }}
753
774
  />
@@ -0,0 +1,63 @@
1
+ /**
2
+ * The viewer's arrival report — GUIDED-TOUR P5 (D6).
3
+ *
4
+ * `browse:resource-viewed` is a REPORT, deliberately distinct from the
5
+ * imperative `browse:resource-open`: it fires when a resource has actually
6
+ * loaded on screen, however the user got there (followed cue, in-app link,
7
+ * back button, typed URL). The contract this pins: once per arrival — never
8
+ * on unrelated re-renders, never before the content is up, and again when
9
+ * the viewer moves to a different resource.
10
+ */
11
+ import { renderHook } from '@testing-library/react';
12
+ import { describe, it, expect, vi } from 'vitest';
13
+ import type { ReactNode } from 'react';
14
+ import { useResourceViewedReport } from '../useResourceViewedReport';
15
+ import { createTestSemiontWrapper } from '../../../../test-utils';
16
+ import { resourceId } from '@semiont/core';
17
+
18
+ function makeWrapper() {
19
+ const { SemiontWrapper, client } = createTestSemiontWrapper();
20
+ const wrapper = ({ children }: { children: ReactNode }) => (
21
+ <SemiontWrapper>{children}</SemiontWrapper>
22
+ );
23
+ const viewed = vi.spyOn(client.browse, 'resourceViewed').mockImplementation(() => {});
24
+ return { wrapper, viewed };
25
+ }
26
+
27
+ const RID_A = resourceId('resaaa11223344556677889900aabbcc');
28
+ const RID_B = resourceId('resbbb11223344556677889900aabbcc');
29
+
30
+ describe('useResourceViewedReport', () => {
31
+ it('reports once when the resource finishes loading, not before and not again on re-render', () => {
32
+ const { wrapper, viewed } = makeWrapper();
33
+
34
+ const { rerender } = renderHook(
35
+ ({ rid, loaded }: { rid: ReturnType<typeof resourceId>; loaded: boolean }) => useResourceViewedReport(rid, loaded),
36
+ { wrapper, initialProps: { rid: RID_A, loaded: false } },
37
+ );
38
+ expect(viewed).not.toHaveBeenCalled();
39
+
40
+ rerender({ rid: RID_A, loaded: true });
41
+ expect(viewed).toHaveBeenCalledTimes(1);
42
+
43
+ rerender({ rid: RID_A, loaded: true });
44
+ rerender({ rid: RID_A, loaded: true });
45
+ expect(viewed).toHaveBeenCalledTimes(1);
46
+ });
47
+
48
+ it('reports again when the viewer arrives at a different resource', () => {
49
+ const { wrapper, viewed } = makeWrapper();
50
+
51
+ const { rerender } = renderHook(
52
+ ({ rid, loaded }: { rid: ReturnType<typeof resourceId>; loaded: boolean }) => useResourceViewedReport(rid, loaded),
53
+ { wrapper, initialProps: { rid: RID_A, loaded: true } },
54
+ );
55
+ expect(viewed).toHaveBeenCalledTimes(1);
56
+
57
+ rerender({ rid: RID_B, loaded: false });
58
+ expect(viewed).toHaveBeenCalledTimes(1);
59
+
60
+ rerender({ rid: RID_B, loaded: true });
61
+ expect(viewed).toHaveBeenCalledTimes(2);
62
+ });
63
+ });
@@ -0,0 +1,33 @@
1
+ import { useEffect, useRef } from 'react';
2
+ import type { ResourceId } from '@semiont/core';
3
+ import { useSemiont } from '../../../session/SemiontProvider';
4
+ import { useObservable } from '../../../hooks/useObservable';
5
+
6
+ /**
7
+ * Report a resource arrival on `browse:resource-viewed` — GUIDED-TOUR P5 (D6).
8
+ *
9
+ * Fires exactly once per arrival, when the resource has finished loading —
10
+ * however the user got here: a followed cue, an in-app link, the back button,
11
+ * a typed URL. That "any means" property is why this lives at the viewer's
12
+ * load-complete transition rather than on any navigation intent path. A
13
+ * re-render never re-reports; arriving at a different resource does.
14
+ *
15
+ * This is the REPORT half of the tour protocol. The imperative half is
16
+ * `browse:resource-open`; they must never share a channel (D6 — the driver
17
+ * would hear its own commands, and one viewer's click would steer another's
18
+ * page).
19
+ */
20
+ export function useResourceViewedReport(rid: ResourceId, loaded: boolean): void {
21
+ const semiont = useSemiont();
22
+ const session = useObservable(semiont.activeSession$);
23
+ // Brand once at the boundary (BRAND-UPSTREAM): the caller already holds a
24
+ // ResourceId; this hook never re-brands.
25
+ const reported = useRef<ResourceId | null>(null);
26
+
27
+ useEffect(() => {
28
+ if (!loaded || !session) return;
29
+ if (reported.current === rid) return;
30
+ reported.current = rid;
31
+ session.client.browse.resourceViewed(rid);
32
+ }, [rid, loaded, session]);
33
+ }
@@ -56,7 +56,7 @@ function clientWithNamespaces(overrides: {
56
56
  },
57
57
  gather: { annotation: vi.fn(() => new Observable(() => {})) },
58
58
  match: { search: vi.fn(() => new Observable(() => {})) },
59
- yield: { fromAnnotation: vi.fn(() => new Observable(() => {})) },
59
+ yield: { fromContext: vi.fn(() => new Observable(() => {})) },
60
60
  bind: { body: vi.fn().mockResolvedValue(undefined) },
61
61
  });
62
62
  }
@@ -357,7 +357,7 @@ describe('createResourceViewerPageStateUnit — list failure states', () => {
357
357
  mark: { annotation: vi.fn(), delete: vi.fn(), assist: vi.fn(() => new Observable(() => {})) },
358
358
  gather: { annotation: vi.fn(() => new Observable(() => {})) },
359
359
  match: { search: vi.fn(() => new Observable(() => {})) },
360
- yield: { fromAnnotation: vi.fn(() => new Observable(() => {})) },
360
+ yield: { fromContext: vi.fn(() => new Observable(() => {})) },
361
361
  bind: { body: vi.fn().mockResolvedValue(undefined) },
362
362
  });
363
363
  const unit = createResourceViewerPageStateUnit(tc.session, RID, 'en', mockBrowse());
@@ -76,7 +76,7 @@ export function createResourceViewerPageStateUnit(
76
76
  const mark = createMarkStateUnit(client, resourceId);
77
77
  const gather = createGatherStateUnit(client, resourceId);
78
78
  const matchStateUnit = createMatchStateUnit(client, resourceId);
79
- const yieldStateUnit = createYieldStateUnit(client, resourceId, locale);
79
+ const yieldStateUnit = createYieldStateUnit(client, locale);
80
80
 
81
81
  disposer.add(beckon);
82
82
  // `browse` (ShellStateUnit) is a *passed-in* dependency — owned by `useShellStateUnit`,
@@ -487,8 +487,10 @@
487
487
  color: var(--semiont-color-white);
488
488
  }
489
489
 
490
+ /* Hover lifts. The dark active state is primary-600, so hovering to 700 sank
491
+ it — the opposite of the sibling .semiont-panel-button:hover above. */
490
492
  [data-theme="dark"] .semiont-panel-button-active:hover {
491
- background-color: var(--semiont-color-primary-700);
493
+ background-color: var(--semiont-color-primary-500);
492
494
  }
493
495
 
494
496
  /* ============================================
@@ -67,11 +67,6 @@
67
67
  color: var(--semiont-text-primary);
68
68
  }
69
69
 
70
- [data-theme="dark"] .semiont-form__checkbox-label,
71
- [data-theme="dark"] .semiont-form__radio-label {
72
- color: var(--semiont-text-primary);
73
- }
74
-
75
70
  /* Form actions */
76
71
  .semiont-form__actions {
77
72
  display: flex;
@@ -121,10 +116,6 @@
121
116
  padding: 0 0.5rem;
122
117
  }
123
118
 
124
- [data-theme="dark"] .semiont-form__legend {
125
- color: var(--semiont-text-primary);
126
- }
127
-
128
119
  /* Note: Disabled states for form controls are defined in their respective core files */
129
120
 
130
121
  /* Panel form elements */
@@ -218,10 +209,6 @@
218
209
  color: var(--semiont-text-primary);
219
210
  }
220
211
 
221
- [data-theme="dark"] .semiont-form-field__label {
222
- color: var(--semiont-text-primary);
223
- }
224
-
225
212
  .semiont-form-field__label--with-checkbox {
226
213
  display: flex;
227
214
  align-items: center;
@@ -235,20 +222,12 @@
235
222
  font-weight: var(--semiont-font-medium);
236
223
  }
237
224
 
238
- [data-theme="dark"] .semiont-form-field__info {
239
- color: var(--semiont-text-tertiary);
240
- }
241
-
242
225
  .semiont-form-field__char-count {
243
226
  font-size: var(--semiont-text-xs);
244
227
  color: var(--semiont-text-tertiary);
245
228
  text-align: right;
246
229
  }
247
230
 
248
- [data-theme="dark"] .semiont-form-field__char-count {
249
- color: var(--semiont-text-tertiary);
250
- }
251
-
252
231
  /* Note: Basic form controls (.semiont-textarea, .semiont-select, .semiont-checkbox)
253
232
  are now defined in /styles/core/ as they are fundamental UI elements.
254
233
  See: core/textareas.css, core/selects.css, core/checkboxes.css */
@@ -131,10 +131,6 @@
131
131
  color: var(--semiont-text-secondary);
132
132
  }
133
133
 
134
- [data-theme="dark"] .semiont-indicator__label {
135
- color: var(--semiont-text-secondary);
136
- }
137
-
138
134
  /* Indicator Badge (dot with count) */
139
135
  .semiont-indicator-badge {
140
136
  position: relative;
@@ -160,10 +160,6 @@
160
160
  min-width: 0;
161
161
  }
162
162
 
163
- [data-theme="dark"] .semiont-input--addon {
164
- border: none;
165
- }
166
-
167
163
  .semiont-input--addon:focus-visible,
168
164
  .semiont-input--addon:focus {
169
165
  outline: none;
@@ -83,7 +83,7 @@
83
83
  }
84
84
 
85
85
  [data-theme="dark"] .semiont-progress__fill--warning {
86
- background-color: var(--semiont-color-warning);
86
+ background-color: var(--semiont-color-warning-light);
87
87
  }
88
88
 
89
89
  .semiont-progress__fill--error {
@@ -91,7 +91,7 @@
91
91
  }
92
92
 
93
93
  [data-theme="dark"] .semiont-progress__fill--error {
94
- background-color: var(--semiont-color-error);
94
+ background-color: var(--semiont-color-error-light);
95
95
  }
96
96
 
97
97
  /* With Label */
@@ -108,10 +108,6 @@
108
108
  color: var(--semiont-text-secondary);
109
109
  }
110
110
 
111
- [data-theme="dark"] .semiont-progress__label {
112
- color: var(--semiont-text-secondary);
113
- }
114
-
115
111
  /* Indeterminate Progress */
116
112
  .semiont-progress--indeterminate .semiont-progress__fill {
117
113
  width: 30%;
@@ -140,31 +136,3 @@
140
136
  }
141
137
  }
142
138
 
143
- /* Legacy Compatibility Mappings (deprecated - use base classes above)
144
- These will be removed in a future version. Update components to use .semiont-progress */
145
- .semiont-progress-bar,
146
- .semiont-panel-progress {
147
- position: relative;
148
- width: 100%;
149
- height: 0.5rem;
150
- background-color: var(--semiont-bg-secondary);
151
- border-radius: var(--semiont-radius-full);
152
- overflow: hidden;
153
- }
154
-
155
- [data-theme="dark"] .semiont-progress-bar,
156
- [data-theme="dark"] .semiont-panel-progress {
157
- background-color: var(--semiont-bg-tertiary);
158
- }
159
-
160
- .semiont-progress-bar__fill,
161
- .semiont-panel-progress__fill {
162
- height: 100%;
163
- background-color: var(--semiont-color-primary-500);
164
- transition: width 0.3s ease;
165
- }
166
-
167
- [data-theme="dark"] .semiont-progress-bar__fill,
168
- [data-theme="dark"] .semiont-panel-progress__fill {
169
- background-color: var(--semiont-color-primary-500);
170
- }
@@ -212,10 +212,6 @@
212
212
  color: var(--semiont-text-secondary);
213
213
  }
214
214
 
215
- [data-theme="dark"] .semiont-slider__labels {
216
- color: var(--semiont-text-secondary);
217
- }
218
-
219
215
  /* Small labels variant — reduced font size for compact inline contexts */
220
216
  .semiont-slider__labels--small {
221
217
  font-size: 0.6875rem;
@@ -17,10 +17,6 @@
17
17
  color: var(--semiont-text-primary);
18
18
  }
19
19
 
20
- [data-theme="dark"] .semiont-toggle-label__text {
21
- color: var(--semiont-text-primary);
22
- }
23
-
24
20
  /* Toggle Switch */
25
21
  .semiont-toggle {
26
22
  position: relative;
@@ -203,6 +203,19 @@
203
203
  color: var(--semiont-color-gray-300);
204
204
  }
205
205
 
206
+ /* Semantic-fallback caption: explains WHY these results, without competing
207
+ with them. Quieter than the heading it sits under (SEMANTIC-FALLBACK P3b). */
208
+ .semiont-card__documents-note {
209
+ margin: -0.25rem 0 0.75rem;
210
+ font-size: 0.8125rem;
211
+ font-style: italic;
212
+ color: var(--semiont-color-gray-600);
213
+ }
214
+
215
+ [data-theme="dark"] .semiont-card__documents-note {
216
+ color: var(--semiont-color-gray-400);
217
+ }
218
+
206
219
  .semiont-card__warning {
207
220
  padding: 1rem;
208
221
  background: var(--semiont-color-yellow-50);
@@ -173,20 +173,12 @@
173
173
  color: var(--semiont-text-secondary);
174
174
  }
175
175
 
176
- [data-theme="dark"] .semiont-browse-view__content h6 {
177
- color: var(--semiont-text-secondary);
178
- }
179
-
180
176
  .semiont-browse-view__content p {
181
177
  margin-bottom: 1em;
182
178
  line-height: var(--semiont-leading-relaxed);
183
179
  color: var(--semiont-text-primary);
184
180
  }
185
181
 
186
- [data-theme="dark"] .semiont-browse-view__content p {
187
- color: var(--semiont-text-primary);
188
- }
189
-
190
182
  .semiont-browse-view__content ul,
191
183
  .semiont-browse-view__content ol {
192
184
  margin-bottom: 1em;
@@ -207,10 +199,6 @@
207
199
  color: var(--semiont-text-primary);
208
200
  }
209
201
 
210
- [data-theme="dark"] .semiont-browse-view__content li {
211
- color: var(--semiont-text-primary);
212
- }
213
-
214
202
  /* Nested lists */
215
203
  .semiont-browse-view__content ul ul,
216
204
  .semiont-browse-view__content ol ol,
@@ -627,19 +615,11 @@
627
615
  margin-bottom: 0.5rem;
628
616
  }
629
617
 
630
- [data-theme="dark"] .semiont-page__title {
631
- color: var(--semiont-text-primary);
632
- }
633
-
634
618
  .semiont-page__subtitle {
635
619
  color: var(--semiont-text-secondary);
636
620
  font-size: 1rem;
637
621
  }
638
622
 
639
- [data-theme="dark"] .semiont-page__subtitle {
640
- color: var(--semiont-text-secondary);
641
- }
642
-
643
623
  .semiont-page__reference-notice {
644
624
  background-color: var(--semiont-color-blue-50);
645
625
  border: 1px solid var(--semiont-color-blue-200);
@@ -246,7 +246,7 @@
246
246
  }
247
247
 
248
248
  [data-theme="dark"] .semiont-assessment-star {
249
- color: var(--semiont-color-warning);
249
+ color: var(--semiont-color-warning-light);
250
250
  }
251
251
 
252
252
  .semiont-assessment-star:hover {
@@ -258,5 +258,5 @@
258
258
  }
259
259
 
260
260
  [data-theme="dark"] .semiont-assessment-star--filled {
261
- color: var(--semiont-color-warning);
261
+ color: var(--semiont-color-warning-light);
262
262
  }
@@ -20,10 +20,6 @@
20
20
  color: var(--semiont-text-primary);
21
21
  }
22
22
 
23
- [data-theme="dark"] .semiont-history-panel__title {
24
- color: var(--semiont-text-primary);
25
- }
26
-
27
23
  .semiont-history-panel__loading {
28
24
  margin: 0;
29
25
  padding: 0.5rem;
@@ -102,10 +98,6 @@
102
98
  color: var(--semiont-text-primary);
103
99
  }
104
100
 
105
- [data-theme="dark"] .semiont-history-group__type {
106
- color: var(--semiont-text-primary);
107
- }
108
-
109
101
  .semiont-history-group__type-icon {
110
102
  font-size: var(--semiont-panel-icon-size);
111
103
  }
@@ -115,10 +107,6 @@
115
107
  color: var(--semiont-text-tertiary);
116
108
  }
117
109
 
118
- [data-theme="dark"] .semiont-history-group__count {
119
- color: var(--semiont-text-tertiary);
120
- }
121
-
122
110
  .semiont-history-group__events {
123
111
  display: flex;
124
112
  flex-direction: column;
@@ -174,19 +162,11 @@
174
162
  margin-left: auto;
175
163
  }
176
164
 
177
- [data-theme="dark"] .semiont-history-event__timestamp {
178
- color: var(--semiont-text-tertiary);
179
- }
180
-
181
165
  .semiont-history-event__text--quoted {
182
166
  font-style: italic;
183
167
  color: var(--semiont-text-secondary);
184
168
  }
185
169
 
186
- [data-theme="dark"] .semiont-history-event__text--quoted {
187
- color: var(--semiont-text-secondary);
188
- }
189
-
190
170
  /* Entity types container - tags themselves use .semiont-tag from core */
191
171
  .semiont-history-event__entity-types {
192
172
  display: flex;
@@ -214,19 +194,11 @@
214
194
  color: var(--semiont-text-tertiary);
215
195
  }
216
196
 
217
- [data-theme="dark"] .semiont-history-event__detail {
218
- color: var(--semiont-text-tertiary);
219
- }
220
-
221
197
  .semiont-history-event__detail-value {
222
198
  color: var(--semiont-text-primary);
223
199
  font-weight: var(--semiont-font-medium);
224
200
  }
225
201
 
226
- [data-theme="dark"] .semiont-history-event__detail-value {
227
- color: var(--semiont-text-primary);
228
- }
229
-
230
202
  .semiont-history-event__detail-value--uppercase {
231
203
  text-transform: uppercase;
232
204
  }
@@ -236,10 +208,6 @@
236
208
  color: var(--semiont-text-primary);
237
209
  }
238
210
 
239
- [data-theme="dark"] .semiont-history-event__action {
240
- color: var(--semiont-text-primary);
241
- }
242
-
243
211
  .semiont-history-event__text {
244
212
  font-size: var(--semiont-text-sm);
245
213
  color: var(--semiont-text-secondary);
@@ -250,10 +218,6 @@
250
218
  line-height: 1.5;
251
219
  }
252
220
 
253
- [data-theme="dark"] .semiont-history-event__text {
254
- color: var(--semiont-text-secondary);
255
- }
256
-
257
221
  .semiont-history-event__meta {
258
222
  display: flex;
259
223
  align-items: center;
@@ -276,10 +240,6 @@
276
240
  color: var(--semiont-text-tertiary);
277
241
  }
278
242
 
279
- [data-theme="dark"] .semiont-history-event__time {
280
- color: var(--semiont-text-tertiary);
281
- }
282
-
283
243
  .semiont-history-event__link {
284
244
  font-size: var(--semiont-text-xs);
285
245
  color: var(--semiont-color-primary-600);
@@ -19,6 +19,3 @@
19
19
  color: var(--semiont-text-primary);
20
20
  }
21
21
 
22
- [data-theme="dark"] .semiont-user-panel__title {
23
- color: var(--semiont-text-primary);
24
- }