@semiont/react-ui 0.5.9 → 0.5.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{PdfAnnotationCanvas.client-U4EVBZEV.js → PdfAnnotationCanvas.client-KWQ7XDWT.js} +5 -2
- package/dist/PdfAnnotationCanvas.client-KWQ7XDWT.js.map +1 -0
- package/dist/{ar-WA47UUWA.js → ar-ZHKO4XDR.js} +45 -4
- package/dist/ar-ZHKO4XDR.js.map +1 -0
- package/dist/{bn-5ANDRIU6.js → bn-3BUDKAK5.js} +45 -4
- package/dist/bn-3BUDKAK5.js.map +1 -0
- package/dist/{chunk-YBGN4ACY.js → chunk-O23TLLXW.js} +1 -6
- package/dist/{chunk-WHUGODTB.js → chunk-PFUXQPWJ.js} +127 -127
- package/dist/{chunk-WHUGODTB.js.map → chunk-PFUXQPWJ.js.map} +1 -1
- package/dist/{chunk-GERSK2DY.js → chunk-R52YAGHK.js} +42 -1
- package/dist/chunk-R52YAGHK.js.map +1 -0
- package/dist/{cs-3RU7F4JX.js → cs-2EFEUKTW.js} +45 -4
- package/dist/cs-2EFEUKTW.js.map +1 -0
- package/dist/{da-GSW5P5HG.js → da-SUD3Q44Q.js} +45 -4
- package/dist/da-SUD3Q44Q.js.map +1 -0
- package/dist/{de-JUAUYF4Z.js → de-6IC5QRX7.js} +45 -4
- package/dist/de-6IC5QRX7.js.map +1 -0
- package/dist/{el-JNLWCKEC.js → el-Q4NAXFRC.js} +45 -4
- package/dist/el-Q4NAXFRC.js.map +1 -0
- package/dist/{en-RBN2GUHF.js → en-P2L4X27N.js} +5 -3
- package/dist/{es-WPOX225H.js → es-YUKZBD4W.js} +45 -4
- package/dist/es-YUKZBD4W.js.map +1 -0
- package/dist/{fa-TAEP6N77.js → fa-OJ47LILE.js} +45 -4
- package/dist/fa-OJ47LILE.js.map +1 -0
- package/dist/{fi-ZVZHANSP.js → fi-QFFL5CUB.js} +45 -4
- package/dist/fi-QFFL5CUB.js.map +1 -0
- package/dist/{fr-VLZW7M4N.js → fr-BOQZZWP4.js} +45 -4
- package/dist/fr-BOQZZWP4.js.map +1 -0
- package/dist/{he-QFAFYA77.js → he-LXDY3TFA.js} +45 -4
- package/dist/he-LXDY3TFA.js.map +1 -0
- package/dist/{hi-AO3DQPCV.js → hi-HOJER77V.js} +45 -4
- package/dist/hi-HOJER77V.js.map +1 -0
- package/dist/{id-GTXF42WM.js → id-I5WQ6EWD.js} +45 -4
- package/dist/id-I5WQ6EWD.js.map +1 -0
- package/dist/index.css +150 -112
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +403 -45
- package/dist/index.js +4660 -18860
- package/dist/index.js.map +1 -1
- package/dist/integrations/css-modules-helper.js +1 -1
- package/dist/integrations/styled-components-theme.js +1 -1
- package/dist/{it-AS5GM232.js → it-UYH642IJ.js} +45 -4
- package/dist/it-UYH642IJ.js.map +1 -0
- package/dist/{ja-GZ4HLUOF.js → ja-JTVWAC4L.js} +45 -4
- package/dist/ja-JTVWAC4L.js.map +1 -0
- package/dist/{ko-A4XUXFGJ.js → ko-IHXFMOIZ.js} +45 -4
- package/dist/ko-IHXFMOIZ.js.map +1 -0
- package/dist/{ms-YBAO3S6K.js → ms-JH25VFLO.js} +45 -4
- package/dist/ms-JH25VFLO.js.map +1 -0
- package/dist/{nl-3TJGIIIU.js → nl-SURXW7IQ.js} +45 -4
- package/dist/nl-SURXW7IQ.js.map +1 -0
- package/dist/{no-4AXIQPRQ.js → no-HXIKQFDZ.js} +45 -4
- package/dist/no-HXIKQFDZ.js.map +1 -0
- package/dist/{pl-5GP6GKXO.js → pl-7A4KU6XK.js} +45 -4
- package/dist/pl-7A4KU6XK.js.map +1 -0
- package/dist/{pt-26Y6JFG5.js → pt-6MGYP7Q5.js} +45 -4
- package/dist/pt-6MGYP7Q5.js.map +1 -0
- package/dist/{ro-C7UXFRWJ.js → ro-IROCHNIA.js} +45 -4
- package/dist/ro-IROCHNIA.js.map +1 -0
- package/dist/{sv-44DVD76T.js → sv-WDJD75CV.js} +45 -4
- package/dist/sv-WDJD75CV.js.map +1 -0
- package/dist/test-utils.js +9 -4
- package/dist/test-utils.js.map +1 -1
- package/dist/{th-GIQRTBOY.js → th-RIO2E6B3.js} +45 -4
- package/dist/th-RIO2E6B3.js.map +1 -0
- package/dist/{tr-WMQWO4D6.js → tr-TZ7ERCIN.js} +45 -4
- package/dist/tr-TZ7ERCIN.js.map +1 -0
- package/dist/{uk-I7ML6RGG.js → uk-ZFWDKLUE.js} +45 -4
- package/dist/uk-ZFWDKLUE.js.map +1 -0
- package/dist/{vi-FGWECASQ.js → vi-UIOSP2JG.js} +45 -4
- package/dist/vi-UIOSP2JG.js.map +1 -0
- package/dist/{zh-L5FN73XC.js → zh-6R6YUY46.js} +45 -4
- package/dist/zh-6R6YUY46.js.map +1 -0
- package/package.json +18 -9
- package/src/components/AnnotateReferencesProgressWidget.tsx +35 -16
- package/src/components/ResourceTagsInline.tsx +94 -11
- package/src/components/StatusDisplay.css +36 -0
- package/src/components/__tests__/AnnotateReferencesProgressWidget.test.tsx +41 -91
- package/src/components/__tests__/ResourceTagsInline.test.tsx +101 -3
- package/src/components/annotation/AnnotateToolbar.tsx +53 -28
- package/src/components/annotation/__tests__/AnnotateToolbar.display.test.tsx +76 -0
- package/src/components/annotation/__tests__/AnnotateToolbar.test.tsx +124 -360
- package/src/components/image-annotation/SvgDrawingCanvas.tsx +5 -1
- package/src/components/modals/ConfigureGatherStep.tsx +102 -0
- package/src/components/modals/ContextSummary.tsx +15 -6
- package/src/components/modals/GatherContextStep.tsx +104 -59
- package/src/components/modals/ResourceGenerateModal.tsx +258 -0
- package/src/components/modals/__tests__/ConfigureGatherStep.test.tsx +82 -0
- package/src/components/modals/__tests__/GatherContextStep.resource.test.tsx +107 -0
- package/src/components/modals/__tests__/ResourceGenerateModal.test.tsx +197 -0
- package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +5 -0
- package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +6 -6
- package/src/components/resource/AnnotateView.tsx +59 -38
- package/src/components/resource/BrowseView.tsx +159 -133
- package/src/components/resource/ResourceViewer.tsx +119 -128
- package/src/components/resource/__tests__/AnnotateView.embeddable.test.tsx +55 -0
- package/src/components/resource/__tests__/BrowseView.embeddable.test.tsx +49 -0
- package/src/components/resource/__tests__/BrowseView.links.test.tsx +60 -0
- package/src/components/resource/__tests__/BrowseView.overlay-async.test.tsx +113 -0
- package/src/components/resource/__tests__/BrowseView.reference-hover.test.tsx +161 -0
- package/src/components/resource/__tests__/BrowseView.registry.test.tsx +43 -0
- package/src/components/resource/__tests__/BrowseView.test.tsx +35 -22
- package/src/components/resource/__tests__/ResourceViewer.embeddable.test.tsx +63 -0
- package/src/components/resource/__tests__/ResourceViewer.mode-switch.test.tsx +12 -7
- package/src/components/resource/__tests__/ResourceViewer.toolbar-opt-out.test.tsx +135 -0
- package/src/components/resource/__tests__/ResourceViewer.toolbar-prefs.test.tsx +200 -0
- package/src/components/resource/__tests__/browse-renderers.test.tsx +34 -0
- package/src/components/resource/__tests__/inline-embedding.test.tsx +81 -0
- package/src/components/resource/__tests__/media-completeness.test.tsx +88 -0
- package/src/components/resource/browse-renderers.tsx +63 -0
- package/src/components/resource/panels/AssessmentPanel.tsx +4 -0
- package/src/components/resource/panels/CommentsPanel.tsx +4 -0
- package/src/components/resource/panels/HighlightPanel.tsx +4 -0
- package/src/components/resource/panels/ReferencesPanel.tsx +14 -0
- package/src/components/resource/panels/ResourceInfoPanel.tsx +22 -0
- package/src/components/resource/panels/TaggingPanel.tsx +4 -0
- package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +5 -2
- package/src/components/resource/panels/__tests__/AssessmentPanel.test.tsx +1 -0
- package/src/components/resource/panels/__tests__/CommentsPanel.test.tsx +1 -0
- package/src/components/resource/panels/__tests__/HighlightPanel.annotationProgress.test.tsx +16 -16
- package/src/components/resource/panels/__tests__/ReferencesPanel.observable-flow.test.tsx +1 -1
- package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +1 -0
- package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +26 -0
- package/src/components/resource/panels/__tests__/TaggingPanel.test.tsx +1 -0
- package/src/components/toolbar/Toolbar.css +14 -0
- package/src/features/admin-exchange/state/__tests__/exchange-state-unit.test.ts +14 -0
- package/src/features/admin-exchange/state/exchange-state-unit.ts +4 -2
- package/src/features/admin-security/state/__tests__/admin-security-state-unit.test.ts +14 -0
- package/src/features/admin-security/state/admin-security-state-unit.ts +4 -2
- package/src/features/admin-users/state/__tests__/admin-users-state-unit.test.ts +12 -5
- package/src/features/admin-users/state/admin-users-state-unit.ts +4 -2
- package/src/features/auth-welcome/state/__tests__/welcome-state-unit.test.ts +11 -0
- package/src/features/auth-welcome/state/welcome-state-unit.ts +1 -1
- package/src/features/moderate-entity-tags/state/__tests__/entity-tags-state-unit.test.ts +14 -0
- package/src/features/moderate-entity-tags/state/entity-tags-state-unit.ts +4 -2
- package/src/features/resource-compose/components/ResourceComposePage.tsx +13 -7
- package/src/features/resource-compose/state/__tests__/compose-page-state-unit.test.ts +13 -0
- package/src/features/resource-compose/state/compose-page-state-unit.ts +5 -2
- package/src/features/resource-discovery/state/__tests__/discover-state-unit.test.ts +15 -9
- package/src/features/resource-discovery/state/discover-state-unit.ts +5 -2
- package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +7 -17
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +119 -17
- package/src/features/resource-viewer/state/__tests__/resource-loader-state-unit.test.ts +11 -0
- package/src/features/resource-viewer/state/__tests__/resource-viewer-page-state-unit.test.ts +30 -0
- package/src/features/resource-viewer/state/resource-loader-state-unit.ts +1 -1
- package/src/features/resource-viewer/state/resource-viewer-page-state-unit.ts +7 -16
- package/src/styles/features/resource-viewer.css +19 -0
- package/translations/ar.json +41 -2
- package/translations/bn.json +41 -2
- package/translations/cs.json +41 -2
- package/translations/da.json +41 -2
- package/translations/de.json +41 -2
- package/translations/el.json +41 -2
- package/translations/en.json +39 -0
- package/translations/es.json +41 -2
- package/translations/fa.json +41 -2
- package/translations/fi.json +41 -2
- package/translations/fr.json +41 -2
- package/translations/he.json +41 -2
- package/translations/hi.json +41 -2
- package/translations/id.json +41 -2
- package/translations/it.json +41 -2
- package/translations/ja.json +41 -2
- package/translations/ko.json +41 -2
- package/translations/ms.json +41 -2
- package/translations/nl.json +41 -2
- package/translations/no.json +41 -2
- package/translations/pl.json +41 -2
- package/translations/pt.json +41 -2
- package/translations/ro.json +41 -2
- package/translations/sv.json +41 -2
- package/translations/th.json +41 -2
- package/translations/tr.json +41 -2
- package/translations/uk.json +41 -2
- package/translations/vi.json +41 -2
- package/translations/zh.json +41 -2
- package/dist/PdfAnnotationCanvas.client-U4EVBZEV.js.map +0 -1
- package/dist/ar-WA47UUWA.js.map +0 -1
- package/dist/bn-5ANDRIU6.js.map +0 -1
- package/dist/chunk-GERSK2DY.js.map +0 -1
- package/dist/cs-3RU7F4JX.js.map +0 -1
- package/dist/da-GSW5P5HG.js.map +0 -1
- package/dist/de-JUAUYF4Z.js.map +0 -1
- package/dist/el-JNLWCKEC.js.map +0 -1
- package/dist/es-WPOX225H.js.map +0 -1
- package/dist/fa-TAEP6N77.js.map +0 -1
- package/dist/fi-ZVZHANSP.js.map +0 -1
- package/dist/fr-VLZW7M4N.js.map +0 -1
- package/dist/he-QFAFYA77.js.map +0 -1
- package/dist/hi-AO3DQPCV.js.map +0 -1
- package/dist/id-GTXF42WM.js.map +0 -1
- package/dist/it-AS5GM232.js.map +0 -1
- package/dist/ja-GZ4HLUOF.js.map +0 -1
- package/dist/ko-A4XUXFGJ.js.map +0 -1
- package/dist/ms-YBAO3S6K.js.map +0 -1
- package/dist/nl-3TJGIIIU.js.map +0 -1
- package/dist/no-4AXIQPRQ.js.map +0 -1
- package/dist/pl-5GP6GKXO.js.map +0 -1
- package/dist/pt-26Y6JFG5.js.map +0 -1
- package/dist/ro-C7UXFRWJ.js.map +0 -1
- package/dist/sv-44DVD76T.js.map +0 -1
- package/dist/th-GIQRTBOY.js.map +0 -1
- package/dist/tr-WMQWO4D6.js.map +0 -1
- package/dist/uk-I7ML6RGG.js.map +0 -1
- package/dist/vi-FGWECASQ.js.map +0 -1
- package/dist/zh-L5FN73XC.js.map +0 -1
- /package/dist/{chunk-YBGN4ACY.js.map → chunk-O23TLLXW.js.map} +0 -0
- /package/dist/{en-RBN2GUHF.js.map → en-P2L4X27N.js.map} +0 -0
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useRef, useEffect, useCallback, lazy, Suspense } from 'react';
|
|
4
|
-
import { capabilitiesOf } from '@semiont/core';
|
|
4
|
+
import { capabilitiesOf, resourceId as toResourceId } from '@semiont/core';
|
|
5
5
|
import { ANNOTATORS } from '../../lib/annotation-registry';
|
|
6
6
|
import { segmentTextWithAnnotations } from '../../lib/text-segmentation';
|
|
7
7
|
import { buildTextSelectors, fallbackTextPosition } from '../../lib/text-selection-handler';
|
|
8
8
|
import { SvgDrawingCanvas } from '../image-annotation/SvgDrawingCanvas';
|
|
9
9
|
|
|
10
|
-
import { useResourceAnnotations } from '../../contexts/ResourceAnnotationsContext';
|
|
11
|
-
|
|
12
10
|
// Lazy load PDF component to avoid SSR issues with browser PDF.js loading
|
|
13
11
|
const PdfAnnotationCanvas = lazy(() => import('../pdf-annotation/PdfAnnotationCanvas.client').then(mod => ({ default: mod.PdfAnnotationCanvas })));
|
|
14
12
|
|
|
15
13
|
import { CodeMirrorRenderer } from '../CodeMirrorRenderer';
|
|
16
14
|
import type { EditorView } from '@codemirror/view';
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import { useEventSubscriptions } from '../../contexts/useEventSubscription';
|
|
15
|
+
import type { SemiontSession } from '@semiont/sdk';
|
|
16
|
+
import { useSessionEventSubscriptions } from '../../hooks/useSessionEventSubscriptions';
|
|
20
17
|
|
|
21
18
|
// Type augmentation for custom DOM properties
|
|
22
19
|
interface EnrichedHTMLElement extends HTMLElement {
|
|
@@ -31,7 +28,8 @@ export type { SelectionMotivation, ClickAction, ShapeType };
|
|
|
31
28
|
interface Props {
|
|
32
29
|
content: string;
|
|
33
30
|
mimeType?: string;
|
|
34
|
-
|
|
31
|
+
/** The '@id' of the shown resource — stamped as `source` on mark:requested (multi-viewer routing). */
|
|
32
|
+
resourceUri: string;
|
|
35
33
|
annotations: AnnotationsCollection;
|
|
36
34
|
uiState: AnnotationUIState;
|
|
37
35
|
onUIStateChange?: (state: Partial<AnnotationUIState>) => void;
|
|
@@ -42,15 +40,20 @@ interface Props {
|
|
|
42
40
|
showLineNumbers?: boolean;
|
|
43
41
|
hoverDelayMs?: number;
|
|
44
42
|
annotateMode: boolean;
|
|
43
|
+
/** Session for the shown resource — its client emits mark:* / mark.request; its bus feeds toolbar + beckon events. */
|
|
44
|
+
session: SemiontSession | null;
|
|
45
|
+
/** Recently-created annotation ids to sparkle (host-provided; was ResourceAnnotationsContext). */
|
|
46
|
+
newAnnotationIds?: Set<string>;
|
|
47
|
+
/** The bar's Mode control reports the chosen mode here (the owner applies it). */
|
|
48
|
+
onModeChange?: (mode: boolean) => void;
|
|
49
|
+
/** Render the built-in bar (default). false → no bar; selection capture and drawing stay live. */
|
|
50
|
+
showToolbar?: boolean;
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
/**
|
|
48
54
|
* View component for annotating resources with text selection and drawing
|
|
49
55
|
*
|
|
50
56
|
* @emits mark:requested - User requested to create annotation. Payload: { selector: Selector | Selector[], motivation: SelectionMotivation }
|
|
51
|
-
* @subscribes mark:selection-changed - Toolbar selection changed. Payload: { motivation: string | null }
|
|
52
|
-
* @subscribes mark:click-changed - Toolbar click action changed. Payload: { action: string }
|
|
53
|
-
* @subscribes mark:shape-changed - Toolbar shape changed. Payload: { shape: string }
|
|
54
57
|
* @subscribes beckon:hover - Annotation hovered. Payload: { annotationId: string | null }
|
|
55
58
|
*/
|
|
56
59
|
export function AnnotateView({
|
|
@@ -65,11 +68,13 @@ export function AnnotateView({
|
|
|
65
68
|
generatingReferenceId,
|
|
66
69
|
showLineNumbers = false,
|
|
67
70
|
hoverDelayMs = 150,
|
|
68
|
-
annotateMode
|
|
71
|
+
annotateMode,
|
|
72
|
+
session,
|
|
73
|
+
newAnnotationIds,
|
|
74
|
+
onModeChange,
|
|
75
|
+
showToolbar = true,
|
|
69
76
|
}: Props) {
|
|
70
|
-
const { newAnnotationIds } = useResourceAnnotations();
|
|
71
77
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
72
|
-
const session = useObservable(useSemiont().activeSession$);
|
|
73
78
|
|
|
74
79
|
const render = capabilitiesOf(mimeType)?.render ?? 'none';
|
|
75
80
|
|
|
@@ -85,28 +90,27 @@ export function AnnotateView({
|
|
|
85
90
|
const onUIStateChangeRef = useRef(onUIStateChange);
|
|
86
91
|
onUIStateChangeRef.current = onUIStateChange;
|
|
87
92
|
|
|
88
|
-
// Toolbar
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
// Toolbar callbacks: the presentational bar reports chosen values; route them up
|
|
94
|
+
// through the existing onUIStateChange path (the owner applies them).
|
|
95
|
+
const handleToolbarSelectionChange = useCallback((motivation: SelectionMotivation | null) => {
|
|
96
|
+
onUIStateChangeRef.current?.({ selectedMotivation: motivation });
|
|
91
97
|
}, []);
|
|
92
98
|
|
|
93
|
-
const
|
|
94
|
-
onUIStateChangeRef.current?.({ selectedClick: action
|
|
99
|
+
const handleToolbarClickActionChange = useCallback((action: ClickAction) => {
|
|
100
|
+
onUIStateChangeRef.current?.({ selectedClick: action });
|
|
95
101
|
}, []);
|
|
96
102
|
|
|
97
|
-
const
|
|
98
|
-
onUIStateChangeRef.current?.({ selectedShape: shape
|
|
103
|
+
const handleToolbarShapeChange = useCallback((shape: ShapeType) => {
|
|
104
|
+
onUIStateChangeRef.current?.({ selectedShape: shape });
|
|
99
105
|
}, []);
|
|
100
106
|
|
|
101
107
|
const handleAnnotationHover = useCallback(({ annotationId }: { annotationId: string | null }) => {
|
|
102
108
|
onUIStateChangeRef.current?.({ hoveredAnnotationId: annotationId });
|
|
103
109
|
}, []);
|
|
104
110
|
|
|
105
|
-
//
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
'mark:click-changed': handleToolbarClickChanged,
|
|
109
|
-
'mark:shape-changed': handleToolbarShapeChanged,
|
|
111
|
+
// Annotation hover (session-scoped). Toolbar preference changes flow through
|
|
112
|
+
// props/callbacks, not the bus (TOOLBAR-PREFS-AS-PROPS).
|
|
113
|
+
useSessionEventSubscriptions(session, {
|
|
110
114
|
'beckon:hover': handleAnnotationHover,
|
|
111
115
|
});
|
|
112
116
|
|
|
@@ -176,7 +180,7 @@ export function AnnotateView({
|
|
|
176
180
|
const selectors = buildTextSelectors(content, text, start, end);
|
|
177
181
|
if (!selectors) return;
|
|
178
182
|
|
|
179
|
-
session?.client.mark.request(selectors, selectedMotivation);
|
|
183
|
+
session?.client.mark.request(toResourceId(resourceUri), selectors, selectedMotivation);
|
|
180
184
|
|
|
181
185
|
// Clear selection after creating annotation
|
|
182
186
|
selection.removeAllRanges();
|
|
@@ -190,20 +194,27 @@ export function AnnotateView({
|
|
|
190
194
|
container.removeEventListener('mouseup', handleMouseUp);
|
|
191
195
|
container.removeEventListener('mousedown', handleMouseDown);
|
|
192
196
|
};
|
|
193
|
-
}, [selectedMotivation, content]);
|
|
197
|
+
}, [selectedMotivation, content, resourceUri]);
|
|
194
198
|
|
|
195
199
|
// Route to the annotation viewer for this media type's render mode.
|
|
196
200
|
switch (render) {
|
|
197
201
|
case 'text':
|
|
198
202
|
return (
|
|
199
203
|
<div className="semiont-annotate-view" data-mime-type="text" ref={containerRef}>
|
|
204
|
+
{showToolbar && (
|
|
200
205
|
<AnnotateToolbar
|
|
201
206
|
selectedMotivation={selectedMotivation}
|
|
202
207
|
selectedClick={selectedClick}
|
|
208
|
+
selectedShape={selectedShape}
|
|
203
209
|
mediaType={mimeType}
|
|
204
210
|
annotateMode={annotateMode}
|
|
205
211
|
annotators={ANNOTATORS}
|
|
212
|
+
onModeChange={onModeChange}
|
|
213
|
+
onClickActionChange={handleToolbarClickActionChange}
|
|
214
|
+
onSelectionChange={handleToolbarSelectionChange}
|
|
215
|
+
onShapeChange={handleToolbarShapeChange}
|
|
206
216
|
/>
|
|
217
|
+
)}
|
|
207
218
|
<div className="semiont-annotate-view__content">
|
|
208
219
|
<CodeMirrorRenderer
|
|
209
220
|
content={content}
|
|
@@ -229,20 +240,26 @@ export function AnnotateView({
|
|
|
229
240
|
// PDF annotation support (spatial, FragmentSelector)
|
|
230
241
|
return (
|
|
231
242
|
<div className="semiont-annotate-view" data-mime-type="pdf" ref={containerRef}>
|
|
243
|
+
{showToolbar && (
|
|
232
244
|
<AnnotateToolbar
|
|
233
245
|
selectedMotivation={selectedMotivation}
|
|
234
246
|
selectedClick={selectedClick}
|
|
235
|
-
showShapeGroup={true}
|
|
236
247
|
selectedShape={selectedShape}
|
|
237
248
|
mediaType={mimeType}
|
|
238
249
|
annotateMode={annotateMode}
|
|
239
250
|
annotators={ANNOTATORS}
|
|
251
|
+
onModeChange={onModeChange}
|
|
252
|
+
onClickActionChange={handleToolbarClickActionChange}
|
|
253
|
+
onSelectionChange={handleToolbarSelectionChange}
|
|
254
|
+
onShapeChange={handleToolbarShapeChange}
|
|
240
255
|
/>
|
|
256
|
+
)}
|
|
241
257
|
<div className="semiont-annotate-view__content">
|
|
242
258
|
{content && (
|
|
243
259
|
<Suspense fallback={<div className="semiont-annotate-view__loading">Loading PDF viewer...</div>}>
|
|
244
260
|
<PdfAnnotationCanvas
|
|
245
261
|
pdfUrl={content}
|
|
262
|
+
resourceUri={resourceUri}
|
|
246
263
|
existingAnnotations={allAnnotations}
|
|
247
264
|
drawingMode={selectedMotivation ? selectedShape : null}
|
|
248
265
|
selectedMotivation={selectedMotivation}
|
|
@@ -260,19 +277,25 @@ export function AnnotateView({
|
|
|
260
277
|
// PNG, JPEG, etc. - full annotation support
|
|
261
278
|
return (
|
|
262
279
|
<div className="semiont-annotate-view" data-mime-type="image" ref={containerRef}>
|
|
280
|
+
{showToolbar && (
|
|
263
281
|
<AnnotateToolbar
|
|
264
282
|
selectedMotivation={selectedMotivation}
|
|
265
283
|
selectedClick={selectedClick}
|
|
266
|
-
showShapeGroup={true}
|
|
267
284
|
selectedShape={selectedShape}
|
|
268
285
|
mediaType={mimeType}
|
|
269
286
|
annotateMode={annotateMode}
|
|
270
287
|
annotators={ANNOTATORS}
|
|
288
|
+
onModeChange={onModeChange}
|
|
289
|
+
onClickActionChange={handleToolbarClickActionChange}
|
|
290
|
+
onSelectionChange={handleToolbarSelectionChange}
|
|
291
|
+
onShapeChange={handleToolbarShapeChange}
|
|
271
292
|
/>
|
|
293
|
+
)}
|
|
272
294
|
<div className="semiont-annotate-view__content">
|
|
273
295
|
{content && (
|
|
274
296
|
<SvgDrawingCanvas
|
|
275
297
|
imageUrl={content}
|
|
298
|
+
resourceUri={resourceUri}
|
|
276
299
|
existingAnnotations={allAnnotations}
|
|
277
300
|
drawingMode={selectedMotivation ? selectedShape : null}
|
|
278
301
|
selectedMotivation={selectedMotivation}
|
|
@@ -293,15 +316,13 @@ export function AnnotateView({
|
|
|
293
316
|
<p className="semiont-annotate-view__empty-message">
|
|
294
317
|
Annotation not supported for {mimeType}
|
|
295
318
|
</p>
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
</a>
|
|
304
|
-
)}
|
|
319
|
+
<a
|
|
320
|
+
href={`/api/resources/${resourceUri}`}
|
|
321
|
+
download
|
|
322
|
+
className="semiont-button semiont-button--primary"
|
|
323
|
+
>
|
|
324
|
+
Download File
|
|
325
|
+
</a>
|
|
305
326
|
</div>
|
|
306
327
|
</div>
|
|
307
328
|
);
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useEffect, useRef, useCallback, useMemo, memo,
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import { capabilitiesOf } from '@semiont/core';
|
|
3
|
+
import { useEffect, useRef, useCallback, useMemo, memo, type MouseEvent as ReactMouseEvent } from 'react';
|
|
4
|
+
import { annotationId as toAnnotationId, resourceId as toResourceId } from '@semiont/core';
|
|
5
|
+
import { capabilitiesOf, getBodySource, isResolvedReference } from '@semiont/core';
|
|
6
|
+
import type { Annotation, ResourceDescriptor } from '@semiont/core';
|
|
8
7
|
import { createHoverHandlers } from '@semiont/sdk';
|
|
9
8
|
import { ANNOTATORS } from '../../lib/annotation-registry';
|
|
10
9
|
import { scrollAnnotationIntoView } from '../../lib/scroll-utils';
|
|
11
|
-
import { ImageViewer } from '../viewers';
|
|
12
10
|
import { AnnotateToolbar, type ClickAction } from '../annotation/AnnotateToolbar';
|
|
13
11
|
import type { AnnotationsCollection } from '../../types/annotation-props';
|
|
14
12
|
import {
|
|
@@ -20,13 +18,9 @@ import {
|
|
|
20
18
|
toOverlayAnnotations,
|
|
21
19
|
} from '../../lib/annotation-overlay';
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
import { useResourceAnnotations } from '../../contexts/ResourceAnnotationsContext';
|
|
27
|
-
import { useSemiont } from '../../session/SemiontProvider';
|
|
28
|
-
import { useObservable } from '../../hooks/useObservable';
|
|
29
|
-
import { useEventSubscriptions } from '../../contexts/useEventSubscription';
|
|
21
|
+
import type { SemiontSession } from '@semiont/sdk';
|
|
22
|
+
import { useSessionEventSubscriptions } from '../../hooks/useSessionEventSubscriptions';
|
|
23
|
+
import { defaultBrowseRenderers, type BrowseMediaRenderers } from './browse-renderers';
|
|
30
24
|
|
|
31
25
|
interface Props {
|
|
32
26
|
content: string;
|
|
@@ -37,25 +31,35 @@ interface Props {
|
|
|
37
31
|
selectedClick?: ClickAction;
|
|
38
32
|
annotateMode: boolean;
|
|
39
33
|
hoverDelayMs?: number;
|
|
34
|
+
/** Session for the shown resource — emits browse:click / beckon:hover; its bus feeds beckon events. */
|
|
35
|
+
session: SemiontSession | null;
|
|
36
|
+
/** Recently-created annotation ids to sparkle (host-provided; was ResourceAnnotationsContext). */
|
|
37
|
+
newAnnotationIds?: Set<string>;
|
|
38
|
+
/** Override the read-only media renderers (render mode → renderer); merged over the defaults. */
|
|
39
|
+
renderers?: BrowseMediaRenderers;
|
|
40
|
+
/** A content link (`<a href>` in the rendered content) was clicked. The viewer preventDefaults and
|
|
41
|
+
* delegates; it never navigates on its own. Omit → the click is still blocked (nothing happens). */
|
|
42
|
+
onLinkClick?: (link: { href: string; event: ReactMouseEvent }) => void;
|
|
43
|
+
/** A RESOLVED reference span is hovered: fires after the dwell AND the referent's cached descriptor
|
|
44
|
+
* resolves; `null` on leave (only if a hover fired — stubs stay silent). Host renders its own preview. */
|
|
45
|
+
onReferenceHover?: (hover: ReferenceHover | null) => void;
|
|
46
|
+
/** Inline display variant: auto-height to content, no inner scroll container, no pane chrome —
|
|
47
|
+
* drops into a chat bubble / card / list item. Default: fill-the-pane (unchanged). */
|
|
48
|
+
inline?: boolean;
|
|
49
|
+
/** The bar's Mode control reports the chosen mode here (the owner applies it). */
|
|
50
|
+
onModeChange?: (mode: boolean) => void;
|
|
51
|
+
/** The bar's Click control reports the chosen action here (the owner applies it). */
|
|
52
|
+
onClickActionChange?: (action: ClickAction) => void;
|
|
53
|
+
/** Render the built-in bar (default). false → no bar; all other seams stay live. */
|
|
54
|
+
showToolbar?: boolean;
|
|
40
55
|
}
|
|
41
56
|
|
|
42
|
-
/**
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}: {
|
|
49
|
-
content: string;
|
|
50
|
-
}) {
|
|
51
|
-
return (
|
|
52
|
-
<ReactMarkdown
|
|
53
|
-
remarkPlugins={[remarkGfm]}
|
|
54
|
-
>
|
|
55
|
-
{content}
|
|
56
|
-
</ReactMarkdown>
|
|
57
|
-
);
|
|
58
|
-
});
|
|
57
|
+
/** Payload for `onReferenceHover` — the hovered linking annotation, its resolved referent, and where the span is. */
|
|
58
|
+
export interface ReferenceHover {
|
|
59
|
+
annotation: Annotation;
|
|
60
|
+
referent: ResourceDescriptor;
|
|
61
|
+
anchorRect: DOMRect;
|
|
62
|
+
}
|
|
59
63
|
|
|
60
64
|
/**
|
|
61
65
|
* View component for browsing annotated resources in read-only mode.
|
|
@@ -76,12 +80,20 @@ export const BrowseView = memo(function BrowseView({
|
|
|
76
80
|
resourceUri,
|
|
77
81
|
annotations,
|
|
78
82
|
selectedClick = 'detail',
|
|
83
|
+
onModeChange,
|
|
84
|
+
onClickActionChange,
|
|
85
|
+
showToolbar = true,
|
|
79
86
|
annotateMode,
|
|
80
|
-
hoverDelayMs = 150
|
|
87
|
+
hoverDelayMs = 150,
|
|
88
|
+
session,
|
|
89
|
+
newAnnotationIds,
|
|
90
|
+
renderers,
|
|
91
|
+
onLinkClick,
|
|
92
|
+
onReferenceHover,
|
|
93
|
+
inline = false,
|
|
81
94
|
}: Props) {
|
|
82
|
-
const { newAnnotationIds } = useResourceAnnotations();
|
|
83
|
-
const session = useObservable(useSemiont().activeSession$);
|
|
84
95
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
96
|
+
const inlineMod = inline ? ' semiont-browse-view--inline' : '';
|
|
85
97
|
|
|
86
98
|
const render = capabilitiesOf(mimeType)?.render ?? 'none';
|
|
87
99
|
|
|
@@ -97,26 +109,23 @@ export const BrowseView = memo(function BrowseView({
|
|
|
97
109
|
[allAnnotations]
|
|
98
110
|
);
|
|
99
111
|
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
//
|
|
112
|
+
// The two-layer overlay in ONE effect, keyed on everything it reads: the
|
|
113
|
+
// rendered content DOM (`content` re-renders it) AND the annotations.
|
|
114
|
+
// Splitting these across two effects with a ref-passed offset map silently
|
|
115
|
+
// dropped the `content` dependency — content arriving after annotations
|
|
116
|
+
// (any async-content host) painted ZERO spans until a remount. The
|
|
117
|
+
// length===0 early-return is safe: the prior run's cleanup already cleared.
|
|
104
118
|
useEffect(() => {
|
|
105
|
-
if (!containerRef.current) return;
|
|
106
|
-
offsetMapRef.current = buildSourceToRenderedMap(content, containerRef.current);
|
|
107
|
-
}, [content]);
|
|
108
|
-
|
|
109
|
-
// Layer 2: overlay annotations after DOM paint
|
|
110
|
-
useEffect(() => {
|
|
111
|
-
if (!containerRef.current || !offsetMapRef.current || overlayAnnotations.length === 0) return;
|
|
119
|
+
if (!containerRef.current || overlayAnnotations.length === 0) return;
|
|
112
120
|
|
|
113
121
|
const container = containerRef.current;
|
|
122
|
+
const offsetMap = buildSourceToRenderedMap(content, container);
|
|
114
123
|
const textNodeIndex = buildTextNodeIndex(container);
|
|
115
|
-
const ranges = resolveAnnotationRanges(overlayAnnotations,
|
|
124
|
+
const ranges = resolveAnnotationRanges(overlayAnnotations, offsetMap, textNodeIndex);
|
|
116
125
|
applyHighlights(ranges);
|
|
117
126
|
|
|
118
127
|
return () => clearHighlights(container);
|
|
119
|
-
}, [overlayAnnotations]);
|
|
128
|
+
}, [content, overlayAnnotations]);
|
|
120
129
|
|
|
121
130
|
// Attach click handler, hover handler, and animations after render
|
|
122
131
|
useEffect(() => {
|
|
@@ -142,8 +151,50 @@ export const BrowseView = memo(function BrowseView({
|
|
|
142
151
|
}
|
|
143
152
|
};
|
|
144
153
|
|
|
154
|
+
// Reference-hover delegation (host-facing event surface): after the dwell,
|
|
155
|
+
// resolve the referent's cached descriptor and hand the host
|
|
156
|
+
// { annotation, referent, anchorRect }; `null` on leave — but only if a hover
|
|
157
|
+
// fired, so stubs stay silent. Rides the SAME dwell emitter as beckon:hover —
|
|
158
|
+
// one state machine, two consumers.
|
|
159
|
+
let hoveredElement: HTMLElement | null = null;
|
|
160
|
+
let referentSub: { unsubscribe(): void } | null = null;
|
|
161
|
+
let referenceHoverFired = false;
|
|
162
|
+
|
|
163
|
+
const startReferenceHover = (id: string) => {
|
|
164
|
+
if (!onReferenceHover) return; // no handler → no referent load
|
|
165
|
+
const annotation = allAnnotations.find(a => a.id === id);
|
|
166
|
+
if (!annotation || !isResolvedReference(annotation)) return; // stub / non-reference: fires nothing
|
|
167
|
+
const referentId = getBodySource(annotation.body);
|
|
168
|
+
const element = hoveredElement;
|
|
169
|
+
if (!referentId || !element) return;
|
|
170
|
+
referentSub?.unsubscribe();
|
|
171
|
+
referentSub = session.client.browse.resource(toResourceId(referentId)).subscribe({
|
|
172
|
+
next: (referent) => {
|
|
173
|
+
if (referent === undefined) return; // cache still loading
|
|
174
|
+
referentSub?.unsubscribe();
|
|
175
|
+
referentSub = null;
|
|
176
|
+
referenceHoverFired = true;
|
|
177
|
+
// anchorRect taken at fire time so it reflects current layout.
|
|
178
|
+
onReferenceHover({ annotation, referent, anchorRect: element.getBoundingClientRect() });
|
|
179
|
+
},
|
|
180
|
+
error: () => { referentSub = null; }, // failed load: never fires
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const endReferenceHover = () => {
|
|
185
|
+
referentSub?.unsubscribe(); // leave-before-resolve: cancel, no fire
|
|
186
|
+
referentSub = null;
|
|
187
|
+
if (referenceHoverFired) {
|
|
188
|
+
referenceHoverFired = false;
|
|
189
|
+
onReferenceHover?.(null);
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
145
193
|
const { handleMouseEnter, handleMouseLeave, cleanup: cleanupHover } = createHoverHandlers(
|
|
146
|
-
(id) =>
|
|
194
|
+
(id) => {
|
|
195
|
+
session.client.beckon.hover(id);
|
|
196
|
+
if (id) startReferenceHover(id); else endReferenceHover();
|
|
197
|
+
},
|
|
147
198
|
hoverDelayMs
|
|
148
199
|
);
|
|
149
200
|
|
|
@@ -152,7 +203,10 @@ export const BrowseView = memo(function BrowseView({
|
|
|
152
203
|
const target = e.target as HTMLElement;
|
|
153
204
|
const annotationElement = target.closest('[data-annotation-id]');
|
|
154
205
|
const annotationId = annotationElement?.getAttribute('data-annotation-id');
|
|
155
|
-
if (annotationId)
|
|
206
|
+
if (annotationId) {
|
|
207
|
+
hoveredElement = annotationElement as HTMLElement; // anchor for onReferenceHover
|
|
208
|
+
handleMouseEnter(toAnnotationId(annotationId));
|
|
209
|
+
}
|
|
156
210
|
};
|
|
157
211
|
|
|
158
212
|
// Single mouseout handler for the container - fires once on exit
|
|
@@ -182,8 +236,9 @@ export const BrowseView = memo(function BrowseView({
|
|
|
182
236
|
container.removeEventListener('mouseover', handleMouseOver);
|
|
183
237
|
container.removeEventListener('mouseout', handleMouseOut);
|
|
184
238
|
cleanupHover();
|
|
239
|
+
referentSub?.unsubscribe(); // in-flight referent load dies with the effect
|
|
185
240
|
};
|
|
186
|
-
}, [content, allAnnotations, newAnnotationIds, hoverDelayMs, session]);
|
|
241
|
+
}, [content, allAnnotations, newAnnotationIds, hoverDelayMs, session, onReferenceHover]);
|
|
187
242
|
|
|
188
243
|
// Helper to scroll annotation into view with pulse effect
|
|
189
244
|
const scrollToAnnotation = useCallback((annotationId: string | null, removePulse = false) => {
|
|
@@ -202,94 +257,65 @@ export const BrowseView = memo(function BrowseView({
|
|
|
202
257
|
scrollToAnnotation(annotationId ?? null, true);
|
|
203
258
|
}, [scrollToAnnotation]);
|
|
204
259
|
|
|
205
|
-
|
|
260
|
+
useSessionEventSubscriptions(session, {
|
|
206
261
|
'beckon:hover': handleAnnotationHover,
|
|
207
262
|
'beckon:focus': handleAnnotationFocus,
|
|
208
263
|
});
|
|
209
264
|
|
|
210
|
-
//
|
|
211
|
-
//
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
<div ref={containerRef} className="semiont-browse-view__content">
|
|
242
|
-
<Suspense fallback={<div className="semiont-browse-view__loading">Loading PDF viewer...</div>}>
|
|
243
|
-
<PdfAnnotationCanvas
|
|
244
|
-
pdfUrl={content}
|
|
245
|
-
existingAnnotations={allAnnotations}
|
|
246
|
-
drawingMode={null}
|
|
247
|
-
selectedMotivation={null}
|
|
248
|
-
/>
|
|
249
|
-
</Suspense>
|
|
250
|
-
</div>
|
|
265
|
+
// A content link inside the rendered output (react-markdown / HTML `<a href>`) must never navigate
|
|
266
|
+
// on its own (embedded/Electron security): always preventDefault, then delegate to the host if it cares.
|
|
267
|
+
const handleContentClick = useCallback((e: ReactMouseEvent) => {
|
|
268
|
+
const anchor = (e.target as HTMLElement).closest('a[href]');
|
|
269
|
+
if (!anchor) return;
|
|
270
|
+
e.preventDefault();
|
|
271
|
+
const href = anchor.getAttribute('href');
|
|
272
|
+
if (href) onLinkClick?.({ href, event: e });
|
|
273
|
+
}, [onLinkClick]);
|
|
274
|
+
|
|
275
|
+
// Route to the media renderer for this render mode. `text`/`image`/`pdf` share
|
|
276
|
+
// the shell (toolbar + annotation-overlay container); `none` (no preview, or an
|
|
277
|
+
// unknown type) has its own metadata+download structure. Callers can override
|
|
278
|
+
// any renderer via `renderers`.
|
|
279
|
+
const mediaRenderers: BrowseMediaRenderers = { ...defaultBrowseRenderers, ...renderers };
|
|
280
|
+
const Renderer = render === 'none' ? undefined : mediaRenderers[render];
|
|
281
|
+
|
|
282
|
+
if (!Renderer) {
|
|
283
|
+
return (
|
|
284
|
+
<div ref={containerRef} className={`semiont-browse-view semiont-browse-view--unsupported${inlineMod}`} data-mime-type="unsupported">
|
|
285
|
+
<div className="semiont-browse-view__empty">
|
|
286
|
+
<p className="semiont-browse-view__empty-message">
|
|
287
|
+
Preview not available for {mimeType}
|
|
288
|
+
</p>
|
|
289
|
+
<a
|
|
290
|
+
href={`/api/resources/${resourceUri}`}
|
|
291
|
+
download
|
|
292
|
+
className="semiont-button semiont-button--primary"
|
|
293
|
+
>
|
|
294
|
+
Download File
|
|
295
|
+
</a>
|
|
251
296
|
</div>
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
case 'image':
|
|
255
|
-
return (
|
|
256
|
-
<div className="semiont-browse-view" data-mime-type="image">
|
|
257
|
-
<AnnotateToolbar
|
|
258
|
-
selectedMotivation={null}
|
|
259
|
-
selectedClick={selectedClick}
|
|
260
|
-
showSelectionGroup={false}
|
|
261
|
-
showDeleteButton={false}
|
|
262
|
-
annotateMode={annotateMode}
|
|
263
|
-
annotators={ANNOTATORS}
|
|
264
|
-
/>
|
|
265
|
-
<div ref={containerRef} className="semiont-browse-view__content">
|
|
266
|
-
<ImageViewer
|
|
267
|
-
imageUrl={content}
|
|
268
|
-
mimeType={mimeType}
|
|
269
|
-
alt="Resource content"
|
|
270
|
-
/>
|
|
271
|
-
</div>
|
|
272
|
-
</div>
|
|
273
|
-
);
|
|
274
|
-
|
|
275
|
-
case 'none':
|
|
276
|
-
// Catalogued type with no preview (render: 'none') or an imported
|
|
277
|
-
// foreign type the registry doesn't know — same UI: metadata + download.
|
|
278
|
-
return (
|
|
279
|
-
<div ref={containerRef} className="semiont-browse-view semiont-browse-view--unsupported" data-mime-type="unsupported">
|
|
280
|
-
<div className="semiont-browse-view__empty">
|
|
281
|
-
<p className="semiont-browse-view__empty-message">
|
|
282
|
-
Preview not available for {mimeType}
|
|
283
|
-
</p>
|
|
284
|
-
<a
|
|
285
|
-
href={`/api/resources/${resourceUri}`}
|
|
286
|
-
download
|
|
287
|
-
className="semiont-button semiont-button--primary"
|
|
288
|
-
>
|
|
289
|
-
Download File
|
|
290
|
-
</a>
|
|
291
|
-
</div>
|
|
292
|
-
</div>
|
|
293
|
-
);
|
|
297
|
+
</div>
|
|
298
|
+
);
|
|
294
299
|
}
|
|
300
|
+
|
|
301
|
+
return (
|
|
302
|
+
<div className={`semiont-browse-view${inlineMod}`} data-mime-type={render}>
|
|
303
|
+
{showToolbar && (
|
|
304
|
+
<AnnotateToolbar
|
|
305
|
+
selectedMotivation={null}
|
|
306
|
+
selectedClick={selectedClick}
|
|
307
|
+
parts={['clickAction', 'mode']}
|
|
308
|
+
showDeleteButton={false}
|
|
309
|
+
annotateMode={annotateMode}
|
|
310
|
+
annotators={ANNOTATORS}
|
|
311
|
+
onModeChange={onModeChange}
|
|
312
|
+
onClickActionChange={onClickActionChange}
|
|
313
|
+
compact={inline}
|
|
314
|
+
/>
|
|
315
|
+
)}
|
|
316
|
+
<div ref={containerRef} className="semiont-browse-view__content" onClick={handleContentClick}>
|
|
317
|
+
<Renderer content={content} mimeType={mimeType} resourceUri={resourceUri} annotations={allAnnotations} />
|
|
318
|
+
</div>
|
|
319
|
+
</div>
|
|
320
|
+
);
|
|
295
321
|
});
|