@semiont/react-ui 0.4.14 → 0.4.16

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 (169) hide show
  1. package/README.md +18 -12
  2. package/dist/KnowledgeBaseSessionContext-BNNunwzO.d.mts +175 -0
  3. package/dist/{PdfAnnotationCanvas.client-CW6SKH2U.mjs → PdfAnnotationCanvas.client-CHDCGQBR.mjs} +3 -3
  4. package/dist/{ar-R4CRNXEF.mjs → ar-3W37O3R3.mjs} +9 -3
  5. package/dist/ar-3W37O3R3.mjs.map +1 -0
  6. package/dist/{bn-CZKGRHTA.mjs → bn-JZTJLMVE.mjs} +9 -3
  7. package/dist/bn-JZTJLMVE.mjs.map +1 -0
  8. package/dist/chunk-FAI3S4BM.mjs +865 -0
  9. package/dist/chunk-FAI3S4BM.mjs.map +1 -0
  10. package/dist/{chunk-HVMAGUFA.mjs → chunk-NOD3NCXE.mjs} +3 -1
  11. package/dist/chunk-NOD3NCXE.mjs.map +1 -0
  12. package/dist/{chunk-HNZOXH4L.mjs → chunk-OZICDVH7.mjs} +5 -3
  13. package/dist/chunk-OZICDVH7.mjs.map +1 -0
  14. package/dist/{chunk-BQJWOK4C.mjs → chunk-VN5NY4SN.mjs} +9 -8
  15. package/dist/chunk-VN5NY4SN.mjs.map +1 -0
  16. package/dist/{cs-4WIB2IHH.mjs → cs-XYHH7HNE.mjs} +9 -3
  17. package/dist/cs-XYHH7HNE.mjs.map +1 -0
  18. package/dist/{da-JWYEUYPX.mjs → da-MZKIECVT.mjs} +9 -3
  19. package/dist/da-MZKIECVT.mjs.map +1 -0
  20. package/dist/{de-GWUQZGER.mjs → de-AYXTMRQW.mjs} +9 -3
  21. package/dist/de-AYXTMRQW.mjs.map +1 -0
  22. package/dist/{el-DM2GT7P5.mjs → el-A6CVQWAW.mjs} +9 -3
  23. package/dist/el-A6CVQWAW.mjs.map +1 -0
  24. package/dist/{en-IUV4ZXKH.mjs → en-YPQQBI4T.mjs} +2 -2
  25. package/dist/{es-6LVQIM3D.mjs → es-M2HXLJGT.mjs} +9 -3
  26. package/dist/es-M2HXLJGT.mjs.map +1 -0
  27. package/dist/{fa-IRUJY3QI.mjs → fa-V6JZJDYP.mjs} +9 -3
  28. package/dist/fa-V6JZJDYP.mjs.map +1 -0
  29. package/dist/{fi-53FBOEVT.mjs → fi-ONDTZ5H7.mjs} +9 -3
  30. package/dist/fi-ONDTZ5H7.mjs.map +1 -0
  31. package/dist/{fr-Q5KY7QL6.mjs → fr-PAPV4H4G.mjs} +9 -3
  32. package/dist/fr-PAPV4H4G.mjs.map +1 -0
  33. package/dist/{he-HJNKULBY.mjs → he-F6VTLJLW.mjs} +9 -3
  34. package/dist/he-F6VTLJLW.mjs.map +1 -0
  35. package/dist/{hi-UYZ4X6CR.mjs → hi-CFUAV4BF.mjs} +9 -3
  36. package/dist/hi-CFUAV4BF.mjs.map +1 -0
  37. package/dist/{id-UAQMH6U2.mjs → id-NBKLCCI7.mjs} +9 -3
  38. package/dist/id-NBKLCCI7.mjs.map +1 -0
  39. package/dist/index.d.mts +141 -169
  40. package/dist/index.mjs +2394 -2116
  41. package/dist/index.mjs.map +1 -1
  42. package/dist/{it-C7QEBNFA.mjs → it-SLSOWVVU.mjs} +9 -3
  43. package/dist/it-SLSOWVVU.mjs.map +1 -0
  44. package/dist/{ja-THS6AOSJ.mjs → ja-L5IG4ECE.mjs} +9 -3
  45. package/dist/ja-L5IG4ECE.mjs.map +1 -0
  46. package/dist/{ko-XKK3TWQG.mjs → ko-QYMTULKK.mjs} +9 -3
  47. package/dist/ko-QYMTULKK.mjs.map +1 -0
  48. package/dist/{ms-GSK7LIF7.mjs → ms-5DGSFKM2.mjs} +9 -3
  49. package/dist/ms-5DGSFKM2.mjs.map +1 -0
  50. package/dist/{nl-KUBWITGY.mjs → nl-VZPCGONO.mjs} +9 -3
  51. package/dist/nl-VZPCGONO.mjs.map +1 -0
  52. package/dist/{no-ECWZUHT6.mjs → no-MF6F352I.mjs} +9 -3
  53. package/dist/no-MF6F352I.mjs.map +1 -0
  54. package/dist/{pl-PLVWSZWS.mjs → pl-WIK72JUO.mjs} +9 -3
  55. package/dist/pl-WIK72JUO.mjs.map +1 -0
  56. package/dist/{pt-AL74ZTKB.mjs → pt-RRP5ZF6A.mjs} +9 -3
  57. package/dist/pt-RRP5ZF6A.mjs.map +1 -0
  58. package/dist/{ro-WTPHLHGS.mjs → ro-XHQLC3T7.mjs} +9 -3
  59. package/dist/ro-XHQLC3T7.mjs.map +1 -0
  60. package/dist/{sv-QCLI7SG4.mjs → sv-EWULDN6E.mjs} +9 -3
  61. package/dist/sv-EWULDN6E.mjs.map +1 -0
  62. package/dist/test-utils.d.mts +13 -62
  63. package/dist/test-utils.mjs +41 -22
  64. package/dist/test-utils.mjs.map +1 -1
  65. package/dist/{th-WCKVZU6U.mjs → th-TGOBHFG4.mjs} +9 -3
  66. package/dist/th-TGOBHFG4.mjs.map +1 -0
  67. package/dist/{tr-2CAFS2XS.mjs → tr-LMMPBMV7.mjs} +9 -3
  68. package/dist/tr-LMMPBMV7.mjs.map +1 -0
  69. package/dist/{uk-TDE4JLCY.mjs → uk-IPGRRJY6.mjs} +9 -3
  70. package/dist/uk-IPGRRJY6.mjs.map +1 -0
  71. package/dist/{vi-KKXZ4PCX.mjs → vi-Q676OJQS.mjs} +9 -3
  72. package/dist/vi-Q676OJQS.mjs.map +1 -0
  73. package/dist/{zh-VH4XN5PV.mjs → zh-F3MTWQDX.mjs} +9 -3
  74. package/dist/zh-F3MTWQDX.mjs.map +1 -0
  75. package/package.json +5 -3
  76. package/src/components/ProtectedErrorBoundary.tsx +95 -0
  77. package/src/components/__tests__/ProtectedErrorBoundary.test.tsx +197 -0
  78. package/src/components/modals/PermissionDeniedModal.tsx +140 -0
  79. package/src/components/modals/ReferenceWizardModal.tsx +3 -2
  80. package/src/components/modals/SessionExpiredModal.tsx +101 -0
  81. package/src/components/modals/__tests__/PermissionDeniedModal.test.tsx +150 -0
  82. package/src/components/modals/__tests__/SessionExpiredModal.test.tsx +115 -0
  83. package/src/components/resource/AnnotationHistory.tsx +5 -6
  84. package/src/components/resource/HistoryEvent.tsx +9 -8
  85. package/src/components/resource/__tests__/AnnotationHistory.test.tsx +33 -34
  86. package/src/components/resource/__tests__/HistoryEvent.test.tsx +18 -19
  87. package/src/components/resource/__tests__/event-formatting.test.ts +70 -94
  88. package/src/components/resource/event-formatting.ts +92 -56
  89. package/src/components/resource/panels/ReferenceEntry.tsx +7 -5
  90. package/src/components/resource/panels/ResourceInfoPanel.tsx +18 -6
  91. package/src/components/resource/panels/__tests__/ReferenceEntry.test.tsx +12 -12
  92. package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +24 -0
  93. package/src/features/resource-compose/components/ResourceComposePage.tsx +10 -1
  94. package/src/features/resource-viewer/__tests__/AnnotationCreationPending.test.tsx +1 -1
  95. package/src/features/resource-viewer/__tests__/AnnotationDeletionIntegration.test.tsx +4 -4
  96. package/src/features/resource-viewer/__tests__/AnnotationProgressDismissal.test.tsx +5 -10
  97. package/src/features/resource-viewer/__tests__/BindFlowIntegration.test.tsx +23 -54
  98. package/src/features/resource-viewer/__tests__/DetectionFlowBug.test.tsx +6 -6
  99. package/src/features/resource-viewer/__tests__/DetectionFlowIntegration.test.tsx +7 -19
  100. package/src/features/resource-viewer/__tests__/ToastNotifications.test.tsx +1 -1
  101. package/src/features/resource-viewer/__tests__/YieldFlowIntegration.test.tsx +18 -44
  102. package/src/features/resource-viewer/__tests__/annotation-progress-flow.test.tsx +6 -6
  103. package/src/features/resource-viewer/components/ResourceViewerPage.tsx +26 -26
  104. package/src/styles/features/compose.css +63 -0
  105. package/translations/ar.json +6 -2
  106. package/translations/bn.json +6 -2
  107. package/translations/cs.json +6 -2
  108. package/translations/da.json +6 -2
  109. package/translations/de.json +6 -2
  110. package/translations/el.json +6 -2
  111. package/translations/en.json +2 -0
  112. package/translations/es.json +6 -2
  113. package/translations/fa.json +6 -2
  114. package/translations/fi.json +6 -2
  115. package/translations/fr.json +6 -2
  116. package/translations/he.json +6 -2
  117. package/translations/hi.json +6 -2
  118. package/translations/id.json +6 -2
  119. package/translations/it.json +6 -2
  120. package/translations/ja.json +6 -2
  121. package/translations/ko.json +6 -2
  122. package/translations/ms.json +6 -2
  123. package/translations/nl.json +6 -2
  124. package/translations/no.json +6 -2
  125. package/translations/pl.json +6 -2
  126. package/translations/pt.json +6 -2
  127. package/translations/ro.json +6 -2
  128. package/translations/sv.json +6 -2
  129. package/translations/th.json +6 -2
  130. package/translations/tr.json +6 -2
  131. package/translations/uk.json +6 -2
  132. package/translations/vi.json +6 -2
  133. package/translations/zh.json +6 -2
  134. package/dist/TranslationManager-CudgH3gw.d.mts +0 -107
  135. package/dist/ar-R4CRNXEF.mjs.map +0 -1
  136. package/dist/bn-CZKGRHTA.mjs.map +0 -1
  137. package/dist/chunk-BQJWOK4C.mjs.map +0 -1
  138. package/dist/chunk-HNZOXH4L.mjs.map +0 -1
  139. package/dist/chunk-HVMAGUFA.mjs.map +0 -1
  140. package/dist/chunk-OL5UST25.mjs +0 -413
  141. package/dist/chunk-OL5UST25.mjs.map +0 -1
  142. package/dist/cs-4WIB2IHH.mjs.map +0 -1
  143. package/dist/da-JWYEUYPX.mjs.map +0 -1
  144. package/dist/de-GWUQZGER.mjs.map +0 -1
  145. package/dist/el-DM2GT7P5.mjs.map +0 -1
  146. package/dist/es-6LVQIM3D.mjs.map +0 -1
  147. package/dist/fa-IRUJY3QI.mjs.map +0 -1
  148. package/dist/fi-53FBOEVT.mjs.map +0 -1
  149. package/dist/fr-Q5KY7QL6.mjs.map +0 -1
  150. package/dist/he-HJNKULBY.mjs.map +0 -1
  151. package/dist/hi-UYZ4X6CR.mjs.map +0 -1
  152. package/dist/id-UAQMH6U2.mjs.map +0 -1
  153. package/dist/it-C7QEBNFA.mjs.map +0 -1
  154. package/dist/ja-THS6AOSJ.mjs.map +0 -1
  155. package/dist/ko-XKK3TWQG.mjs.map +0 -1
  156. package/dist/ms-GSK7LIF7.mjs.map +0 -1
  157. package/dist/nl-KUBWITGY.mjs.map +0 -1
  158. package/dist/no-ECWZUHT6.mjs.map +0 -1
  159. package/dist/pl-PLVWSZWS.mjs.map +0 -1
  160. package/dist/pt-AL74ZTKB.mjs.map +0 -1
  161. package/dist/ro-WTPHLHGS.mjs.map +0 -1
  162. package/dist/sv-QCLI7SG4.mjs.map +0 -1
  163. package/dist/th-WCKVZU6U.mjs.map +0 -1
  164. package/dist/tr-2CAFS2XS.mjs.map +0 -1
  165. package/dist/uk-TDE4JLCY.mjs.map +0 -1
  166. package/dist/vi-KKXZ4PCX.mjs.map +0 -1
  167. package/dist/zh-VH4XN5PV.mjs.map +0 -1
  168. /package/dist/{PdfAnnotationCanvas.client-CW6SKH2U.mjs.map → PdfAnnotationCanvas.client-CHDCGQBR.mjs.map} +0 -0
  169. /package/dist/{en-IUV4ZXKH.mjs.map → en-YPQQBI4T.mjs.map} +0 -0
@@ -26,10 +26,9 @@ import { EventBusProvider, useEventBus } from '../../../contexts/EventBusContext
26
26
  import { ApiClientProvider } from '../../../contexts/ApiClientContext';
27
27
  import { AuthTokenProvider } from '../../../contexts/AuthTokenContext';
28
28
  import { useBindFlow } from '../../../hooks/useBindFlow';
29
- import { SSEClient } from '@semiont/api-client';
29
+ import { SemiontApiClient } from '@semiont/api-client';
30
30
  import type { AnnotationId, ResourceId } from '@semiont/core';
31
31
  import { resourceId, annotationId } from '@semiont/core';
32
- import type { Emitter } from 'mitt';
33
32
  import type { EventMap } from '@semiont/core';
34
33
 
35
34
  // Mock Toast module to prevent "useToast must be used within a ToastProvider" errors
@@ -42,15 +41,7 @@ vi.mock('../../../components/Toast', () => ({
42
41
  }),
43
42
  }));
44
43
 
45
- // Mock SSE stream - SSE now emits directly to EventBus, no callbacks
46
- const createMockGenerationStream = () => {
47
- return {
48
- close: vi.fn(),
49
- };
50
- };
51
-
52
44
  describe('Generation Flow - Feature Integration', () => {
53
- let mockStream: ReturnType<typeof createMockGenerationStream>;
54
45
  let generateResourceSpy: any;
55
46
  let mockShowSuccess: ReturnType<typeof vi.fn>;
56
47
  let mockShowError: ReturnType<typeof vi.fn>;
@@ -59,11 +50,8 @@ describe('Generation Flow - Feature Integration', () => {
59
50
  beforeEach(() => {
60
51
  vi.clearAllMocks();
61
52
 
62
- // Create fresh mock stream for each test
63
- mockStream = createMockGenerationStream();
64
-
65
- // Spy on SSEClient prototype method
66
- generateResourceSpy = vi.spyOn(SSEClient.prototype, 'yieldResource').mockReturnValue(mockStream as any);
53
+ // Spy on SemiontApiClient prototype HTTP method (namespace methods call this)
54
+ generateResourceSpy = vi.spyOn(SemiontApiClient.prototype, 'yieldResourceFromAnnotation').mockResolvedValue({ correlationId: 'c1', jobId: 'j1' });
67
55
 
68
56
  // Mock callbacks
69
57
  mockShowSuccess = vi.fn();
@@ -107,17 +95,13 @@ describe('Generation Flow - Feature Integration', () => {
107
95
  expect(generateResourceSpy).toHaveBeenCalledWith(
108
96
  testResourceId,
109
97
  testAnnotationId,
110
- {
98
+ expect.objectContaining({
111
99
  title: 'Generated Document',
112
100
  prompt: 'Create a comprehensive document',
113
101
  language: 'en',
114
102
  temperature: 0.7,
115
103
  maxTokens: 2000,
116
- context: {
117
- sourceText: 'Reference text from the document',
118
- entityTypes: ['Person', 'Organization'],
119
- },
120
- },
104
+ }),
121
105
  expect.objectContaining({ auth: undefined })
122
106
  );
123
107
  });
@@ -295,7 +279,7 @@ describe('Generation Flow - Feature Integration', () => {
295
279
 
296
280
  // Emit failure
297
281
  act(() => {
298
- getEventBus().get('yield:failed').next({ error: new Error('Network error') });
282
+ getEventBus().get('yield:failed').next({ error: 'Network error' });
299
283
  });
300
284
 
301
285
  // Verify: progress cleared and not generating
@@ -305,18 +289,14 @@ describe('Generation Flow - Feature Integration', () => {
305
289
  });
306
290
  });
307
291
 
308
- it('should only call API once even with multiple event listeners', async () => {
292
+ it('should only call API once even with multiple renders', async () => {
309
293
  const testResourceId = resourceId('test-resource');
310
294
  const testAnnotationId = annotationId('test-annotation');
311
295
 
312
- const { emitGenerationStart, getEventBus } = renderYieldFlow(
296
+ const { emitGenerationStart } = renderYieldFlow(
313
297
  testResourceId
314
298
  );
315
299
 
316
- // Add an additional event listener (simulating multiple subscribers)
317
- const additionalListener = vi.fn();
318
- const subscription = getEventBus().get('yield:request').subscribe(additionalListener);
319
-
320
300
  // Trigger generation
321
301
  act(() => {
322
302
  emitGenerationStart(testAnnotationId, testResourceId, {
@@ -330,13 +310,8 @@ describe('Generation Flow - Feature Integration', () => {
330
310
  expect(generateResourceSpy).toHaveBeenCalled();
331
311
  });
332
312
 
333
- // VERIFY: API called exactly once, even though multiple listeners exist
313
+ // VERIFY: API called exactly once
334
314
  expect(generateResourceSpy).toHaveBeenCalledTimes(1);
335
-
336
- // VERIFY: Our additional listener was called (events work)
337
- expect(additionalListener).toHaveBeenCalledTimes(1);
338
-
339
- subscription.unsubscribe();
340
315
  });
341
316
 
342
317
  it('should forward final chunk as progress before emitting complete', async () => {
@@ -385,15 +360,13 @@ describe('Generation Flow - Feature Integration', () => {
385
360
  function renderYieldFlow(
386
361
  testResourceId: ResourceId
387
362
  ) {
388
- let eventBusInstance: Emitter<EventMap>;
363
+ let eventBusInstance: ReturnType<typeof useEventBus>;
364
+ let generateFn: ReturnType<typeof useYieldFlow>['onGenerateDocument'];
389
365
 
390
366
  // Component to capture EventBus instance and set up event operations
391
367
  function EventBusCapture() {
392
368
  eventBusInstance = useEventBus();
393
-
394
- // Set up resolution flow (resolve:update-body, resolve:link)
395
369
  useBindFlow(testResourceId);
396
-
397
370
  return null;
398
371
  }
399
372
 
@@ -402,12 +375,15 @@ function renderYieldFlow(
402
375
  const {
403
376
  isGenerating,
404
377
  generationProgress,
378
+ onGenerateDocument,
405
379
  } = useYieldFlow(
406
380
  'en',
407
381
  testResourceId,
408
382
  vi.fn()
409
383
  );
410
384
 
385
+ generateFn = onGenerateDocument;
386
+
411
387
  return (
412
388
  <div>
413
389
  <div data-testid="is-generating">
@@ -434,9 +410,10 @@ function renderYieldFlow(
434
410
  return {
435
411
  emitGenerationStart: (
436
412
  aId: AnnotationId,
437
- rId: ResourceId,
413
+ _rId: ResourceId,
438
414
  options: {
439
415
  title: string;
416
+ storageUri?: string;
440
417
  prompt?: string;
441
418
  language?: string;
442
419
  temperature?: number;
@@ -444,11 +421,8 @@ function renderYieldFlow(
444
421
  context: any;
445
422
  }
446
423
  ) => {
447
- eventBusInstance.get('yield:request').next({
448
- annotationId: aId,
449
- resourceId: rId,
450
- options,
451
- });
424
+ // Call the hook's callback directly (no longer EventBus-driven)
425
+ generateFn(aId as string, { storageUri: options.storageUri ?? 'file:///tmp/test', ...options });
452
426
  },
453
427
  getEventBus: () => eventBusInstance,
454
428
  };
@@ -31,7 +31,7 @@ import { useMarkFlow } from '../../../hooks/useMarkFlow';
31
31
  import { EventBusProvider, useEventBus } from '../../../contexts/EventBusContext';
32
32
  import { ApiClientProvider } from '../../../contexts/ApiClientContext';
33
33
  import { AuthTokenProvider } from '../../../contexts/AuthTokenContext';
34
- import { SSEClient } from '@semiont/api-client';
34
+ import { SemiontApiClient } from '@semiont/api-client';
35
35
  import type { components } from '@semiont/core';
36
36
 
37
37
  // Mock Toast module to prevent "useToast must be used within a ToastProvider" errors
@@ -167,11 +167,11 @@ describe('Detection Progress Flow Integration (Layer 3)', () => {
167
167
  // Create fresh stream for each test
168
168
  mockStream = new MockSSEStream();
169
169
 
170
- // Spy on SSEClient prototype methods to inject mock stream
171
- vi.spyOn(SSEClient.prototype, 'markHighlights').mockReturnValue(mockStream as any);
172
- vi.spyOn(SSEClient.prototype, 'markAssessments').mockReturnValue(mockStream as any);
173
- vi.spyOn(SSEClient.prototype, 'markComments').mockReturnValue(mockStream as any);
174
- vi.spyOn(SSEClient.prototype, 'markReferences').mockReturnValue(mockStream as any);
170
+ // Spy on SemiontApiClient prototype HTTP methods (namespace methods call these)
171
+ vi.spyOn(SemiontApiClient.prototype, 'annotateHighlights').mockResolvedValue({ correlationId: 'c1', jobId: 'j1' });
172
+ vi.spyOn(SemiontApiClient.prototype, 'annotateAssessments').mockResolvedValue({ correlationId: 'c1', jobId: 'j1' });
173
+ vi.spyOn(SemiontApiClient.prototype, 'annotateComments').mockResolvedValue({ correlationId: 'c1', jobId: 'j1' });
174
+ vi.spyOn(SemiontApiClient.prototype, 'annotateReferences').mockResolvedValue({ correlationId: 'c1', jobId: 'j1' });
175
175
 
176
176
  mockAnnotations = [];
177
177
  });
@@ -7,7 +7,7 @@
7
7
 
8
8
  import React, { useState, useEffect, useCallback, useMemo } from 'react';
9
9
  import { useQueryClient } from '@tanstack/react-query';
10
- import type { components, ResourceId, ResourceEvent, GatheredContext } from '@semiont/core';
10
+ import type { components, ResourceId, GatheredContext, EventMap } from '@semiont/core';
11
11
  import { annotationId } from '@semiont/core';
12
12
  import { getLanguage, getPrimaryRepresentation, getPrimaryMediaType, getMimeCategory } from '@semiont/api-client';
13
13
  import { ANNOTATORS } from '@semiont/react-ui';
@@ -140,7 +140,7 @@ export function ResourceViewerPage({
140
140
 
141
141
  // Get unified event bus for subscribing to UI events
142
142
  const eventBus = useEventBus();
143
- const client = useApiClient();
143
+ const semiont = useApiClient();
144
144
  const queryClient = useQueryClient(); // retained for non-store queries (events log)
145
145
 
146
146
  // UI state hooks
@@ -164,17 +164,17 @@ export function ResourceViewerPage({
164
164
 
165
165
  // Binary path: fetch short-lived media token, construct URL
166
166
  const { token: mediaToken, loading: mediaTokenLoading } = useMediaToken(rUri);
167
- const binaryContent = (isBinary && mediaToken && client)
168
- ? `${client.baseUrl}/api/resources/${rUri}?token=${mediaToken}`
167
+ const binaryContent = (isBinary && mediaToken && semiont)
168
+ ? `${semiont.baseUrl}/api/resources/${rUri}?token=${mediaToken}`
169
169
  : '';
170
170
 
171
171
  const content = isBinary ? binaryContent : textContent;
172
172
  const contentLoading = isBinary ? mediaTokenLoading : textLoading;
173
173
 
174
- const annotationsData = useObservable(client.stores.annotations.listForResource(rUri));
174
+ const annotationsData = useObservable(semiont.browse.annotations(rUri));
175
175
  const annotations = useMemo(
176
- () => annotationsData?.annotations || [],
177
- [annotationsData?.annotations]
176
+ () => annotationsData || [],
177
+ [annotationsData]
178
178
  );
179
179
 
180
180
  const { data: referencedByData, isLoading: referencedByLoading } = resources.referencedBy.useQuery(rUri);
@@ -209,8 +209,8 @@ export function ResourceViewerPage({
209
209
  setWizardEntityTypes(event.entityTypes);
210
210
  setWizardOpen(true);
211
211
 
212
- // Trigger context gathering
213
- eventBus.get('gather:requested').next({ correlationId: crypto.randomUUID(), annotationId: event.annotationId, resourceId: event.resourceId });
212
+ // Trigger context gathering — gather:requested is consumed by useContextGatherFlow
213
+ eventBus.get('gather:requested').next({ correlationId: crypto.randomUUID(), annotationId: event.annotationId, resourceId: event.resourceId, options: { contextWindow: 2000 } });
214
214
  });
215
215
  return () => subscription.unsubscribe();
216
216
  }, [eventBus]);
@@ -231,21 +231,18 @@ export function ResourceViewerPage({
231
231
  });
232
232
  }, [onGenerateDocument]);
233
233
 
234
- const handleWizardLinkResource = useCallback((referenceId: string, targetResourceId: string) => {
235
- eventBus.get('bind:update-body').next({
236
- annotationId: annotationId(referenceId),
237
- resourceId: rUri,
238
- operations: [{
239
- op: 'add',
240
- item: {
241
- type: 'SpecificResource' as const,
242
- source: targetResourceId,
243
- purpose: 'linking' as const,
244
- },
245
- }],
246
- });
247
- showSuccess('Reference linked successfully');
248
- }, [rUri, showSuccess]); // eventBus is stable singleton
234
+ const handleWizardLinkResource = useCallback(async (referenceId: string, targetResourceId: string) => {
235
+ try {
236
+ await semiont.bind.body(
237
+ rUri,
238
+ annotationId(referenceId),
239
+ [{ op: 'add', item: { type: 'SpecificResource' as const, source: targetResourceId, purpose: 'linking' as const } }],
240
+ );
241
+ showSuccess('Reference linked successfully');
242
+ } catch (error) {
243
+ showError(`Failed to link reference: ${error instanceof Error ? error.message : String(error)}`);
244
+ }
245
+ }, [rUri, semiont, showSuccess, showError]);
249
246
 
250
247
  const handleWizardComposeNavigate = useCallback((
251
248
  context: GatheredContext,
@@ -364,8 +361,8 @@ export function ResourceViewerPage({
364
361
  triggerSparkleAnimation(annotationId);
365
362
  }, [triggerSparkleAnimation]);
366
363
 
367
- const handleAnnotationAdded = useCallback((event: Extract<ResourceEvent, { type: 'annotation.added' }>) => {
368
- triggerSparkleAnimation(event.payload.annotation.id);
364
+ const handleAnnotationAdded = useCallback((stored: EventMap['mark:added']) => {
365
+ triggerSparkleAnimation(stored.payload.annotation.id);
369
366
  }, [triggerSparkleAnimation]);
370
367
 
371
368
  const handleAnnotationCreateFailed = useCallback(() => showError('Failed to create annotation'), [showError]);
@@ -560,6 +557,7 @@ export function ResourceViewerPage({
560
557
  activePanel={activePanel}
561
558
  theme={theme}
562
559
  showLineNumbers={showLineNumbers}
560
+ hoverDelayMs={hoverDelayMs}
563
561
  width={
564
562
  activePanel === 'jsonld' ? 'w-[600px]' :
565
563
  activePanel === 'annotations' ? 'w-[400px]' :
@@ -615,10 +613,12 @@ export function ResourceViewerPage({
615
613
  {/* Document Info Panel */}
616
614
  {activePanel === 'info' && (
617
615
  <ResourceInfoPanel
616
+ resourceId={rUri}
618
617
  documentEntityTypes={documentEntityTypes}
619
618
  documentLocale={getLanguage(resource)}
620
619
  primaryMediaType={primaryMediaType}
621
620
  primaryByteSize={primaryByteSize}
621
+ storageUri={resource.storageUri}
622
622
  isArchived={resource.archived ?? false}
623
623
  dateCreated={resource.dateCreated}
624
624
  dateModified={resource.dateModified}
@@ -9,6 +9,69 @@
9
9
  /* Use full width like the Discover page instead of centering */
10
10
  }
11
11
 
12
+ /* Two-column compose grid: metadata left, content right on wide screens */
13
+ .semiont-compose-grid {
14
+ display: grid;
15
+ grid-template-columns: 1fr;
16
+ gap: 1rem;
17
+ }
18
+
19
+ .semiont-compose-grid__meta {
20
+ display: flex;
21
+ flex-direction: column;
22
+ gap: 1rem;
23
+ }
24
+
25
+ .semiont-compose-grid__content {
26
+ display: flex;
27
+ flex-direction: column;
28
+ gap: 1rem;
29
+ min-height: 0;
30
+ }
31
+
32
+ /* Let the editor fill remaining vertical space */
33
+ .semiont-compose-grid__content .semiont-form__editor {
34
+ flex: 1;
35
+ display: flex;
36
+ flex-direction: column;
37
+ }
38
+
39
+ .semiont-compose-grid__content .semiont-form__editor-wrapper {
40
+ flex: 1;
41
+ min-height: 200px;
42
+ }
43
+
44
+ /* On screens wider than 768px, split into two columns */
45
+ @media (min-width: 768px) {
46
+ .semiont-compose-grid {
47
+ grid-template-columns: 280px 1fr;
48
+ grid-template-rows: 1fr auto;
49
+ }
50
+
51
+ .semiont-compose-grid__meta {
52
+ grid-column: 1;
53
+ grid-row: 1;
54
+ }
55
+
56
+ .semiont-compose-grid__content {
57
+ grid-column: 2;
58
+ grid-row: 1;
59
+ }
60
+
61
+ /* Action buttons span full width below both columns */
62
+ .semiont-compose-grid ~ .semiont-form__actions,
63
+ .semiont-form__actions {
64
+ grid-column: 1 / -1;
65
+ }
66
+ }
67
+
68
+ /* On very wide screens, give metadata more room */
69
+ @media (min-width: 1200px) {
70
+ .semiont-compose-grid {
71
+ grid-template-columns: 320px 1fr;
72
+ }
73
+ }
74
+
12
75
  /* Form Upload Section */
13
76
  .semiont-form__upload-section {
14
77
  display: flex;
@@ -28,7 +28,8 @@
28
28
  "creationMethod": "طريقة الإنشاء",
29
29
  "attributedTo": "منسوب إلى",
30
30
  "derivedFrom": "مشتق من",
31
- "generatedBy": "أُنشئ بواسطة"
31
+ "generatedBy": "أُنشئ بواسطة",
32
+ "storageUri": "التخزين"
32
33
  },
33
34
  "CollaborationPanel": {
34
35
  "title": "التعاون",
@@ -373,5 +374,8 @@
373
374
  "semanticScoringHelp": "استخدام الذكاء الاصطناعي لتقييم النتائج حسب الصلة الدلالية",
374
375
  "searching": "جاري البحث...",
375
376
  "search": "بحث"
377
+ },
378
+ "History": {
379
+ "embeddingComputed": "تم حساب التضمين"
376
380
  }
377
- }
381
+ }
@@ -28,7 +28,8 @@
28
28
  "creationMethod": "তৈরির পদ্ধতি",
29
29
  "attributedTo": "সম্পাদককৃত",
30
30
  "derivedFrom": "থেকে উৎপন্ন",
31
- "generatedBy": "তৈরিকারক"
31
+ "generatedBy": "তৈরিকারক",
32
+ "storageUri": "সঞ্চয়স্থান"
32
33
  },
33
34
  "CollaborationPanel": {
34
35
  "title": "সহযোগিতা",
@@ -373,5 +374,8 @@
373
374
  "semanticScoringHelp": "অর্থগত প্রাসঙ্গিকতা অনুযায়ী ফলাফল স্কোর করতে AI ব্যবহার করুন",
374
375
  "searching": "অনুসন্ধান করা হচ্ছে...",
375
376
  "search": "অনুসন্ধান"
377
+ },
378
+ "History": {
379
+ "embeddingComputed": "এমবেডিং গণনা করা হয়েছে"
376
380
  }
377
- }
381
+ }
@@ -28,7 +28,8 @@
28
28
  "creationMethod": "Způsob vytvoření",
29
29
  "attributedTo": "Připsáno",
30
30
  "derivedFrom": "Odvozeno z",
31
- "generatedBy": "Vygenerováno pomocí"
31
+ "generatedBy": "Vygenerováno pomocí",
32
+ "storageUri": "Úložiště"
32
33
  },
33
34
  "CollaborationPanel": {
34
35
  "title": "Spolupráce",
@@ -373,5 +374,8 @@
373
374
  "semanticScoringHelp": "Použít AI k hodnocení výsledků podle sémantické relevance",
374
375
  "searching": "Vyhledávání...",
375
376
  "search": "Hledat"
377
+ },
378
+ "History": {
379
+ "embeddingComputed": "Embedding vypočten"
376
380
  }
377
- }
381
+ }
@@ -28,7 +28,8 @@
28
28
  "creationMethod": "Oprettelsesmetode",
29
29
  "attributedTo": "Tilskrevet",
30
30
  "derivedFrom": "Afledt af",
31
- "generatedBy": "Genereret af"
31
+ "generatedBy": "Genereret af",
32
+ "storageUri": "Lagring"
32
33
  },
33
34
  "CollaborationPanel": {
34
35
  "title": "Samarbejde",
@@ -373,5 +374,8 @@
373
374
  "semanticScoringHelp": "Brug AI til at score resultater efter semantisk relevans",
374
375
  "searching": "Søger...",
375
376
  "search": "Søg"
377
+ },
378
+ "History": {
379
+ "embeddingComputed": "Embedding beregnet"
376
380
  }
377
- }
381
+ }
@@ -28,7 +28,8 @@
28
28
  "creationMethod": "Erstellungsmethode",
29
29
  "attributedTo": "Zugeschrieben",
30
30
  "derivedFrom": "Abgeleitet von",
31
- "generatedBy": "Erstellt von"
31
+ "generatedBy": "Erstellt von",
32
+ "storageUri": "Speicher"
32
33
  },
33
34
  "CollaborationPanel": {
34
35
  "title": "Zusammenarbeit",
@@ -373,5 +374,8 @@
373
374
  "semanticScoringHelp": "KI zur Bewertung der Ergebnisse nach semantischer Relevanz verwenden",
374
375
  "searching": "Suche läuft...",
375
376
  "search": "Suchen"
377
+ },
378
+ "History": {
379
+ "embeddingComputed": "Embedding berechnet"
376
380
  }
377
- }
381
+ }
@@ -28,7 +28,8 @@
28
28
  "creationMethod": "Μέθοδος δημιουργίας",
29
29
  "attributedTo": "Αποδίδεται σε",
30
30
  "derivedFrom": "Παράγεται από",
31
- "generatedBy": "Δημιουργήθηκε από"
31
+ "generatedBy": "Δημιουργήθηκε από",
32
+ "storageUri": "Αποθήκευση"
32
33
  },
33
34
  "CollaborationPanel": {
34
35
  "title": "Συνεργασία",
@@ -373,5 +374,8 @@
373
374
  "semanticScoringHelp": "Χρήση AI για βαθμολόγηση αποτελεσμάτων κατά σημασιολογική συνάφεια",
374
375
  "searching": "Αναζήτηση...",
375
376
  "search": "Αναζήτηση"
377
+ },
378
+ "History": {
379
+ "embeddingComputed": "Ενσωμάτωση υπολογίστηκε"
376
380
  }
377
- }
381
+ }
@@ -16,6 +16,7 @@
16
16
  "representation": "Representation",
17
17
  "mediaType": "Media Type",
18
18
  "byteSize": "Size",
19
+ "storageUri": "Storage",
19
20
  "clone": "Clone",
20
21
  "cloneDescription": "Generate a shareable clone link for this resource",
21
22
  "archive": "Archive",
@@ -73,6 +74,7 @@
73
74
  "annotationRemoved": "Annotation Removed",
74
75
  "annotationBodyUpdated": "Annotation Updated",
75
76
  "jobEvent": "Job Event",
77
+ "embeddingComputed": "Embedding Computed",
76
78
  "justNow": "just now",
77
79
  "minutesAgo": "{{count}}m ago",
78
80
  "hoursAgo": "{{count}}h ago",
@@ -28,7 +28,8 @@
28
28
  "creationMethod": "Cómo se creó",
29
29
  "attributedTo": "Atribuido a",
30
30
  "derivedFrom": "Derivado de",
31
- "generatedBy": "Generado por"
31
+ "generatedBy": "Generado por",
32
+ "storageUri": "Almacenamiento"
32
33
  },
33
34
  "CollaborationPanel": {
34
35
  "title": "Colaboración",
@@ -373,5 +374,8 @@
373
374
  "semanticScoringHelp": "Usar IA para puntuar resultados por relevancia semántica",
374
375
  "searching": "Buscando...",
375
376
  "search": "Buscar"
377
+ },
378
+ "History": {
379
+ "embeddingComputed": "Embedding calculado"
376
380
  }
377
- }
381
+ }
@@ -28,7 +28,8 @@
28
28
  "creationMethod": "روش ایجاد",
29
29
  "attributedTo": "منتسب به",
30
30
  "derivedFrom": "مشتق از",
31
- "generatedBy": "تولیدشده توسط"
31
+ "generatedBy": "تولیدشده توسط",
32
+ "storageUri": "ذخیره‌سازی"
32
33
  },
33
34
  "CollaborationPanel": {
34
35
  "title": "همکاری",
@@ -373,5 +374,8 @@
373
374
  "semanticScoringHelp": "استفاده از هوش مصنوعی برای امتیازدهی نتایج بر اساس ارتباط معنایی",
374
375
  "searching": "در حال جستجو...",
375
376
  "search": "جستجو"
377
+ },
378
+ "History": {
379
+ "embeddingComputed": "تعبیه محاسبه شد"
376
380
  }
377
- }
381
+ }
@@ -28,7 +28,8 @@
28
28
  "creationMethod": "Luontitapa",
29
29
  "attributedTo": "Tekijä",
30
30
  "derivedFrom": "Johdettu",
31
- "generatedBy": "Luonut"
31
+ "generatedBy": "Luonut",
32
+ "storageUri": "Tallennuspaikka"
32
33
  },
33
34
  "CollaborationPanel": {
34
35
  "title": "Yhteistyö",
@@ -373,5 +374,8 @@
373
374
  "semanticScoringHelp": "Käytä tekoälyä tulosten pisteyttämiseen semanttisen relevanssin mukaan",
374
375
  "searching": "Haetaan...",
375
376
  "search": "Hae"
377
+ },
378
+ "History": {
379
+ "embeddingComputed": "Upotus laskettu"
376
380
  }
377
- }
381
+ }
@@ -28,7 +28,8 @@
28
28
  "creationMethod": "Mode de création",
29
29
  "attributedTo": "Attribué à",
30
30
  "derivedFrom": "Dérivé de",
31
- "generatedBy": "Généré par"
31
+ "generatedBy": "Généré par",
32
+ "storageUri": "Stockage"
32
33
  },
33
34
  "CollaborationPanel": {
34
35
  "title": "Collaboration",
@@ -373,5 +374,8 @@
373
374
  "semanticScoringHelp": "Utiliser l'IA pour évaluer les résultats par pertinence sémantique",
374
375
  "searching": "Recherche en cours...",
375
376
  "search": "Rechercher"
377
+ },
378
+ "History": {
379
+ "embeddingComputed": "Embedding calculé"
376
380
  }
377
- }
381
+ }
@@ -28,7 +28,8 @@
28
28
  "creationMethod": "אופן יצירה",
29
29
  "attributedTo": "מיוחס ל",
30
30
  "derivedFrom": "נגזר מ",
31
- "generatedBy": "נוצר על ידי"
31
+ "generatedBy": "נוצר על ידי",
32
+ "storageUri": "אחסון"
32
33
  },
33
34
  "CollaborationPanel": {
34
35
  "title": "שיתוף פעולה",
@@ -373,5 +374,8 @@
373
374
  "semanticScoringHelp": "שימוש ב-AI לדירוג תוצאות לפי רלוונטיות סמנטית",
374
375
  "searching": "מחפש...",
375
376
  "search": "חיפוש"
377
+ },
378
+ "History": {
379
+ "embeddingComputed": "הטמעה חושבה"
376
380
  }
377
- }
381
+ }
@@ -28,7 +28,8 @@
28
28
  "creationMethod": "निर्माण विधि",
29
29
  "attributedTo": "श्रेय दिया गया",
30
30
  "derivedFrom": "से व्युत्पन्न",
31
- "generatedBy": "द्वारा उत्पन्न"
31
+ "generatedBy": "द्वारा उत्पन्न",
32
+ "storageUri": "भंडारण"
32
33
  },
33
34
  "CollaborationPanel": {
34
35
  "title": "सहयोग",
@@ -373,5 +374,8 @@
373
374
  "semanticScoringHelp": "अर्थगत प्रासंगिकता के अनुसार परिणामों को स्कोर करने के लिए AI का उपयोग करें",
374
375
  "searching": "खोज रहे हैं...",
375
376
  "search": "खोजें"
377
+ },
378
+ "History": {
379
+ "embeddingComputed": "एम्बेडिंग गणना की गई"
376
380
  }
377
- }
381
+ }
@@ -28,7 +28,8 @@
28
28
  "creationMethod": "Cara pembuatan",
29
29
  "attributedTo": "Dikaitkan dengan",
30
30
  "derivedFrom": "Diturunkan dari",
31
- "generatedBy": "Dibuat oleh"
31
+ "generatedBy": "Dibuat oleh",
32
+ "storageUri": "Penyimpanan"
32
33
  },
33
34
  "CollaborationPanel": {
34
35
  "title": "Kolaborasi",
@@ -373,5 +374,8 @@
373
374
  "semanticScoringHelp": "Gunakan AI untuk menilai hasil berdasarkan relevansi semantik",
374
375
  "searching": "Mencari...",
375
376
  "search": "Cari"
377
+ },
378
+ "History": {
379
+ "embeddingComputed": "Embedding dihitung"
376
380
  }
377
- }
381
+ }