@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
@@ -6,10 +6,12 @@
6
6
  */
7
7
 
8
8
  import React, { useState, useEffect, useCallback } from 'react';
9
- import type { components, ResourceDescriptor, ResourceId, GatheredContext, EventMap } from '@semiont/core';
9
+ import { useResourceViewedReport } from '../hooks/useResourceViewedReport';
10
+ import type { components, ResourceDescriptor, ResourceId, EventMap } from '@semiont/core';
10
11
  import type { ConnectionState } from '@semiont/core';
11
12
  import { annotationId } from '@semiont/core';
12
- import { getLanguage, getPrimaryRepresentation, getPrimaryMediaType, capabilitiesOf } from '@semiont/core';
13
+ import type { ComposeParams } from '../../../components/modals/ComposeStep';
14
+ import { getLanguage, getPrimaryRepresentation, getPrimaryMediaType, capabilitiesOf, extensionForMediaType } from '@semiont/core';
13
15
  import { ANNOTATORS } from '@semiont/react-ui';
14
16
  import { ErrorBoundary } from '@semiont/react-ui';
15
17
  import { AnnotationHistory } from '@semiont/react-ui';
@@ -24,9 +26,11 @@ 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';
33
+ import { useGenerationArrival } from '../../../hooks/useGenerationArrival';
30
34
  import { useTheme } from '../../../contexts/ThemeContext';
31
35
  import { useLineNumbers } from '../../../contexts/LineNumbersContext';
32
36
  import { useHoverDelay } from '../../../hooks/useHoverDelay';
@@ -42,7 +46,6 @@ import { useShellStateUnit } from '../../../hooks/useShellStateUnit';
42
46
  import { useTranslations } from '../../../contexts/TranslationContext';
43
47
  import { ReferenceWizardModal } from '../../../components/modals/ReferenceWizardModal';
44
48
  import { ResourceGenerateModal } from '../../../components/modals/ResourceGenerateModal';
45
- import { AssistProgress } from '../../../components/AssistProgress';
46
49
  import type { GenerationConfig } from '../../../components/modals/ConfigureGenerationStep';
47
50
 
48
51
  type SemiontResource = ResourceDescriptor;
@@ -117,7 +120,8 @@ 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 mark:body-updated - Reference resolved via a linking-add operation (sparkle, both loci)
124
+ * @subscribes browse:resource-open - Open a resource in the viewer (local links + the launcher's tour verbs)
121
125
  * @subscribes browse:entity-type-clicked - Navigate filtered by entity type
122
126
  *
123
127
  * Outcome-notification channels (mark:create-error, mark:delete-error,
@@ -140,13 +144,24 @@ export function ResourceViewerPage({
140
144
  // Translations
141
145
  const tw = useTranslations('ReferenceWizard');
142
146
  const tg = useTranslations('ResourceGenerate');
143
- const ta = useTranslations('AssistProgress');
144
147
 
145
148
  const browser = useSemiont();
146
149
  const session = useObservable(browser.activeSession$);
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) => {
@@ -162,7 +177,7 @@ export function ResourceViewerPage({
162
177
  const { theme } = useTheme();
163
178
  const { showLineNumbers } = useLineNumbers();
164
179
  const { hoverDelayMs } = useHoverDelay();
165
- const { triggerSparkleAnimation, clearNewAnnotationId, newAnnotationIds } = useResourceAnnotations();
180
+ const { triggerSparkleAnimation, clearSparkle, sparkleAnnotationIds } = useResourceAnnotations();
166
181
 
167
182
  // Render mode chooses the content path: 'text' decodes inline; 'image'
168
183
  // and 'pdf' go through the media-token (binary) path. 'none'/registry-miss
@@ -224,9 +239,28 @@ 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;
243
+ const generationOutcome = useObservable(stateUnit?.yield.outcome$) ?? null;
244
+
245
+ // GENERATION-ARRIVAL P2: a completion witnessed on this page reveals the
246
+ // derivation edge the worker minted — the annotations panel opens on
247
+ // References, scrolls to the provenance reference, and its sparkle is
248
+ // re-armed (the mark:added glow burned its window unseen). Never navigates
249
+ // (A2); a held outcome on remount stays quiet (D6, inside the hook).
250
+ const handleGenerationArrival = useCallback((annId: string) => {
251
+ browser.emit('panel:open', { panel: 'annotations', scrollToAnnotationId: annId, motivation: 'linking' });
252
+ triggerSparkleAnimation(annId);
253
+ }, [browser, triggerSparkleAnimation]);
254
+ useGenerationArrival(generationOutcome, annotations, handleGenerationArrival);
255
+
227
256
  const gatherContext = useObservable(stateUnit?.gather.context$) ?? null;
228
257
  const gatherLoading = useObservable(stateUnit?.gather.loading$) ?? false;
229
258
  const gatherError = useObservable(stateUnit?.gather.error$) ?? null;
259
+ // Resource-gather slots (FLOW-LIFECYCLE-CONVERGENCE D2a: separate from the
260
+ // annotation slots above — the two gathers can be live at once).
261
+ const resourceGatherContext = useObservable(stateUnit?.gather.resourceContext$) ?? null;
262
+ const resourceGatherLoading = useObservable(stateUnit?.gather.resourceLoading$) ?? false;
263
+ const resourceGatherError = useObservable(stateUnit?.gather.resourceError$) ?? null;
230
264
  const wizardState = useObservable(stateUnit?.wizard$);
231
265
  const wizardOpen = wizardState?.open ?? false;
232
266
  const wizardAnnotationId = wizardState?.annotationId ?? null;
@@ -240,8 +274,8 @@ export function ResourceViewerPage({
240
274
  }, [stateUnit]);
241
275
 
242
276
  const handleWizardGenerateSubmit = useCallback((referenceId: string, config: GenerationConfig) => {
243
- clearNewAnnotationId(annotationId(referenceId));
244
- stateUnit?.yield.generate(referenceId, {
277
+ clearSparkle(annotationId(referenceId));
278
+ stateUnit?.yield.generate(config.context, {
245
279
  title: config.title,
246
280
  storageUri: config.storagePath,
247
281
  prompt: config.prompt,
@@ -252,16 +286,15 @@ export function ResourceViewerPage({
252
286
  sourceLanguage: getLanguage(resource),
253
287
  temperature: config.temperature,
254
288
  maxTokens: config.maxTokens,
255
- context: config.context,
256
289
  });
257
- }, [stateUnit, clearNewAnnotationId, resource]);
290
+ }, [stateUnit, clearSparkle, resource]);
258
291
 
259
292
  // 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.
293
+ // the annotation path uses so the full `AssistProgress` widget shows — NOT a
294
+ // toast. Both paths are one `generate(context, options)` now: the context's
295
+ // focus.kind (resource here, annotation above) decides the shape.
263
296
  const handleResourceGenerateSubmit = useCallback((_resourceId: string, config: GenerationConfig) => {
264
- stateUnit?.yield.generateFromResource({
297
+ stateUnit?.yield.generate(config.context, {
265
298
  title: config.title,
266
299
  storageUri: config.storagePath,
267
300
  ...(config.prompt ? { prompt: config.prompt } : {}),
@@ -269,7 +302,6 @@ export function ResourceViewerPage({
269
302
  sourceLanguage: getLanguage(resource),
270
303
  temperature: config.temperature,
271
304
  maxTokens: config.maxTokens,
272
- context: config.context,
273
305
  });
274
306
  }, [stateUnit, resource]);
275
307
 
@@ -287,26 +319,43 @@ export function ResourceViewerPage({
287
319
  }
288
320
  }, [rUri, semiont, showSuccess, showError]);
289
321
 
290
- const handleWizardComposeNavigate = useCallback((
291
- context: GatheredContext,
292
- annId: string,
293
- resId: string,
294
- title: string,
295
- entTypes: string[],
296
- ) => {
297
- // Store context in sessionStorage for the compose page
298
- sessionStorage.setItem(`gather-context:${annId}`, JSON.stringify(context));
299
- const params = new URLSearchParams({
300
- annotationUri: annId,
301
- sourceDocumentId: resId,
302
- name: title,
303
- entityTypes: entTypes.join(','),
304
- });
305
- browser.emit('nav:push', {
306
- path: `/know/compose?${params.toString()}`,
307
- reason: 'compose-from-wizard',
308
- });
309
- }, [browser]);
322
+ // COMPOSE-IN-MODAL P3: create-and-link, in place. The old flow stashed the
323
+ // context in sessionStorage and navigated to the compose page; the modal
324
+ // already holds the context, so the side-channel dies with the mode.
325
+ // Text-only by design — uploads stay on the standalone compose page.
326
+ const handleWizardComposeSubmit = useCallback(async (referenceId: string, params: ComposeParams) => {
327
+ if (!semiont) throw new Error('No active session');
328
+ try {
329
+ const format = 'text/markdown';
330
+ const file = new File(
331
+ [new Blob([params.content], { type: format })],
332
+ params.name + extensionForMediaType(format),
333
+ { type: format },
334
+ );
335
+ const newResourceId = await new Promise<ResourceId>((resolve, reject) => {
336
+ semiont.yield.resource({
337
+ name: params.name,
338
+ file,
339
+ format,
340
+ entityTypes: params.entityTypes,
341
+ language: params.language,
342
+ storageUri: params.storagePath,
343
+ }).subscribe({
344
+ next: (event) => { if (event.phase === 'finished') resolve(event.resourceId); },
345
+ error: reject,
346
+ });
347
+ });
348
+ await semiont.bind.body(
349
+ rUri,
350
+ annotationId(referenceId),
351
+ [{ op: 'add', item: { type: 'SpecificResource' as const, source: newResourceId, purpose: 'linking' as const } }],
352
+ );
353
+ showSuccess('Reference successfully linked to the new resource');
354
+ } catch (error) {
355
+ showError(`Failed to compose resource: ${error instanceof Error ? error.message : String(error)}`);
356
+ throw error; // ComposeStep re-enables its footer on rejection
357
+ }
358
+ }, [semiont, rUri, showSuccess, showError]);
310
359
 
311
360
  // Add resource to open tabs when it loads, and record it as this KB's
312
361
  // last-viewed for the /know landing route to resume from. Both are per-KB
@@ -378,7 +427,20 @@ export function ResourceViewerPage({
378
427
  triggerSparkleAnimation(stored.payload.annotation.id);
379
428
  }, [triggerSparkleAnimation]);
380
429
 
381
- const handleReferenceNavigate = useCallback(({ resourceId }: { resourceId: string }) => {
430
+ // RESOLUTION-SPARKLE D2: a reference resolving Compose, Search Link, a
431
+ // Generate job landing, or a remote collaborator — is a body update whose
432
+ // operations add a linking SpecificResource. Exactly that sparkles; an unlink
433
+ // (remove) or an entity-tag body change stays dark.
434
+ const handleAnnotationBodyUpdated = useCallback((stored: EventMap['mark:body-updated']) => {
435
+ const resolves = stored.payload.operations.some(
436
+ (op) => op.op === 'add' && op.item.type === 'SpecificResource' && op.item.purpose === 'linking',
437
+ );
438
+ if (resolves) {
439
+ triggerSparkleAnimation(stored.payload.annotationId);
440
+ }
441
+ }, [triggerSparkleAnimation]);
442
+
443
+ const handleResourceOpen = useCallback(({ resourceId }: { resourceId: string }) => {
382
444
  if (routes.resourceDetail) {
383
445
  const path = routes.resourceDetail(resourceId);
384
446
  browser.emit('nav:push', { path, reason: 'reference-link' });
@@ -407,7 +469,8 @@ export function ResourceViewerPage({
407
469
  'yield:clone': handleResourceClone,
408
470
  'beckon:sparkle': handleAnnotationSparkle,
409
471
  'mark:added': handleAnnotationAdded,
410
- 'browse:reference-navigate': handleReferenceNavigate,
472
+ 'mark:body-updated': handleAnnotationBodyUpdated,
473
+ 'browse:resource-open': handleResourceOpen,
411
474
  'browse:entity-type-clicked': handleEntityTypeClicked,
412
475
  });
413
476
 
@@ -426,6 +489,11 @@ export function ResourceViewerPage({
426
489
  }
427
490
  }, [contentLoading, content, resource.name, announceResourceLoading, announceResourceLoaded]);
428
491
 
492
+ // Report the arrival on the wire (browse:resource-viewed) — same
493
+ // load-complete condition the announcement uses, so "viewed" means the
494
+ // content is actually on screen (GUIDED-TOUR P5, D6).
495
+ useResourceViewedReport(rUri, !contentLoading && !!content);
496
+
429
497
  // Derived state
430
498
  const documentEntityTypes = resource.entityTypes || [];
431
499
 
@@ -471,24 +539,6 @@ export function ResourceViewerPage({
471
539
  </h2>
472
540
  </div>
473
541
  </div>
474
- {/* Resource-generation progress (GENERATE-FROM-BUTTON P7) — no annotationId ⇒ a resource-gen job */}
475
- {generationProgress && !generationProgress.annotationId && (
476
- <AssistProgress
477
- progress={generationProgress}
478
- dataType="generation"
479
- 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
- }}
490
- />
491
- )}
492
542
  {/* Scrollable body wrapper - contains document content, header is sibling above */}
493
543
  <div className="semiont-document-viewer__scrollable-body" lang={getLanguage(resource) || undefined}>
494
544
  <ErrorBoundary
@@ -528,7 +578,7 @@ export function ResourceViewerPage({
528
578
  onSelectionMotivationChange={toolbarPrefs.setSelectionMotivation}
529
579
  shape={toolbarPrefs.shape}
530
580
  onShapeChange={toolbarPrefs.setShape}
531
- newAnnotationIds={newAnnotationIds}
581
+ sparkleAnnotationIds={sparkleAnnotationIds}
532
582
  generatingReferenceId={generationProgress?.annotationId ?? null}
533
583
  showLineNumbers={showLineNumbers}
534
584
  hoverDelayMs={hoverDelayMs}
@@ -578,6 +628,7 @@ export function ResourceViewerPage({
578
628
  onRetryAnnotations={stateUnit?.annotations.retry}
579
629
  entityTypesError={entityTypesError}
580
630
  generatingReferenceId={generationProgress?.annotationId ?? null}
631
+ sparkleAnnotationIds={sparkleAnnotationIds}
581
632
  referencedBy={referencedBy}
582
633
  referencedByLoading={referencedByLoading}
583
634
  referencedByError={referencedByError}
@@ -588,7 +639,7 @@ export function ResourceViewerPage({
588
639
  scrollToAnnotationId={scrollToAnnotationId}
589
640
  hoveredAnnotationId={hoveredAnnotationId}
590
641
  onScrollCompleted={onScrollCompleted}
591
- initialTab={panelInitialTab?.tab as any}
642
+ initialTab={panelInitialTab?.tab}
592
643
  initialTabGeneration={panelInitialTab?.generation}
593
644
  Link={Link}
594
645
  routes={routes}
@@ -628,6 +679,15 @@ export function ResourceViewerPage({
628
679
  wasDerivedFrom={resource.wasDerivedFrom}
629
680
  generator={resource.generator as components['schemas']['Agent'] | components['schemas']['Agent'][] | undefined}
630
681
  onGenerate={() => setGenerateOpen(true)}
682
+ // The panel is generation's progress surface (GENERATE-FROM-
683
+ // RESOURCE D7); no annotationId ⇒ a resource-gen job — the
684
+ // annotation path's frame renders in the reference wizard.
685
+ isGenerating={isGenerating}
686
+ generationProgress={
687
+ generationProgress && !generationProgress.annotationId ? generationProgress : null
688
+ }
689
+ generationOutcome={generationOutcome}
690
+ onDismissProgress={() => stateUnit?.yield.dismissProgress()}
631
691
  />
632
692
  )}
633
693
 
@@ -637,6 +697,7 @@ export function ResourceViewerPage({
637
697
  state={streamStatus}
638
698
  eventCount={0}
639
699
  knowledgeBaseName={knowledgeBaseName}
700
+ collaborators={collaborators}
640
701
  />
641
702
  )}
642
703
 
@@ -657,32 +718,40 @@ export function ResourceViewerPage({
657
718
 
658
719
  {/* Reference Resolution Wizard */}
659
720
  <ReferenceWizardModal
721
+ {...(generationAgent ? { generationAgent } : {})}
660
722
  isOpen={wizardOpen}
661
723
  onClose={handleWizardClose}
662
724
  annotationId={wizardAnnotationId}
663
725
  resourceId={wizardResourceId}
664
726
  defaultTitle={wizardDefaultTitle}
665
727
  entityTypes={wizardEntityTypes}
728
+ resourceName={resource.name}
666
729
  locale={locale}
667
730
  context={gatherContext}
668
731
  contextLoading={gatherLoading}
669
732
  contextError={gatherError}
670
733
  onGenerateSubmit={handleWizardGenerateSubmit}
671
734
  onLinkResource={handleWizardLinkResource}
672
- onComposeNavigate={handleWizardComposeNavigate}
735
+ onComposeSubmit={handleWizardComposeSubmit}
736
+ entityTypeOptions={allEntityTypes}
737
+ hoverDelayMs={hoverDelayMs}
673
738
  translations={{
674
- gatherTitle: tw('gatherTitle'),
675
- configureGenerationTitle: tw('configureGenerationTitle'),
676
- configureSearchTitle: tw('configureSearchTitle'),
677
- searchResultsTitle: tw('searchResultsTitle'),
739
+ resolveTitle: tw('resolveTitle'),
678
740
  sourceContextLabel: tw('sourceContextLabel'),
679
741
  connectionsLabel: tw('connectionsLabel'),
680
742
  citedByLabel: tw('citedByLabel'),
681
743
  userHintLabel: tw('userHintLabel'),
744
+ userHintEffect: tw('userHintEffect'),
682
745
  userHintPlaceholder: tw('userHintPlaceholder'),
746
+ graphPaneTitle: tw('graphPaneTitle'),
747
+ graphEmpty: tw('graphEmpty'),
748
+ resourceLinkLabel: tw('resourceLinkLabel'),
749
+ corpusPaneTitle: tw('corpusPaneTitle'),
750
+ corpusEmpty: tw('corpusEmpty'),
751
+ excludedReceipt: tw('excludedReceipt'),
752
+ machineRead: tw('machineRead'),
683
753
  loadingContext: tw('loadingContext'),
684
754
  failedContext: tw('failedContext'),
685
- cancel: tw('cancel'),
686
755
  search: tw('search'),
687
756
  searching: tw('searching'),
688
757
  generate: tw('generate'),
@@ -694,6 +763,7 @@ export function ResourceViewerPage({
694
763
  noResults: tw('noResults'),
695
764
  resourceTitle: tw('resourceTitle'),
696
765
  resourceTitlePlaceholder: tw('resourceTitlePlaceholder'),
766
+ saveLocation: tw('saveLocation'),
697
767
  additionalInstructions: tw('additionalInstructions'),
698
768
  additionalInstructionsPlaceholder: tw('additionalInstructionsPlaceholder'),
699
769
  language: tw('language'),
@@ -703,42 +773,63 @@ export function ResourceViewerPage({
703
773
  creativityCreative: tw('creativityCreative'),
704
774
  maxLength: tw('maxLength'),
705
775
  maxLengthHelp: tw('maxLengthHelp'),
776
+ maxLengthCeiling: tw('maxLengthCeiling'),
706
777
  maxResults: tw('maxResults'),
707
778
  semanticScoring: tw('semanticScoring'),
708
779
  semanticScoringHelp: tw('semanticScoringHelp'),
780
+ searchFailed: tw('searchFailed'),
781
+ entityTypes: tw('entityTypes'),
782
+ contentLabel: tw('contentLabel'),
783
+ createAndLink: tw('createAndLink'),
784
+ creatingAndLinking: tw('creatingAndLinking'),
785
+ discardDraftPrompt: tw('discardDraftPrompt'),
786
+ discardDraft: tw('discardDraft'),
787
+ keepEditing: tw('keepEditing'),
709
788
  }}
710
789
  />
711
790
 
712
791
  {/* Resource-generate flow (GENERATE-FROM-BUTTON) */}
713
792
  <ResourceGenerateModal
793
+ {...(generationAgent ? { generationAgent } : {})}
714
794
  isOpen={generateOpen}
715
795
  onClose={() => setGenerateOpen(false)}
716
796
  resourceId={rUri}
717
- defaultTitle=""
797
+ // Seed the proposed title from the source resource's name (GFR D4/A4);
798
+ // the field stays editable and required.
799
+ defaultTitle={resource.name}
718
800
  locale={locale}
719
801
  entityTypeOptions={allEntityTypes}
720
802
  onGenerateSubmit={handleResourceGenerateSubmit}
803
+ gatherContext={resourceGatherContext}
804
+ gatherLoading={resourceGatherLoading}
805
+ gatherError={resourceGatherError}
806
+ onGather={(options) => stateUnit?.gather.gatherResource(rUri, options)}
721
807
  translations={{
722
- gatherTitle: tg('gatherTitle'),
723
- reviewTitle: tg('reviewTitle'),
724
- configureTitle: tg('configureTitle'),
725
- next: tg('next'),
726
- back: tg('back'),
727
- cancel: tg('cancel'),
808
+ title: tg('title'),
728
809
  gatherIntro: tg('gatherIntro'),
729
810
  includeContent: tg('includeContent'),
730
811
  includeSummary: tg('includeSummary'),
731
812
  gatherDepth: tg('gatherDepth'),
732
813
  gatherMaxResources: tg('gatherMaxResources'),
733
814
  gatherButton: tg('gatherButton'),
734
- excludeLabel: tg('excludeLabel'),
815
+ editGather: tg('editGather'),
816
+ recallLabel: tg('recallLabel'),
735
817
  loadingContext: tg('loadingContext'),
736
818
  failedContext: tg('failedContext'),
737
819
  sourceContextLabel: tg('sourceContextLabel'),
738
820
  connectionsLabel: tg('connectionsLabel'),
739
821
  citedByLabel: tg('citedByLabel'),
822
+ graphPaneTitle: tg('graphPaneTitle'),
823
+ graphEmpty: tg('graphEmpty'),
824
+ resourceLinkLabel: tg('resourceLinkLabel'),
825
+ corpusPaneTitle: tg('corpusPaneTitle'),
826
+ corpusEmpty: tg('corpusEmpty'),
827
+ excludedReceipt: tg('excludedReceipt'),
828
+ machineRead: tg('machineRead'),
829
+ score: tg('score'),
740
830
  resourceTitle: tg('resourceTitle'),
741
831
  resourceTitlePlaceholder: tg('resourceTitlePlaceholder'),
832
+ saveLocation: tg('saveLocation'),
742
833
  additionalInstructions: tg('additionalInstructions'),
743
834
  additionalInstructionsPlaceholder: tg('additionalInstructionsPlaceholder'),
744
835
  language: tg('language'),
@@ -748,7 +839,11 @@ export function ResourceViewerPage({
748
839
  creativityCreative: tg('creativityCreative'),
749
840
  maxLength: tg('maxLength'),
750
841
  maxLengthHelp: tg('maxLengthHelp'),
842
+ maxLengthCeiling: tg('maxLengthCeiling'),
751
843
  generate: tg('generate'),
844
+ discardDraftPrompt: tg('discardDraftPrompt'),
845
+ discardDraft: tg('discardDraft'),
846
+ keepEditing: tg('keepEditing'),
752
847
  }}
753
848
  />
754
849
  </div>
@@ -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
  /* ============================================