@semiont/react-ui 0.5.8 → 0.5.10
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/{PdfAnnotationCanvas.client-U4EVBZEV.js → PdfAnnotationCanvas.client-P37SXBWQ.js} +2 -2
- 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-WHUGODTB.js → chunk-5LFFAVDI.js} +32 -32
- package/dist/{chunk-YBGN4ACY.js → chunk-O23TLLXW.js} +1 -6
- 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.d.ts +119 -9
- package/dist/index.js +4417 -18858
- package/dist/index.js.map +1 -1
- package/dist/integrations/css-modules-helper.js +1 -1
- package/dist/integrations/styled-components-theme.js +28 -1449
- package/dist/integrations/styled-components-theme.js.map +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 +5 -5
- 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 +23 -7
- package/src/components/AnnotateReferencesProgressWidget.tsx +35 -16
- package/src/components/__tests__/AnnotateReferencesProgressWidget.test.tsx +41 -91
- 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/resource/panels/ReferencesPanel.tsx +10 -0
- package/src/components/resource/panels/ResourceInfoPanel.tsx +22 -0
- package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +26 -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/components/ResourceViewerPage.tsx +79 -0
- 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 +5 -2
- 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/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/{PdfAnnotationCanvas.client-U4EVBZEV.js.map → PdfAnnotationCanvas.client-P37SXBWQ.js.map} +0 -0
- /package/dist/{chunk-WHUGODTB.js.map → chunk-5LFFAVDI.js.map} +0 -0
- /package/dist/{chunk-YBGN4ACY.js.map → chunk-O23TLLXW.js.map} +0 -0
- /package/dist/{en-RBN2GUHF.js.map → en-P2L4X27N.js.map} +0 -0
|
@@ -7,76 +7,56 @@ import type { components } from '@semiont/core';
|
|
|
7
7
|
|
|
8
8
|
type JobProgress = components['schemas']['JobProgress'];
|
|
9
9
|
|
|
10
|
+
// The widget no longer reads translations internally — the caller supplies the
|
|
11
|
+
// copy + cancel job type. These are the annotation-flow values for these tests.
|
|
12
|
+
const tr = {
|
|
13
|
+
title: 'Annotating',
|
|
14
|
+
cancel: 'Cancel',
|
|
15
|
+
inProgress: 'In progress',
|
|
16
|
+
complete: 'Complete',
|
|
17
|
+
failed: 'Failed',
|
|
18
|
+
found: (count: number) => `Found ${count}`,
|
|
19
|
+
current: (entityType: string) => `Current ${entityType}`,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function renderWidget(progress: JobProgress | null, opts?: { returnEventBus?: boolean }) {
|
|
23
|
+
return renderWithProviders(
|
|
24
|
+
<AnnotateReferencesProgressWidget progress={progress} cancelJobType="annotation" translations={tr} />,
|
|
25
|
+
opts,
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
10
29
|
describe('AnnotateReferencesProgressWidget', () => {
|
|
11
30
|
it('returns null when progress is null', () => {
|
|
12
|
-
const { container } =
|
|
13
|
-
<AnnotateReferencesProgressWidget progress={null} />
|
|
14
|
-
);
|
|
15
|
-
|
|
31
|
+
const { container } = renderWidget(null);
|
|
16
32
|
expect(container.firstChild).toBeNull();
|
|
17
33
|
});
|
|
18
34
|
|
|
19
35
|
it('renders progress with stage message', () => {
|
|
20
|
-
const progress: JobProgress = {
|
|
21
|
-
|
|
22
|
-
percentage: 50,
|
|
23
|
-
message: 'Processing entities...',
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
renderWithProviders(
|
|
27
|
-
<AnnotateReferencesProgressWidget progress={progress} />
|
|
28
|
-
);
|
|
29
|
-
|
|
36
|
+
const progress: JobProgress = { stage: 'in-progress', percentage: 50, message: 'Processing entities...' };
|
|
37
|
+
renderWidget(progress);
|
|
30
38
|
expect(screen.getByText('Processing entities...')).toBeInTheDocument();
|
|
31
39
|
});
|
|
32
40
|
|
|
33
41
|
it('shows cancel button when not complete', () => {
|
|
34
|
-
const progress: JobProgress = {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
message: 'Working...',
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
renderWithProviders(
|
|
41
|
-
<AnnotateReferencesProgressWidget progress={progress} />
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
const cancelButton = screen.getByTitle('ReferencesPanel.cancelAnnotation');
|
|
45
|
-
expect(cancelButton).toBeInTheDocument();
|
|
42
|
+
const progress: JobProgress = { stage: 'in-progress', percentage: 30, message: 'Working...' };
|
|
43
|
+
renderWidget(progress);
|
|
44
|
+
expect(screen.getByTitle('Cancel')).toBeInTheDocument();
|
|
46
45
|
});
|
|
47
46
|
|
|
48
47
|
it('hides cancel button when complete', () => {
|
|
49
|
-
const progress: JobProgress = {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
message: '',
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
renderWithProviders(
|
|
56
|
-
<AnnotateReferencesProgressWidget progress={progress} />
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
expect(screen.queryByTitle('ReferencesPanel.cancelAnnotation')).not.toBeInTheDocument();
|
|
48
|
+
const progress: JobProgress = { stage: 'complete', percentage: 100, message: '' };
|
|
49
|
+
renderWidget(progress);
|
|
50
|
+
expect(screen.queryByTitle('Cancel')).not.toBeInTheDocument();
|
|
60
51
|
});
|
|
61
52
|
|
|
62
53
|
it('emits job:cancel-requested on cancel click', () => {
|
|
63
54
|
const handler = vi.fn();
|
|
64
|
-
const progress: JobProgress = {
|
|
65
|
-
|
|
66
|
-
percentage: 40,
|
|
67
|
-
message: 'Working...',
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
const { eventBus } = renderWithProviders(
|
|
71
|
-
<AnnotateReferencesProgressWidget progress={progress} />,
|
|
72
|
-
{ returnEventBus: true }
|
|
73
|
-
);
|
|
55
|
+
const progress: JobProgress = { stage: 'in-progress', percentage: 40, message: 'Working...' };
|
|
56
|
+
const { eventBus } = renderWidget(progress, { returnEventBus: true });
|
|
74
57
|
|
|
75
58
|
const subscription = eventBus!.get('job:cancel-requested').subscribe(handler);
|
|
76
|
-
|
|
77
|
-
const cancelButton = screen.getByTitle('ReferencesPanel.cancelAnnotation');
|
|
78
|
-
fireEvent.click(cancelButton);
|
|
79
|
-
|
|
59
|
+
fireEvent.click(screen.getByTitle('Cancel'));
|
|
80
60
|
expect(handler).toHaveBeenCalledWith({ jobType: 'annotation' });
|
|
81
61
|
|
|
82
62
|
subscription.unsubscribe();
|
|
@@ -92,60 +72,30 @@ describe('AnnotateReferencesProgressWidget', () => {
|
|
|
92
72
|
{ entityType: 'Organization', foundCount: 3 },
|
|
93
73
|
],
|
|
94
74
|
};
|
|
95
|
-
|
|
96
|
-
renderWithProviders(
|
|
97
|
-
<AnnotateReferencesProgressWidget progress={progress} />
|
|
98
|
-
);
|
|
99
|
-
|
|
75
|
+
renderWidget(progress);
|
|
100
76
|
expect(screen.getByText('Person:')).toBeInTheDocument();
|
|
101
77
|
expect(screen.getByText('Organization:')).toBeInTheDocument();
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
expect(foundLabels).toHaveLength(2);
|
|
78
|
+
expect(screen.getByText('Found 5')).toBeInTheDocument();
|
|
79
|
+
expect(screen.getByText('Found 3')).toBeInTheDocument();
|
|
105
80
|
});
|
|
106
81
|
|
|
107
82
|
it('shows complete icon for complete stage', () => {
|
|
108
|
-
const progress: JobProgress = {
|
|
109
|
-
|
|
110
|
-
percentage: 100,
|
|
111
|
-
message: '',
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
const { container } = renderWithProviders(
|
|
115
|
-
<AnnotateReferencesProgressWidget progress={progress} />
|
|
116
|
-
);
|
|
117
|
-
|
|
83
|
+
const progress: JobProgress = { stage: 'complete', percentage: 100, message: '' };
|
|
84
|
+
const { container } = renderWidget(progress);
|
|
118
85
|
expect(container.querySelector('[data-status="complete"]')).toBeInTheDocument();
|
|
119
|
-
expect(screen.getByText('
|
|
86
|
+
expect(screen.getByText('Complete')).toBeInTheDocument();
|
|
120
87
|
});
|
|
121
88
|
|
|
122
89
|
it('shows error message for error stage', () => {
|
|
123
|
-
const progress: JobProgress = {
|
|
124
|
-
|
|
125
|
-
percentage: 0,
|
|
126
|
-
message: 'Something went wrong',
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
const { container } = renderWithProviders(
|
|
130
|
-
<AnnotateReferencesProgressWidget progress={progress} />
|
|
131
|
-
);
|
|
132
|
-
|
|
90
|
+
const progress: JobProgress = { stage: 'error', percentage: 0, message: 'Something went wrong' };
|
|
91
|
+
const { container } = renderWidget(progress);
|
|
133
92
|
expect(container.querySelector('[data-status="error"]')).toBeInTheDocument();
|
|
134
93
|
expect(screen.getByText('Something went wrong')).toBeInTheDocument();
|
|
135
94
|
});
|
|
136
95
|
|
|
137
96
|
it('shows current entity type processing details', () => {
|
|
138
|
-
const progress: JobProgress = {
|
|
139
|
-
|
|
140
|
-
percentage: 50,
|
|
141
|
-
message: '',
|
|
142
|
-
currentEntityType: 'Location',
|
|
143
|
-
};
|
|
144
|
-
|
|
145
|
-
renderWithProviders(
|
|
146
|
-
<AnnotateReferencesProgressWidget progress={progress} />
|
|
147
|
-
);
|
|
148
|
-
|
|
97
|
+
const progress: JobProgress = { stage: 'in-progress', percentage: 50, message: '', currentEntityType: 'Location' };
|
|
98
|
+
renderWidget(progress);
|
|
149
99
|
expect(screen.getByText(/Processing: Location/)).toBeInTheDocument();
|
|
150
100
|
});
|
|
151
101
|
});
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, { useState } from 'react';
|
|
4
|
+
|
|
5
|
+
export interface ResourceGatherConfig {
|
|
6
|
+
depth: number;
|
|
7
|
+
maxResources: number;
|
|
8
|
+
includeContent: boolean;
|
|
9
|
+
includeSummary: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ConfigureGatherStepProps {
|
|
13
|
+
defaults?: Partial<ResourceGatherConfig>;
|
|
14
|
+
onGather: (config: ResourceGatherConfig) => void;
|
|
15
|
+
onCancel: () => void;
|
|
16
|
+
translations: {
|
|
17
|
+
intro: string;
|
|
18
|
+
includeContent: string;
|
|
19
|
+
includeSummary: string;
|
|
20
|
+
depth: string;
|
|
21
|
+
maxResources: string;
|
|
22
|
+
cancel: string;
|
|
23
|
+
gather: string;
|
|
24
|
+
};
|
|
25
|
+
/** Slot for the exclusion multi-select (GENERATE-FROM-BUTTON Phase 4). */
|
|
26
|
+
children?: React.ReactNode;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* First step of the resource-generate flow: pick the gather options before
|
|
31
|
+
* `gather.resource` runs. The `children` slot hosts the entity-type exclusion
|
|
32
|
+
* multi-select (Phase 4).
|
|
33
|
+
*/
|
|
34
|
+
export function ConfigureGatherStep({ defaults, onGather, onCancel, translations: t, children }: ConfigureGatherStepProps) {
|
|
35
|
+
const [includeContent, setIncludeContent] = useState(defaults?.includeContent ?? true);
|
|
36
|
+
const [includeSummary, setIncludeSummary] = useState(defaults?.includeSummary ?? true);
|
|
37
|
+
const [depth, setDepth] = useState(defaults?.depth ?? 2);
|
|
38
|
+
const [maxResources, setMaxResources] = useState(defaults?.maxResources ?? 10);
|
|
39
|
+
|
|
40
|
+
const handleSubmit = (e: React.FormEvent) => {
|
|
41
|
+
e.preventDefault();
|
|
42
|
+
onGather({ includeContent, includeSummary, depth, maxResources });
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<form onSubmit={handleSubmit} className="semiont-form semiont-form--scrollable">
|
|
47
|
+
<p className="semiont-form__helper-text">{t.intro}</p>
|
|
48
|
+
|
|
49
|
+
<div className="semiont-form__checkbox-field">
|
|
50
|
+
<label className="semiont-form__checkbox-label">
|
|
51
|
+
<input type="checkbox" checked={includeContent} onChange={(e) => setIncludeContent(e.target.checked)} />
|
|
52
|
+
{t.includeContent}
|
|
53
|
+
</label>
|
|
54
|
+
</div>
|
|
55
|
+
<div className="semiont-form__checkbox-field">
|
|
56
|
+
<label className="semiont-form__checkbox-label">
|
|
57
|
+
<input type="checkbox" checked={includeSummary} onChange={(e) => setIncludeSummary(e.target.checked)} />
|
|
58
|
+
{t.includeSummary}
|
|
59
|
+
</label>
|
|
60
|
+
</div>
|
|
61
|
+
|
|
62
|
+
<div className="semiont-form__inline-row">
|
|
63
|
+
<div className="semiont-form__field semiont-form__field--inline semiont-form__field--narrow">
|
|
64
|
+
<label htmlFor="gather-depth" className="semiont-form__label">{t.depth}</label>
|
|
65
|
+
<input
|
|
66
|
+
id="gather-depth"
|
|
67
|
+
type="number"
|
|
68
|
+
min="1"
|
|
69
|
+
max="5"
|
|
70
|
+
value={depth}
|
|
71
|
+
onChange={(e) => setDepth(parseInt(e.target.value))}
|
|
72
|
+
className="semiont-input"
|
|
73
|
+
/>
|
|
74
|
+
</div>
|
|
75
|
+
<div className="semiont-form__field semiont-form__field--inline semiont-form__field--narrow">
|
|
76
|
+
<label htmlFor="gather-max" className="semiont-form__label">{t.maxResources}</label>
|
|
77
|
+
<input
|
|
78
|
+
id="gather-max"
|
|
79
|
+
type="number"
|
|
80
|
+
min="1"
|
|
81
|
+
max="50"
|
|
82
|
+
value={maxResources}
|
|
83
|
+
onChange={(e) => setMaxResources(parseInt(e.target.value))}
|
|
84
|
+
className="semiont-input"
|
|
85
|
+
/>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
{/* Exclusion multi-select slot (Phase 4) */}
|
|
90
|
+
{children}
|
|
91
|
+
|
|
92
|
+
<div className="semiont-modal__actions" style={{ paddingTop: '0.5rem' }}>
|
|
93
|
+
<button type="button" onClick={onCancel} className="semiont-button--secondary semiont-button--flex">
|
|
94
|
+
✕ {t.cancel}
|
|
95
|
+
</button>
|
|
96
|
+
<button type="submit" className="semiont-button--primary semiont-button--flex">
|
|
97
|
+
✨ {t.gather}
|
|
98
|
+
</button>
|
|
99
|
+
</div>
|
|
100
|
+
</form>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import type
|
|
3
|
+
import { deriveViews, type GatheredContext } from '@semiont/core';
|
|
4
4
|
|
|
5
5
|
export interface ContextSummaryTranslations {
|
|
6
6
|
sourceContextLabel: string;
|
|
@@ -16,14 +16,23 @@ export interface ContextSummaryProps {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export function ContextSummary({ context, translations: t }: ContextSummaryProps) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const
|
|
22
|
-
|
|
19
|
+
// Graph views are focus-agnostic — only the focal resource id (and an optional
|
|
20
|
+
// focal annotation id) differ by kind.
|
|
21
|
+
const mainResourceId = context.focus.kind === 'annotation'
|
|
22
|
+
? String(context.focus.sourceResource.id)
|
|
23
|
+
: String(context.focus.resource.id);
|
|
24
|
+
const focalAnnotationId = context.focus.kind === 'annotation'
|
|
25
|
+
? context.focus.annotation.id
|
|
26
|
+
: undefined;
|
|
27
|
+
const { connections, citedBy, citedByCount } = deriveViews(
|
|
28
|
+
context.graph,
|
|
29
|
+
mainResourceId,
|
|
30
|
+
focalAnnotationId,
|
|
31
|
+
);
|
|
23
32
|
|
|
24
33
|
return (
|
|
25
34
|
<>
|
|
26
|
-
{
|
|
35
|
+
{(connections.length > 0 || citedByCount > 0) && (
|
|
27
36
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
|
|
28
37
|
|
|
29
38
|
{connections.length > 0 && (
|
|
@@ -9,11 +9,12 @@ export interface GatherContextStepProps {
|
|
|
9
9
|
context: GatheredContext | null;
|
|
10
10
|
contextLoading: boolean;
|
|
11
11
|
contextError: Error | null;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
/** Annotation-wizard controls. Omit for a display-only (e.g. resource-focus) render. */
|
|
13
|
+
userHint?: string;
|
|
14
|
+
onUserHintChange?: (value: string) => void;
|
|
15
|
+
onBind?: () => void;
|
|
16
|
+
onGenerate?: () => void;
|
|
17
|
+
onCompose?: () => void;
|
|
17
18
|
translations: {
|
|
18
19
|
title: string;
|
|
19
20
|
loadingContext: string;
|
|
@@ -29,7 +30,7 @@ export function GatherContextStep({
|
|
|
29
30
|
context,
|
|
30
31
|
contextLoading,
|
|
31
32
|
contextError,
|
|
32
|
-
userHint,
|
|
33
|
+
userHint = '',
|
|
33
34
|
onUserHintChange,
|
|
34
35
|
onBind,
|
|
35
36
|
onGenerate,
|
|
@@ -38,7 +39,8 @@ export function GatherContextStep({
|
|
|
38
39
|
}: GatherContextStepProps) {
|
|
39
40
|
const [sourceExpanded, setSourceExpanded] = useState(false);
|
|
40
41
|
const contextReady = !contextLoading && !contextError && !!context;
|
|
41
|
-
const
|
|
42
|
+
const focus = context?.focus.kind === 'annotation' ? context.focus : null;
|
|
43
|
+
const resourceFocus = context?.focus.kind === 'resource' ? context.focus : null;
|
|
42
44
|
const highlightRef = useRef<HTMLSpanElement>(null);
|
|
43
45
|
|
|
44
46
|
// Scroll the highlighted term into view when context loads
|
|
@@ -69,16 +71,16 @@ export function GatherContextStep({
|
|
|
69
71
|
|
|
70
72
|
{context && (
|
|
71
73
|
<>
|
|
72
|
-
{/* Full-width source context strip */}
|
|
73
|
-
{
|
|
74
|
+
{/* Full-width source context strip — annotation focus */}
|
|
75
|
+
{focus?.selected && (
|
|
74
76
|
<div className="semiont-gather__source-strip">
|
|
75
77
|
<label className="semiont-form__label" style={{ marginBottom: '0.375rem' }}>
|
|
76
|
-
{t.sourceContextLabel}{
|
|
78
|
+
{t.sourceContextLabel}{focus.sourceResource.name ? ` "${focus.sourceResource.name}"` : ''}
|
|
77
79
|
</label>
|
|
78
80
|
<div className={`semiont-gather__source-box${sourceExpanded ? ' semiont-gather__source-box--expanded' : ''}`}>
|
|
79
81
|
<div className="semiont-gather__source-context">
|
|
80
82
|
<div style={{ fontSize: 'var(--semiont-text-sm)', fontFamily: 'monospace', whiteSpace: 'pre-wrap', color: 'var(--semiont-text-secondary)' }}>
|
|
81
|
-
{
|
|
83
|
+
{focus.selected.before && <span>{focus.selected.before}</span>}
|
|
82
84
|
<span
|
|
83
85
|
ref={highlightRef}
|
|
84
86
|
style={{
|
|
@@ -88,19 +90,19 @@ export function GatherContextStep({
|
|
|
88
90
|
color: 'var(--semiont-color-primary-900)',
|
|
89
91
|
}}
|
|
90
92
|
>
|
|
91
|
-
{
|
|
93
|
+
{focus.selected.text}
|
|
92
94
|
</span>
|
|
93
95
|
{(context.metadata?.entityTypes ?? []).map(et => (
|
|
94
96
|
<span key={et} className="semiont-chip" style={{ fontSize: 'var(--semiont-text-xs)', padding: '0.125rem 0.375rem', fontWeight: 400, verticalAlign: 'middle', marginLeft: '0.25rem' }}>
|
|
95
97
|
{et}
|
|
96
98
|
</span>
|
|
97
99
|
))}
|
|
98
|
-
{
|
|
100
|
+
{focus.annotation.motivation && (
|
|
99
101
|
<span className="semiont-chip" style={{ fontSize: 'var(--semiont-text-xs)', padding: '0.125rem 0.375rem', fontWeight: 400, verticalAlign: 'middle', marginLeft: '0.25rem' }}>
|
|
100
|
-
{
|
|
102
|
+
{focus.annotation.motivation}
|
|
101
103
|
</span>
|
|
102
104
|
)}
|
|
103
|
-
{
|
|
105
|
+
{focus.selected.after && <span>{focus.selected.after}</span>}
|
|
104
106
|
</div>
|
|
105
107
|
</div>
|
|
106
108
|
<button
|
|
@@ -114,60 +116,103 @@ export function GatherContextStep({
|
|
|
114
116
|
</div>
|
|
115
117
|
)}
|
|
116
118
|
|
|
119
|
+
{/* Full-width source context strip — resource focus */}
|
|
120
|
+
{resourceFocus && (
|
|
121
|
+
<div className="semiont-gather__source-strip">
|
|
122
|
+
<label className="semiont-form__label" style={{ marginBottom: '0.375rem' }}>
|
|
123
|
+
{t.sourceContextLabel}{resourceFocus.resource.name ? ` "${resourceFocus.resource.name}"` : ''}
|
|
124
|
+
</label>
|
|
125
|
+
{(resourceFocus.summary || resourceFocus.content?.main) && (
|
|
126
|
+
<div className={`semiont-gather__source-box${sourceExpanded ? ' semiont-gather__source-box--expanded' : ''}`}>
|
|
127
|
+
<div className="semiont-gather__source-context">
|
|
128
|
+
<div style={{ fontSize: 'var(--semiont-text-sm)', whiteSpace: 'pre-wrap', color: 'var(--semiont-text-secondary)' }}>
|
|
129
|
+
{resourceFocus.summary ?? resourceFocus.content?.main}
|
|
130
|
+
{(context.metadata?.entityTypes ?? []).map(et => (
|
|
131
|
+
<span key={et} className="semiont-chip" style={{ fontSize: 'var(--semiont-text-xs)', padding: '0.125rem 0.375rem', fontWeight: 400, verticalAlign: 'middle', marginLeft: '0.25rem' }}>
|
|
132
|
+
{et}
|
|
133
|
+
</span>
|
|
134
|
+
))}
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
<button
|
|
138
|
+
type="button"
|
|
139
|
+
className="semiont-gather__expand-btn"
|
|
140
|
+
onClick={() => setSourceExpanded(v => !v)}
|
|
141
|
+
>
|
|
142
|
+
{sourceExpanded ? '▲ less' : '▼ more'}
|
|
143
|
+
</button>
|
|
144
|
+
</div>
|
|
145
|
+
)}
|
|
146
|
+
{resourceFocus.suggestedReferences && resourceFocus.suggestedReferences.length > 0 && (
|
|
147
|
+
<div style={{ marginTop: '0.5rem' }}>
|
|
148
|
+
{resourceFocus.suggestedReferences.map(ref => (
|
|
149
|
+
<span key={ref} className="semiont-chip" style={{ fontSize: 'var(--semiont-text-xs)', padding: '0.125rem 0.375rem', marginRight: '0.25rem' }}>
|
|
150
|
+
{ref}
|
|
151
|
+
</span>
|
|
152
|
+
))}
|
|
153
|
+
</div>
|
|
154
|
+
)}
|
|
155
|
+
</div>
|
|
156
|
+
)}
|
|
157
|
+
|
|
117
158
|
{/* Two-column body */}
|
|
118
159
|
<div className="semiont-gather__body">
|
|
119
|
-
{/* Left:
|
|
160
|
+
{/* Left: context summary (graph views) */}
|
|
120
161
|
<div className="semiont-gather__left">
|
|
121
162
|
<ContextSummary context={context} translations={t} />
|
|
122
163
|
</div>
|
|
123
164
|
|
|
124
|
-
{/* Right: hint textarea */}
|
|
125
|
-
|
|
126
|
-
<div className="semiont-
|
|
127
|
-
<
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
165
|
+
{/* Right: hint textarea (annotation focus only) */}
|
|
166
|
+
{focus && (
|
|
167
|
+
<div className="semiont-gather__right">
|
|
168
|
+
<div className="semiont-form__field">
|
|
169
|
+
<label className="semiont-form__label">
|
|
170
|
+
{t.userHintLabel}
|
|
171
|
+
</label>
|
|
172
|
+
<textarea
|
|
173
|
+
value={userHint}
|
|
174
|
+
onChange={(e) => onUserHintChange?.(e.target.value)}
|
|
175
|
+
placeholder={t.userHintPlaceholder}
|
|
176
|
+
className="semiont-search-modal__search-input semiont-gather__hint-textarea"
|
|
177
|
+
style={{ resize: 'vertical', fontFamily: 'inherit' }}
|
|
178
|
+
/>
|
|
179
|
+
</div>
|
|
137
180
|
</div>
|
|
138
|
-
|
|
181
|
+
)}
|
|
139
182
|
</div>
|
|
140
183
|
|
|
141
|
-
{/* Full-width footer: resolution strategy */}
|
|
142
|
-
|
|
143
|
-
<div className="semiont-gather__footer
|
|
144
|
-
|
|
145
|
-
<
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
184
|
+
{/* Full-width footer: resolution strategy (annotation focus only) */}
|
|
185
|
+
{focus && (
|
|
186
|
+
<div className="semiont-gather__footer">
|
|
187
|
+
<div className="semiont-gather__footer-label">{t.resolutionStrategyLabel}</div>
|
|
188
|
+
<div className="semiont-gather__actions">
|
|
189
|
+
<button
|
|
190
|
+
type="button"
|
|
191
|
+
onClick={onBind}
|
|
192
|
+
disabled={!contextReady}
|
|
193
|
+
className="semiont-button--primary semiont-button--flex"
|
|
194
|
+
>
|
|
195
|
+
🔍 {t.search}…
|
|
196
|
+
</button>
|
|
197
|
+
<button
|
|
198
|
+
type="button"
|
|
199
|
+
onClick={onGenerate}
|
|
200
|
+
disabled={!contextReady}
|
|
201
|
+
className="semiont-button--primary semiont-button--flex"
|
|
202
|
+
>
|
|
203
|
+
✨ {t.generate}…
|
|
204
|
+
</button>
|
|
205
|
+
<button
|
|
206
|
+
type="button"
|
|
207
|
+
onClick={onCompose}
|
|
208
|
+
disabled={!contextReady}
|
|
209
|
+
className="semiont-button--secondary semiont-button--flex"
|
|
210
|
+
>
|
|
211
|
+
✍️ {t.compose}
|
|
212
|
+
</button>
|
|
213
|
+
</div>
|
|
169
214
|
</div>
|
|
170
|
-
|
|
215
|
+
)}
|
|
171
216
|
</>
|
|
172
217
|
)}
|
|
173
218
|
</div>
|