@semiont/react-ui 0.2.33-build.78 → 0.2.33-build.80
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/dist/EventBusContext-7GvDyO0d.d.mts +414 -0
- package/dist/{PdfAnnotationCanvas.client-ADC4FFSE.mjs → PdfAnnotationCanvas.client-RAJRPQLU.mjs} +42 -27
- package/dist/PdfAnnotationCanvas.client-RAJRPQLU.mjs.map +1 -0
- package/dist/{ar-RNNSPLQB.mjs → ar-4ZEORRW2.mjs} +8 -4
- package/dist/ar-4ZEORRW2.mjs.map +1 -0
- package/dist/{bn-S2CDL7EC.mjs → bn-SEDE5BQJ.mjs} +8 -4
- package/dist/bn-SEDE5BQJ.mjs.map +1 -0
- package/dist/{chunk-UDX2Q35T.mjs → chunk-D7NBW4RV.mjs} +8 -4
- package/dist/chunk-D7NBW4RV.mjs.map +1 -0
- package/dist/{chunk-35LLVRFK.mjs → chunk-ZR4ZV2LY.mjs} +206 -146
- package/dist/chunk-ZR4ZV2LY.mjs.map +1 -0
- package/dist/{cs-RSV675WU.mjs → cs-7W4WF5WD.mjs} +8 -4
- package/dist/cs-7W4WF5WD.mjs.map +1 -0
- package/dist/{da-CHXNPWJC.mjs → da-75XGBCBK.mjs} +8 -4
- package/dist/da-75XGBCBK.mjs.map +1 -0
- package/dist/{de-KPEZ53D4.mjs → de-ODJVFLHM.mjs} +8 -4
- package/dist/de-ODJVFLHM.mjs.map +1 -0
- package/dist/{el-MW2BME5T.mjs → el-C4PM4WB3.mjs} +8 -4
- package/dist/el-C4PM4WB3.mjs.map +1 -0
- package/dist/{en-EVMIX24Y.mjs → en-KJCJQ4OO.mjs} +2 -2
- package/dist/{es-HQ24NYS3.mjs → es-WD33R7QL.mjs} +8 -4
- package/dist/es-WD33R7QL.mjs.map +1 -0
- package/dist/{fa-W34LRLHG.mjs → fa-2BP6V56P.mjs} +8 -4
- package/dist/fa-2BP6V56P.mjs.map +1 -0
- package/dist/{fi-3U44IGOA.mjs → fi-USRRW24J.mjs} +8 -4
- package/dist/fi-USRRW24J.mjs.map +1 -0
- package/dist/{fr-N7DKX6NN.mjs → fr-EC5S6WVF.mjs} +8 -4
- package/dist/fr-EC5S6WVF.mjs.map +1 -0
- package/dist/{he-CS4WRXN3.mjs → he-7TBVIKAA.mjs} +8 -4
- package/dist/he-7TBVIKAA.mjs.map +1 -0
- package/dist/{hi-GJDY46KA.mjs → hi-FO4VIZLA.mjs} +8 -4
- package/dist/hi-FO4VIZLA.mjs.map +1 -0
- package/dist/{id-WAEZJK2Y.mjs → id-7U7GGVWY.mjs} +8 -4
- package/dist/id-7U7GGVWY.mjs.map +1 -0
- package/dist/index.css +123 -85
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +699 -529
- package/dist/index.mjs +4291 -3491
- package/dist/index.mjs.map +1 -1
- package/dist/{it-VDNDMZPU.mjs → it-Y4OPL6I2.mjs} +8 -4
- package/dist/it-Y4OPL6I2.mjs.map +1 -0
- package/dist/{ja-5PEH56J5.mjs → ja-PK7SQL55.mjs} +8 -4
- package/dist/ja-PK7SQL55.mjs.map +1 -0
- package/dist/{ko-JYPL3WVA.mjs → ko-L25PXMYD.mjs} +8 -4
- package/dist/ko-L25PXMYD.mjs.map +1 -0
- package/dist/{ms-5PZVW76T.mjs → ms-STH777QM.mjs} +8 -4
- package/dist/ms-STH777QM.mjs.map +1 -0
- package/dist/{nl-YXES36KM.mjs → nl-Y7LECDDR.mjs} +8 -4
- package/dist/nl-Y7LECDDR.mjs.map +1 -0
- package/dist/{no-XRA2UCQD.mjs → no-KEKCEWU6.mjs} +8 -4
- package/dist/no-KEKCEWU6.mjs.map +1 -0
- package/dist/{pl-WH6LJA5G.mjs → pl-7A7OC75O.mjs} +8 -4
- package/dist/pl-7A7OC75O.mjs.map +1 -0
- package/dist/{pt-7GAG57BM.mjs → pt-35HTM7RA.mjs} +8 -4
- package/dist/pt-35HTM7RA.mjs.map +1 -0
- package/dist/{ro-BTDDRB7N.mjs → ro-VAWL5KQA.mjs} +8 -4
- package/dist/ro-VAWL5KQA.mjs.map +1 -0
- package/dist/{sv-7V5C2IT4.mjs → sv-7ZK5EQEB.mjs} +8 -4
- package/dist/sv-7ZK5EQEB.mjs.map +1 -0
- package/dist/test-utils.d.mts +18 -8
- package/dist/test-utils.mjs +36 -14
- package/dist/test-utils.mjs.map +1 -1
- package/dist/{th-LPKYLBX5.mjs → th-UDWZ4X34.mjs} +8 -4
- package/dist/th-UDWZ4X34.mjs.map +1 -0
- package/dist/{tr-DU4RQL4M.mjs → tr-4WMPK3UX.mjs} +8 -4
- package/dist/tr-4WMPK3UX.mjs.map +1 -0
- package/dist/{uk-36UHTDDI.mjs → uk-SSLASQYJ.mjs} +8 -4
- package/dist/uk-SSLASQYJ.mjs.map +1 -0
- package/dist/{vi-GDHOUZDH.mjs → vi-IF42Z5PU.mjs} +8 -4
- package/dist/vi-IF42Z5PU.mjs.map +1 -0
- package/dist/{zh-TYUID4XZ.mjs → zh-HRQTNTAI.mjs} +8 -4
- package/dist/zh-HRQTNTAI.mjs.map +1 -0
- package/package.json +8 -2
- package/src/components/CodeMirrorRenderer.tsx +66 -93
- package/src/components/DetectionProgressWidget.tsx +16 -5
- package/src/components/LiveRegion.tsx +18 -18
- package/src/components/ResizeHandle.tsx +10 -4
- package/src/components/SessionTimer.tsx +2 -2
- package/src/components/Toolbar.tsx +18 -9
- package/src/components/__tests__/SessionTimer.test.tsx +9 -9
- package/src/components/annotation/AnnotateToolbar.tsx +17 -15
- package/src/components/annotation/__tests__/AnnotateToolbar.test.tsx +165 -63
- package/src/components/annotation/annotation-entries.css +10 -0
- package/src/components/annotation-popups/JsonLdView.tsx +8 -2
- package/src/components/image-annotation/AnnotationOverlay.tsx +42 -22
- package/src/components/image-annotation/SvgDrawingCanvas.tsx +27 -30
- package/src/components/layout/__tests__/LeftSidebar.test.tsx +12 -33
- package/src/components/layout/__tests__/PageLayout.test.tsx +37 -32
- package/src/components/layout/__tests__/UnifiedHeader.test.tsx +21 -40
- package/src/components/modals/ResourceSearchModal.tsx +2 -2
- package/src/components/modals/SearchModal.tsx +1 -1
- package/src/components/navigation/CollapsibleResourceNavigation.tsx +14 -9
- package/src/components/navigation/NavigationTabs.css +36 -24
- package/src/components/navigation/ObservableLink.tsx +91 -0
- package/src/components/navigation/SimpleNavigation.tsx +20 -16
- package/src/components/navigation/SortableResourceTab.tsx +11 -5
- package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +51 -26
- package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +28 -22
- package/src/components/resource/AnnotateView.tsx +64 -138
- package/src/components/resource/AnnotationHistory.tsx +12 -13
- package/src/components/resource/BrowseView.tsx +89 -177
- package/src/components/resource/HistoryEvent.tsx +16 -11
- package/src/components/resource/ResourceViewer.tsx +201 -370
- package/src/components/resource/__tests__/BrowseView.test.tsx +631 -0
- package/src/components/resource/__tests__/ResourceViewer.mode-switch.test.tsx +231 -0
- package/src/components/resource/event-formatting.ts +316 -0
- package/src/components/resource/panels/AssessmentEntry.tsx +25 -33
- package/src/components/resource/panels/AssessmentPanel.tsx +137 -31
- package/src/components/resource/panels/CollaborationPanel.tsx +20 -13
- package/src/components/resource/panels/CommentEntry.tsx +38 -32
- package/src/components/resource/panels/CommentsPanel.tsx +153 -31
- package/src/components/resource/panels/DetectSection.css +36 -1
- package/src/components/resource/panels/DetectSection.tsx +38 -10
- package/src/components/resource/panels/HighlightEntry.tsx +25 -33
- package/src/components/resource/panels/HighlightPanel.tsx +100 -25
- package/src/components/resource/panels/ReferenceEntry.tsx +61 -75
- package/src/components/resource/panels/ReferencesPanel.tsx +166 -49
- package/src/components/resource/panels/ResourceInfoPanel.tsx +47 -48
- package/src/components/resource/panels/StatisticsPanel.tsx +9 -19
- package/src/components/resource/panels/TagEntry.tsx +25 -33
- package/src/components/resource/panels/TaggingPanel.tsx +141 -25
- package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +46 -101
- package/src/components/resource/panels/__tests__/AssessmentPanel.test.tsx +566 -0
- package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +86 -78
- package/src/components/resource/panels/__tests__/CommentsPanel.test.tsx +146 -141
- package/src/components/resource/panels/__tests__/DetectSection.test.tsx +480 -0
- package/src/components/resource/panels/__tests__/HighlightPanel.detectionProgress.test.tsx +362 -0
- package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +228 -103
- package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +117 -61
- package/src/components/resource/panels/__tests__/TaggingPanel.test.tsx +586 -0
- package/src/components/settings/SettingsPanel.tsx +15 -12
- package/src/features/admin-devops/__tests__/AdminDevOpsPage.test.tsx +1 -46
- package/src/features/admin-devops/components/AdminDevOpsPage.tsx +0 -9
- package/src/features/admin-security/__tests__/AdminSecurityPage.test.tsx +0 -3
- package/src/features/admin-security/components/AdminSecurityPage.tsx +0 -9
- package/src/features/admin-users/__tests__/AdminUsersPage.test.tsx +0 -3
- package/src/features/admin-users/components/AdminUsersPage.tsx +0 -9
- package/src/features/moderate-entity-tags/__tests__/EntityTagsPage.test.tsx +0 -3
- package/src/features/moderate-entity-tags/components/EntityTagsPage.tsx +1 -9
- package/src/features/moderate-recent/__tests__/RecentDocumentsPage.test.tsx +0 -32
- package/src/features/moderate-recent/components/RecentDocumentsPage.tsx +1 -9
- package/src/features/moderate-tag-schemas/__tests__/TagSchemasPage.test.tsx +0 -32
- package/src/features/moderate-tag-schemas/components/TagSchemasPage.tsx +1 -9
- package/src/features/resource-compose/__tests__/ResourceComposePage.test.tsx +51 -54
- package/src/features/resource-compose/components/ResourceComposePage.tsx +3 -13
- package/src/features/resource-discovery/__tests__/ResourceDiscoveryPage.test.tsx +39 -45
- package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +16 -27
- package/src/features/resource-viewer/__tests__/AnnotationDeletionIntegration.test.tsx +231 -0
- package/src/features/resource-viewer/__tests__/DetectionFlowBug.test.tsx +234 -0
- package/src/features/resource-viewer/__tests__/DetectionFlowIntegration.test.tsx +388 -0
- package/src/features/resource-viewer/__tests__/DetectionProgressDismissal.test.tsx +318 -0
- package/src/features/resource-viewer/__tests__/GenerationFlowIntegration.test.tsx +504 -0
- package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +145 -91
- package/src/features/resource-viewer/__tests__/detection-progress-flow.test.tsx +322 -0
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +325 -476
- package/src/styles/motivations/motivation-assessment.css +28 -0
- package/src/styles/patterns/panel-helpers.css +26 -0
- package/translations/ar.json +7 -3
- package/translations/bn.json +7 -3
- package/translations/cs.json +7 -3
- package/translations/da.json +7 -3
- package/translations/de.json +7 -3
- package/translations/el.json +7 -3
- package/translations/en.json +7 -3
- package/translations/es.json +7 -3
- package/translations/fa.json +7 -3
- package/translations/fi.json +7 -3
- package/translations/fr.json +7 -3
- package/translations/he.json +7 -3
- package/translations/hi.json +7 -3
- package/translations/id.json +7 -3
- package/translations/it.json +7 -3
- package/translations/ja.json +7 -3
- package/translations/ko.json +7 -3
- package/translations/ms.json +7 -3
- package/translations/nl.json +7 -3
- package/translations/no.json +7 -3
- package/translations/pl.json +7 -3
- package/translations/pt.json +7 -3
- package/translations/ro.json +7 -3
- package/translations/sv.json +7 -3
- package/translations/th.json +7 -3
- package/translations/tr.json +7 -3
- package/translations/uk.json +7 -3
- package/translations/vi.json +7 -3
- package/translations/zh.json +7 -3
- package/dist/PdfAnnotationCanvas.client-ADC4FFSE.mjs.map +0 -1
- package/dist/TranslationManager-Co_5fSxl.d.mts +0 -118
- package/dist/ar-RNNSPLQB.mjs.map +0 -1
- package/dist/bn-S2CDL7EC.mjs.map +0 -1
- package/dist/chunk-35LLVRFK.mjs.map +0 -1
- package/dist/chunk-UDX2Q35T.mjs.map +0 -1
- package/dist/cs-RSV675WU.mjs.map +0 -1
- package/dist/da-CHXNPWJC.mjs.map +0 -1
- package/dist/de-KPEZ53D4.mjs.map +0 -1
- package/dist/el-MW2BME5T.mjs.map +0 -1
- package/dist/es-HQ24NYS3.mjs.map +0 -1
- package/dist/fa-W34LRLHG.mjs.map +0 -1
- package/dist/fi-3U44IGOA.mjs.map +0 -1
- package/dist/fr-N7DKX6NN.mjs.map +0 -1
- package/dist/he-CS4WRXN3.mjs.map +0 -1
- package/dist/hi-GJDY46KA.mjs.map +0 -1
- package/dist/id-WAEZJK2Y.mjs.map +0 -1
- package/dist/it-VDNDMZPU.mjs.map +0 -1
- package/dist/ja-5PEH56J5.mjs.map +0 -1
- package/dist/ko-JYPL3WVA.mjs.map +0 -1
- package/dist/ms-5PZVW76T.mjs.map +0 -1
- package/dist/nl-YXES36KM.mjs.map +0 -1
- package/dist/no-XRA2UCQD.mjs.map +0 -1
- package/dist/pl-WH6LJA5G.mjs.map +0 -1
- package/dist/pt-7GAG57BM.mjs.map +0 -1
- package/dist/ro-BTDDRB7N.mjs.map +0 -1
- package/dist/sv-7V5C2IT4.mjs.map +0 -1
- package/dist/th-LPKYLBX5.mjs.map +0 -1
- package/dist/tr-DU4RQL4M.mjs.map +0 -1
- package/dist/uk-36UHTDDI.mjs.map +0 -1
- package/dist/vi-GDHOUZDH.mjs.map +0 -1
- package/dist/zh-TYUID4XZ.mjs.map +0 -1
- /package/dist/{en-EVMIX24Y.mjs.map → en-KJCJQ4OO.mjs.map} +0 -0
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { useEffect, useRef } from 'react';
|
|
4
4
|
import { EditorView, Decoration, DecorationSet, lineNumbers } from '@codemirror/view';
|
|
5
5
|
import { EditorState, RangeSetBuilder, StateField, StateEffect, Compartment } from '@codemirror/state';
|
|
6
6
|
import { markdown } from '@codemirror/lang-markdown';
|
|
7
|
-
import
|
|
7
|
+
import { ANNOTATORS } from '../lib/annotation-registry';
|
|
8
8
|
import { ReferenceResolutionWidget } from '../lib/codemirror-widgets';
|
|
9
9
|
import { isHighlight, isReference, isResolvedReference, isComment, isAssessment, isTag, getBodySource } from '@semiont/api-client';
|
|
10
10
|
import type { components } from '@semiont/api-client';
|
|
11
|
+
import type { EventBus } from '../contexts/EventBusContext';
|
|
11
12
|
|
|
12
13
|
type Annotation = components['schemas']['Annotation'];
|
|
13
14
|
|
|
@@ -26,9 +27,7 @@ export interface TextSegment {
|
|
|
26
27
|
|
|
27
28
|
interface Props {
|
|
28
29
|
content: string;
|
|
29
|
-
segments
|
|
30
|
-
onAnnotationClick?: (annotation: Annotation) => void;
|
|
31
|
-
onAnnotationHover?: (annotationId: string | null) => void;
|
|
30
|
+
segments?: TextSegment[]; // Optional - only needed for annotation rendering
|
|
32
31
|
onTextSelect?: (exact: string, position: { start: number; end: number }) => void;
|
|
33
32
|
onChange?: (content: string) => void;
|
|
34
33
|
editable?: boolean;
|
|
@@ -39,13 +38,9 @@ interface Props {
|
|
|
39
38
|
sourceView?: boolean; // If true, show raw source (no markdown rendering)
|
|
40
39
|
showLineNumbers?: boolean; // If true, show line numbers
|
|
41
40
|
enableWidgets?: boolean; // If true, show inline widgets (reference previews, entity badges)
|
|
42
|
-
|
|
43
|
-
onReferenceNavigate?: (documentId: string) => void;
|
|
44
|
-
onUnresolvedReferenceClick?: (annotation: Annotation) => void;
|
|
41
|
+
eventBus?: EventBus;
|
|
45
42
|
getTargetDocumentName?: (documentId: string) => string | undefined;
|
|
46
43
|
generatingReferenceId?: string | null; // ID of reference currently generating a document
|
|
47
|
-
onDeleteAnnotation?: (annotation: Annotation) => void;
|
|
48
|
-
annotators: Record<string, Annotator>;
|
|
49
44
|
}
|
|
50
45
|
|
|
51
46
|
// Effect to update annotation decorations with segments and new IDs
|
|
@@ -61,13 +56,8 @@ interface WidgetUpdate {
|
|
|
61
56
|
content: string;
|
|
62
57
|
segments: TextSegment[];
|
|
63
58
|
generatingReferenceId?: string | null | undefined;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
onReferenceNavigate?: (documentId: string) => void;
|
|
67
|
-
onUnresolvedReferenceClick?: (annotation: Annotation) => void;
|
|
68
|
-
getTargetDocumentName?: (documentId: string) => string | undefined;
|
|
69
|
-
onDeleteAnnotation?: (annotation: Annotation) => void;
|
|
70
|
-
};
|
|
59
|
+
eventBus?: EventBus;
|
|
60
|
+
getTargetDocumentName?: (documentId: string) => string | undefined;
|
|
71
61
|
}
|
|
72
62
|
|
|
73
63
|
const updateWidgetsEffect = StateEffect.define<WidgetUpdate>();
|
|
@@ -139,7 +129,6 @@ function getAnnotationTooltip(annotation: Annotation): string {
|
|
|
139
129
|
// Build decorations from segments
|
|
140
130
|
function buildAnnotationDecorations(
|
|
141
131
|
segments: TextSegment[],
|
|
142
|
-
annotators: Record<string, Annotator>,
|
|
143
132
|
newAnnotationIds?: Set<string>
|
|
144
133
|
): DecorationSet {
|
|
145
134
|
const builder = new RangeSetBuilder<Decoration>();
|
|
@@ -152,7 +141,7 @@ function buildAnnotationDecorations(
|
|
|
152
141
|
if (!segment.annotation) continue;
|
|
153
142
|
|
|
154
143
|
const isNew = newAnnotationIds?.has(segment.annotation.id) || false;
|
|
155
|
-
const baseClassName = Object.values(
|
|
144
|
+
const baseClassName = Object.values(ANNOTATORS).find(a => a.matchesAnnotation(segment.annotation!))?.className || 'annotation-highlight';
|
|
156
145
|
const className = isNew ? `${baseClassName} annotation-sparkle` : baseClassName;
|
|
157
146
|
|
|
158
147
|
// Use W3C helpers to determine annotation type
|
|
@@ -185,8 +174,8 @@ function buildAnnotationDecorations(
|
|
|
185
174
|
return builder.finish();
|
|
186
175
|
}
|
|
187
176
|
|
|
188
|
-
// Create state field
|
|
189
|
-
function createAnnotationDecorationsField(
|
|
177
|
+
// Create state field for annotation decorations
|
|
178
|
+
function createAnnotationDecorationsField() {
|
|
190
179
|
return StateField.define<DecorationSet>({
|
|
191
180
|
create() {
|
|
192
181
|
return Decoration.none;
|
|
@@ -196,7 +185,7 @@ function createAnnotationDecorationsField(annotators: Record<string, Annotator>)
|
|
|
196
185
|
|
|
197
186
|
for (const effect of tr.effects) {
|
|
198
187
|
if (effect.is(updateAnnotationsEffect)) {
|
|
199
|
-
decorations = buildAnnotationDecorations(effect.value.segments,
|
|
188
|
+
decorations = buildAnnotationDecorations(effect.value.segments, effect.value.newAnnotationIds);
|
|
200
189
|
}
|
|
201
190
|
}
|
|
202
191
|
|
|
@@ -211,13 +200,8 @@ function buildWidgetDecorations(
|
|
|
211
200
|
_content: string,
|
|
212
201
|
segments: TextSegment[],
|
|
213
202
|
generatingReferenceId: string | null | undefined,
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
onReferenceNavigate?: (documentId: string) => void;
|
|
217
|
-
onUnresolvedReferenceClick?: (annotation: Annotation) => void;
|
|
218
|
-
getTargetDocumentName?: (documentId: string) => string | undefined;
|
|
219
|
-
onDeleteAnnotation?: (annotation: Annotation) => void;
|
|
220
|
-
}
|
|
203
|
+
eventBus: any,
|
|
204
|
+
getTargetDocumentName?: (documentId: string) => string | undefined
|
|
221
205
|
): DecorationSet {
|
|
222
206
|
const builder = new RangeSetBuilder<Decoration>();
|
|
223
207
|
|
|
@@ -239,7 +223,7 @@ function buildWidgetDecorations(
|
|
|
239
223
|
if (isReference(annotation)) {
|
|
240
224
|
const bodySource = getBodySource(annotation.body);
|
|
241
225
|
const targetName = bodySource
|
|
242
|
-
?
|
|
226
|
+
? getTargetDocumentName?.(bodySource)
|
|
243
227
|
: undefined;
|
|
244
228
|
// Compare by ID portion (handle both URI and internal ID formats)
|
|
245
229
|
const isGenerating = generatingReferenceId
|
|
@@ -248,8 +232,7 @@ function buildWidgetDecorations(
|
|
|
248
232
|
const widget = new ReferenceResolutionWidget(
|
|
249
233
|
annotation,
|
|
250
234
|
targetName,
|
|
251
|
-
|
|
252
|
-
callbacks.onUnresolvedReferenceClick,
|
|
235
|
+
eventBus,
|
|
253
236
|
isGenerating
|
|
254
237
|
);
|
|
255
238
|
builder.add(
|
|
@@ -278,7 +261,8 @@ const widgetDecorationsField = StateField.define<DecorationSet>({
|
|
|
278
261
|
effect.value.content,
|
|
279
262
|
effect.value.segments,
|
|
280
263
|
effect.value.generatingReferenceId,
|
|
281
|
-
effect.value.
|
|
264
|
+
effect.value.eventBus,
|
|
265
|
+
effect.value.getTargetDocumentName
|
|
282
266
|
);
|
|
283
267
|
}
|
|
284
268
|
}
|
|
@@ -290,9 +274,7 @@ const widgetDecorationsField = StateField.define<DecorationSet>({
|
|
|
290
274
|
|
|
291
275
|
export function CodeMirrorRenderer({
|
|
292
276
|
content,
|
|
293
|
-
segments,
|
|
294
|
-
onAnnotationClick,
|
|
295
|
-
onAnnotationHover,
|
|
277
|
+
segments = [],
|
|
296
278
|
onChange,
|
|
297
279
|
editable = false,
|
|
298
280
|
newAnnotationIds,
|
|
@@ -302,13 +284,9 @@ export function CodeMirrorRenderer({
|
|
|
302
284
|
sourceView = false,
|
|
303
285
|
showLineNumbers = false,
|
|
304
286
|
enableWidgets = false,
|
|
305
|
-
|
|
306
|
-
onReferenceNavigate,
|
|
307
|
-
onUnresolvedReferenceClick,
|
|
287
|
+
eventBus,
|
|
308
288
|
getTargetDocumentName,
|
|
309
|
-
generatingReferenceId
|
|
310
|
-
onDeleteAnnotation,
|
|
311
|
-
annotators
|
|
289
|
+
generatingReferenceId
|
|
312
290
|
}: Props) {
|
|
313
291
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
314
292
|
const viewRef = useRef<EditorView | null>(null);
|
|
@@ -320,39 +298,20 @@ export function CodeMirrorRenderer({
|
|
|
320
298
|
|
|
321
299
|
const segmentsRef = useRef(convertedSegments);
|
|
322
300
|
const lineNumbersCompartment = useRef(new Compartment());
|
|
323
|
-
const
|
|
324
|
-
|
|
325
|
-
onEntityTypeClick?: (entityType: string) => void;
|
|
326
|
-
onReferenceNavigate?: (documentId: string) => void;
|
|
327
|
-
onUnresolvedReferenceClick?: (annotation: Annotation) => void;
|
|
328
|
-
getTargetDocumentName?: (documentId: string) => string | undefined;
|
|
329
|
-
onDeleteAnnotation?: (annotation: Annotation) => void;
|
|
330
|
-
onAnnotationClick?: (annotation: Annotation, event?: React.MouseEvent) => void;
|
|
331
|
-
onAnnotationHover?: (annotationId: string | null) => void;
|
|
332
|
-
}>({});
|
|
333
|
-
|
|
334
|
-
// Update segments ref when they change
|
|
335
|
-
segmentsRef.current = segments;
|
|
301
|
+
const eventBusRef = useRef(eventBus);
|
|
302
|
+
const getTargetDocumentNameRef = useRef(getTargetDocumentName);
|
|
336
303
|
|
|
337
|
-
// Update
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
...(onReferenceNavigate && { onReferenceNavigate }),
|
|
342
|
-
...(onUnresolvedReferenceClick && { onUnresolvedReferenceClick }),
|
|
343
|
-
...(getTargetDocumentName && { getTargetDocumentName }),
|
|
344
|
-
...(onDeleteAnnotation && { onDeleteAnnotation }),
|
|
345
|
-
...(onAnnotationClick && { onAnnotationClick }),
|
|
346
|
-
...(onAnnotationHover && { onAnnotationHover })
|
|
347
|
-
};
|
|
348
|
-
}, [onEntityTypeClick, onReferenceNavigate, onUnresolvedReferenceClick, getTargetDocumentName, onDeleteAnnotation, onAnnotationClick, onAnnotationHover]);
|
|
304
|
+
// Update refs when they change
|
|
305
|
+
segmentsRef.current = segments;
|
|
306
|
+
eventBusRef.current = eventBus;
|
|
307
|
+
getTargetDocumentNameRef.current = getTargetDocumentName;
|
|
349
308
|
|
|
350
309
|
// Initialize CodeMirror view once
|
|
351
310
|
useEffect(() => {
|
|
352
311
|
if (!containerRef.current || viewRef.current) return;
|
|
353
312
|
|
|
354
|
-
// Create annotation decorations field
|
|
355
|
-
const annotationDecorationsField = createAnnotationDecorationsField(
|
|
313
|
+
// Create annotation decorations field
|
|
314
|
+
const annotationDecorationsField = createAnnotationDecorationsField();
|
|
356
315
|
|
|
357
316
|
// Create CodeMirror state with markdown mode
|
|
358
317
|
const state = EditorState.create({
|
|
@@ -379,31 +338,17 @@ export function CodeMirrorRenderer({
|
|
|
379
338
|
const annotationElement = target.closest('[data-annotation-id]');
|
|
380
339
|
const annotationId = annotationElement?.getAttribute('data-annotation-id');
|
|
381
340
|
|
|
382
|
-
if (annotationId &&
|
|
341
|
+
if (annotationId && eventBusRef.current) {
|
|
383
342
|
const segment = segmentsRef.current.find(s => s.annotation?.id === annotationId);
|
|
384
343
|
if (segment?.annotation) {
|
|
385
344
|
event.preventDefault();
|
|
386
|
-
|
|
345
|
+
eventBusRef.current.emit('annotation:click', {
|
|
346
|
+
annotationId,
|
|
347
|
+
motivation: segment.annotation.motivation
|
|
348
|
+
});
|
|
387
349
|
return true; // Stop propagation
|
|
388
350
|
}
|
|
389
351
|
}
|
|
390
|
-
return false;
|
|
391
|
-
},
|
|
392
|
-
mousemove: (event, view) => {
|
|
393
|
-
const target = event.target as HTMLElement;
|
|
394
|
-
const annotationElement = target.closest('[data-annotation-id]');
|
|
395
|
-
const annotationId = annotationElement?.getAttribute('data-annotation-id');
|
|
396
|
-
|
|
397
|
-
// Track last hovered ID to avoid redundant calls
|
|
398
|
-
const enrichedDom = view.dom as EnrichedHTMLElement;
|
|
399
|
-
const lastHovered = enrichedDom.__lastHoveredAnnotation;
|
|
400
|
-
if (annotationId !== lastHovered) {
|
|
401
|
-
enrichedDom.__lastHoveredAnnotation = annotationId || null;
|
|
402
|
-
if (callbacksRef.current.onAnnotationHover) {
|
|
403
|
-
callbacksRef.current.onAnnotationHover(annotationId || null);
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
352
|
return false;
|
|
408
353
|
}
|
|
409
354
|
}),
|
|
@@ -461,11 +406,38 @@ export function CodeMirrorRenderer({
|
|
|
461
406
|
// Store the view on the container for position calculation
|
|
462
407
|
(containerRef.current as EnrichedHTMLElement).__cmView = view;
|
|
463
408
|
|
|
409
|
+
// Attach hover event listeners using native DOM events with delegation
|
|
410
|
+
const container = view.dom;
|
|
411
|
+
|
|
412
|
+
const handleMouseOver = (e: MouseEvent) => {
|
|
413
|
+
const target = e.target as HTMLElement;
|
|
414
|
+
const annotationElement = target.closest('[data-annotation-id]');
|
|
415
|
+
const annotationId = annotationElement?.getAttribute('data-annotation-id');
|
|
416
|
+
|
|
417
|
+
if (annotationId && eventBusRef.current) {
|
|
418
|
+
eventBusRef.current.emit('annotation:hover', { annotationId });
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
const handleMouseOut = (e: MouseEvent) => {
|
|
423
|
+
const target = e.target as HTMLElement;
|
|
424
|
+
const annotationElement = target.closest('[data-annotation-id]');
|
|
425
|
+
|
|
426
|
+
if (annotationElement && eventBusRef.current) {
|
|
427
|
+
eventBusRef.current.emit('annotation:hover', { annotationId: null });
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
container.addEventListener('mouseover', handleMouseOver);
|
|
432
|
+
container.addEventListener('mouseout', handleMouseOut);
|
|
433
|
+
|
|
464
434
|
return () => {
|
|
435
|
+
container.removeEventListener('mouseover', handleMouseOver);
|
|
436
|
+
container.removeEventListener('mouseout', handleMouseOut);
|
|
465
437
|
view.destroy();
|
|
466
438
|
viewRef.current = null;
|
|
467
439
|
};
|
|
468
|
-
}, [
|
|
440
|
+
}, []); // Only initialize once
|
|
469
441
|
|
|
470
442
|
// Update content when it changes externally (not from user typing)
|
|
471
443
|
useEffect(() => {
|
|
@@ -473,9 +445,9 @@ export function CodeMirrorRenderer({
|
|
|
473
445
|
|
|
474
446
|
const currentContent = viewRef.current.state.doc.toString();
|
|
475
447
|
|
|
476
|
-
// Only update if content is different
|
|
477
|
-
//
|
|
478
|
-
if (content === currentContent
|
|
448
|
+
// Only update if content is different from what's in the editor
|
|
449
|
+
// Skip if content matches current editor state (prevents cursor jumping)
|
|
450
|
+
if (content === currentContent) return;
|
|
479
451
|
|
|
480
452
|
// Save cursor position
|
|
481
453
|
const selection = viewRef.current.state.selection.main;
|
|
@@ -520,7 +492,8 @@ export function CodeMirrorRenderer({
|
|
|
520
492
|
content,
|
|
521
493
|
segments: convertedSegments,
|
|
522
494
|
generatingReferenceId,
|
|
523
|
-
|
|
495
|
+
eventBus: eventBusRef.current,
|
|
496
|
+
getTargetDocumentName: getTargetDocumentNameRef.current
|
|
524
497
|
})
|
|
525
498
|
});
|
|
526
499
|
}, [content, convertedSegments, enableWidgets, generatingReferenceId]);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useTranslations } from '../contexts/TranslationContext';
|
|
4
|
-
import
|
|
4
|
+
import { useEventBus } from '../contexts/EventBusContext';
|
|
5
|
+
import type { DetectionProgress } from '../types/progress';
|
|
5
6
|
import type { components } from '@semiont/api-client';
|
|
6
7
|
|
|
7
8
|
type Motivation = components['schemas']['Motivation'];
|
|
@@ -13,12 +14,22 @@ interface EnrichedDetectionProgress extends DetectionProgress {
|
|
|
13
14
|
|
|
14
15
|
interface DetectionProgressWidgetProps {
|
|
15
16
|
progress: DetectionProgress | null;
|
|
16
|
-
onCancel?: () => void;
|
|
17
17
|
annotationType?: Motivation | 'reference';
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Widget for displaying detection progress with cancel functionality
|
|
22
|
+
*
|
|
23
|
+
* @emits job:cancel-requested - User requested to cancel detection job. Payload: { jobType: string }
|
|
24
|
+
*/
|
|
25
|
+
export function DetectionProgressWidget({ progress, annotationType = 'reference' }: DetectionProgressWidgetProps) {
|
|
21
26
|
const t = useTranslations('DetectionProgressWidget');
|
|
27
|
+
const eventBus = useEventBus();
|
|
28
|
+
|
|
29
|
+
const handleCancel = () => {
|
|
30
|
+
// Emit event for job cancellation
|
|
31
|
+
eventBus.emit('job:cancel-requested', { jobType: 'detection' });
|
|
32
|
+
};
|
|
22
33
|
|
|
23
34
|
if (!progress) return null;
|
|
24
35
|
|
|
@@ -34,9 +45,9 @@ export function DetectionProgressWidget({ progress, onCancel, annotationType = '
|
|
|
34
45
|
<span className="semiont-detection-sparkle">✨</span>
|
|
35
46
|
{t('title')}
|
|
36
47
|
</h3>
|
|
37
|
-
{progress.status !== 'complete' &&
|
|
48
|
+
{progress.status !== 'complete' && (
|
|
38
49
|
<button
|
|
39
|
-
onClick={
|
|
50
|
+
onClick={handleCancel}
|
|
40
51
|
className="semiont-detection-cancel"
|
|
41
52
|
title={t('cancelDetection')}
|
|
42
53
|
>
|
|
@@ -76,11 +76,11 @@ export function useSearchAnnouncements() {
|
|
|
76
76
|
} else {
|
|
77
77
|
announce(`${count} result${count === 1 ? '' : 's'} found for ${query}`, 'polite');
|
|
78
78
|
}
|
|
79
|
-
}, [
|
|
79
|
+
}, []);
|
|
80
80
|
|
|
81
81
|
const announceSearching = useCallback(() => {
|
|
82
82
|
announce('Searching...', 'polite');
|
|
83
|
-
}, [
|
|
83
|
+
}, []);
|
|
84
84
|
|
|
85
85
|
return {
|
|
86
86
|
announceSearchResults,
|
|
@@ -93,31 +93,31 @@ export function useDocumentAnnouncements(annotators?: Record<string, Annotator>)
|
|
|
93
93
|
|
|
94
94
|
const announceDocumentSaved = useCallback(() => {
|
|
95
95
|
announce('Document saved successfully', 'polite');
|
|
96
|
-
}, [
|
|
96
|
+
}, []);
|
|
97
97
|
|
|
98
98
|
const announceDocumentDeleted = useCallback(() => {
|
|
99
99
|
announce('Document deleted', 'polite');
|
|
100
|
-
}, [
|
|
100
|
+
}, []);
|
|
101
101
|
|
|
102
102
|
const announceAnnotationCreated = useCallback((annotation: Annotation) => {
|
|
103
103
|
const metadata = annotators ? Object.values(annotators).find(a => a.matchesAnnotation(annotation)) : null;
|
|
104
104
|
const message = metadata?.announceOnCreate ?? 'Annotation created';
|
|
105
105
|
announce(message, 'polite');
|
|
106
|
-
}, [
|
|
106
|
+
}, [annotators]);
|
|
107
107
|
|
|
108
108
|
const announceAnnotationDeleted = useCallback(() => {
|
|
109
109
|
announce('Annotation deleted', 'polite');
|
|
110
|
-
}, [
|
|
110
|
+
}, []);
|
|
111
111
|
|
|
112
112
|
const announceAnnotationUpdated = useCallback((annotation: Annotation) => {
|
|
113
113
|
const metadata = annotators ? Object.values(annotators).find(a => a.matchesAnnotation(annotation)) : null;
|
|
114
114
|
const message = `${metadata?.displayName ?? 'Annotation'} updated`;
|
|
115
115
|
announce(message, 'polite');
|
|
116
|
-
}, [
|
|
116
|
+
}, [annotators]);
|
|
117
117
|
|
|
118
118
|
const announceError = useCallback((message: string) => {
|
|
119
119
|
announce(`Error: ${message}`, 'assertive');
|
|
120
|
-
}, [
|
|
120
|
+
}, []);
|
|
121
121
|
|
|
122
122
|
return {
|
|
123
123
|
announceDocumentSaved,
|
|
@@ -138,22 +138,22 @@ export function useResourceLoadingAnnouncements() {
|
|
|
138
138
|
? `Loading ${resourceName}...`
|
|
139
139
|
: 'Loading resource...';
|
|
140
140
|
announce(message, 'polite');
|
|
141
|
-
}, [
|
|
141
|
+
}, []);
|
|
142
142
|
|
|
143
143
|
const announceResourceLoaded = useCallback((resourceName: string) => {
|
|
144
144
|
announce(`${resourceName} loaded successfully`, 'polite');
|
|
145
|
-
}, [
|
|
145
|
+
}, []);
|
|
146
146
|
|
|
147
147
|
const announceResourceLoadError = useCallback((resourceName?: string) => {
|
|
148
148
|
const message = resourceName
|
|
149
149
|
? `Failed to load ${resourceName}`
|
|
150
150
|
: 'Failed to load resource';
|
|
151
151
|
announce(message, 'assertive');
|
|
152
|
-
}, [
|
|
152
|
+
}, []);
|
|
153
153
|
|
|
154
154
|
const announceResourceUpdating = useCallback((resourceName: string) => {
|
|
155
155
|
announce(`Updating ${resourceName}...`, 'polite');
|
|
156
|
-
}, [
|
|
156
|
+
}, []);
|
|
157
157
|
|
|
158
158
|
return {
|
|
159
159
|
announceResourceLoading,
|
|
@@ -169,22 +169,22 @@ export function useFormAnnouncements() {
|
|
|
169
169
|
|
|
170
170
|
const announceFormSubmitting = useCallback(() => {
|
|
171
171
|
announce('Submitting form...', 'polite');
|
|
172
|
-
}, [
|
|
172
|
+
}, []);
|
|
173
173
|
|
|
174
174
|
const announceFormSuccess = useCallback((message?: string) => {
|
|
175
175
|
announce(message || 'Form submitted successfully', 'polite');
|
|
176
|
-
}, [
|
|
176
|
+
}, []);
|
|
177
177
|
|
|
178
178
|
const announceFormError = useCallback((message?: string) => {
|
|
179
179
|
announce(message || 'Form submission failed. Please check your entries and try again.', 'assertive');
|
|
180
|
-
}, [
|
|
180
|
+
}, []);
|
|
181
181
|
|
|
182
182
|
const announceFormValidationError = useCallback((fieldCount: number) => {
|
|
183
183
|
const message = fieldCount === 1
|
|
184
184
|
? 'There is 1 field with an error'
|
|
185
185
|
: `There are ${fieldCount} fields with errors`;
|
|
186
186
|
announce(message, 'assertive');
|
|
187
|
-
}, [
|
|
187
|
+
}, []);
|
|
188
188
|
|
|
189
189
|
return {
|
|
190
190
|
announceFormSubmitting,
|
|
@@ -200,11 +200,11 @@ export function useLanguageChangeAnnouncements() {
|
|
|
200
200
|
|
|
201
201
|
const announceLanguageChanging = useCallback((newLanguage: string) => {
|
|
202
202
|
announce(`Changing language to ${newLanguage}...`, 'polite');
|
|
203
|
-
}, [
|
|
203
|
+
}, []);
|
|
204
204
|
|
|
205
205
|
const announceLanguageChanged = useCallback((newLanguage: string) => {
|
|
206
206
|
announce(`Language changed to ${newLanguage}`, 'polite');
|
|
207
|
-
}, [
|
|
207
|
+
}, []);
|
|
208
208
|
|
|
209
209
|
return {
|
|
210
210
|
announceLanguageChanging,
|
|
@@ -50,6 +50,12 @@ export function ResizeHandle({
|
|
|
50
50
|
const startXRef = useRef<number>(0);
|
|
51
51
|
const startWidthRef = useRef<number>(0);
|
|
52
52
|
|
|
53
|
+
// Store callback in ref to avoid including in dependency arrays
|
|
54
|
+
const onResizeRef = useRef(onResize);
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
onResizeRef.current = onResize;
|
|
57
|
+
});
|
|
58
|
+
|
|
53
59
|
const handleMouseDown = useCallback((e: React.MouseEvent) => {
|
|
54
60
|
e.preventDefault();
|
|
55
61
|
setIsDragging(true);
|
|
@@ -73,8 +79,8 @@ export function ResizeHandle({
|
|
|
73
79
|
|
|
74
80
|
// Enforce constraints
|
|
75
81
|
const constrainedWidth = Math.max(minWidth, Math.min(maxWidth, newWidth));
|
|
76
|
-
|
|
77
|
-
}, [isDragging,
|
|
82
|
+
onResizeRef.current(constrainedWidth);
|
|
83
|
+
}, [isDragging, minWidth, maxWidth, position]);
|
|
78
84
|
|
|
79
85
|
const handleMouseUp = useCallback(() => {
|
|
80
86
|
setIsDragging(false);
|
|
@@ -113,9 +119,9 @@ export function ResizeHandle({
|
|
|
113
119
|
// Only resize if arrow key was pressed
|
|
114
120
|
if (newWidth !== currentWidth) {
|
|
115
121
|
const constrainedWidth = Math.max(minWidth, Math.min(maxWidth, newWidth));
|
|
116
|
-
|
|
122
|
+
onResizeRef.current(constrainedWidth);
|
|
117
123
|
}
|
|
118
|
-
}, [
|
|
124
|
+
}, [minWidth, maxWidth, position]);
|
|
119
125
|
|
|
120
126
|
// Add/remove global mouse event listeners for dragging
|
|
121
127
|
useEffect(() => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { useSessionExpiry } from '
|
|
4
|
-
import { useFormattedTime } from '
|
|
3
|
+
import { useSessionExpiry } from '../hooks/useSessionExpiry';
|
|
4
|
+
import { useFormattedTime } from '../hooks/useFormattedTime';
|
|
5
5
|
|
|
6
6
|
export function SessionTimer() {
|
|
7
7
|
const { timeRemaining } = useSessionExpiry();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useTranslations } from '../contexts/TranslationContext';
|
|
4
|
+
import { useEventBus } from '../contexts/EventBusContext';
|
|
4
5
|
import './toolbar/Toolbar.css';
|
|
5
6
|
|
|
6
7
|
type ToolbarContext = 'document' | 'simple';
|
|
@@ -8,19 +9,27 @@ type ToolbarContext = 'document' | 'simple';
|
|
|
8
9
|
interface Props<T extends string = string> {
|
|
9
10
|
context: ToolbarContext;
|
|
10
11
|
activePanel: T | null;
|
|
11
|
-
onPanelToggle: (panel: T) => void;
|
|
12
12
|
|
|
13
13
|
// Document context specific
|
|
14
14
|
isArchived?: boolean;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Toolbar component for panel navigation
|
|
19
|
+
*
|
|
20
|
+
* @emits panel:toggle - Toggle panel visibility. Payload: { panel: string }
|
|
21
|
+
*/
|
|
17
22
|
export function Toolbar<T extends string = string>({
|
|
18
23
|
context,
|
|
19
24
|
activePanel,
|
|
20
|
-
onPanelToggle,
|
|
21
25
|
isArchived = false
|
|
22
26
|
}: Props<T>) {
|
|
23
27
|
const t = useTranslations('Toolbar');
|
|
28
|
+
const eventBus = useEventBus();
|
|
29
|
+
|
|
30
|
+
const handlePanelToggle = (panel: string) => {
|
|
31
|
+
eventBus.emit('panel:toggle', { panel });
|
|
32
|
+
};
|
|
24
33
|
|
|
25
34
|
return (
|
|
26
35
|
<div className="semiont-toolbar" data-context={context}>
|
|
@@ -30,7 +39,7 @@ export function Toolbar<T extends string = string>({
|
|
|
30
39
|
{/* Annotations Icon - unified panel for all annotation types */}
|
|
31
40
|
{!isArchived && (
|
|
32
41
|
<button
|
|
33
|
-
onClick={() =>
|
|
42
|
+
onClick={() => handlePanelToggle('annotations')}
|
|
34
43
|
className="semiont-toolbar-button"
|
|
35
44
|
data-active={activePanel === 'annotations'}
|
|
36
45
|
data-panel="annotations"
|
|
@@ -44,7 +53,7 @@ export function Toolbar<T extends string = string>({
|
|
|
44
53
|
|
|
45
54
|
{/* Document Info Icon */}
|
|
46
55
|
<button
|
|
47
|
-
onClick={() =>
|
|
56
|
+
onClick={() => handlePanelToggle('info')}
|
|
48
57
|
className="semiont-toolbar-button"
|
|
49
58
|
data-active={activePanel === 'info'}
|
|
50
59
|
data-panel="info"
|
|
@@ -57,7 +66,7 @@ export function Toolbar<T extends string = string>({
|
|
|
57
66
|
|
|
58
67
|
{/* History Icon */}
|
|
59
68
|
<button
|
|
60
|
-
onClick={() =>
|
|
69
|
+
onClick={() => handlePanelToggle('history')}
|
|
61
70
|
className="semiont-toolbar-button"
|
|
62
71
|
data-active={activePanel === 'history'}
|
|
63
72
|
data-panel="history"
|
|
@@ -70,7 +79,7 @@ export function Toolbar<T extends string = string>({
|
|
|
70
79
|
|
|
71
80
|
{/* Collaboration Icon */}
|
|
72
81
|
<button
|
|
73
|
-
onClick={() =>
|
|
82
|
+
onClick={() => handlePanelToggle('collaboration')}
|
|
74
83
|
className="semiont-toolbar-button"
|
|
75
84
|
data-active={activePanel === 'collaboration'}
|
|
76
85
|
data-panel="collaboration"
|
|
@@ -83,7 +92,7 @@ export function Toolbar<T extends string = string>({
|
|
|
83
92
|
|
|
84
93
|
{/* JSON-LD Icon */}
|
|
85
94
|
<button
|
|
86
|
-
onClick={() =>
|
|
95
|
+
onClick={() => handlePanelToggle('jsonld')}
|
|
87
96
|
className="semiont-toolbar-button"
|
|
88
97
|
data-active={activePanel === 'jsonld'}
|
|
89
98
|
data-panel="jsonld"
|
|
@@ -98,7 +107,7 @@ export function Toolbar<T extends string = string>({
|
|
|
98
107
|
|
|
99
108
|
{/* User Icon - always visible, appears above settings */}
|
|
100
109
|
<button
|
|
101
|
-
onClick={() =>
|
|
110
|
+
onClick={() => handlePanelToggle('user')}
|
|
102
111
|
className="semiont-toolbar-button"
|
|
103
112
|
data-active={activePanel === 'user'}
|
|
104
113
|
data-panel="user"
|
|
@@ -111,7 +120,7 @@ export function Toolbar<T extends string = string>({
|
|
|
111
120
|
|
|
112
121
|
{/* Settings Icon - always visible without scrolling */}
|
|
113
122
|
<button
|
|
114
|
-
onClick={() =>
|
|
123
|
+
onClick={() => handlePanelToggle('settings')}
|
|
115
124
|
className="semiont-toolbar-button"
|
|
116
125
|
data-active={activePanel === 'settings'}
|
|
117
126
|
data-panel="settings"
|
|
@@ -3,16 +3,16 @@ import { render, screen } from '@testing-library/react';
|
|
|
3
3
|
import { SessionTimer } from '../SessionTimer';
|
|
4
4
|
|
|
5
5
|
// Mock the hooks
|
|
6
|
-
vi.mock('
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
});
|
|
6
|
+
vi.mock('../../hooks/useSessionExpiry', () => ({
|
|
7
|
+
useSessionExpiry: vi.fn(),
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
vi.mock('../../hooks/useFormattedTime', () => ({
|
|
11
|
+
useFormattedTime: vi.fn(),
|
|
12
|
+
}));
|
|
14
13
|
|
|
15
|
-
import { useSessionExpiry
|
|
14
|
+
import { useSessionExpiry } from '../../hooks/useSessionExpiry';
|
|
15
|
+
import { useFormattedTime } from '../../hooks/useFormattedTime';
|
|
16
16
|
|
|
17
17
|
describe('SessionTimer', () => {
|
|
18
18
|
describe('Rendering', () => {
|