@semiont/react-ui 0.5.9 → 0.5.11

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 (209) hide show
  1. package/README.md +1 -1
  2. package/dist/{PdfAnnotationCanvas.client-U4EVBZEV.js → PdfAnnotationCanvas.client-KWQ7XDWT.js} +5 -2
  3. package/dist/PdfAnnotationCanvas.client-KWQ7XDWT.js.map +1 -0
  4. package/dist/{ar-WA47UUWA.js → ar-ZHKO4XDR.js} +45 -4
  5. package/dist/ar-ZHKO4XDR.js.map +1 -0
  6. package/dist/{bn-5ANDRIU6.js → bn-3BUDKAK5.js} +45 -4
  7. package/dist/bn-3BUDKAK5.js.map +1 -0
  8. package/dist/{chunk-YBGN4ACY.js → chunk-O23TLLXW.js} +1 -6
  9. package/dist/{chunk-WHUGODTB.js → chunk-PFUXQPWJ.js} +127 -127
  10. package/dist/{chunk-WHUGODTB.js.map → chunk-PFUXQPWJ.js.map} +1 -1
  11. package/dist/{chunk-GERSK2DY.js → chunk-R52YAGHK.js} +42 -1
  12. package/dist/chunk-R52YAGHK.js.map +1 -0
  13. package/dist/{cs-3RU7F4JX.js → cs-2EFEUKTW.js} +45 -4
  14. package/dist/cs-2EFEUKTW.js.map +1 -0
  15. package/dist/{da-GSW5P5HG.js → da-SUD3Q44Q.js} +45 -4
  16. package/dist/da-SUD3Q44Q.js.map +1 -0
  17. package/dist/{de-JUAUYF4Z.js → de-6IC5QRX7.js} +45 -4
  18. package/dist/de-6IC5QRX7.js.map +1 -0
  19. package/dist/{el-JNLWCKEC.js → el-Q4NAXFRC.js} +45 -4
  20. package/dist/el-Q4NAXFRC.js.map +1 -0
  21. package/dist/{en-RBN2GUHF.js → en-P2L4X27N.js} +5 -3
  22. package/dist/{es-WPOX225H.js → es-YUKZBD4W.js} +45 -4
  23. package/dist/es-YUKZBD4W.js.map +1 -0
  24. package/dist/{fa-TAEP6N77.js → fa-OJ47LILE.js} +45 -4
  25. package/dist/fa-OJ47LILE.js.map +1 -0
  26. package/dist/{fi-ZVZHANSP.js → fi-QFFL5CUB.js} +45 -4
  27. package/dist/fi-QFFL5CUB.js.map +1 -0
  28. package/dist/{fr-VLZW7M4N.js → fr-BOQZZWP4.js} +45 -4
  29. package/dist/fr-BOQZZWP4.js.map +1 -0
  30. package/dist/{he-QFAFYA77.js → he-LXDY3TFA.js} +45 -4
  31. package/dist/he-LXDY3TFA.js.map +1 -0
  32. package/dist/{hi-AO3DQPCV.js → hi-HOJER77V.js} +45 -4
  33. package/dist/hi-HOJER77V.js.map +1 -0
  34. package/dist/{id-GTXF42WM.js → id-I5WQ6EWD.js} +45 -4
  35. package/dist/id-I5WQ6EWD.js.map +1 -0
  36. package/dist/index.css +150 -112
  37. package/dist/index.css.map +1 -1
  38. package/dist/index.d.ts +403 -45
  39. package/dist/index.js +4660 -18860
  40. package/dist/index.js.map +1 -1
  41. package/dist/integrations/css-modules-helper.js +1 -1
  42. package/dist/integrations/styled-components-theme.js +1 -1
  43. package/dist/{it-AS5GM232.js → it-UYH642IJ.js} +45 -4
  44. package/dist/it-UYH642IJ.js.map +1 -0
  45. package/dist/{ja-GZ4HLUOF.js → ja-JTVWAC4L.js} +45 -4
  46. package/dist/ja-JTVWAC4L.js.map +1 -0
  47. package/dist/{ko-A4XUXFGJ.js → ko-IHXFMOIZ.js} +45 -4
  48. package/dist/ko-IHXFMOIZ.js.map +1 -0
  49. package/dist/{ms-YBAO3S6K.js → ms-JH25VFLO.js} +45 -4
  50. package/dist/ms-JH25VFLO.js.map +1 -0
  51. package/dist/{nl-3TJGIIIU.js → nl-SURXW7IQ.js} +45 -4
  52. package/dist/nl-SURXW7IQ.js.map +1 -0
  53. package/dist/{no-4AXIQPRQ.js → no-HXIKQFDZ.js} +45 -4
  54. package/dist/no-HXIKQFDZ.js.map +1 -0
  55. package/dist/{pl-5GP6GKXO.js → pl-7A4KU6XK.js} +45 -4
  56. package/dist/pl-7A4KU6XK.js.map +1 -0
  57. package/dist/{pt-26Y6JFG5.js → pt-6MGYP7Q5.js} +45 -4
  58. package/dist/pt-6MGYP7Q5.js.map +1 -0
  59. package/dist/{ro-C7UXFRWJ.js → ro-IROCHNIA.js} +45 -4
  60. package/dist/ro-IROCHNIA.js.map +1 -0
  61. package/dist/{sv-44DVD76T.js → sv-WDJD75CV.js} +45 -4
  62. package/dist/sv-WDJD75CV.js.map +1 -0
  63. package/dist/test-utils.js +9 -4
  64. package/dist/test-utils.js.map +1 -1
  65. package/dist/{th-GIQRTBOY.js → th-RIO2E6B3.js} +45 -4
  66. package/dist/th-RIO2E6B3.js.map +1 -0
  67. package/dist/{tr-WMQWO4D6.js → tr-TZ7ERCIN.js} +45 -4
  68. package/dist/tr-TZ7ERCIN.js.map +1 -0
  69. package/dist/{uk-I7ML6RGG.js → uk-ZFWDKLUE.js} +45 -4
  70. package/dist/uk-ZFWDKLUE.js.map +1 -0
  71. package/dist/{vi-FGWECASQ.js → vi-UIOSP2JG.js} +45 -4
  72. package/dist/vi-UIOSP2JG.js.map +1 -0
  73. package/dist/{zh-L5FN73XC.js → zh-6R6YUY46.js} +45 -4
  74. package/dist/zh-6R6YUY46.js.map +1 -0
  75. package/package.json +18 -9
  76. package/src/components/AnnotateReferencesProgressWidget.tsx +35 -16
  77. package/src/components/ResourceTagsInline.tsx +94 -11
  78. package/src/components/StatusDisplay.css +36 -0
  79. package/src/components/__tests__/AnnotateReferencesProgressWidget.test.tsx +41 -91
  80. package/src/components/__tests__/ResourceTagsInline.test.tsx +101 -3
  81. package/src/components/annotation/AnnotateToolbar.tsx +53 -28
  82. package/src/components/annotation/__tests__/AnnotateToolbar.display.test.tsx +76 -0
  83. package/src/components/annotation/__tests__/AnnotateToolbar.test.tsx +124 -360
  84. package/src/components/image-annotation/SvgDrawingCanvas.tsx +5 -1
  85. package/src/components/modals/ConfigureGatherStep.tsx +102 -0
  86. package/src/components/modals/ContextSummary.tsx +15 -6
  87. package/src/components/modals/GatherContextStep.tsx +104 -59
  88. package/src/components/modals/ResourceGenerateModal.tsx +258 -0
  89. package/src/components/modals/__tests__/ConfigureGatherStep.test.tsx +82 -0
  90. package/src/components/modals/__tests__/GatherContextStep.resource.test.tsx +107 -0
  91. package/src/components/modals/__tests__/ResourceGenerateModal.test.tsx +197 -0
  92. package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +5 -0
  93. package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +6 -6
  94. package/src/components/resource/AnnotateView.tsx +59 -38
  95. package/src/components/resource/BrowseView.tsx +159 -133
  96. package/src/components/resource/ResourceViewer.tsx +119 -128
  97. package/src/components/resource/__tests__/AnnotateView.embeddable.test.tsx +55 -0
  98. package/src/components/resource/__tests__/BrowseView.embeddable.test.tsx +49 -0
  99. package/src/components/resource/__tests__/BrowseView.links.test.tsx +60 -0
  100. package/src/components/resource/__tests__/BrowseView.overlay-async.test.tsx +113 -0
  101. package/src/components/resource/__tests__/BrowseView.reference-hover.test.tsx +161 -0
  102. package/src/components/resource/__tests__/BrowseView.registry.test.tsx +43 -0
  103. package/src/components/resource/__tests__/BrowseView.test.tsx +35 -22
  104. package/src/components/resource/__tests__/ResourceViewer.embeddable.test.tsx +63 -0
  105. package/src/components/resource/__tests__/ResourceViewer.mode-switch.test.tsx +12 -7
  106. package/src/components/resource/__tests__/ResourceViewer.toolbar-opt-out.test.tsx +135 -0
  107. package/src/components/resource/__tests__/ResourceViewer.toolbar-prefs.test.tsx +200 -0
  108. package/src/components/resource/__tests__/browse-renderers.test.tsx +34 -0
  109. package/src/components/resource/__tests__/inline-embedding.test.tsx +81 -0
  110. package/src/components/resource/__tests__/media-completeness.test.tsx +88 -0
  111. package/src/components/resource/browse-renderers.tsx +63 -0
  112. package/src/components/resource/panels/AssessmentPanel.tsx +4 -0
  113. package/src/components/resource/panels/CommentsPanel.tsx +4 -0
  114. package/src/components/resource/panels/HighlightPanel.tsx +4 -0
  115. package/src/components/resource/panels/ReferencesPanel.tsx +14 -0
  116. package/src/components/resource/panels/ResourceInfoPanel.tsx +22 -0
  117. package/src/components/resource/panels/TaggingPanel.tsx +4 -0
  118. package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +5 -2
  119. package/src/components/resource/panels/__tests__/AssessmentPanel.test.tsx +1 -0
  120. package/src/components/resource/panels/__tests__/CommentsPanel.test.tsx +1 -0
  121. package/src/components/resource/panels/__tests__/HighlightPanel.annotationProgress.test.tsx +16 -16
  122. package/src/components/resource/panels/__tests__/ReferencesPanel.observable-flow.test.tsx +1 -1
  123. package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +1 -0
  124. package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +26 -0
  125. package/src/components/resource/panels/__tests__/TaggingPanel.test.tsx +1 -0
  126. package/src/components/toolbar/Toolbar.css +14 -0
  127. package/src/features/admin-exchange/state/__tests__/exchange-state-unit.test.ts +14 -0
  128. package/src/features/admin-exchange/state/exchange-state-unit.ts +4 -2
  129. package/src/features/admin-security/state/__tests__/admin-security-state-unit.test.ts +14 -0
  130. package/src/features/admin-security/state/admin-security-state-unit.ts +4 -2
  131. package/src/features/admin-users/state/__tests__/admin-users-state-unit.test.ts +12 -5
  132. package/src/features/admin-users/state/admin-users-state-unit.ts +4 -2
  133. package/src/features/auth-welcome/state/__tests__/welcome-state-unit.test.ts +11 -0
  134. package/src/features/auth-welcome/state/welcome-state-unit.ts +1 -1
  135. package/src/features/moderate-entity-tags/state/__tests__/entity-tags-state-unit.test.ts +14 -0
  136. package/src/features/moderate-entity-tags/state/entity-tags-state-unit.ts +4 -2
  137. package/src/features/resource-compose/components/ResourceComposePage.tsx +13 -7
  138. package/src/features/resource-compose/state/__tests__/compose-page-state-unit.test.ts +13 -0
  139. package/src/features/resource-compose/state/compose-page-state-unit.ts +5 -2
  140. package/src/features/resource-discovery/state/__tests__/discover-state-unit.test.ts +15 -9
  141. package/src/features/resource-discovery/state/discover-state-unit.ts +5 -2
  142. package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +7 -17
  143. package/src/features/resource-viewer/components/ResourceViewerPage.tsx +119 -17
  144. package/src/features/resource-viewer/state/__tests__/resource-loader-state-unit.test.ts +11 -0
  145. package/src/features/resource-viewer/state/__tests__/resource-viewer-page-state-unit.test.ts +30 -0
  146. package/src/features/resource-viewer/state/resource-loader-state-unit.ts +1 -1
  147. package/src/features/resource-viewer/state/resource-viewer-page-state-unit.ts +7 -16
  148. package/src/styles/features/resource-viewer.css +19 -0
  149. package/translations/ar.json +41 -2
  150. package/translations/bn.json +41 -2
  151. package/translations/cs.json +41 -2
  152. package/translations/da.json +41 -2
  153. package/translations/de.json +41 -2
  154. package/translations/el.json +41 -2
  155. package/translations/en.json +39 -0
  156. package/translations/es.json +41 -2
  157. package/translations/fa.json +41 -2
  158. package/translations/fi.json +41 -2
  159. package/translations/fr.json +41 -2
  160. package/translations/he.json +41 -2
  161. package/translations/hi.json +41 -2
  162. package/translations/id.json +41 -2
  163. package/translations/it.json +41 -2
  164. package/translations/ja.json +41 -2
  165. package/translations/ko.json +41 -2
  166. package/translations/ms.json +41 -2
  167. package/translations/nl.json +41 -2
  168. package/translations/no.json +41 -2
  169. package/translations/pl.json +41 -2
  170. package/translations/pt.json +41 -2
  171. package/translations/ro.json +41 -2
  172. package/translations/sv.json +41 -2
  173. package/translations/th.json +41 -2
  174. package/translations/tr.json +41 -2
  175. package/translations/uk.json +41 -2
  176. package/translations/vi.json +41 -2
  177. package/translations/zh.json +41 -2
  178. package/dist/PdfAnnotationCanvas.client-U4EVBZEV.js.map +0 -1
  179. package/dist/ar-WA47UUWA.js.map +0 -1
  180. package/dist/bn-5ANDRIU6.js.map +0 -1
  181. package/dist/chunk-GERSK2DY.js.map +0 -1
  182. package/dist/cs-3RU7F4JX.js.map +0 -1
  183. package/dist/da-GSW5P5HG.js.map +0 -1
  184. package/dist/de-JUAUYF4Z.js.map +0 -1
  185. package/dist/el-JNLWCKEC.js.map +0 -1
  186. package/dist/es-WPOX225H.js.map +0 -1
  187. package/dist/fa-TAEP6N77.js.map +0 -1
  188. package/dist/fi-ZVZHANSP.js.map +0 -1
  189. package/dist/fr-VLZW7M4N.js.map +0 -1
  190. package/dist/he-QFAFYA77.js.map +0 -1
  191. package/dist/hi-AO3DQPCV.js.map +0 -1
  192. package/dist/id-GTXF42WM.js.map +0 -1
  193. package/dist/it-AS5GM232.js.map +0 -1
  194. package/dist/ja-GZ4HLUOF.js.map +0 -1
  195. package/dist/ko-A4XUXFGJ.js.map +0 -1
  196. package/dist/ms-YBAO3S6K.js.map +0 -1
  197. package/dist/nl-3TJGIIIU.js.map +0 -1
  198. package/dist/no-4AXIQPRQ.js.map +0 -1
  199. package/dist/pl-5GP6GKXO.js.map +0 -1
  200. package/dist/pt-26Y6JFG5.js.map +0 -1
  201. package/dist/ro-C7UXFRWJ.js.map +0 -1
  202. package/dist/sv-44DVD76T.js.map +0 -1
  203. package/dist/th-GIQRTBOY.js.map +0 -1
  204. package/dist/tr-WMQWO4D6.js.map +0 -1
  205. package/dist/uk-I7ML6RGG.js.map +0 -1
  206. package/dist/vi-FGWECASQ.js.map +0 -1
  207. package/dist/zh-L5FN73XC.js.map +0 -1
  208. /package/dist/{chunk-YBGN4ACY.js.map → chunk-O23TLLXW.js.map} +0 -0
  209. /package/dist/{en-RBN2GUHF.js.map → en-P2L4X27N.js.map} +0 -0
@@ -59,8 +59,9 @@ interface UnifiedAnnotationsPanelProps {
59
59
  referencedBy?: any[];
60
60
  referencedByLoading?: boolean;
61
61
 
62
- // Resource context
63
- resourceId?: string;
62
+ // Resource context — threaded to every per-motivation panel, which stamps it
63
+ // as `source` on mark:submit (multi-viewer routing).
64
+ resourceId: string;
64
65
  initialTab?: TabKey;
65
66
  initialTabGeneration?: number; // Generation counter for tab switching
66
67
 
@@ -244,6 +245,7 @@ export function UnifiedAnnotationsPanel(props: UnifiedAnnotationsPanelProps) {
244
245
 
245
246
  // Common props for all annotation panels
246
247
  const commonProps = {
248
+ resourceId: props.resourceId,
247
249
  annotations,
248
250
  pendingAnnotation: props.pendingAnnotation,
249
251
  isAssisting,
@@ -268,6 +270,7 @@ export function UnifiedAnnotationsPanel(props: UnifiedAnnotationsPanelProps) {
268
270
  if (activeTab === 'reference') {
269
271
  return (
270
272
  <ReferencesPanel
273
+ resourceId={commonProps.resourceId}
271
274
  annotations={commonProps.annotations}
272
275
  pendingAnnotation={commonProps.pendingAnnotation}
273
276
  isAssisting={commonProps.isAssisting}
@@ -140,6 +140,7 @@ const createPendingAnnotation = (exact: string) => ({
140
140
 
141
141
  describe('AssessmentPanel Component', () => {
142
142
  const defaultProps = {
143
+ resourceId: 'res-1',
143
144
  annotations: mockAssessments.empty,
144
145
  pendingAnnotation: null,
145
146
  };
@@ -141,6 +141,7 @@ const createPendingAnnotation = (exact: string) => ({
141
141
 
142
142
  describe('CommentsPanel Component', () => {
143
143
  const defaultProps = {
144
+ resourceId: 'res-1',
144
145
  annotations: mockComments.empty,
145
146
  pendingAnnotation: null,
146
147
  };
@@ -74,7 +74,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
74
74
  describe('Detection Progress Prop Passing', () => {
75
75
  it('should pass progress to AssistSection when provided', () => {
76
76
  renderWithProviders(
77
- <HighlightPanel
77
+ <HighlightPanel resourceId="res-1"
78
78
  annotations={mockAnnotations}
79
79
  pendingAnnotation={null}
80
80
  isAssisting={true}
@@ -93,7 +93,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
93
93
 
94
94
  it('should pass null progress to AssistSection', () => {
95
95
  renderWithProviders(
96
- <HighlightPanel
96
+ <HighlightPanel resourceId="res-1"
97
97
  annotations={mockAnnotations}
98
98
  pendingAnnotation={null}
99
99
  isAssisting={false}
@@ -109,7 +109,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
109
109
 
110
110
  it('should pass undefined progress to AssistSection', () => {
111
111
  renderWithProviders(
112
- <HighlightPanel
112
+ <HighlightPanel resourceId="res-1"
113
113
  annotations={mockAnnotations}
114
114
  pendingAnnotation={null}
115
115
  isAssisting={false}
@@ -125,7 +125,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
125
125
 
126
126
  it('should keep progress visible after detection completes (isAssisting=false)', () => {
127
127
  renderWithProviders(
128
- <HighlightPanel
128
+ <HighlightPanel resourceId="res-1"
129
129
  annotations={mockAnnotations}
130
130
  pendingAnnotation={null}
131
131
  isAssisting={false}
@@ -146,7 +146,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
146
146
 
147
147
  it('should pass progress with request parameters to AssistSection', () => {
148
148
  renderWithProviders(
149
- <HighlightPanel
149
+ <HighlightPanel resourceId="res-1"
150
150
  annotations={mockAnnotations}
151
151
  pendingAnnotation={null}
152
152
  isAssisting={true}
@@ -172,7 +172,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
172
172
  describe('Annotate Mode Toggling', () => {
173
173
  it('should render AssistSection when annotateMode is true', () => {
174
174
  renderWithProviders(
175
- <HighlightPanel
175
+ <HighlightPanel resourceId="res-1"
176
176
  annotations={mockAnnotations}
177
177
  pendingAnnotation={null}
178
178
  isAssisting={false}
@@ -186,7 +186,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
186
186
 
187
187
  it('should NOT render AssistSection when annotateMode is false', () => {
188
188
  renderWithProviders(
189
- <HighlightPanel
189
+ <HighlightPanel resourceId="res-1"
190
190
  annotations={mockAnnotations}
191
191
  pendingAnnotation={null}
192
192
  isAssisting={false}
@@ -200,7 +200,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
200
200
 
201
201
  it('should hide progress when switching to browse mode (annotateMode=false)', () => {
202
202
  const { rerender } = renderWithProviders(
203
- <HighlightPanel
203
+ <HighlightPanel resourceId="res-1"
204
204
  annotations={mockAnnotations}
205
205
  pendingAnnotation={null}
206
206
  isAssisting={true}
@@ -217,7 +217,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
217
217
 
218
218
  // Switch to browse mode
219
219
  rerender(
220
- <HighlightPanel
220
+ <HighlightPanel resourceId="res-1"
221
221
  annotations={mockAnnotations}
222
222
  pendingAnnotation={null}
223
223
  isAssisting={true}
@@ -238,7 +238,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
238
238
  describe('State Combinations', () => {
239
239
  it('should handle isAssisting=true with no progress (starting state)', () => {
240
240
  renderWithProviders(
241
- <HighlightPanel
241
+ <HighlightPanel resourceId="res-1"
242
242
  annotations={mockAnnotations}
243
243
  pendingAnnotation={null}
244
244
  isAssisting={true}
@@ -253,7 +253,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
253
253
 
254
254
  it('should handle isAssisting=false with progress (final state)', () => {
255
255
  renderWithProviders(
256
- <HighlightPanel
256
+ <HighlightPanel resourceId="res-1"
257
257
  annotations={mockAnnotations}
258
258
  pendingAnnotation={null}
259
259
  isAssisting={false}
@@ -274,7 +274,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
274
274
 
275
275
  it('should handle multiple progress updates', () => {
276
276
  const { rerender } = renderWithProviders(
277
- <HighlightPanel
277
+ <HighlightPanel resourceId="res-1"
278
278
  annotations={mockAnnotations}
279
279
  pendingAnnotation={null}
280
280
  isAssisting={true}
@@ -291,7 +291,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
291
291
 
292
292
  // Update to analyzing
293
293
  rerender(
294
- <HighlightPanel
294
+ <HighlightPanel resourceId="res-1"
295
295
  annotations={mockAnnotations}
296
296
  pendingAnnotation={null}
297
297
  isAssisting={true}
@@ -309,7 +309,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
309
309
 
310
310
  // Update to complete
311
311
  rerender(
312
- <HighlightPanel
312
+ <HighlightPanel resourceId="res-1"
313
313
  annotations={mockAnnotations}
314
314
  pendingAnnotation={null}
315
315
  isAssisting={false}
@@ -330,7 +330,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
330
330
  describe('Highlights List Rendering', () => {
331
331
  it('should render highlights list alongside detection progress', () => {
332
332
  renderWithProviders(
333
- <HighlightPanel
333
+ <HighlightPanel resourceId="res-1"
334
334
  annotations={mockAnnotations}
335
335
  pendingAnnotation={null}
336
336
  isAssisting={true}
@@ -350,7 +350,7 @@ describe('HighlightPanel + AssistSection Integration', () => {
350
350
 
351
351
  it('should show empty state when no highlights', () => {
352
352
  renderWithProviders(
353
- <HighlightPanel
353
+ <HighlightPanel resourceId="res-1"
354
354
  annotations={[]}
355
355
  pendingAnnotation={null}
356
356
  isAssisting={false}
@@ -69,7 +69,7 @@ const mockRoutes = { resourceDetail: (id: string) => `/resource/${id}` } as any;
69
69
  function ObservableHarness({ source$ }: { source$: BehaviorSubject<string[]> }) {
70
70
  const entityTypes = useObservable(source$) ?? [];
71
71
  return (
72
- <ReferencesPanel
72
+ <ReferencesPanel resourceId="res-1"
73
73
  annotations={[]}
74
74
  isAssisting={false}
75
75
  progress={null}
@@ -86,6 +86,7 @@ describe('ReferencesPanel Component', () => {
86
86
  } as any;
87
87
 
88
88
  const defaultProps = {
89
+ resourceId: 'res-1',
89
90
  allEntityTypes: ['Person', 'Organization', 'Location', 'Date'],
90
91
  isAssisting: false,
91
92
  progress: null,
@@ -20,6 +20,8 @@ vi.mock('../../../../contexts/TranslationContext', () => ({
20
20
  storageUri: 'Storage',
21
21
  clone: 'Clone',
22
22
  cloneDescription: 'Generate a shareable clone link for this resource',
23
+ generate: 'Generate',
24
+ generateDescription: "Generate a new resource from this one's context",
23
25
  archive: 'Archive',
24
26
  archiveDescription: 'Move this resource to archived status',
25
27
  unarchive: 'Unarchive',
@@ -269,6 +271,30 @@ describe('ResourceInfoPanel Component', () => {
269
271
  });
270
272
  });
271
273
 
274
+ describe('Generate Action', () => {
275
+ it('renders the Generate button when onGenerate is provided', () => {
276
+ renderWithEventBus(
277
+ <ResourceInfoPanel {...defaultProps} onGenerate={() => {}} />
278
+ );
279
+ expect(screen.getByRole('button', { name: /Generate/i })).toBeInTheDocument();
280
+ expect(screen.getByText("Generate a new resource from this one's context")).toBeInTheDocument();
281
+ });
282
+
283
+ it('hides the Generate button when onGenerate is omitted', () => {
284
+ renderWithEventBus(<ResourceInfoPanel {...defaultProps} />);
285
+ expect(screen.queryByRole('button', { name: /Generate/i })).not.toBeInTheDocument();
286
+ });
287
+
288
+ it('calls onGenerate when clicked', () => {
289
+ const onGenerate = vi.fn();
290
+ renderWithEventBus(
291
+ <ResourceInfoPanel {...defaultProps} onGenerate={onGenerate} />
292
+ );
293
+ fireEvent.click(screen.getByRole('button', { name: /Generate/i }));
294
+ expect(onGenerate).toHaveBeenCalledTimes(1);
295
+ });
296
+ });
297
+
272
298
  describe('Provenance Section', () => {
273
299
  it('should not render provenance section when no provenance data', () => {
274
300
  renderWithEventBus(<ResourceInfoPanel {...defaultProps} />);
@@ -185,6 +185,7 @@ const createPendingAnnotation = (exact: string) => ({
185
185
 
186
186
  describe('TaggingPanel Component', () => {
187
187
  const defaultProps = {
188
+ resourceId: 'res-1',
188
189
  annotations: mockTags.empty,
189
190
  pendingAnnotation: null,
190
191
  };
@@ -219,3 +219,17 @@
219
219
  }
220
220
 
221
221
  /* Popup Components */
222
+
223
+ /* ── Compact display form ────────────────────────────────────────────────────
224
+ * Display-only variant for inline embeds: icon-only, tight, chromeless. The
225
+ * bar's functionality is identical — only its form changes. */
226
+ .semiont-annotate-toolbar--compact {
227
+ gap: 0.375rem;
228
+ padding: 0.25rem 0;
229
+ background: transparent; /* the host container owns the background */
230
+ border-bottom: none;
231
+ }
232
+
233
+ .semiont-annotate-toolbar--compact .semiont-dropdown-label {
234
+ display: none; /* icon-only collapsed triggers; expanded menus keep labels */
235
+ }
@@ -2,6 +2,7 @@ import { describe, it, expect, vi } from 'vitest';
2
2
  import { firstValueFrom, of, throwError } from 'rxjs';
3
3
  import type { ShellStateUnit } from '../../../../state/shell-state-unit';
4
4
  import { createExchangeStateUnit } from '../exchange-state-unit';
5
+ import { assertStateUnitAxioms, disposeProbe } from '@semiont/core/testing';
5
6
 
6
7
  function mockBrowse(): ShellStateUnit {
7
8
  return { dispose: vi.fn() } as unknown as ShellStateUnit;
@@ -169,3 +170,16 @@ describe('createExchangeStateUnit', () => {
169
170
  stateUnit.dispose();
170
171
  });
171
172
  });
173
+
174
+ describe('ExchangeStateUnit — StateUnit axioms', () => {
175
+ it('satisfies the StateUnit axioms (incl. A7-passed: never disposes the injected browse)', () => {
176
+ assertStateUnitAxioms({
177
+ setup: () => {
178
+ const browse = disposeProbe();
179
+ return { unit: createExchangeStateUnit(browse as unknown as ShellStateUnit, vi.fn(), vi.fn()), passedIn: [browse] };
180
+ },
181
+ surfaces: (u) => [u.selectedFile$, u.preview$, u.importPhase$, u.importMessage$, u.importResult$, u.isExporting$, u.isImporting$],
182
+ invocations: (u) => [() => u.cancelImport(), () => u.doImport()],
183
+ });
184
+ });
185
+ });
@@ -1,7 +1,7 @@
1
1
  import { BehaviorSubject, lastValueFrom, type Observable } from 'rxjs';
2
2
  import { tap } from 'rxjs/operators';
3
3
  import { createDisposer } from '@semiont/sdk';
4
- import type { StateUnit } from '@semiont/sdk';
4
+ import type { StateUnit } from '@semiont/core';
5
5
  import type { ShellStateUnit } from '../../../state/shell-state-unit';
6
6
  import type { BackendDownload, ProgressEvent } from '@semiont/core';
7
7
 
@@ -33,7 +33,9 @@ export function createExchangeStateUnit(
33
33
  importFn: (file: File) => Observable<ProgressEvent>,
34
34
  ): ExchangeStateUnit {
35
35
  const disposer = createDisposer();
36
- disposer.add(browse);
36
+ // `browse` (ShellStateUnit) is a *passed-in* dependency owned by the caller
37
+ // (`useShellStateUnit`), not this unit — do NOT add it to the disposer (it's the
38
+ // shared, app-scoped shell). See packages/sdk/docs/STATE-UNITS.md (composition rule).
37
39
 
38
40
  const selectedFile$ = new BehaviorSubject<File | null>(null);
39
41
  const preview$ = new BehaviorSubject<ImportPreview | null>(null);
@@ -4,6 +4,7 @@ import { filter } from 'rxjs/operators';
4
4
  import type { SemiontClient } from '@semiont/sdk';
5
5
  import type { ShellStateUnit } from '../../../../state/shell-state-unit';
6
6
  import { createAdminSecurityStateUnit } from '../admin-security-state-unit';
7
+ import { assertStateUnitAxioms, disposeProbe } from '@semiont/core/testing';
7
8
 
8
9
  function mockBrowse(): ShellStateUnit {
9
10
  return { dispose: vi.fn() } as unknown as ShellStateUnit;
@@ -66,3 +67,16 @@ describe('createAdminSecurityStateUnit', () => {
66
67
  stateUnit.dispose();
67
68
  });
68
69
  });
70
+
71
+ describe('AdminSecurityStateUnit — StateUnit axioms', () => {
72
+ it('satisfies the StateUnit axioms (incl. A7-passed: never disposes the injected browse)', () => {
73
+ assertStateUnitAxioms({
74
+ setup: () => {
75
+ const browse = disposeProbe();
76
+ const client = mockClient(vi.fn().mockResolvedValue({ providers: [], allowedDomains: [] }));
77
+ return { unit: createAdminSecurityStateUnit(client, browse as unknown as ShellStateUnit), passedIn: [browse] };
78
+ },
79
+ surfaces: (u) => [u.providers$, u.allowedDomains$, u.isLoading$],
80
+ });
81
+ });
82
+ });
@@ -1,6 +1,6 @@
1
1
  import { BehaviorSubject, type Observable } from 'rxjs';
2
2
  import { createDisposer } from '@semiont/sdk';
3
- import type { StateUnit } from '@semiont/sdk';
3
+ import type { StateUnit } from '@semiont/core';
4
4
  import type { ShellStateUnit } from '../../../state/shell-state-unit';
5
5
  import type { SemiontClient } from '@semiont/sdk';
6
6
 
@@ -16,7 +16,9 @@ export function createAdminSecurityStateUnit(
16
16
  browse: ShellStateUnit,
17
17
  ): AdminSecurityStateUnit {
18
18
  const disposer = createDisposer();
19
- disposer.add(browse);
19
+ // `browse` (ShellStateUnit) is a *passed-in* dependency owned by the caller
20
+ // (`useShellStateUnit`), not this unit — do NOT add it to the disposer (it's the
21
+ // shared, app-scoped shell). See packages/sdk/docs/STATE-UNITS.md (composition rule).
20
22
 
21
23
  const providers$ = new BehaviorSubject<unknown[]>([]);
22
24
  const allowedDomains$ = new BehaviorSubject<string[]>([]);
@@ -4,6 +4,7 @@ import { filter } from 'rxjs/operators';
4
4
  import type { SemiontClient } from '@semiont/sdk';
5
5
  import type { ShellStateUnit } from '../../../../state/shell-state-unit';
6
6
  import { createAdminUsersStateUnit } from '../admin-users-state-unit';
7
+ import { assertStateUnitAxioms, disposeProbe } from '@semiont/core/testing';
7
8
 
8
9
  function mockBrowse(): ShellStateUnit {
9
10
  return { dispose: vi.fn() } as unknown as ShellStateUnit;
@@ -77,10 +78,16 @@ describe('createAdminUsersStateUnit', () => {
77
78
  stateUnit.dispose();
78
79
  });
79
80
 
80
- it('disposes browse on dispose', () => {
81
- const browse = mockBrowse();
82
- const stateUnit = createAdminUsersStateUnit(mockClient(), browse);
83
- stateUnit.dispose();
84
- expect(browse.dispose).toHaveBeenCalled();
81
+ });
82
+
83
+ describe('AdminUsersStateUnit StateUnit axioms', () => {
84
+ it('satisfies the StateUnit axioms (incl. A7-passed: never disposes the injected browse)', () => {
85
+ assertStateUnitAxioms({
86
+ setup: () => {
87
+ const browse = disposeProbe();
88
+ return { unit: createAdminUsersStateUnit(mockClient(), browse as unknown as ShellStateUnit), passedIn: [browse] };
89
+ },
90
+ surfaces: (u) => [u.users$, u.stats$, u.usersLoading$, u.statsLoading$],
91
+ });
85
92
  });
86
93
  });
@@ -1,7 +1,7 @@
1
1
  import { BehaviorSubject, type Observable } from 'rxjs';
2
2
  import { userDID } from '@semiont/core';
3
3
  import { createDisposer } from '@semiont/sdk';
4
- import type { StateUnit } from '@semiont/sdk';
4
+ import type { StateUnit } from '@semiont/core';
5
5
  import type { ShellStateUnit } from '../../../state/shell-state-unit';
6
6
  import type { SemiontClient } from '@semiont/sdk';
7
7
 
@@ -19,7 +19,9 @@ export function createAdminUsersStateUnit(
19
19
  browse: ShellStateUnit,
20
20
  ): AdminUsersStateUnit {
21
21
  const disposer = createDisposer();
22
- disposer.add(browse);
22
+ // `browse` (ShellStateUnit) is a *passed-in* dependency owned by the caller
23
+ // (`useShellStateUnit`), not this unit — do NOT add it to the disposer (it's the
24
+ // shared, app-scoped shell). See packages/sdk/docs/STATE-UNITS.md (composition rule).
23
25
 
24
26
  const users$ = new BehaviorSubject<unknown[]>([]);
25
27
  const stats$ = new BehaviorSubject<unknown | null>(null);
@@ -3,6 +3,7 @@ import { firstValueFrom } from 'rxjs';
3
3
  import { filter } from 'rxjs/operators';
4
4
  import type { SemiontClient } from '@semiont/sdk';
5
5
  import { createWelcomeStateUnit } from '../welcome-state-unit';
6
+ import { assertStateUnitAxioms } from '@semiont/core/testing';
6
7
 
7
8
  function mockClient(overrides: {
8
9
  getMe?: ReturnType<typeof vi.fn>;
@@ -84,3 +85,13 @@ describe('createWelcomeStateUnit', () => {
84
85
  stateUnit.dispose();
85
86
  });
86
87
  });
88
+
89
+ describe('WelcomeStateUnit — StateUnit axioms', () => {
90
+ it('satisfies the StateUnit axioms', () => {
91
+ assertStateUnitAxioms({
92
+ setup: () => createWelcomeStateUnit(mockClient()),
93
+ surfaces: (u) => [u.userData$, u.isProcessing$],
94
+ invocations: (u) => [() => u.acceptTerms()],
95
+ });
96
+ });
97
+ });
@@ -1,6 +1,6 @@
1
1
  import { BehaviorSubject, type Observable } from 'rxjs';
2
2
  import { createDisposer } from '@semiont/sdk';
3
- import type { StateUnit } from '@semiont/sdk';
3
+ import type { StateUnit } from '@semiont/core';
4
4
  import type { SemiontClient } from '@semiont/sdk';
5
5
 
6
6
  export interface WelcomeStateUnit extends StateUnit {
@@ -4,6 +4,7 @@ import { filter } from 'rxjs/operators';
4
4
  import type { SemiontClient } from '@semiont/sdk';
5
5
  import type { ShellStateUnit } from '../../../../state/shell-state-unit';
6
6
  import { createEntityTagsStateUnit } from '../entity-tags-state-unit';
7
+ import { assertStateUnitAxioms, disposeProbe } from '@semiont/core/testing';
7
8
 
8
9
  function mockBrowse(): ShellStateUnit {
9
10
  return { dispose: vi.fn() } as unknown as ShellStateUnit;
@@ -100,3 +101,16 @@ describe('createEntityTagsStateUnit', () => {
100
101
  stateUnit.dispose();
101
102
  });
102
103
  });
104
+
105
+ describe('EntityTagsStateUnit — StateUnit axioms', () => {
106
+ it('satisfies the StateUnit axioms (incl. A7-passed: never disposes the injected browse)', () => {
107
+ assertStateUnitAxioms({
108
+ setup: () => {
109
+ const browse = disposeProbe();
110
+ return { unit: createEntityTagsStateUnit(mockClient(), browse as unknown as ShellStateUnit), passedIn: [browse] };
111
+ },
112
+ surfaces: (u) => [u.newTag$, u.error$, u.isAdding$],
113
+ invocations: (u) => [() => u.setNewTag(''), () => u.addTag()],
114
+ });
115
+ });
116
+ });
@@ -1,6 +1,6 @@
1
1
  import { BehaviorSubject, type Observable, map } from 'rxjs';
2
2
  import { createDisposer } from '@semiont/sdk';
3
- import type { StateUnit } from '@semiont/sdk';
3
+ import type { StateUnit } from '@semiont/core';
4
4
  import type { ShellStateUnit } from '../../../state/shell-state-unit';
5
5
  import type { SemiontClient } from '@semiont/sdk';
6
6
 
@@ -20,7 +20,9 @@ export function createEntityTagsStateUnit(
20
20
  browse: ShellStateUnit,
21
21
  ): EntityTagsStateUnit {
22
22
  const disposer = createDisposer();
23
- disposer.add(browse);
23
+ // `browse` (ShellStateUnit) is a *passed-in* dependency owned by the caller
24
+ // (`useShellStateUnit`), not this unit — do NOT add it to the disposer (it's the
25
+ // shared, app-scoped shell). See packages/sdk/docs/STATE-UNITS.md (composition rule).
24
26
 
25
27
  const newTag$ = new BehaviorSubject<string>('');
26
28
  const error$ = new BehaviorSubject<string>('');
@@ -7,7 +7,7 @@
7
7
  */
8
8
 
9
9
  import React, { useState, useEffect } from 'react';
10
- import type { GatheredContext } from '@semiont/core';
10
+ import { deriveViews, type GatheredContext } from '@semiont/core';
11
11
  import { capabilitiesOf, AUTHORABLE_MEDIA_TYPES, MEDIA_TYPES, mediaTypeForExtension, isSupportedMediaType, LOCALES } from '@semiont/core';
12
12
  import type { UploadProgress } from '@semiont/sdk';
13
13
  import { type CloneData, type ReferenceData } from '../state/compose-page-state-unit';
@@ -283,6 +283,12 @@ export function ResourceComposePage({
283
283
  const isClone = mode === 'clone';
284
284
  const isReferenceCompletion = mode === 'reference';
285
285
 
286
+ // Gathered-context panel — annotation-focus only for now.
287
+ const composeFocus = gatheredContext?.focus.kind === 'annotation' ? gatheredContext.focus : null;
288
+ const composeConnections = gatheredContext && composeFocus
289
+ ? deriveViews(gatheredContext.graph, String(composeFocus.sourceResource.id)).connections
290
+ : [];
291
+
286
292
  return (
287
293
  <div className={`semiont-page${activePanel && COMMON_PANELS.includes(activePanel as ToolbarPanelType) ? ' semiont-page--panel-open' : ''}`}>
288
294
  {/* Main Content Area */}
@@ -316,7 +322,7 @@ export function ResourceComposePage({
316
322
  border: '1px solid var(--semiont-border-primary)',
317
323
  }}>
318
324
  <h3 className="semiont-form__label" style={{ marginBottom: '0.75rem' }}>Gathered Context</h3>
319
- {gatheredContext.sourceContext && (
325
+ {composeFocus?.selected && (
320
326
  <div style={{
321
327
  padding: '0.75rem',
322
328
  backgroundColor: 'var(--semiont-bg-primary)',
@@ -327,27 +333,27 @@ export function ResourceComposePage({
327
333
  marginBottom: '0.5rem',
328
334
  }}>
329
335
  <div style={{ fontSize: 'var(--semiont-text-sm)', fontFamily: 'monospace', whiteSpace: 'pre-wrap', color: 'var(--semiont-text-secondary)' }}>
330
- {gatheredContext.sourceContext.before && <span>{gatheredContext.sourceContext.before}</span>}
336
+ {composeFocus.selected.before && <span>{composeFocus.selected.before}</span>}
331
337
  <span style={{
332
338
  backgroundColor: 'var(--semiont-color-primary-100)',
333
339
  padding: '0 0.25rem',
334
340
  fontWeight: 600,
335
341
  color: 'var(--semiont-color-primary-900)',
336
342
  }}>
337
- {gatheredContext.sourceContext.selected}
343
+ {composeFocus.selected.text}
338
344
  </span>
339
- {gatheredContext.sourceContext.after && <span>{gatheredContext.sourceContext.after}</span>}
345
+ {composeFocus.selected.after && <span>{composeFocus.selected.after}</span>}
340
346
  </div>
341
347
  </div>
342
348
  )}
343
- {gatheredContext.graphContext && gatheredContext.graphContext.connections && gatheredContext.graphContext.connections.length > 0 && (
349
+ {composeConnections.length > 0 && (
344
350
  <div style={{
345
351
  padding: '0.5rem 0',
346
352
  fontSize: 'var(--semiont-text-sm)',
347
353
  color: 'var(--semiont-text-secondary)',
348
354
  }}>
349
355
  <span style={{ fontSize: 'var(--semiont-text-xs)', fontWeight: 500, marginRight: '0.5rem' }}>Connections:</span>
350
- {gatheredContext.graphContext.connections.map(conn => (
356
+ {composeConnections.map(conn => (
351
357
  <span key={conn.resourceId} className="semiont-chip" style={{ fontSize: 'var(--semiont-text-xs)', padding: '0.125rem 0.5rem', marginRight: '0.25rem' }}>
352
358
  {conn.resourceName}
353
359
  </span>
@@ -4,6 +4,7 @@ import { filter } from 'rxjs/operators';
4
4
  import type { SemiontClient } from '@semiont/sdk';
5
5
  import type { ShellStateUnit } from '../../../../state/shell-state-unit';
6
6
  import { createComposePageStateUnit } from '../compose-page-state-unit';
7
+ import { assertStateUnitAxioms, disposeProbe } from '@semiont/core/testing';
7
8
 
8
9
  /** Build an `UploadObservable`-shaped mock that emits started → finished. */
9
10
  function mockUpload(resourceId: string) {
@@ -185,3 +186,15 @@ describe('createComposePageStateUnit', () => {
185
186
  stateUnit.dispose();
186
187
  });
187
188
  });
189
+
190
+ describe('ComposePageStateUnit — StateUnit axioms', () => {
191
+ it('satisfies the StateUnit axioms (incl. A7-passed: never disposes the injected browse)', () => {
192
+ assertStateUnitAxioms({
193
+ setup: () => {
194
+ const browse = disposeProbe();
195
+ return { unit: createComposePageStateUnit(mockClient(), browse as unknown as ShellStateUnit, {}), passedIn: [browse] };
196
+ },
197
+ surfaces: (u) => [u.mode$, u.loading$, u.cloneData$, u.referenceData$, u.gatheredContext$, u.uploadProgress$],
198
+ });
199
+ });
200
+ });
@@ -2,7 +2,7 @@ import { BehaviorSubject, type Observable, map } from 'rxjs';
2
2
  import type { GatheredContext, AnnotationId, AccessToken, ResourceDescriptor, ResourceId } from '@semiont/core';
3
3
  import { resourceId as makeResourceId, annotationId as makeAnnotationId } from '@semiont/core';
4
4
  import { createDisposer } from '@semiont/sdk';
5
- import type { StateUnit } from '@semiont/sdk';
5
+ import type { StateUnit } from '@semiont/core';
6
6
  import type { ShellStateUnit } from '../../../state/shell-state-unit';
7
7
  import type { SemiontClient } from '@semiont/sdk';
8
8
  import { decodeWithCharset, extensionForMediaType } from '@semiont/core';
@@ -72,7 +72,9 @@ export function createComposePageStateUnit(
72
72
  auth?: AccessToken,
73
73
  ): ComposePageStateUnit {
74
74
  const disposer = createDisposer();
75
- disposer.add(browse);
75
+ // `browse` (ShellStateUnit) is a *passed-in* dependency owned by the caller
76
+ // (`useShellStateUnit`), not this unit — do NOT add it to the disposer (it's the
77
+ // shared, app-scoped shell). See packages/sdk/docs/STATE-UNITS.md (composition rule).
76
78
 
77
79
  const isReferenceMode = Boolean(params.annotationUri && params.sourceDocumentId && params.name);
78
80
  const isCloneMode = params.mode === 'clone' && Boolean(params.token);
@@ -199,6 +201,7 @@ export function createComposePageStateUnit(
199
201
  cloneData$.complete();
200
202
  referenceData$.complete();
201
203
  gatheredContext$.complete();
204
+ uploadProgress$.complete();
202
205
  disposer.dispose();
203
206
  },
204
207
  };
@@ -4,6 +4,7 @@ import { filter, skip, take, toArray } from 'rxjs/operators';
4
4
  import type { SemiontClient } from '@semiont/sdk';
5
5
  import type { ShellStateUnit } from '../../../../state/shell-state-unit';
6
6
  import { createDiscoverStateUnit } from '../discover-state-unit';
7
+ import { assertStateUnitAxioms, disposeProbe } from '@semiont/core/testing';
7
8
 
8
9
  function mockBrowse(): ShellStateUnit {
9
10
  return { dispose: vi.fn() } as unknown as ShellStateUnit;
@@ -100,15 +101,6 @@ describe('createDiscoverStateUnit', () => {
100
101
  stateUnit.dispose();
101
102
  });
102
103
 
103
- it('disposes browse and search on dispose', () => {
104
- const browse = mockBrowse();
105
- const { client } = mockClient();
106
- const stateUnit = createDiscoverStateUnit(client, browse);
107
- stateUnit.dispose();
108
-
109
- expect(browse.dispose).toHaveBeenCalled();
110
- });
111
-
112
104
  it('initial selectedEntityType$ is empty and recent fetch carries no entityType', async () => {
113
105
  const { client, resourceCalls } = mockClient();
114
106
  const stateUnit = createDiscoverStateUnit(client, mockBrowse());
@@ -267,3 +259,17 @@ describe('createDiscoverStateUnit', () => {
267
259
  stateUnit.dispose();
268
260
  });
269
261
  });
262
+
263
+ describe('DiscoverStateUnit — StateUnit axioms', () => {
264
+ it('satisfies the StateUnit axioms (incl. A7-passed: never disposes the injected browse)', () => {
265
+ assertStateUnitAxioms({
266
+ setup: () => {
267
+ const browse = disposeProbe();
268
+ const { client } = mockClient();
269
+ return { unit: createDiscoverStateUnit(client, browse as unknown as ShellStateUnit), passedIn: [browse] };
270
+ },
271
+ surfaces: (u) => [u.selectedEntityType$],
272
+ invocations: (u) => [() => u.setSelectedEntityType('')],
273
+ });
274
+ });
275
+ });