@semiont/react-ui 0.2.46 → 0.3.0

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 (214) hide show
  1. package/dist/{PdfAnnotationCanvas.client-COQREPXU.mjs → PdfAnnotationCanvas.client-PVTVPDBQ.mjs} +3 -4
  2. package/dist/PdfAnnotationCanvas.client-PVTVPDBQ.mjs.map +1 -0
  3. package/dist/{ar-7SUXNE34.mjs → ar-APUOG2AP.mjs} +46 -6
  4. package/dist/ar-APUOG2AP.mjs.map +1 -0
  5. package/dist/{bn-XOET3DOI.mjs → bn-EFK2LJGK.mjs} +46 -6
  6. package/dist/bn-EFK2LJGK.mjs.map +1 -0
  7. package/dist/{chunk-JH7BXE2P.mjs → chunk-7DW2P4UE.mjs} +45 -5
  8. package/dist/chunk-7DW2P4UE.mjs.map +1 -0
  9. package/dist/{chunk-Q2KV6Y2J.mjs → chunk-7GEYABC6.mjs} +32 -32
  10. package/dist/{chunk-3JTO27MH.mjs → chunk-D4GAAQMM.mjs} +2 -9
  11. package/dist/{cs-X63DXX7L.mjs → cs-A26MEEQE.mjs} +46 -6
  12. package/dist/cs-A26MEEQE.mjs.map +1 -0
  13. package/dist/{da-OWTCV57A.mjs → da-U3L2FHSZ.mjs} +46 -6
  14. package/dist/da-U3L2FHSZ.mjs.map +1 -0
  15. package/dist/{de-77BMFDVF.mjs → de-Y5BHEBT7.mjs} +46 -6
  16. package/dist/de-Y5BHEBT7.mjs.map +1 -0
  17. package/dist/dist-YLEIY3JJ.mjs +547 -0
  18. package/dist/dist-YLEIY3JJ.mjs.map +1 -0
  19. package/dist/{el-FIBNLH2V.mjs → el-HU7LAWQY.mjs} +46 -6
  20. package/dist/el-HU7LAWQY.mjs.map +1 -0
  21. package/dist/{en-XWEPVTB4.mjs → en-HAKDCFKL.mjs} +5 -3
  22. package/dist/{es-726NTS53.mjs → es-4BN64QH5.mjs} +46 -6
  23. package/dist/es-4BN64QH5.mjs.map +1 -0
  24. package/dist/{fa-3N4CIWE6.mjs → fa-6ELTBARU.mjs} +46 -6
  25. package/dist/fa-6ELTBARU.mjs.map +1 -0
  26. package/dist/{fi-JOM3M7Z4.mjs → fi-DJ4WGIFW.mjs} +46 -6
  27. package/dist/fi-DJ4WGIFW.mjs.map +1 -0
  28. package/dist/{fr-56QSXS7E.mjs → fr-23XM6H6H.mjs} +46 -6
  29. package/dist/fr-23XM6H6H.mjs.map +1 -0
  30. package/dist/{he-SNAXPJEK.mjs → he-JSWJC2XU.mjs} +46 -6
  31. package/dist/he-JSWJC2XU.mjs.map +1 -0
  32. package/dist/{hi-CRBRD5TB.mjs → hi-BENHG3OJ.mjs} +46 -6
  33. package/dist/hi-BENHG3OJ.mjs.map +1 -0
  34. package/dist/{id-BRCVLICF.mjs → id-4HHQJQNF.mjs} +46 -6
  35. package/dist/id-4HHQJQNF.mjs.map +1 -0
  36. package/dist/index.css +108 -12
  37. package/dist/index.css.map +1 -1
  38. package/dist/index.d.mts +351 -107
  39. package/dist/index.mjs +3111 -1811
  40. package/dist/index.mjs.map +1 -1
  41. package/dist/{it-M2Z27BNB.mjs → it-U6I5PDKU.mjs} +46 -6
  42. package/dist/it-U6I5PDKU.mjs.map +1 -0
  43. package/dist/{ja-TZUKW7HD.mjs → ja-K3YBDWDP.mjs} +46 -6
  44. package/dist/ja-K3YBDWDP.mjs.map +1 -0
  45. package/dist/{ko-NKBGGOL6.mjs → ko-KC2HXRXG.mjs} +46 -6
  46. package/dist/ko-KC2HXRXG.mjs.map +1 -0
  47. package/dist/{magic-string.es-7FJ3LUGB.mjs → magic-string.es-K77I4ZQN.mjs} +2 -2
  48. package/dist/{ms-XFXPN6RX.mjs → ms-KY5QGBNN.mjs} +46 -6
  49. package/dist/ms-KY5QGBNN.mjs.map +1 -0
  50. package/dist/{nl-MVYXAS5C.mjs → nl-6PZFLGY2.mjs} +46 -6
  51. package/dist/nl-6PZFLGY2.mjs.map +1 -0
  52. package/dist/{no-XOLO4JPV.mjs → no-5QR7PLVJ.mjs} +46 -6
  53. package/dist/no-5QR7PLVJ.mjs.map +1 -0
  54. package/dist/{pl-TRWLMMC4.mjs → pl-4GV2NQXE.mjs} +46 -6
  55. package/dist/pl-4GV2NQXE.mjs.map +1 -0
  56. package/dist/{pt-M3TE24UI.mjs → pt-F3F5QD2P.mjs} +46 -6
  57. package/dist/pt-F3F5QD2P.mjs.map +1 -0
  58. package/dist/{ro-QBFG2T64.mjs → ro-TFYL2IQB.mjs} +46 -6
  59. package/dist/ro-TFYL2IQB.mjs.map +1 -0
  60. package/dist/{sv-IUECBXWX.mjs → sv-PRVF2QLR.mjs} +46 -6
  61. package/dist/sv-PRVF2QLR.mjs.map +1 -0
  62. package/dist/test-utils.mjs +16994 -22140
  63. package/dist/test-utils.mjs.map +1 -1
  64. package/dist/{th-US7KIN5Q.mjs → th-SUQOQFUZ.mjs} +46 -6
  65. package/dist/th-SUQOQFUZ.mjs.map +1 -0
  66. package/dist/{tr-DWJ2FFUK.mjs → tr-AYUJZOFJ.mjs} +46 -6
  67. package/dist/tr-AYUJZOFJ.mjs.map +1 -0
  68. package/dist/{uk-M4ZE4DPZ.mjs → uk-YY5WGLBM.mjs} +46 -6
  69. package/dist/uk-YY5WGLBM.mjs.map +1 -0
  70. package/dist/{vi-FERZNPSH.mjs → vi-6RO77ITD.mjs} +46 -6
  71. package/dist/{vi-FERZNPSH.mjs.map → vi-6RO77ITD.mjs.map} +1 -1
  72. package/dist/{zh-3J2I3WYK.mjs → zh-L6GA65H6.mjs} +46 -6
  73. package/dist/zh-L6GA65H6.mjs.map +1 -0
  74. package/package.json +18 -14
  75. package/src/components/Button/Button.tsx +23 -25
  76. package/src/components/annotation/AnnotateToolbar.tsx +1 -1
  77. package/src/components/annotation-popups/SharedPopupElements.tsx +5 -7
  78. package/src/components/image-annotation/SvgDrawingCanvas.tsx +3 -4
  79. package/src/components/modals/ConfigureGenerationStep.tsx +190 -0
  80. package/src/components/modals/ConfigureSearchStep.tsx +105 -0
  81. package/src/components/modals/ContextSummary.tsx +183 -0
  82. package/src/components/modals/GatherContextStep.tsx +89 -0
  83. package/src/components/modals/KeyboardShortcutsHelpModal.tsx +4 -6
  84. package/src/components/modals/ProposeEntitiesModal.tsx +4 -6
  85. package/src/components/modals/ReferenceWizardModal.tsx +326 -0
  86. package/src/components/modals/ResourceSearchModal.tsx +4 -6
  87. package/src/components/modals/SearchModal.css +43 -0
  88. package/src/components/modals/SearchModal.tsx +4 -6
  89. package/src/components/modals/SearchResultsStep.tsx +126 -0
  90. package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +3 -4
  91. package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +36 -14
  92. package/src/components/resource/AnnotateView.tsx +4 -4
  93. package/src/components/resource/AnnotationHistory.tsx +2 -2
  94. package/src/components/resource/BrowseView.tsx +4 -4
  95. package/src/components/resource/ResourceViewer.tsx +4 -7
  96. package/src/components/resource/__tests__/AnnotationHistory.test.tsx +16 -16
  97. package/src/components/resource/__tests__/BrowseView.test.tsx +2 -2
  98. package/src/components/resource/__tests__/HistoryEvent.test.tsx +1 -1
  99. package/src/components/resource/__tests__/ResourceViewer.mode-switch.test.tsx +1 -1
  100. package/src/components/resource/panels/AssessmentEntry.tsx +9 -11
  101. package/src/components/resource/panels/CommentEntry.tsx +10 -12
  102. package/src/components/resource/panels/HighlightEntry.tsx +9 -11
  103. package/src/components/resource/panels/ReferenceEntry.tsx +57 -104
  104. package/src/components/resource/panels/ReferencesPanel.css +85 -13
  105. package/src/components/resource/panels/ReferencesPanel.tsx +1 -2
  106. package/src/components/resource/panels/TagEntry.tsx +9 -11
  107. package/src/components/resource/panels/__tests__/AssistSection.test.tsx +7 -7
  108. package/src/components/resource/panels/__tests__/HighlightPanel.annotationProgress.test.tsx +2 -2
  109. package/src/components/resource/panels/__tests__/ReferenceEntry.test.tsx +64 -101
  110. package/src/components/resource/panels/__tests__/StatisticsPanel.test.tsx +1 -1
  111. package/src/components/resource/panels/__tests__/TaggingPanel.test.tsx +3 -3
  112. package/src/components/viewers/ImageViewer.tsx +3 -6
  113. package/src/components/viewers/__tests__/ImageViewer.test.tsx +3 -3
  114. package/src/features/admin-devops/__tests__/AdminDevOpsPage.test.tsx +5 -5
  115. package/src/features/admin-exchange/__tests__/AdminExchangePage.test.tsx +141 -0
  116. package/src/features/admin-exchange/__tests__/ExportCard.test.tsx +41 -0
  117. package/src/features/admin-exchange/__tests__/ImportCard.test.tsx +148 -0
  118. package/src/features/admin-exchange/__tests__/ImportProgress.test.tsx +106 -0
  119. package/src/features/admin-exchange/components/AdminExchangePage.tsx +120 -0
  120. package/src/features/admin-exchange/components/ExportCard.tsx +35 -0
  121. package/src/features/admin-exchange/components/ImportCard.tsx +188 -0
  122. package/src/features/admin-exchange/components/ImportProgress.tsx +86 -0
  123. package/src/features/admin-security/__tests__/AdminSecurityPage.test.tsx +3 -3
  124. package/src/features/moderate-entity-tags/__tests__/EntityTagsPage.test.tsx +2 -2
  125. package/src/features/moderate-recent/__tests__/RecentDocumentsPage.test.tsx +4 -4
  126. package/src/features/moderate-tag-schemas/__tests__/TagSchemasPage.test.tsx +3 -3
  127. package/src/features/moderation-linked-data/__tests__/LinkedDataPage.test.tsx +117 -0
  128. package/src/features/moderation-linked-data/components/LinkedDataPage.tsx +121 -0
  129. package/src/features/resource-compose/__tests__/ResourceComposePage.test.tsx +5 -5
  130. package/src/features/resource-compose/components/ResourceComposePage.tsx +56 -1
  131. package/src/features/resource-discovery/__tests__/ResourceCard.test.tsx +1 -1
  132. package/src/features/resource-discovery/__tests__/ResourceDiscoveryPage.test.tsx +2 -2
  133. package/src/features/resource-viewer/__tests__/AnnotationCreationPending.test.tsx +3 -3
  134. package/src/features/resource-viewer/__tests__/AnnotationDeletionIntegration.test.tsx +11 -10
  135. package/src/features/resource-viewer/__tests__/AnnotationProgressDismissal.test.tsx +2 -2
  136. package/src/features/resource-viewer/__tests__/BindFlowIntegration.test.tsx +22 -115
  137. package/src/features/resource-viewer/__tests__/DetectionFlowBug.test.tsx +3 -3
  138. package/src/features/resource-viewer/__tests__/DetectionFlowIntegration.test.tsx +20 -20
  139. package/src/features/resource-viewer/__tests__/ResourceMutations.test.tsx +7 -7
  140. package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +5 -21
  141. package/src/features/resource-viewer/__tests__/ToastNotifications.test.tsx +2 -2
  142. package/src/features/resource-viewer/__tests__/YieldFlowIntegration.test.tsx +45 -82
  143. package/src/features/resource-viewer/__tests__/annotation-progress-flow.test.tsx +4 -4
  144. package/src/features/resource-viewer/components/ResourceViewerPage.tsx +144 -72
  145. package/src/integrations/tailwind-plugin.js +3 -3
  146. package/src/styles/core/buttons.css +31 -0
  147. package/src/styles/features/exchange.css +404 -0
  148. package/src/styles/index.css +1 -0
  149. package/translations/ar.json +42 -4
  150. package/translations/bn.json +42 -4
  151. package/translations/cs.json +42 -4
  152. package/translations/da.json +128 -90
  153. package/translations/de.json +122 -84
  154. package/translations/el.json +42 -4
  155. package/translations/en.json +42 -4
  156. package/translations/es.json +42 -4
  157. package/translations/fa.json +42 -4
  158. package/translations/fi.json +68 -30
  159. package/translations/fr.json +42 -4
  160. package/translations/he.json +42 -4
  161. package/translations/hi.json +42 -4
  162. package/translations/id.json +43 -5
  163. package/translations/it.json +62 -24
  164. package/translations/ja.json +43 -5
  165. package/translations/ko.json +42 -4
  166. package/translations/ms.json +43 -5
  167. package/translations/nl.json +41 -3
  168. package/translations/no.json +104 -66
  169. package/translations/pl.json +42 -4
  170. package/translations/pt.json +43 -5
  171. package/translations/ro.json +42 -4
  172. package/translations/sv.json +42 -4
  173. package/translations/th.json +42 -4
  174. package/translations/tr.json +42 -4
  175. package/translations/uk.json +42 -4
  176. package/translations/vi.json +42 -4
  177. package/translations/zh.json +42 -4
  178. package/dist/PdfAnnotationCanvas.client-COQREPXU.mjs.map +0 -1
  179. package/dist/ar-7SUXNE34.mjs.map +0 -1
  180. package/dist/bn-XOET3DOI.mjs.map +0 -1
  181. package/dist/chunk-JH7BXE2P.mjs.map +0 -1
  182. package/dist/cs-X63DXX7L.mjs.map +0 -1
  183. package/dist/da-OWTCV57A.mjs.map +0 -1
  184. package/dist/de-77BMFDVF.mjs.map +0 -1
  185. package/dist/el-FIBNLH2V.mjs.map +0 -1
  186. package/dist/es-726NTS53.mjs.map +0 -1
  187. package/dist/fa-3N4CIWE6.mjs.map +0 -1
  188. package/dist/fi-JOM3M7Z4.mjs.map +0 -1
  189. package/dist/fr-56QSXS7E.mjs.map +0 -1
  190. package/dist/he-SNAXPJEK.mjs.map +0 -1
  191. package/dist/hi-CRBRD5TB.mjs.map +0 -1
  192. package/dist/id-BRCVLICF.mjs.map +0 -1
  193. package/dist/it-M2Z27BNB.mjs.map +0 -1
  194. package/dist/ja-TZUKW7HD.mjs.map +0 -1
  195. package/dist/ko-NKBGGOL6.mjs.map +0 -1
  196. package/dist/ms-XFXPN6RX.mjs.map +0 -1
  197. package/dist/nl-MVYXAS5C.mjs.map +0 -1
  198. package/dist/no-XOLO4JPV.mjs.map +0 -1
  199. package/dist/pl-TRWLMMC4.mjs.map +0 -1
  200. package/dist/pt-M3TE24UI.mjs.map +0 -1
  201. package/dist/ro-QBFG2T64.mjs.map +0 -1
  202. package/dist/sv-IUECBXWX.mjs.map +0 -1
  203. package/dist/th-US7KIN5Q.mjs.map +0 -1
  204. package/dist/tr-DWJ2FFUK.mjs.map +0 -1
  205. package/dist/uk-M4ZE4DPZ.mjs.map +0 -1
  206. package/dist/zh-3J2I3WYK.mjs.map +0 -1
  207. package/src/examples/ButtonUsageExample.tsx +0 -242
  208. package/src/examples/button-css-modules.module.css +0 -164
  209. package/src/examples/button-styled-components.tsx +0 -215
  210. package/src/examples/button-tailwind.css +0 -51
  211. /package/dist/{chunk-Q2KV6Y2J.mjs.map → chunk-7GEYABC6.mjs.map} +0 -0
  212. /package/dist/{chunk-3JTO27MH.mjs.map → chunk-D4GAAQMM.mjs.map} +0 -0
  213. /package/dist/{en-XWEPVTB4.mjs.map → en-HAKDCFKL.mjs.map} +0 -0
  214. /package/dist/{magic-string.es-7FJ3LUGB.mjs.map → magic-string.es-K77I4ZQN.mjs.map} +0 -0
@@ -1,16 +1,15 @@
1
1
  /**
2
- * Layer 3: Feature Integration Test - Resolution Flow (search modal & body update)
2
+ * Layer 3: Feature Integration Test - Bind Flow (body update)
3
3
  *
4
- * Tests the UNCOVERED half of useBindFlow:
5
- * - bind:link → emits bind:search-requested
6
- * - bind:search-requested → opens search modal with pendingReferenceId
7
- * - onCloseSearchModal → closes modal
4
+ * Tests the write side of useBindFlow:
8
5
  * - bind:update-body → calls updateAnnotationBody API
9
6
  * - bind:update-body → emits bind:body-updated on success
10
7
  * - bind:update-body → emits bind:body-update-failed on error
11
8
  * - auth token passed to updateAnnotationBody
12
9
  *
13
- * The deletion half of useBindFlow is covered by AnnotationDeletionIntegration.test.tsx.
10
+ * The wizard modal (ReferenceWizardModal) handles modal state, context
11
+ * gathering, search configuration, and result display. This test covers
12
+ * only the downstream API calls after the wizard emits bind:update-body.
14
13
  *
15
14
  * Uses real providers (EventBus, ApiClient, AuthToken) with mocked API boundary.
16
15
  */
@@ -23,7 +22,7 @@ import { EventBusProvider, useEventBus, resetEventBusForTesting } from '../../..
23
22
  import { ApiClientProvider } from '../../../contexts/ApiClientContext';
24
23
  import { AuthTokenProvider } from '../../../contexts/AuthTokenContext';
25
24
  import { SemiontApiClient } from '@semiont/api-client';
26
- import { resourceUri, accessToken } from '@semiont/core';
25
+ import { resourceId, accessToken, annotationId } from '@semiont/core';
27
26
 
28
27
  // Mock Toast module to prevent "useToast must be used within a ToastProvider" errors
29
28
  vi.mock('../../../components/Toast', () => ({
@@ -35,9 +34,9 @@ vi.mock('../../../components/Toast', () => ({
35
34
  }),
36
35
  }));
37
36
 
38
- describe('Resolution Flow - Search Modal & Body Update Integration', () => {
37
+ describe('Bind Flow - Body Update Integration', () => {
39
38
  let updateAnnotationBodySpy: ReturnType<typeof vi.fn>;
40
- const testUri = resourceUri('http://localhost:4000/resources/test-resource');
39
+ const testId = resourceId('test-resource');
41
40
  const testToken = 'test-resolution-token';
42
41
  const testBaseUrl = 'http://localhost:4000';
43
42
 
@@ -57,11 +56,10 @@ describe('Resolution Flow - Search Modal & Body Update Integration', () => {
57
56
 
58
57
  function renderBindFlow() {
59
58
  let eventBusInstance: ReturnType<typeof useEventBus> | null = null;
60
- let lastState: ReturnType<typeof useBindFlow> | null = null;
61
59
 
62
60
  function TestComponent() {
63
61
  eventBusInstance = useEventBus();
64
- lastState = useBindFlow(testUri);
62
+ useBindFlow(testId);
65
63
  return null;
66
64
  }
67
65
 
@@ -76,109 +74,18 @@ describe('Resolution Flow - Search Modal & Body Update Integration', () => {
76
74
  );
77
75
 
78
76
  return {
79
- getState: () => lastState!,
80
77
  getEventBus: () => eventBusInstance!,
81
78
  };
82
79
  }
83
80
 
84
- // ─── Initial state ──────────────────────────────────────────────────────────
85
-
86
- it('starts with search modal closed and no pending reference', () => {
87
- const { getState } = renderBindFlow();
88
- expect(getState().searchModalOpen).toBe(false);
89
- expect(getState().pendingReferenceId).toBeNull();
90
- });
91
-
92
- // ─── bind:link ─────────────────────────────────────────────────────────
93
-
94
- it('bind:link emits bind:search-requested with referenceId and searchTerm', () => {
95
- const { getEventBus } = renderBindFlow();
96
- const searchRequestedSpy = vi.fn();
97
-
98
- const subscription = getEventBus().get('bind:search-requested').subscribe(searchRequestedSpy);
99
- act(() => { getEventBus().get('bind:link').next({ annotationUri: 'ann-uri-123', searchTerm: 'climate change' }); });
100
- subscription.unsubscribe();
101
-
102
- expect(searchRequestedSpy).toHaveBeenCalledTimes(1);
103
- expect(searchRequestedSpy).toHaveBeenCalledWith({
104
- referenceId: 'ann-uri-123',
105
- searchTerm: 'climate change',
106
- });
107
- });
108
-
109
- // ─── bind:search-requested ────────────────────────────────────────────
110
-
111
- it('bind:search-requested opens the search modal', async () => {
112
- const { getState, getEventBus } = renderBindFlow();
113
-
114
- expect(getState().searchModalOpen).toBe(false);
115
-
116
- act(() => { getEventBus().get('bind:search-requested').next({ referenceId: 'ref-abc', searchTerm: 'oceans' }); });
117
-
118
- await waitFor(() => {
119
- expect(getState().searchModalOpen).toBe(true);
120
- });
121
- });
122
-
123
- it('bind:search-requested sets pendingReferenceId', async () => {
124
- const { getState, getEventBus } = renderBindFlow();
125
-
126
- act(() => { getEventBus().get('bind:search-requested').next({ referenceId: 'ref-xyz', searchTerm: 'forests' }); });
127
-
128
- await waitFor(() => {
129
- expect(getState().pendingReferenceId).toBe('ref-xyz');
130
- });
131
- });
132
-
133
- it('bind:link → bind:search-requested chain opens modal end-to-end', async () => {
134
- const { getState, getEventBus } = renderBindFlow();
135
-
136
- // Simulate the full user journey: user clicks "Link Document" on a reference entry
137
- act(() => { getEventBus().get('bind:link').next({ annotationUri: 'ann-full-chain', searchTerm: 'biodiversity' }); });
138
-
139
- await waitFor(() => {
140
- expect(getState().searchModalOpen).toBe(true);
141
- expect(getState().pendingReferenceId).toBe('ann-full-chain');
142
- });
143
- });
144
-
145
- // ─── onCloseSearchModal ──────────────────────────────────────────────────────
146
-
147
- it('onCloseSearchModal closes the search modal', async () => {
148
- const { getState, getEventBus } = renderBindFlow();
149
-
150
- act(() => { getEventBus().get('bind:search-requested').next({ referenceId: 'ref-close', searchTerm: 'test' }); });
151
-
152
- await waitFor(() => expect(getState().searchModalOpen).toBe(true));
153
-
154
- act(() => { getState().onCloseSearchModal(); });
155
-
156
- await waitFor(() => {
157
- expect(getState().searchModalOpen).toBe(false);
158
- });
159
- });
160
-
161
- it('onCloseSearchModal does not clear pendingReferenceId (preserves for re-open)', async () => {
162
- const { getState, getEventBus } = renderBindFlow();
163
-
164
- act(() => { getEventBus().get('bind:search-requested').next({ referenceId: 'ref-persist', searchTerm: 'test' }); });
165
- await waitFor(() => expect(getState().searchModalOpen).toBe(true));
166
-
167
- act(() => { getState().onCloseSearchModal(); });
168
- await waitFor(() => expect(getState().searchModalOpen).toBe(false));
169
-
170
- // pendingReferenceId remains — modal may reopen
171
- expect(getState().pendingReferenceId).toBe('ref-persist');
172
- });
173
-
174
81
  // ─── bind:update-body ──────────────────────────────────────────────────
175
82
 
176
83
  it('bind:update-body calls updateAnnotationBody API', async () => {
177
84
  const { getEventBus } = renderBindFlow();
178
85
 
179
86
  act(() => { getEventBus().get('bind:update-body').next({
180
- annotationUri: 'http://localhost:4000/resources/test-resource/annotations/ann-body-1',
181
- resourceId: 'linked-resource-id',
87
+ annotationId: annotationId('ann-body-1'),
88
+ resourceId: resourceId('linked-resource-id'),
182
89
  operations: [{ op: 'add', item: { id: 'linked-resource-id' } }],
183
90
  }); });
184
91
 
@@ -191,8 +98,8 @@ describe('Resolution Flow - Search Modal & Body Update Integration', () => {
191
98
  const { getEventBus } = renderBindFlow();
192
99
 
193
100
  act(() => { getEventBus().get('bind:update-body').next({
194
- annotationUri: 'http://localhost:4000/resources/test-resource/annotations/ann-auth',
195
- resourceId: 'resource-id',
101
+ annotationId: annotationId('ann-auth'),
102
+ resourceId: resourceId('resource-id'),
196
103
  operations: [{ op: 'replace', newItem: { id: 'resource-id' } }],
197
104
  }); });
198
105
 
@@ -201,8 +108,8 @@ describe('Resolution Flow - Search Modal & Body Update Integration', () => {
201
108
  });
202
109
 
203
110
  const callArgs = updateAnnotationBodySpy.mock.calls[0];
204
- expect(callArgs[2]).toHaveProperty('auth');
205
- expect(callArgs[2].auth).toBe(accessToken(testToken));
111
+ expect(callArgs[3]).toHaveProperty('auth');
112
+ expect(callArgs[3].auth).toBe(accessToken(testToken));
206
113
  });
207
114
 
208
115
  it('bind:update-body emits bind:body-updated on success', async () => {
@@ -212,8 +119,8 @@ describe('Resolution Flow - Search Modal & Body Update Integration', () => {
212
119
  const subscription = getEventBus().get('bind:body-updated').subscribe(bodyUpdatedSpy);
213
120
 
214
121
  act(() => { getEventBus().get('bind:update-body').next({
215
- annotationUri: 'http://localhost:4000/resources/test-resource/annotations/ann-success',
216
- resourceId: 'resource-id',
122
+ annotationId: annotationId('ann-success'),
123
+ resourceId: resourceId('resource-id'),
217
124
  operations: [{ op: 'add', item: { id: 'resource-id' } }],
218
125
  }); });
219
126
 
@@ -224,7 +131,7 @@ describe('Resolution Flow - Search Modal & Body Update Integration', () => {
224
131
  subscription.unsubscribe();
225
132
 
226
133
  expect(bodyUpdatedSpy).toHaveBeenCalledWith({
227
- annotationUri: 'http://localhost:4000/resources/test-resource/annotations/ann-success',
134
+ annotationId: annotationId('ann-success'),
228
135
  });
229
136
  });
230
137
 
@@ -237,8 +144,8 @@ describe('Resolution Flow - Search Modal & Body Update Integration', () => {
237
144
  const subscription = getEventBus().get('bind:body-update-failed').subscribe(bodyUpdateFailedSpy);
238
145
 
239
146
  act(() => { getEventBus().get('bind:update-body').next({
240
- annotationUri: 'http://localhost:4000/resources/test-resource/annotations/ann-fail',
241
- resourceId: 'resource-id',
147
+ annotationId: annotationId('ann-fail'),
148
+ resourceId: resourceId('resource-id'),
242
149
  operations: [{ op: 'remove', item: { id: 'old-id' } }],
243
150
  }); });
244
151
 
@@ -257,8 +164,8 @@ describe('Resolution Flow - Search Modal & Body Update Integration', () => {
257
164
  const { getEventBus } = renderBindFlow();
258
165
 
259
166
  act(() => { getEventBus().get('bind:update-body').next({
260
- annotationUri: 'http://localhost:4000/resources/test-resource/annotations/ann-dedup',
261
- resourceId: 'resource-id',
167
+ annotationId: annotationId('ann-dedup'),
168
+ resourceId: resourceId('resource-id'),
262
169
  operations: [{ op: 'add', item: { id: 'resource-id' } }],
263
170
  }); });
264
171
 
@@ -52,7 +52,7 @@ describe('REPRODUCING BUG: Detection state not updating', () => {
52
52
  // Component to capture EventBus and hook state
53
53
  function TestComponent() {
54
54
  eventBusInstance = useEventBus();
55
- const state = useMarkFlow('http://localhost:8080/resources/test' as any);
55
+ const state = useMarkFlow('test' as any);
56
56
  currentState = state;
57
57
 
58
58
  console.log('[TEST] useMarkFlow state:', {
@@ -109,7 +109,7 @@ describe('REPRODUCING BUG: Detection state not updating', () => {
109
109
 
110
110
  function TestComponent() {
111
111
  eventBusInstance = useEventBus();
112
- const state = useMarkFlow('http://localhost:8080/resources/test' as any);
112
+ const state = useMarkFlow('test' as any);
113
113
  currentState = state;
114
114
 
115
115
  return (
@@ -162,7 +162,7 @@ describe('REPRODUCING BUG: Detection state not updating', () => {
162
162
 
163
163
  function TestComponent() {
164
164
  eventBusInstance = useEventBus();
165
- const state = useMarkFlow('http://localhost:8080/resources/f45fd44f9cb0b0fe1b7980d3d034bc61' as any);
165
+ const state = useMarkFlow('f45fd44f9cb0b0fe1b7980d3d034bc61' as any);
166
166
 
167
167
  stateSnapshots.push({
168
168
  assistingMotivation: state.assistingMotivation,
@@ -31,7 +31,7 @@ import { ApiClientProvider } from '../../../contexts/ApiClientContext';
31
31
  import { AuthTokenProvider } from '../../../contexts/AuthTokenContext';
32
32
  import { SSEClient } from '@semiont/api-client';
33
33
  import type { Motivation } from '@semiont/core';
34
- import { resourceUri } from '@semiont/core';
34
+ import { resourceId } from '@semiont/core';
35
35
  import type { Emitter } from 'mitt';
36
36
 
37
37
  // Mock Toast module to prevent "useToast must be used within a ToastProvider" errors
@@ -77,10 +77,10 @@ describe('Detection Flow - Feature Integration', () => {
77
77
  });
78
78
 
79
79
  it('should call annotateReferences exactly ONCE when detection starts (not twice)', async () => {
80
- const testUri = resourceUri('http://localhost:4000/resources/test-resource');
80
+ const testId = resourceId('test-resource');
81
81
 
82
82
  // Render with real component composition
83
- const { emitDetectionStart } = renderDetectionFlow(testUri);
83
+ const { emitDetectionStart } = renderDetectionFlow(testId);
84
84
 
85
85
  // Trigger detection for linking (uses annotateReferences)
86
86
  act(() => {
@@ -98,7 +98,7 @@ describe('Detection Flow - Feature Integration', () => {
98
98
 
99
99
  // Verify correct parameters (eventBus is passed but we don't need to verify its exact value)
100
100
  expect(annotateReferencesSpy).toHaveBeenCalledWith(
101
- testUri,
101
+ testId,
102
102
  {
103
103
  entityTypes: ['Person', 'Organization'],
104
104
  includeDescriptiveReferences: false,
@@ -108,10 +108,10 @@ describe('Detection Flow - Feature Integration', () => {
108
108
  });
109
109
 
110
110
  it('should propagate SSE progress events to useMarkFlow state', async () => {
111
- const testUri = resourceUri('http://localhost:4000/resources/test-resource');
111
+ const testId = resourceId('test-resource');
112
112
 
113
113
  // Render with state observer
114
- const { emitDetectionStart, getEventBus } = renderDetectionFlow(testUri);
114
+ const { emitDetectionStart, getEventBus } = renderDetectionFlow(testId);
115
115
 
116
116
  // Start detection
117
117
  act(() => {
@@ -145,8 +145,8 @@ describe('Detection Flow - Feature Integration', () => {
145
145
  });
146
146
 
147
147
  it('should handle multiple progress updates correctly', async () => {
148
- const testUri = resourceUri('http://localhost:4000/resources/test-resource');
149
- const { emitDetectionStart, getEventBus } = renderDetectionFlow(testUri);
148
+ const testId = resourceId('test-resource');
149
+ const { emitDetectionStart, getEventBus } = renderDetectionFlow(testId);
150
150
 
151
151
  // Start detection
152
152
  act(() => {
@@ -200,8 +200,8 @@ describe('Detection Flow - Feature Integration', () => {
200
200
  });
201
201
 
202
202
  it('should keep progress visible after detection completes', async () => {
203
- const testUri = resourceUri('http://localhost:4000/resources/test-resource');
204
- const { emitDetectionStart, getEventBus } = renderDetectionFlow(testUri);
203
+ const testId = resourceId('test-resource');
204
+ const { emitDetectionStart, getEventBus } = renderDetectionFlow(testId);
205
205
 
206
206
  // Start detection
207
207
  act(() => {
@@ -237,8 +237,8 @@ describe('Detection Flow - Feature Integration', () => {
237
237
  });
238
238
 
239
239
  it('should clear progress on detection failure', async () => {
240
- const testUri = resourceUri('http://localhost:4000/resources/test-resource');
241
- const { emitDetectionStart, getEventBus } = renderDetectionFlow(testUri);
240
+ const testId = resourceId('test-resource');
241
+ const { emitDetectionStart, getEventBus } = renderDetectionFlow(testId);
242
242
 
243
243
  // Start detection
244
244
  act(() => {
@@ -282,8 +282,8 @@ describe('Detection Flow - Feature Integration', () => {
282
282
  });
283
283
 
284
284
  it('should handle different detection motivations with correct API calls', async () => {
285
- const testUri = resourceUri('http://localhost:4000/resources/test-resource');
286
- const { emitDetectionStart } = renderDetectionFlow(testUri);
285
+ const testId = resourceId('test-resource');
286
+ const { emitDetectionStart } = renderDetectionFlow(testId);
287
287
 
288
288
  // Test highlighting
289
289
  act(() => {
@@ -292,7 +292,7 @@ describe('Detection Flow - Feature Integration', () => {
292
292
 
293
293
  await waitFor(() => {
294
294
  expect(annotateHighlightsSpy).toHaveBeenCalledTimes(1);
295
- expect(annotateHighlightsSpy).toHaveBeenCalledWith(testUri, {
295
+ expect(annotateHighlightsSpy).toHaveBeenCalledWith(testId, {
296
296
  instructions: 'Find important text',
297
297
  }, expect.objectContaining({ auth: undefined }));
298
298
  });
@@ -312,7 +312,7 @@ describe('Detection Flow - Feature Integration', () => {
312
312
 
313
313
  await waitFor(() => {
314
314
  expect(detectCommentsSpy).toHaveBeenCalledTimes(1);
315
- expect(detectCommentsSpy).toHaveBeenCalledWith(testUri, {
315
+ expect(detectCommentsSpy).toHaveBeenCalledWith(testId, {
316
316
  instructions: 'Add helpful comments',
317
317
  tone: 'educational',
318
318
  }, expect.objectContaining({ auth: undefined }));
@@ -320,11 +320,11 @@ describe('Detection Flow - Feature Integration', () => {
320
320
  });
321
321
 
322
322
  it('should only call API once even with multiple event listeners', async () => {
323
- const testUri = resourceUri('http://localhost:4000/resources/test-resource');
323
+ const testId = resourceId('test-resource');
324
324
 
325
325
  // This test specifically catches the duplicate useBindFlow bug
326
326
  // If multiple components call useBindFlow, we'll see multiple API calls
327
- const { emitDetectionStart, getEventBus } = renderDetectionFlow(testUri);
327
+ const { emitDetectionStart, getEventBus } = renderDetectionFlow(testId);
328
328
 
329
329
  // Add an additional event listener (simulating multiple subscribers)
330
330
  const additionalListener = vi.fn();
@@ -354,7 +354,7 @@ describe('Detection Flow - Feature Integration', () => {
354
354
  * Helper: Render useMarkFlow hook with real component composition
355
355
  * Returns methods to interact with the rendered component
356
356
  */
357
- function renderDetectionFlow(testUri: string) {
357
+ function renderDetectionFlow(testId: string) {
358
358
  let eventBusInstance: Emitter<EventMap>;
359
359
 
360
360
  // Component to capture EventBus instance
@@ -365,7 +365,7 @@ function renderDetectionFlow(testUri: string) {
365
365
 
366
366
  // Test harness component that uses the hook
367
367
  function DetectionFlowTestHarness() {
368
- const { progress, assistingMotivation } = useMarkFlow(testUri as any);
368
+ const { progress, assistingMotivation } = useMarkFlow(testId as any);
369
369
  return (
370
370
  <div>
371
371
  <div data-testid="detecting">{assistingMotivation || 'none'}</div>
@@ -26,7 +26,7 @@ import { render, waitFor } from '@testing-library/react';
26
26
  import { act } from 'react';
27
27
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
28
28
  import { SemiontApiClient } from '@semiont/api-client';
29
- import { resourceUri, accessToken } from '@semiont/core';
29
+ import { resourceId, accessToken } from '@semiont/core';
30
30
  import { EventBusProvider, useEventBus, resetEventBusForTesting } from '../../../contexts/EventBusContext';
31
31
  import { useEventSubscriptions } from '../../../contexts/useEventSubscription';
32
32
  import { ApiClientProvider } from '../../../contexts/ApiClientContext';
@@ -36,7 +36,7 @@ import type { EventMap, EventBus } from '@semiont/core';
36
36
 
37
37
  // ─── Constants ────────────────────────────────────────────────────────────────
38
38
 
39
- const TEST_URI = resourceUri('http://localhost:4000/resources/test-resource');
39
+ const TEST_URI = resourceId('test-resource');
40
40
  const TEST_TOKEN = 'test-auth-token-123';
41
41
  const BASE_URL = 'http://localhost:4000';
42
42
  const CLONE_TOKEN = 'generated-clone-token-xyz';
@@ -66,11 +66,11 @@ function ResourceMutationHarness({ onEventBus }: { onEventBus: (eventBus: EventB
66
66
  const generateCloneTokenMutation = resources.generateCloneToken.useMutation();
67
67
 
68
68
  const handleResourceArchive = useCallback(async () => {
69
- await updateMutation.mutateAsync({ rUri: TEST_URI, data: { archived: true } });
69
+ await updateMutation.mutateAsync({ id: TEST_URI, data: { archived: true } });
70
70
  }, [updateMutation]);
71
71
 
72
72
  const handleResourceUnarchive = useCallback(async () => {
73
- await updateMutation.mutateAsync({ rUri: TEST_URI, data: { archived: false } });
73
+ await updateMutation.mutateAsync({ id: TEST_URI, data: { archived: false } });
74
74
  }, [updateMutation]);
75
75
 
76
76
  const handleResourceClone = useCallback(async () => {
@@ -175,7 +175,7 @@ describe('Resource mutations — hooks hoisted to top level', () => {
175
175
  await waitFor(() => {
176
176
  expect(generateCloneTokenSpy).toHaveBeenCalledWith(
177
177
  TEST_URI,
178
- expect.anything()
178
+ expect.anything(),
179
179
  );
180
180
  });
181
181
  });
@@ -241,7 +241,7 @@ describe('Resource mutations — hooks hoisted to top level', () => {
241
241
  expect(updateResourceSpy).toHaveBeenCalledWith(
242
242
  TEST_URI,
243
243
  expect.objectContaining({ archived: true }),
244
- expect.anything()
244
+ expect.anything(),
245
245
  );
246
246
  });
247
247
 
@@ -275,7 +275,7 @@ describe('Resource mutations — hooks hoisted to top level', () => {
275
275
  expect(updateResourceSpy).toHaveBeenCalledWith(
276
276
  TEST_URI,
277
277
  expect.objectContaining({ archived: false }),
278
- expect.anything()
278
+ expect.anything(),
279
279
  );
280
280
  });
281
281
 
@@ -129,19 +129,11 @@ vi.mock('@/components/toolbar/ToolbarPanels', () => ({
129
129
  ToolbarPanels: ({ children }: any) => <div data-testid="toolbar-panels">{children}</div>,
130
130
  }));
131
131
 
132
- vi.mock('@/components/modals/SearchResourcesModal', () => ({
133
- SearchResourcesModal: () => <div data-testid="search-modal">Search Modal</div>,
134
- }));
135
-
136
- vi.mock('@/components/modals/GenerationConfigModal', () => ({
137
- GenerationConfigModal: () => <div data-testid="generation-modal">Generation Modal</div>,
138
- }));
139
-
140
132
  // Create mock props matching the current ResourceViewerPageProps
141
133
  const createMockProps = (overrides?: Partial<ResourceViewerPageProps>): ResourceViewerPageProps => ({
142
134
  resource: {
143
135
  '@context': 'https://www.w3.org/ns/anno.jsonld',
144
- '@id': 'http://localhost/resources/test-123',
136
+ '@id': 'test-123',
145
137
  '@type': 'schema:DigitalDocument',
146
138
  name: 'Test Resource',
147
139
  description: 'A test resource for unit testing',
@@ -155,7 +147,7 @@ const createMockProps = (overrides?: Partial<ResourceViewerPageProps>): Resource
155
147
  },
156
148
  ],
157
149
  },
158
- rUri: 'http://localhost/resources/test-123' as any,
150
+ rUri: 'test-123' as any,
159
151
  locale: 'en',
160
152
  cacheManager: {},
161
153
  Link: ({ children }: any) => <a>{children}</a>,
@@ -163,8 +155,6 @@ const createMockProps = (overrides?: Partial<ResourceViewerPageProps>): Resource
163
155
  refetchDocument: vi.fn().mockResolvedValue(undefined),
164
156
  ToolbarPanels: ({ children, activePanel }: any) =>
165
157
  !activePanel ? null : <div data-testid="toolbar-panels">{children}</div>,
166
- SearchResourcesModal: () => <div data-testid="search-modal">Search Modal</div>,
167
- GenerationConfigModal: () => <div data-testid="generation-modal">Generation Modal</div>,
168
158
  ...overrides,
169
159
  });
170
160
 
@@ -323,18 +313,12 @@ describe('ResourceViewerPage', () => {
323
313
  });
324
314
 
325
315
  describe('Modals', () => {
326
- it('renders search resources modal', () => {
327
- const props = createMockProps();
328
- renderWithProviders(<ResourceViewerPage {...props} />);
329
-
330
- expect(screen.getByTestId('search-modal')).toBeInTheDocument();
331
- });
332
-
333
- it('renders generation config modal', () => {
316
+ it('renders reference wizard modal', () => {
334
317
  const props = createMockProps();
335
318
  renderWithProviders(<ResourceViewerPage {...props} />);
336
319
 
337
- expect(screen.getByTestId('generation-modal')).toBeInTheDocument();
320
+ // Wizard modal is rendered but closed by default
321
+ // It opens when bind:initiate is emitted from ReferenceEntry
338
322
  });
339
323
  });
340
324
 
@@ -19,7 +19,7 @@ import { render, waitFor, act } from '@testing-library/react';
19
19
  import { EventBusProvider, resetEventBusForTesting, useEventBus } from '../../../contexts/EventBusContext';
20
20
  import { ApiClientProvider } from '../../../contexts/ApiClientContext';
21
21
  import { AuthTokenProvider } from '../../../contexts/AuthTokenContext';
22
- import { resourceUri } from '@semiont/core';
22
+ import { resourceId } from '@semiont/core';
23
23
  import { useMarkFlow } from '../../../hooks/useMarkFlow';
24
24
  import { useYieldFlow } from '../../../hooks/useYieldFlow';
25
25
 
@@ -38,7 +38,7 @@ vi.mock('../../../components/Toast', () => ({
38
38
 
39
39
  describe('Toast Notifications - Verifies Toast Integration', () => {
40
40
  let eventBusInstance: any;
41
- const rUri = resourceUri('https://example.com/resources/test');
41
+ const rUri = resourceId('test');
42
42
 
43
43
  beforeEach(() => {
44
44
  resetEventBusForTesting();