@semiont/react-ui 0.4.20 → 0.4.22
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.
- package/README.md +8 -5
- package/dist/{PdfAnnotationCanvas.client-CHDCGQBR.mjs → PdfAnnotationCanvas.client-5QESNO5H.mjs} +13 -16
- package/dist/PdfAnnotationCanvas.client-5QESNO5H.mjs.map +1 -0
- package/dist/TranslationManager-9Xj3MIWQ.d.mts +16 -0
- package/dist/chunk-4NOUO3W6.mjs +7788 -0
- package/dist/chunk-4NOUO3W6.mjs.map +1 -0
- package/dist/index.d.mts +212 -1206
- package/dist/index.mjs +3332 -13712
- package/dist/index.mjs.map +1 -1
- package/dist/test-utils.d.mts +48 -21
- package/dist/test-utils.mjs +2505 -87
- package/dist/test-utils.mjs.map +1 -1
- package/package.json +2 -2
- package/src/components/AnnotateReferencesProgressWidget.tsx +21 -28
- package/src/components/CodeMirrorRenderer.tsx +12 -12
- package/src/components/LiveRegion.tsx +1 -2
- package/src/components/StatusDisplay.tsx +42 -16
- package/src/components/Toolbar.tsx +4 -4
- package/src/components/__tests__/AnnotateReferencesProgressWidget.test.tsx +34 -20
- package/src/components/__tests__/StatusDisplay.test.tsx +50 -65
- package/src/components/__tests__/Toolbar.test.tsx +4 -4
- package/src/components/annotation/AnnotateToolbar.tsx +8 -9
- package/src/components/annotation/__tests__/AnnotateToolbar.test.tsx +31 -77
- package/src/components/annotation-popups/JsonLdView.tsx +1 -2
- package/src/components/annotation-popups/__tests__/JsonLdView.test.tsx +1 -2
- package/src/components/image-annotation/AnnotationOverlay.tsx +15 -18
- package/src/components/image-annotation/SvgDrawingCanvas.tsx +12 -17
- package/src/components/modals/ConfigureGenerationStep.tsx +1 -2
- package/src/components/modals/PermissionDeniedModal.tsx +11 -11
- package/src/components/modals/ReferenceWizardModal.tsx +14 -18
- package/src/components/modals/ResourceSearchModal.tsx +12 -8
- package/src/components/modals/SearchModal.tsx +11 -6
- package/src/components/modals/SearchResultsStep.tsx +1 -3
- package/src/components/modals/SessionExpiredModal.tsx +11 -11
- package/src/components/modals/__tests__/PermissionDeniedModal.test.tsx +7 -7
- package/src/components/modals/__tests__/ResourceSearchModal.test.tsx +10 -8
- package/src/components/modals/__tests__/SearchModal.accessibility.test.tsx +6 -2
- package/src/components/modals/__tests__/SearchModal.basic.test.tsx +6 -2
- package/src/components/modals/__tests__/SearchModal.keyboard.test.tsx +6 -2
- package/src/components/modals/__tests__/SearchModal.search-wiring.test.tsx +10 -7
- package/src/components/modals/__tests__/SearchModal.visual.test.tsx +6 -2
- package/src/components/modals/__tests__/SessionExpiredModal.test.tsx +5 -5
- package/src/components/navigation/CollapsibleResourceNavigation.tsx +10 -10
- package/src/components/navigation/ObservableLink.tsx +6 -6
- package/src/components/navigation/SimpleNavigation.tsx +4 -4
- package/src/components/navigation/__tests__/ObservableLink.test.tsx +4 -4
- package/src/components/navigation/__tests__/SimpleNavigation.test.tsx +4 -4
- package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +15 -18
- package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +1 -2
- package/src/components/resource/AnnotateView.tsx +8 -10
- package/src/components/resource/AnnotationHistory.tsx +9 -12
- package/src/components/resource/BrowseView.tsx +11 -8
- package/src/components/resource/ResourceViewer.tsx +22 -34
- package/src/components/resource/__tests__/AnnotationHistory.test.tsx +54 -192
- package/src/components/resource/__tests__/BrowseView.test.tsx +38 -87
- package/src/components/resource/__tests__/ResourceViewer.mode-switch.test.tsx +41 -31
- package/src/components/resource/__tests__/event-formatting.test.ts +6 -2
- package/src/components/resource/event-formatting.ts +2 -3
- package/src/components/resource/panels/AssessmentEntry.tsx +7 -8
- package/src/components/resource/panels/AssessmentPanel.tsx +21 -17
- package/src/components/resource/panels/AssistSection.tsx +15 -21
- package/src/components/resource/panels/CollaborationPanel.tsx +29 -7
- package/src/components/resource/panels/CommentEntry.tsx +7 -8
- package/src/components/resource/panels/CommentsPanel.tsx +11 -13
- package/src/components/resource/panels/HighlightEntry.tsx +7 -8
- package/src/components/resource/panels/HighlightPanel.tsx +12 -13
- package/src/components/resource/panels/ReferenceEntry.tsx +13 -15
- package/src/components/resource/panels/ReferencesPanel.tsx +17 -19
- package/src/components/resource/panels/ResourceInfoPanel.tsx +8 -7
- package/src/components/resource/panels/StatisticsPanel.tsx +2 -3
- package/src/components/resource/panels/TagEntry.tsx +7 -8
- package/src/components/resource/panels/TaggingPanel.tsx +14 -23
- package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +4 -3
- package/src/components/resource/panels/__tests__/AssessmentEntry.test.tsx +4 -4
- package/src/components/resource/panels/__tests__/AssessmentPanel.test.tsx +22 -57
- package/src/components/resource/panels/__tests__/CollaborationPanel.test.tsx +51 -20
- package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +4 -4
- package/src/components/resource/panels/__tests__/CommentsPanel.test.tsx +22 -61
- package/src/components/resource/panels/__tests__/HighlightEntry.test.tsx +4 -4
- package/src/components/resource/panels/__tests__/HighlightPanel.annotationProgress.test.tsx +1 -2
- package/src/components/resource/panels/__tests__/ReferenceEntry.test.tsx +7 -8
- package/src/components/resource/panels/__tests__/ReferencesPanel.observable-flow.test.tsx +153 -0
- package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +51 -106
- package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +28 -53
- package/src/components/resource/panels/__tests__/StatisticsPanel.test.tsx +3 -3
- package/src/components/resource/panels/__tests__/TagEntry.test.tsx +4 -4
- package/src/components/resource/panels/__tests__/TaggingPanel.test.tsx +19 -52
- package/src/components/settings/SettingsPanel.tsx +9 -9
- package/src/components/settings/__tests__/SettingsPanel.test.tsx +15 -15
- package/src/features/admin-devops/components/AdminDevOpsPage.tsx +1 -2
- package/src/features/admin-exchange/components/AdminExchangePage.tsx +1 -1
- package/src/features/admin-exchange/components/ImportCard.tsx +2 -7
- package/src/features/admin-security/components/AdminSecurityPage.tsx +1 -2
- package/src/features/admin-users/components/AdminUsersPage.tsx +1 -1
- package/src/features/moderate-entity-tags/components/EntityTagsPage.tsx +1 -2
- package/src/features/moderate-recent/components/RecentDocumentsPage.tsx +1 -2
- package/src/features/moderate-tag-schemas/components/TagSchemasPage.tsx +1 -1
- package/src/features/moderation-linked-data/components/LinkedDataPage.tsx +1 -1
- package/src/features/resource-compose/__tests__/ResourceComposePage.test.tsx +5 -3
- package/src/features/resource-compose/components/ResourceComposePage.tsx +6 -22
- package/src/features/resource-discovery/__tests__/ResourceDiscoveryPage.test.tsx +4 -3
- package/src/features/resource-discovery/components/ResourceCard.tsx +1 -2
- package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +3 -4
- package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +37 -45
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +129 -197
- package/dist/KnowledgeBaseSessionContext-BNNunwzO.d.mts +0 -175
- package/dist/PdfAnnotationCanvas.client-CHDCGQBR.mjs.map +0 -1
- package/dist/chunk-OZICDVH7.mjs +0 -62
- package/dist/chunk-OZICDVH7.mjs.map +0 -1
- package/dist/chunk-R4CCMFJH.mjs +0 -877
- package/dist/chunk-R4CCMFJH.mjs.map +0 -1
- package/dist/chunk-VN5NY4SN.mjs +0 -200
- package/dist/chunk-VN5NY4SN.mjs.map +0 -1
- package/src/components/modals/ProposeEntitiesModal.tsx +0 -179
- package/src/components/modals/__tests__/ProposeEntitiesModal.test.tsx +0 -129
- package/src/features/resource-viewer/__tests__/AnnotationCreationPending.test.tsx +0 -323
- package/src/features/resource-viewer/__tests__/AnnotationDeletionIntegration.test.tsx +0 -245
- package/src/features/resource-viewer/__tests__/AnnotationProgressDismissal.test.tsx +0 -303
- package/src/features/resource-viewer/__tests__/BindFlowIntegration.test.tsx +0 -150
- package/src/features/resource-viewer/__tests__/DetectionFlowBug.test.tsx +0 -243
- package/src/features/resource-viewer/__tests__/DetectionFlowIntegration.test.tsx +0 -383
- package/src/features/resource-viewer/__tests__/ResourceMutations.test.tsx +0 -299
- package/src/features/resource-viewer/__tests__/ToastNotifications.test.tsx +0 -186
- package/src/features/resource-viewer/__tests__/YieldFlowIntegration.test.tsx +0 -429
- package/src/features/resource-viewer/__tests__/annotation-progress-flow.test.tsx +0 -348
|
@@ -5,11 +5,12 @@
|
|
|
5
5
|
* Only requires minimal props from the framework layer (routing, modals).
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import React, { useState, useEffect, useCallback
|
|
9
|
-
import {
|
|
10
|
-
import type {
|
|
8
|
+
import React, { useState, useEffect, useCallback } from 'react';
|
|
9
|
+
import type { components, ResourceDescriptor, ResourceId, GatheredContext, EventMap } from '@semiont/core';
|
|
10
|
+
import type { ConnectionState } from '@semiont/core';
|
|
11
11
|
import { annotationId } from '@semiont/core';
|
|
12
|
-
import { getLanguage, getPrimaryRepresentation, getPrimaryMediaType
|
|
12
|
+
import { getLanguage, getPrimaryRepresentation, getPrimaryMediaType } from '@semiont/core';
|
|
13
|
+
import { getMimeCategory } from '@semiont/core';
|
|
13
14
|
import { ANNOTATORS } from '@semiont/react-ui';
|
|
14
15
|
import { ErrorBoundary } from '@semiont/react-ui';
|
|
15
16
|
import { AnnotationHistory } from '@semiont/react-ui';
|
|
@@ -21,34 +22,23 @@ import { Toolbar } from '@semiont/react-ui';
|
|
|
21
22
|
import { useResourceLoadingAnnouncements } from '@semiont/react-ui';
|
|
22
23
|
import { ResourceViewer } from '@semiont/react-ui';
|
|
23
24
|
import { useObservable } from '@semiont/react-ui';
|
|
24
|
-
import { QUERY_KEYS } from '../../../lib/query-keys';
|
|
25
|
-
import { useResources, useEntityTypes } from '../../../lib/api-hooks';
|
|
26
25
|
import { useResourceContent } from '../../../hooks/useResourceContent';
|
|
27
26
|
import { useMediaToken } from '../../../hooks/useMediaToken';
|
|
28
27
|
import { useToast } from '../../../components/Toast';
|
|
29
28
|
import { useTheme } from '../../../contexts/ThemeContext';
|
|
30
29
|
import { useLineNumbers } from '../../../hooks/useLineNumbers';
|
|
31
30
|
import { useHoverDelay } from '../../../hooks/useHoverDelay';
|
|
32
|
-
import { useResourceEvents } from '../../../hooks/useResourceEvents';
|
|
33
|
-
import { useOpenResources } from '../../../contexts/OpenResourcesContext';
|
|
34
|
-
// Import EventBus hooks directly from context to avoid mocking issues in tests
|
|
35
|
-
import { useEventBus } from '../../../contexts/EventBusContext';
|
|
36
31
|
import { useEventSubscriptions } from '../../../contexts/useEventSubscription';
|
|
37
32
|
import { useResourceAnnotations } from '../../../contexts/ResourceAnnotationsContext';
|
|
38
|
-
import {
|
|
39
|
-
import {
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
42
|
-
import type { StreamStatus } from '../../../hooks/useResourceEvents';
|
|
43
|
-
import { usePanelBrowse } from '../../../hooks/usePanelBrowse';
|
|
44
|
-
import { useYieldFlow } from '../../../hooks/useYieldFlow';
|
|
45
|
-
import { useContextGatherFlow } from '../../../hooks/useContextGatherFlow';
|
|
33
|
+
import { useSemiont } from '../../../session/SemiontProvider';
|
|
34
|
+
import { createResourceViewerPageVM } from '@semiont/sdk';
|
|
35
|
+
import { useViewModel } from '../../../hooks/useViewModel';
|
|
36
|
+
import { useShellVM } from '../../../hooks/useShellVM';
|
|
46
37
|
import { useTranslations } from '../../../contexts/TranslationContext';
|
|
47
38
|
import { ReferenceWizardModal } from '../../../components/modals/ReferenceWizardModal';
|
|
48
39
|
import type { GenerationConfig } from '../../../components/modals/ConfigureGenerationStep';
|
|
49
40
|
|
|
50
|
-
type SemiontResource =
|
|
51
|
-
type Annotation = components['schemas']['Annotation'];
|
|
41
|
+
type SemiontResource = ResourceDescriptor;
|
|
52
42
|
|
|
53
43
|
export interface ResourceViewerPageProps {
|
|
54
44
|
/**
|
|
@@ -87,9 +77,11 @@ export interface ResourceViewerPageProps {
|
|
|
87
77
|
refetchDocument: () => Promise<unknown>;
|
|
88
78
|
|
|
89
79
|
/**
|
|
90
|
-
*
|
|
80
|
+
* Bus connection state for the active workspace. Six-valued state
|
|
81
|
+
* machine from `actor.state$`; CollaborationPanel maps it to the
|
|
82
|
+
* "Live" / "Disconnected" visual.
|
|
91
83
|
*/
|
|
92
|
-
streamStatus:
|
|
84
|
+
streamStatus: ConnectionState;
|
|
93
85
|
|
|
94
86
|
/**
|
|
95
87
|
* Name of the active knowledge base (for display in panels)
|
|
@@ -102,7 +94,7 @@ export interface ResourceViewerPageProps {
|
|
|
102
94
|
*
|
|
103
95
|
* Uses hooks directly (NO containers, NO render props, NO ResourceViewerPageContent wrapper)
|
|
104
96
|
*
|
|
105
|
-
* @emits
|
|
97
|
+
* @emits nav:push - Navigate to a resource or filtered view
|
|
106
98
|
* @emits beckon:sparkle - Trigger sparkle animation on an annotation
|
|
107
99
|
* @emits bind:update-body - Update annotation body content
|
|
108
100
|
* @subscribes mark:archive - Archive the current resource
|
|
@@ -138,23 +130,17 @@ export function ResourceViewerPage({
|
|
|
138
130
|
// Translations
|
|
139
131
|
const tw = useTranslations('ReferenceWizard');
|
|
140
132
|
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
const semiont =
|
|
144
|
-
const queryClient = useQueryClient(); // retained for non-store queries (events log)
|
|
133
|
+
const browser = useSemiont();
|
|
134
|
+
const session = useObservable(browser.activeSession$);
|
|
135
|
+
const semiont = session?.client;
|
|
145
136
|
|
|
146
137
|
// UI state hooks
|
|
147
|
-
const { showError, showSuccess } = useToast();
|
|
138
|
+
const { showError, showSuccess, showInfo } = useToast();
|
|
148
139
|
const { theme, setTheme } = useTheme();
|
|
149
140
|
const { showLineNumbers, toggleLineNumbers } = useLineNumbers();
|
|
150
141
|
const { hoverDelayMs } = useHoverDelay();
|
|
151
|
-
const { addResource } = useOpenResources();
|
|
152
142
|
const { triggerSparkleAnimation, clearNewAnnotationId } = useResourceAnnotations();
|
|
153
143
|
|
|
154
|
-
// API hooks
|
|
155
|
-
const resources = useResources();
|
|
156
|
-
const entityTypesAPI = useEntityTypes();
|
|
157
|
-
|
|
158
144
|
// Determine MIME category to choose content path
|
|
159
145
|
const resourceMediaType = getPrimaryMediaType(resource) || 'text/plain';
|
|
160
146
|
const isBinary = getMimeCategory(resourceMediaType) === 'image';
|
|
@@ -171,56 +157,42 @@ export function ResourceViewerPage({
|
|
|
171
157
|
const content = isBinary ? binaryContent : textContent;
|
|
172
158
|
const contentLoading = isBinary ? mediaTokenLoading : textLoading;
|
|
173
159
|
|
|
174
|
-
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
const
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
const
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const
|
|
188
|
-
const
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const
|
|
199
|
-
const
|
|
200
|
-
const
|
|
201
|
-
const
|
|
202
|
-
const [wizardEntityTypes, setWizardEntityTypes] = useState<string[]>([]);
|
|
203
|
-
|
|
204
|
-
useEffect(() => {
|
|
205
|
-
const subscription = eventBus.get('bind:initiate').subscribe((event) => {
|
|
206
|
-
setWizardAnnotationId(event.annotationId);
|
|
207
|
-
setWizardResourceId(event.resourceId);
|
|
208
|
-
setWizardDefaultTitle(event.defaultTitle);
|
|
209
|
-
setWizardEntityTypes(event.entityTypes);
|
|
210
|
-
setWizardOpen(true);
|
|
211
|
-
|
|
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
|
-
});
|
|
215
|
-
return () => subscription.unsubscribe();
|
|
216
|
-
}, [eventBus]);
|
|
160
|
+
// Composite VM — owns all flow VMs, wizard state, annotations, entity types
|
|
161
|
+
const browseVM = useShellVM();
|
|
162
|
+
const vm = useViewModel(() => createResourceViewerPageVM(semiont!, rUri, locale, browseVM));
|
|
163
|
+
|
|
164
|
+
const annotations = useObservable(vm.annotations$) ?? [];
|
|
165
|
+
const groups = useObservable(vm.annotationGroups$);
|
|
166
|
+
const allEntityTypes = useObservable(vm.entityTypes$) ?? [];
|
|
167
|
+
const referencedByRaw = useObservable(vm.referencedBy$);
|
|
168
|
+
const referencedBy = referencedByRaw ?? [];
|
|
169
|
+
const referencedByLoading = referencedByRaw === undefined;
|
|
170
|
+
const hoveredAnnotationId = useObservable(vm.beckon.hoveredAnnotationId$) ?? null;
|
|
171
|
+
const pendingAnnotation = useObservable(vm.mark.pendingAnnotation$) ?? null;
|
|
172
|
+
const assistingMotivation = useObservable(vm.mark.assistingMotivation$) ?? null;
|
|
173
|
+
const progress = useObservable(vm.mark.progress$) ?? null;
|
|
174
|
+
const activePanel = useObservable(vm.browse.activePanel$) ?? null;
|
|
175
|
+
const scrollToAnnotationId = useObservable(vm.browse.scrollToAnnotationId$) ?? null;
|
|
176
|
+
const panelInitialTab = useObservable(vm.browse.panelInitialTab$) ?? null;
|
|
177
|
+
const onScrollCompleted = vm.browse.onScrollCompleted;
|
|
178
|
+
const generationProgress = useObservable(vm.yield.progress$) ?? null;
|
|
179
|
+
const gatherContext = useObservable(vm.gather.context$) ?? null;
|
|
180
|
+
const gatherLoading = useObservable(vm.gather.loading$) ?? false;
|
|
181
|
+
const gatherError = useObservable(vm.gather.error$) ?? null;
|
|
182
|
+
const wizardState = useObservable(vm.wizard$);
|
|
183
|
+
const wizardOpen = wizardState?.open ?? false;
|
|
184
|
+
const wizardAnnotationId = wizardState?.annotationId ?? null;
|
|
185
|
+
const wizardResourceId = wizardState?.resourceId ?? null;
|
|
186
|
+
const wizardDefaultTitle = wizardState?.defaultTitle ?? '';
|
|
187
|
+
const wizardEntityTypes = wizardState?.entityTypes ?? [];
|
|
217
188
|
|
|
218
189
|
const handleWizardClose = useCallback(() => {
|
|
219
|
-
|
|
220
|
-
}, []);
|
|
190
|
+
vm.closeWizard();
|
|
191
|
+
}, [vm]);
|
|
221
192
|
|
|
222
193
|
const handleWizardGenerateSubmit = useCallback((referenceId: string, config: GenerationConfig) => {
|
|
223
|
-
|
|
194
|
+
clearNewAnnotationId(annotationId(referenceId));
|
|
195
|
+
vm.yield.generate(referenceId, {
|
|
224
196
|
title: config.title,
|
|
225
197
|
storageUri: config.storagePath,
|
|
226
198
|
prompt: config.prompt,
|
|
@@ -229,9 +201,10 @@ export function ResourceViewerPage({
|
|
|
229
201
|
maxTokens: config.maxTokens,
|
|
230
202
|
context: config.context,
|
|
231
203
|
});
|
|
232
|
-
}, [
|
|
204
|
+
}, [vm, clearNewAnnotationId]);
|
|
233
205
|
|
|
234
206
|
const handleWizardLinkResource = useCallback(async (referenceId: string, targetResourceId: string) => {
|
|
207
|
+
if (!semiont) return;
|
|
235
208
|
try {
|
|
236
209
|
await semiont.bind.body(
|
|
237
210
|
rUri,
|
|
@@ -259,103 +232,71 @@ export function ResourceViewerPage({
|
|
|
259
232
|
name: title,
|
|
260
233
|
entityTypes: entTypes.join(','),
|
|
261
234
|
});
|
|
262
|
-
|
|
235
|
+
browser.emit('nav:push', {
|
|
263
236
|
path: `/know/compose?${params.toString()}`,
|
|
264
237
|
reason: 'compose-from-wizard',
|
|
265
238
|
});
|
|
266
|
-
}, []);
|
|
239
|
+
}, [session]);
|
|
267
240
|
|
|
268
241
|
// Add resource to open tabs when it loads
|
|
269
242
|
useEffect(() => {
|
|
270
243
|
if (resource && rUri) {
|
|
271
244
|
const mediaType = getPrimaryMediaType(resource);
|
|
272
|
-
|
|
245
|
+
browser.addOpenResource(rUri, resource.name, mediaType || undefined, resource.storageUri);
|
|
273
246
|
if (typeof localStorage !== 'undefined') {
|
|
274
247
|
localStorage.setItem('lastViewedDocumentId', rUri);
|
|
275
248
|
}
|
|
276
249
|
}
|
|
277
|
-
}, [resource, rUri,
|
|
278
|
-
|
|
279
|
-
// Real-time document events (SSE)
|
|
280
|
-
// Annotation updates are handled by AnnotationStore reacting to EventBus events.
|
|
281
|
-
// Callbacks here only handle non-annotation side effects.
|
|
282
|
-
useResourceEvents({
|
|
283
|
-
rUri,
|
|
284
|
-
autoConnect: true,
|
|
285
|
-
|
|
286
|
-
onAnnotationAdded: useCallback((_event: any) => {
|
|
287
|
-
// Store handles annotation refresh; events log needs explicit invalidation
|
|
288
|
-
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.resources.events(rUri) });
|
|
289
|
-
}, [queryClient, rUri]),
|
|
290
|
-
|
|
291
|
-
onAnnotationRemoved: useCallback((_event: any) => {
|
|
292
|
-
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.resources.events(rUri) });
|
|
293
|
-
}, [queryClient, rUri]),
|
|
294
|
-
|
|
295
|
-
onAnnotationBodyUpdated: useCallback((_event: any) => {
|
|
296
|
-
queryClient.invalidateQueries({ queryKey: QUERY_KEYS.resources.events(rUri) });
|
|
297
|
-
}, [queryClient, rUri]),
|
|
298
|
-
|
|
299
|
-
// Document status events
|
|
300
|
-
onDocumentArchived: useCallback((_event: any) => {
|
|
301
|
-
refetchDocument();
|
|
302
|
-
showSuccess('This document has been archived');
|
|
303
|
-
}, [refetchDocument, showSuccess]),
|
|
304
|
-
|
|
305
|
-
onDocumentUnarchived: useCallback((_event: any) => {
|
|
306
|
-
refetchDocument();
|
|
307
|
-
showSuccess('This document has been unarchived');
|
|
308
|
-
}, [refetchDocument, showSuccess]),
|
|
309
|
-
|
|
310
|
-
// Entity tag events
|
|
311
|
-
onEntityTagAdded: useCallback((_event: any) => {
|
|
312
|
-
refetchDocument();
|
|
313
|
-
}, [refetchDocument]),
|
|
314
|
-
|
|
315
|
-
onEntityTagRemoved: useCallback((_event: any) => {
|
|
316
|
-
refetchDocument();
|
|
317
|
-
}, [refetchDocument]),
|
|
318
|
-
|
|
319
|
-
onError: useCallback((error: any) => {
|
|
320
|
-
console.error('[RealTime] Event stream error:', error);
|
|
321
|
-
}, []),
|
|
322
|
-
});
|
|
250
|
+
}, [resource, rUri, browser]);
|
|
323
251
|
|
|
324
|
-
//
|
|
325
|
-
|
|
326
|
-
|
|
252
|
+
// Bridge: when the mark VM produces a pending annotation, open the
|
|
253
|
+
// annotations panel. The mark VM (session-scoped) can't emit `panel:open`
|
|
254
|
+
// (app-scoped) directly — the React tree is the natural seam between
|
|
255
|
+
// the two buses.
|
|
256
|
+
useEffect(() => {
|
|
257
|
+
if (pendingAnnotation) {
|
|
258
|
+
browser.emit('panel:open', { panel: 'annotations' });
|
|
259
|
+
}
|
|
260
|
+
}, [pendingAnnotation, browser]);
|
|
261
|
+
|
|
262
|
+
// Domain events flow through the bus gateway (ActorVM → local EventBus).
|
|
263
|
+
// BrowseNamespace cache invalidation handles annotation/resource updates.
|
|
264
|
+
// The resource-viewer-page-vm calls client.subscribeToResource(resourceId)
|
|
265
|
+
// which bridges scoped domain events into the local EventBus.
|
|
327
266
|
|
|
328
|
-
// Event handlers extracted to useCallback (tenet: no inline handlers in useEventSubscriptions)
|
|
329
267
|
const handleResourceArchive = useCallback(async () => {
|
|
268
|
+
if (!semiont) return;
|
|
330
269
|
try {
|
|
331
|
-
await
|
|
270
|
+
await semiont.mark.archive(rUri);
|
|
332
271
|
await refetchDocument();
|
|
333
272
|
} catch (err) {
|
|
334
273
|
console.error('Failed to archive document:', err);
|
|
335
274
|
showError('Failed to archive document');
|
|
336
275
|
}
|
|
337
|
-
}, [
|
|
276
|
+
}, [semiont, rUri, refetchDocument, showError]);
|
|
338
277
|
|
|
339
278
|
const handleResourceUnarchive = useCallback(async () => {
|
|
279
|
+
if (!semiont) return;
|
|
340
280
|
try {
|
|
341
|
-
await
|
|
281
|
+
await semiont.mark.unarchive(rUri);
|
|
342
282
|
await refetchDocument();
|
|
343
283
|
} catch (err) {
|
|
344
284
|
console.error('Failed to unarchive document:', err);
|
|
345
285
|
showError('Failed to unarchive document');
|
|
346
286
|
}
|
|
347
|
-
}, [
|
|
287
|
+
}, [semiont, rUri, refetchDocument, showError]);
|
|
348
288
|
|
|
349
289
|
const handleResourceClone = useCallback(async () => {
|
|
290
|
+
if (!semiont) return;
|
|
350
291
|
try {
|
|
351
|
-
const result = await
|
|
292
|
+
const result = await semiont.yield.cloneToken(rUri);
|
|
352
293
|
const token = result.token;
|
|
353
|
-
|
|
294
|
+
browser.emit('nav:push', { path: `/know/compose?mode=clone&token=${token}`, reason: 'clone' });
|
|
354
295
|
} catch (err) {
|
|
355
296
|
console.error('Failed to generate clone token:', err);
|
|
356
297
|
showError('Failed to generate clone link');
|
|
357
298
|
}
|
|
358
|
-
}, [
|
|
299
|
+
}, [semiont, rUri, showError, session]);
|
|
359
300
|
|
|
360
301
|
const handleAnnotationSparkle = useCallback(({ annotationId }: { annotationId: string }) => {
|
|
361
302
|
triggerSparkleAnimation(annotationId);
|
|
@@ -365,43 +306,58 @@ export function ResourceViewerPage({
|
|
|
365
306
|
triggerSparkleAnimation(stored.payload.annotation.id);
|
|
366
307
|
}, [triggerSparkleAnimation]);
|
|
367
308
|
|
|
368
|
-
const handleAnnotationCreateFailed = useCallback((
|
|
369
|
-
|
|
309
|
+
const handleAnnotationCreateFailed = useCallback(({ message }: { message?: string }) =>
|
|
310
|
+
showError(`Failed to create annotation: ${message || 'unknown error'}`), [showError]);
|
|
311
|
+
const handleAnnotationDeleteFailed = useCallback(({ message }: { message?: string }) =>
|
|
312
|
+
showError(`Failed to delete annotation: ${message || 'unknown error'}`), [showError]);
|
|
370
313
|
const handleAnnotateBodyUpdated = useCallback(() => {
|
|
371
|
-
// Success - optimistic update already applied via
|
|
314
|
+
// Success - optimistic update already applied via EventBus
|
|
372
315
|
}, []);
|
|
373
|
-
const handleAnnotateBodyUpdateFailed = useCallback((
|
|
316
|
+
const handleAnnotateBodyUpdateFailed = useCallback(({ message }: { message: string }) =>
|
|
317
|
+
showError(`Failed to update reference: ${message}`), [showError]);
|
|
374
318
|
|
|
375
319
|
const handleSettingsThemeChanged = useCallback(({ theme }: { theme: any }) => setTheme(theme), [setTheme]);
|
|
376
320
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
321
|
+
// Unified job lifecycle handlers. `job:complete` / `job:fail` fire
|
|
322
|
+
// for every job type (annotation + generation); we dispatch on
|
|
323
|
+
// jobType and filter to this resource. `annotationId` is present on
|
|
324
|
+
// jobs attached to a specific annotation (today: generation from a
|
|
325
|
+
// reference); it's what UI consumers lower down in the tree use to
|
|
326
|
+
// attach per-annotation visual feedback.
|
|
327
|
+
const handleJobComplete = useCallback((event: components['schemas']['JobCompleteCommand']) => {
|
|
328
|
+
if (event.resourceId !== (resource.id as string)) return;
|
|
329
|
+
if (event.jobType === 'generation') {
|
|
330
|
+
const result = event.result as components['schemas']['JobGenerationResult'] | undefined;
|
|
331
|
+
const name = result?.resourceName;
|
|
332
|
+
showSuccess(name
|
|
333
|
+
? `Resource "${name}" created successfully!`
|
|
334
|
+
: 'Resource created successfully!');
|
|
335
|
+
} else {
|
|
336
|
+
showSuccess('Annotation complete');
|
|
337
|
+
}
|
|
338
|
+
}, [resource.id, showSuccess]);
|
|
339
|
+
const handleJobFailed = useCallback((event: components['schemas']['JobFailCommand']) => {
|
|
340
|
+
if (event.resourceId !== (resource.id as string)) return;
|
|
341
|
+
if (event.jobType === 'generation') {
|
|
342
|
+
showError(`Resource generation failed: ${event.error}`);
|
|
343
|
+
} else {
|
|
344
|
+
showError(event.error || 'Annotation failed');
|
|
345
|
+
}
|
|
346
|
+
}, [resource.id, showError]);
|
|
391
347
|
|
|
392
348
|
const handleReferenceNavigate = useCallback(({ resourceId }: { resourceId: string }) => {
|
|
393
349
|
if (routes.resourceDetail) {
|
|
394
350
|
const path = routes.resourceDetail(resourceId);
|
|
395
|
-
|
|
351
|
+
browser.emit('nav:push', { path, reason: 'reference-link' });
|
|
396
352
|
}
|
|
397
|
-
}, [routes.resourceDetail]);
|
|
353
|
+
}, [routes.resourceDetail, session]);
|
|
398
354
|
|
|
399
355
|
const handleEntityTypeClicked = useCallback(({ entityType }: { entityType: string }) => {
|
|
400
356
|
if (routes.know) {
|
|
401
357
|
const path = `${routes.know}?entityType=${encodeURIComponent(entityType)}`;
|
|
402
|
-
|
|
358
|
+
browser.emit('nav:push', { path, reason: 'entity-type-filter' });
|
|
403
359
|
}
|
|
404
|
-
}, [routes.know]);
|
|
360
|
+
}, [routes.know, session]);
|
|
405
361
|
|
|
406
362
|
const handleModeToggled = useCallback(() => {
|
|
407
363
|
setAnnotateMode(prev => !prev);
|
|
@@ -421,10 +377,9 @@ export function ResourceViewerPage({
|
|
|
421
377
|
'bind:body-update-failed': handleAnnotateBodyUpdateFailed,
|
|
422
378
|
'settings:theme-changed': handleSettingsThemeChanged,
|
|
423
379
|
'settings:line-numbers-toggled': toggleLineNumbers,
|
|
424
|
-
'
|
|
425
|
-
'
|
|
426
|
-
'
|
|
427
|
-
'yield:failed': handleGenerationFailed,
|
|
380
|
+
'job:complete': handleJobComplete,
|
|
381
|
+
'job:fail': handleJobFailed,
|
|
382
|
+
'mark:assist-cancelled': () => showInfo('Annotation cancelled'),
|
|
428
383
|
'browse:reference-navigate': handleReferenceNavigate,
|
|
429
384
|
'browse:entity-type-clicked': handleEntityTypeClicked,
|
|
430
385
|
});
|
|
@@ -460,38 +415,16 @@ export function ResourceViewerPage({
|
|
|
460
415
|
return false;
|
|
461
416
|
});
|
|
462
417
|
|
|
463
|
-
// Group annotations by type using static ANNOTATORS (memoized to avoid re-grouping on unrelated re-renders)
|
|
464
|
-
const groups = useMemo(() => {
|
|
465
|
-
const result = {
|
|
466
|
-
highlights: [] as Annotation[],
|
|
467
|
-
references: [] as Annotation[],
|
|
468
|
-
assessments: [] as Annotation[],
|
|
469
|
-
comments: [] as Annotation[],
|
|
470
|
-
tags: [] as Annotation[]
|
|
471
|
-
};
|
|
472
|
-
|
|
473
|
-
for (const ann of annotations) {
|
|
474
|
-
const annotator = Object.values(ANNOTATORS).find(a => a.matchesAnnotation(ann));
|
|
475
|
-
if (annotator) {
|
|
476
|
-
const key = annotator.internalType + 's'; // highlight -> highlights
|
|
477
|
-
if (result[key as keyof typeof result]) {
|
|
478
|
-
result[key as keyof typeof result].push(ann);
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
return result;
|
|
484
|
-
}, [annotations]);
|
|
485
418
|
|
|
486
419
|
// Combine resource with content
|
|
487
420
|
const resourceWithContent = { ...resource, content };
|
|
488
421
|
|
|
489
422
|
// Handlers for AnnotationHistory (legacy event-based interaction)
|
|
490
|
-
const handleEventHover = useCallback((
|
|
491
|
-
if (
|
|
492
|
-
|
|
423
|
+
const handleEventHover = useCallback((id: string | null) => {
|
|
424
|
+
if (id) {
|
|
425
|
+
session?.client.beckon.sparkle(annotationId(id));
|
|
493
426
|
}
|
|
494
|
-
}, []);
|
|
427
|
+
}, [session]);
|
|
495
428
|
|
|
496
429
|
const handleEventClick = useCallback((_annotationId: string | null) => {
|
|
497
430
|
// ResourceViewer now manages scroll state internally
|
|
@@ -539,8 +472,8 @@ export function ResourceViewerPage({
|
|
|
539
472
|
) : (
|
|
540
473
|
<ResourceViewer
|
|
541
474
|
resource={resourceWithContent}
|
|
542
|
-
annotations={groups}
|
|
543
|
-
generatingReferenceId={generationProgress?.
|
|
475
|
+
annotations={groups ?? { highlights: [], comments: [], assessments: [], references: [], tags: [] }}
|
|
476
|
+
generatingReferenceId={generationProgress?.annotationId ?? null}
|
|
544
477
|
showLineNumbers={showLineNumbers}
|
|
545
478
|
hoverDelayMs={hoverDelayMs}
|
|
546
479
|
hoveredAnnotationId={hoveredAnnotationId}
|
|
@@ -583,7 +516,7 @@ export function ResourceViewerPage({
|
|
|
583
516
|
progress={progress}
|
|
584
517
|
pendingAnnotation={pendingAnnotation}
|
|
585
518
|
allEntityTypes={allEntityTypes}
|
|
586
|
-
generatingReferenceId={generationProgress?.
|
|
519
|
+
generatingReferenceId={generationProgress?.annotationId ?? null}
|
|
587
520
|
referencedBy={referencedBy}
|
|
588
521
|
referencedByLoading={referencedByLoading}
|
|
589
522
|
resourceId={rUri}
|
|
@@ -632,7 +565,7 @@ export function ResourceViewerPage({
|
|
|
632
565
|
{/* Collaboration Panel */}
|
|
633
566
|
{activePanel === 'collaboration' && (
|
|
634
567
|
<CollaborationPanel
|
|
635
|
-
|
|
568
|
+
state={streamStatus}
|
|
636
569
|
eventCount={0}
|
|
637
570
|
knowledgeBaseName={knowledgeBaseName}
|
|
638
571
|
/>
|
|
@@ -665,7 +598,6 @@ export function ResourceViewerPage({
|
|
|
665
598
|
context={gatherContext}
|
|
666
599
|
contextLoading={gatherLoading}
|
|
667
600
|
contextError={gatherError}
|
|
668
|
-
eventBus={eventBus}
|
|
669
601
|
onGenerateSubmit={handleWizardGenerateSubmit}
|
|
670
602
|
onLinkResource={handleWizardLinkResource}
|
|
671
603
|
onComposeNavigate={handleWizardComposeNavigate}
|