@semiont/react-ui 0.5.27 → 0.5.29
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 +3 -3
- package/dist/{PdfAnnotationCanvas.client-EVUPTUWZ.js → PdfAnnotationCanvas.client-LSH3PZPY.js} +32 -5
- package/dist/{ar-OU3DQGC6.js → ar-AZBRADEB.js} +43 -23
- package/dist/{bn-PHWCR6AZ.js → bn-CCEW6AOS.js} +43 -23
- package/dist/{chunk-VXASXU4K.js → chunk-3ZBKCWUZ.js} +1 -1
- package/dist/{chunk-5VDTWRXJ.js → chunk-G4VHL4UA.js} +31 -31
- package/dist/{chunk-PCRRBU7H.js → chunk-SDKFZLEX.js} +43 -23
- package/dist/{cs-NEXYZ62I.js → cs-HXPF2CTE.js} +43 -23
- package/dist/{da-FAT43YCU.js → da-K5NTK7LH.js} +43 -23
- package/dist/{de-KRQ3WBCU.js → de-GMRQEDZI.js} +43 -23
- package/dist/{el-Q4IU3EKC.js → el-EUB4CI4G.js} +43 -23
- package/dist/{en-2KADE7N4.js → en-R5XA26NF.js} +2 -2
- package/dist/{es-2IO3R2MK.js → es-Z32DAZ6W.js} +43 -23
- package/dist/{fa-Q5GOQFXX.js → fa-G7HN6B32.js} +43 -23
- package/dist/{fi-R6NMRWBE.js → fi-3YBLZO7I.js} +43 -23
- package/dist/{fr-QY4D5E6J.js → fr-VUBH7BBS.js} +43 -23
- package/dist/{he-X6Y77XHY.js → he-WQQEY26B.js} +43 -23
- package/dist/{hi-XL7IOEQM.js → hi-HJZ6W4JB.js} +43 -23
- package/dist/{id-QZ4ZZS67.js → id-ZOGZ37MI.js} +43 -23
- package/dist/index.css +279 -74
- package/dist/index.d.ts +318 -393
- package/dist/index.js +2619 -2687
- package/dist/{it-ME2Q7OB6.js → it-DT6UW66F.js} +43 -23
- package/dist/{ja-HX7QASRI.js → ja-RUKQCOLO.js} +43 -23
- package/dist/{ko-EQO5AHAV.js → ko-ZH5ADKZM.js} +43 -23
- package/dist/{ms-UILFSF3I.js → ms-Y2AFPZTU.js} +43 -23
- package/dist/{nl-NNI45PMK.js → nl-OQEWIXNA.js} +43 -23
- package/dist/{no-MAL5F6P6.js → no-KPAYJZWY.js} +43 -23
- package/dist/{pl-4RMY6JAJ.js → pl-WSXK2UUD.js} +43 -23
- package/dist/{pt-VQH2X3QQ.js → pt-NWRJRUCK.js} +43 -23
- package/dist/{ro-XXCQO2OG.js → ro-3CGVMOC6.js} +43 -23
- package/dist/{sv-7Z6EJ6U4.js → sv-HGSEBA7S.js} +43 -23
- package/dist/test-utils.js +16 -7
- package/dist/{th-L3DXXYVQ.js → th-NZ4G352Z.js} +43 -23
- package/dist/{tr-P3DFVLFJ.js → tr-LGGS4DLW.js} +43 -23
- package/dist/{uk-7C5D3PXS.js → uk-AIX536L4.js} +43 -23
- package/dist/{vi-ZLTLEO7P.js → vi-V6CVQZLQ.js} +43 -23
- package/dist/{zh-5FHVWDIN.js → zh-AGKPGI3L.js} +43 -23
- package/package.json +16 -15
- package/src/components/AssistProgress.tsx +36 -2
- package/src/components/CodeMirrorRenderer.tsx +8 -8
- package/src/components/StatusDisplay.tsx +13 -13
- package/src/components/__tests__/AssistProgress.test.tsx +59 -0
- package/src/components/__tests__/StatusDisplay.test.tsx +9 -9
- package/src/components/modals/ComposeStep.tsx +233 -0
- package/src/components/modals/ConfigureGatherStep.tsx +10 -18
- package/src/components/modals/ConfigureGenerationStep.tsx +178 -34
- package/src/components/modals/ConfigureSearchStep.tsx +19 -0
- package/src/components/modals/ContextSummary.tsx +185 -72
- package/src/components/modals/CorpusPane.tsx +75 -0
- package/src/components/modals/DiscardPrompt.tsx +38 -0
- package/src/components/modals/GatherContextStep.tsx +237 -107
- package/src/components/modals/ReferenceWizardModal.tsx +252 -59
- package/src/components/modals/ResourceGenerateModal.tsx +225 -112
- package/src/components/modals/SearchModal.css +337 -101
- package/src/components/modals/SearchResultsStep.tsx +61 -68
- package/src/components/modals/WizardFooter.tsx +7 -1
- package/src/components/modals/__tests__/ComposeStep.test.tsx +152 -0
- package/src/components/modals/__tests__/ConfigureGatherStep.test.tsx +14 -26
- package/src/components/modals/__tests__/ConfigureGenerationStep.test.tsx +206 -8
- package/src/components/modals/__tests__/ConfigureSearchStep.test.tsx +24 -1
- package/src/components/modals/__tests__/GatherContextStep.resource.test.tsx +463 -25
- package/src/components/modals/__tests__/ReferenceWizardModal.test.tsx +442 -39
- package/src/components/modals/__tests__/ResourceGenerateModal.test.tsx +300 -86
- package/src/components/modals/__tests__/SearchResultsStep.test.tsx +61 -0
- package/src/components/modals/__tests__/WizardFooter.test.tsx +116 -9
- package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +34 -1
- package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +38 -1
- package/src/components/resource/AnnotateView.tsx +3 -3
- package/src/components/resource/BrowseView.tsx +8 -8
- package/src/components/resource/DownloadFileLink.tsx +1 -1
- package/src/components/resource/ResourceViewer.tsx +4 -4
- package/src/components/resource/__tests__/AnnotateView.embeddable.test.tsx +1 -1
- package/src/components/resource/__tests__/BrowseView.embeddable.test.tsx +1 -1
- package/src/components/resource/__tests__/BrowseView.reference-hover.test.tsx +1 -1
- package/src/components/resource/__tests__/BrowseView.test.tsx +11 -11
- package/src/components/resource/__tests__/download-link.test.tsx +6 -6
- package/src/components/resource/annotate-renderers.tsx +3 -3
- package/src/components/resource/panels/AssistSection.css +35 -0
- package/src/components/resource/panels/ReferenceEntry.tsx +11 -4
- package/src/components/resource/panels/ReferencesPanel.tsx +9 -1
- package/src/components/resource/panels/ResourceInfoPanel.tsx +89 -20
- package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +7 -2
- package/src/components/resource/panels/__tests__/ReferenceEntry.test.tsx +41 -0
- package/src/components/resource/panels/__tests__/ReferencesPanel.observable-flow.test.tsx +1 -1
- package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +84 -3
- package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +149 -7
- package/src/features/auth/__tests__/SignInForm.a11y.test.tsx +12 -12
- package/src/features/auth/components/SignInForm.tsx +53 -53
- package/src/features/auth-welcome/state/__tests__/welcome-state-unit.test.ts +2 -2
- package/src/features/resource-compose/__tests__/ResourceComposePage.test.tsx +0 -65
- package/src/features/resource-compose/components/ResourceComposePage.tsx +35 -133
- package/src/features/resource-compose/state/__tests__/compose-page-state-unit.test.ts +1 -100
- package/src/features/resource-compose/state/compose-page-state-unit.ts +5 -57
- package/src/features/resource-discovery/__tests__/ResourceDiscoveryPage.test.tsx +1 -1
- package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +2 -2
- package/src/features/resource-discovery/state/discover-state-unit.ts +1 -1
- package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +73 -17
- package/src/features/resource-viewer/__tests__/generation-options.test.ts +53 -0
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +148 -83
- package/src/features/resource-viewer/generation-options.ts +29 -0
- package/src/styles/core/forms.css +35 -8
- package/src/styles/features/resource-viewer.css +0 -22
- package/src/styles/features/resource.css +0 -4
- package/src/styles/index.css +0 -1
- package/translations/ar.json +42 -22
- package/translations/bn.json +42 -22
- package/translations/cs.json +42 -22
- package/translations/da.json +42 -22
- package/translations/de.json +42 -22
- package/translations/el.json +42 -22
- package/translations/en.json +42 -22
- package/translations/es.json +42 -22
- package/translations/fa.json +42 -22
- package/translations/fi.json +42 -22
- package/translations/fr.json +42 -22
- package/translations/he.json +42 -22
- package/translations/hi.json +42 -22
- package/translations/id.json +42 -22
- package/translations/it.json +42 -22
- package/translations/ja.json +42 -22
- package/translations/ko.json +42 -22
- package/translations/ms.json +42 -22
- package/translations/nl.json +42 -22
- package/translations/no.json +42 -22
- package/translations/pl.json +42 -22
- package/translations/pt.json +42 -22
- package/translations/ro.json +42 -22
- package/translations/sv.json +42 -22
- package/translations/th.json +42 -22
- package/translations/tr.json +42 -22
- package/translations/uk.json +42 -22
- package/translations/vi.json +42 -22
- package/translations/zh.json +42 -22
- package/dist/PdfAnnotationCanvas.client-EVUPTUWZ.js.map +0 -1
- package/dist/PdfAnnotationCanvas.client-TGDPPGXJ.css.map +0 -1
- package/dist/ar-OU3DQGC6.js.map +0 -1
- package/dist/bn-PHWCR6AZ.js.map +0 -1
- package/dist/chunk-5VDTWRXJ.js.map +0 -1
- package/dist/chunk-BTUG3VJJ.js.map +0 -1
- package/dist/chunk-O23TLLXW.js.map +0 -1
- package/dist/chunk-PCRRBU7H.js.map +0 -1
- package/dist/chunk-QT7LYM72.js.map +0 -1
- package/dist/chunk-VXASXU4K.js.map +0 -1
- package/dist/cs-NEXYZ62I.js.map +0 -1
- package/dist/da-FAT43YCU.js.map +0 -1
- package/dist/de-KRQ3WBCU.js.map +0 -1
- package/dist/el-Q4IU3EKC.js.map +0 -1
- package/dist/en-2KADE7N4.js.map +0 -1
- package/dist/es-2IO3R2MK.js.map +0 -1
- package/dist/fa-Q5GOQFXX.js.map +0 -1
- package/dist/fi-R6NMRWBE.js.map +0 -1
- package/dist/fr-QY4D5E6J.js.map +0 -1
- package/dist/he-X6Y77XHY.js.map +0 -1
- package/dist/hi-XL7IOEQM.js.map +0 -1
- package/dist/id-QZ4ZZS67.js.map +0 -1
- package/dist/index.css.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/integrations/css-modules-helper.js.map +0 -1
- package/dist/integrations/styled-components-theme.js.map +0 -1
- package/dist/it-ME2Q7OB6.js.map +0 -1
- package/dist/ja-HX7QASRI.js.map +0 -1
- package/dist/ko-EQO5AHAV.js.map +0 -1
- package/dist/ms-UILFSF3I.js.map +0 -1
- package/dist/nl-NNI45PMK.js.map +0 -1
- package/dist/no-MAL5F6P6.js.map +0 -1
- package/dist/pl-4RMY6JAJ.js.map +0 -1
- package/dist/pt-VQH2X3QQ.js.map +0 -1
- package/dist/ro-XXCQO2OG.js.map +0 -1
- package/dist/sv-7Z6EJ6U4.js.map +0 -1
- package/dist/test-utils.css.map +0 -1
- package/dist/test-utils.js.map +0 -1
- package/dist/th-L3DXXYVQ.js.map +0 -1
- package/dist/tr-P3DFVLFJ.js.map +0 -1
- package/dist/uk-7C5D3PXS.js.map +0 -1
- package/dist/vi-ZLTLEO7P.js.map +0 -1
- package/dist/zh-5FHVWDIN.js.map +0 -1
- package/src/components/modals/ResourceSearchModal.tsx +0 -224
- package/src/components/modals/__tests__/ResourceSearchModal.test.tsx +0 -163
- package/src/features/admin-exchange/__tests__/AdminExchangePage.test.tsx +0 -137
- package/src/features/admin-exchange/__tests__/ExportCard.test.tsx +0 -41
- package/src/features/admin-exchange/__tests__/ImportCard.test.tsx +0 -148
- package/src/features/admin-exchange/__tests__/ImportProgress.test.tsx +0 -117
- package/src/features/admin-exchange/components/AdminExchangePage.tsx +0 -117
- package/src/features/admin-exchange/components/ExportCard.tsx +0 -35
- package/src/features/admin-exchange/components/ImportCard.tsx +0 -183
- package/src/features/admin-exchange/components/ImportProgress.tsx +0 -80
- package/src/features/admin-exchange/state/__tests__/exchange-state-unit.test.ts +0 -185
- package/src/features/admin-exchange/state/exchange-state-unit.ts +0 -133
- package/src/features/moderation-linked-data/__tests__/LinkedDataPage.test.tsx +0 -118
- package/src/features/moderation-linked-data/components/LinkedDataPage.tsx +0 -118
- package/src/styles/features/exchange.css +0 -374
|
@@ -14,7 +14,8 @@ import { ToastProvider } from '../../../components/Toast';
|
|
|
14
14
|
import { ThemeProvider } from '../../../contexts/ThemeContext';
|
|
15
15
|
import { LineNumbersProvider } from '../../../contexts/LineNumbersContext';
|
|
16
16
|
import { createTestSemiontWrapper } from '../../../test-utils';
|
|
17
|
-
import type { ResourceId } from '@semiont/core';
|
|
17
|
+
import type { BodyOperation, EventMap, ResourceId, UserId } from '@semiont/core';
|
|
18
|
+
import { annotationId as makeAnnotationId, resourceId as makeResourceId } from '@semiont/core';
|
|
18
19
|
|
|
19
20
|
// jsdom doesn't implement window.matchMedia — mock it for useTheme
|
|
20
21
|
Object.defineProperty(window, 'matchMedia', {
|
|
@@ -46,8 +47,8 @@ const { stubBrowser, stubClient } = vi.hoisted(() => {
|
|
|
46
47
|
const { SemiontClient, HttpTransport, HttpContentTransport } = require('@semiont/sdk');
|
|
47
48
|
const { baseUrl } = require('@semiont/core');
|
|
48
49
|
const transport = new HttpTransport({ baseUrl: baseUrl('http://localhost:4000') });
|
|
49
|
-
// HttpTransport implements both ITransport and
|
|
50
|
-
// as
|
|
50
|
+
// HttpTransport implements both ITransport and IGatewayOperations; pass it
|
|
51
|
+
// as gateway so `client.auth` (used by useMediaToken) is wired.
|
|
51
52
|
const client = new SemiontClient(transport, new HttpContentTransport(transport), transport);
|
|
52
53
|
const stubActiveSession$ = new BehaviorSubject({ client });
|
|
53
54
|
const stubOpenResources$ = new BehaviorSubject([]);
|
|
@@ -102,24 +103,22 @@ useDebouncedCallback: (fn: any) => fn,
|
|
|
102
103
|
announceResourceLoaded: vi.fn(),
|
|
103
104
|
}),
|
|
104
105
|
useEventSubscriptions: vi.fn(),
|
|
105
|
-
useResourceAnnotations: () =>
|
|
106
|
-
clearNewAnnotationId: vi.fn(),
|
|
107
|
-
newAnnotationIds: new Set(),
|
|
108
|
-
markAnnotation: vi.fn(),
|
|
109
|
-
deleteAnnotation: vi.fn(),
|
|
110
|
-
triggerSparkleAnimation: vi.fn(),
|
|
111
|
-
}),
|
|
106
|
+
useResourceAnnotations: () => sparkleContext,
|
|
112
107
|
};
|
|
113
108
|
});
|
|
114
109
|
|
|
110
|
+
// Shared, assertable spies — the page's sparkle wiring is pinned against
|
|
111
|
+
// these (a per-call `vi.fn()` object would be unobservable from the tests).
|
|
112
|
+
const sparkleContext = vi.hoisted(() => ({
|
|
113
|
+
clearSparkle: vi.fn(),
|
|
114
|
+
sparkleAnnotationIds: new Set<string>(),
|
|
115
|
+
markAnnotation: vi.fn(),
|
|
116
|
+
deleteAnnotation: vi.fn(),
|
|
117
|
+
triggerSparkleAnimation: vi.fn(),
|
|
118
|
+
}));
|
|
119
|
+
|
|
115
120
|
vi.mock('../../../contexts/ResourceAnnotationsContext', () => ({
|
|
116
|
-
useResourceAnnotations: () =>
|
|
117
|
-
clearNewAnnotationId: vi.fn(),
|
|
118
|
-
newAnnotationIds: new Set(),
|
|
119
|
-
markAnnotation: vi.fn(),
|
|
120
|
-
deleteAnnotation: vi.fn(),
|
|
121
|
-
triggerSparkleAnimation: vi.fn(),
|
|
122
|
-
}),
|
|
121
|
+
useResourceAnnotations: () => sparkleContext,
|
|
123
122
|
ResourceAnnotationsProvider: ({ children }: any) => children,
|
|
124
123
|
}));
|
|
125
124
|
|
|
@@ -206,6 +205,63 @@ describe('ResourceViewerPage — outcome toasts reach the user', () => {
|
|
|
206
205
|
});
|
|
207
206
|
});
|
|
208
207
|
|
|
208
|
+
// RESOLUTION-SPARKLE D2: a reference resolving — from ANY strategy, local or
|
|
209
|
+
// remote — arrives as mark:body-updated with a linking-add operation, and the
|
|
210
|
+
// page turns exactly that into a sparkle. The harness mocks
|
|
211
|
+
// useEventSubscriptions, so the pin drives the captured handler directly.
|
|
212
|
+
describe('ResourceViewerPage — resolution sparkles', () => {
|
|
213
|
+
const bodyUpdated = (operations: BodyOperation[]): EventMap['mark:body-updated'] => ({
|
|
214
|
+
id: 'evt-1',
|
|
215
|
+
timestamp: '2026-08-21T12:00:00Z',
|
|
216
|
+
resourceId: makeResourceId('test-123'),
|
|
217
|
+
userId: 'did:web:example.com:users:alice' as UserId,
|
|
218
|
+
version: 1,
|
|
219
|
+
type: 'mark:body-updated',
|
|
220
|
+
payload: { annotationId: makeAnnotationId('ann-7'), operations },
|
|
221
|
+
metadata: { sequenceNumber: 1 },
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
const renderAndGetHandler = () => {
|
|
225
|
+
mockUseEventSubscriptions.mockClear();
|
|
226
|
+
sparkleContext.triggerSparkleAnimation.mockClear();
|
|
227
|
+
renderWithProviders(<ResourceViewerPage {...createMockProps()} />);
|
|
228
|
+
const map = mockUseEventSubscriptions.mock.calls.at(-1)?.[0] as
|
|
229
|
+
Record<string, (event: EventMap['mark:body-updated']) => void>;
|
|
230
|
+
return map['mark:body-updated'];
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
it('an operation adding a linking SpecificResource sparkles the annotation', () => {
|
|
234
|
+
const handler = renderAndGetHandler();
|
|
235
|
+
expect(handler).toBeDefined();
|
|
236
|
+
|
|
237
|
+
handler!(bodyUpdated([
|
|
238
|
+
{ op: 'add', item: { type: 'SpecificResource', source: 'res-target', purpose: 'linking' } },
|
|
239
|
+
]));
|
|
240
|
+
|
|
241
|
+
expect(sparkleContext.triggerSparkleAnimation).toHaveBeenCalledWith('ann-7');
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('unlink (remove-only operations) stays dark — A2', () => {
|
|
245
|
+
const handler = renderAndGetHandler();
|
|
246
|
+
|
|
247
|
+
handler?.(bodyUpdated([
|
|
248
|
+
{ op: 'remove', item: { type: 'SpecificResource', source: 'res-target', purpose: 'linking' } },
|
|
249
|
+
]));
|
|
250
|
+
|
|
251
|
+
expect(sparkleContext.triggerSparkleAnimation).not.toHaveBeenCalled();
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('a TextualBody add stays dark', () => {
|
|
255
|
+
const handler = renderAndGetHandler();
|
|
256
|
+
|
|
257
|
+
handler?.(bodyUpdated([
|
|
258
|
+
{ op: 'add', item: { type: 'TextualBody', value: 'Person', purpose: 'tagging' } },
|
|
259
|
+
]));
|
|
260
|
+
|
|
261
|
+
expect(sparkleContext.triggerSparkleAnimation).not.toHaveBeenCalled();
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
|
|
209
265
|
describe('ResourceViewerPage', () => {
|
|
210
266
|
beforeEach(() => {
|
|
211
267
|
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GENERATION-OUTPUT-FORMAT D8 — the submitted config becomes generation
|
|
3
|
+
* options in ONE place, by spread.
|
|
4
|
+
*
|
|
5
|
+
* Both page handlers used to build the options object field-by-field, so any
|
|
6
|
+
* field not explicitly listed was silently dropped. That is precisely what
|
|
7
|
+
* stranded `outputMediaType` in the SDK one layer down (P1), relocated one
|
|
8
|
+
* layer up. These pins state the PROPERTY — everything the config carries is
|
|
9
|
+
* forwarded — rather than enumerating today's fields, so a knob added later
|
|
10
|
+
* cannot regress the mode back into existence.
|
|
11
|
+
*/
|
|
12
|
+
import { describe, it, expect } from 'vitest';
|
|
13
|
+
import type { GatheredContext } from '@semiont/core';
|
|
14
|
+
import { toGenerationOptions } from '../generation-options';
|
|
15
|
+
import type { GenerationConfig } from '../../../components/modals/ConfigureGenerationStep';
|
|
16
|
+
|
|
17
|
+
const context = { focus: { kind: 'resource' } } as unknown as GatheredContext;
|
|
18
|
+
|
|
19
|
+
const config: GenerationConfig = {
|
|
20
|
+
title: 'Cedar County',
|
|
21
|
+
storageUri: 'file://research/notes.pdf',
|
|
22
|
+
prompt: 'lead with the treaty',
|
|
23
|
+
language: 'en',
|
|
24
|
+
temperature: 0.7,
|
|
25
|
+
maxTokens: 500,
|
|
26
|
+
outputMediaType: 'application/pdf',
|
|
27
|
+
context,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
describe('toGenerationOptions', () => {
|
|
31
|
+
it('forwards EVERY field the config carries except the context', () => {
|
|
32
|
+
// Stated structurally on purpose: the expectation is derived from the
|
|
33
|
+
// same object, so adding a field to GenerationConfig extends this pin
|
|
34
|
+
// automatically. A field-by-field assertion would have passed happily
|
|
35
|
+
// while the real handler dropped the new one.
|
|
36
|
+
const { context: _positional, ...forwarded } = config;
|
|
37
|
+
expect(toGenerationOptions(config, 'fr')).toMatchObject(forwarded);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('does not pass the context as an option — it is the positional argument', () => {
|
|
41
|
+
expect(toGenerationOptions(config, 'fr')).not.toHaveProperty('context');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('injects the sourceLanguage, which comes from the viewed resource and not the form', () => {
|
|
45
|
+
expect(toGenerationOptions(config, 'fr').sourceLanguage).toBe('fr');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('omits sourceLanguage entirely when the resource has no language', () => {
|
|
49
|
+
// Absence must stay absence: a manufactured '' would tell the prompt the
|
|
50
|
+
// source is in a language named empty string.
|
|
51
|
+
expect(toGenerationOptions(config, undefined)).not.toHaveProperty('sourceLanguage');
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -6,12 +6,12 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import React, { useState, useEffect, useCallback } from 'react';
|
|
9
|
-
import { assistProgressTranslations } from '../../../lib/assist-progress-copy';
|
|
10
9
|
import { useResourceViewedReport } from '../hooks/useResourceViewedReport';
|
|
11
|
-
import type { components, ResourceDescriptor, ResourceId,
|
|
10
|
+
import type { components, ResourceDescriptor, ResourceId, EventMap } from '@semiont/core';
|
|
12
11
|
import type { ConnectionState } from '@semiont/core';
|
|
13
|
-
import { annotationId } from '@semiont/core';
|
|
14
|
-
import {
|
|
12
|
+
import { annotationId, folderOf } from '@semiont/core';
|
|
13
|
+
import type { ComposeParams } from '../../../components/modals/ComposeStep';
|
|
14
|
+
import { getLanguage, getPrimaryRepresentation, getPrimaryMediaType, getStorageUri, capabilitiesOf, extensionForMediaType } from '@semiont/core';
|
|
15
15
|
import { ANNOTATORS } from '@semiont/react-ui';
|
|
16
16
|
import { ErrorBoundary } from '@semiont/react-ui';
|
|
17
17
|
import { AnnotationHistory } from '@semiont/react-ui';
|
|
@@ -30,6 +30,7 @@ import { useCollaborators } from '../../../hooks/useCollaborators';
|
|
|
30
30
|
import { mediaUrl } from '../../../lib/media-url';
|
|
31
31
|
import { useToast } from '../../../components/Toast';
|
|
32
32
|
import { useOutcomeToasts } from '../../../hooks/useOutcomeToasts';
|
|
33
|
+
import { useGenerationArrival } from '../../../hooks/useGenerationArrival';
|
|
33
34
|
import { useTheme } from '../../../contexts/ThemeContext';
|
|
34
35
|
import { useLineNumbers } from '../../../contexts/LineNumbersContext';
|
|
35
36
|
import { useHoverDelay } from '../../../hooks/useHoverDelay';
|
|
@@ -45,8 +46,8 @@ import { useShellStateUnit } from '../../../hooks/useShellStateUnit';
|
|
|
45
46
|
import { useTranslations } from '../../../contexts/TranslationContext';
|
|
46
47
|
import { ReferenceWizardModal } from '../../../components/modals/ReferenceWizardModal';
|
|
47
48
|
import { ResourceGenerateModal } from '../../../components/modals/ResourceGenerateModal';
|
|
48
|
-
import { AssistProgress } from '../../../components/AssistProgress';
|
|
49
49
|
import type { GenerationConfig } from '../../../components/modals/ConfigureGenerationStep';
|
|
50
|
+
import { toGenerationOptions } from '../generation-options';
|
|
50
51
|
|
|
51
52
|
type SemiontResource = ResourceDescriptor;
|
|
52
53
|
|
|
@@ -120,6 +121,7 @@ export interface ResourceViewerPageProps {
|
|
|
120
121
|
* @subscribes yield:clone - Clone the current resource
|
|
121
122
|
* @subscribes beckon:sparkle - Trigger sparkle animation
|
|
122
123
|
* @subscribes mark:added - Annotation was created (sparkle)
|
|
124
|
+
* @subscribes mark:body-updated - Reference resolved via a linking-add operation (sparkle, both loci)
|
|
123
125
|
* @subscribes browse:resource-open - Open a resource in the viewer (local links + the launcher's tour verbs)
|
|
124
126
|
* @subscribes browse:entity-type-clicked - Navigate filtered by entity type
|
|
125
127
|
*
|
|
@@ -143,7 +145,6 @@ export function ResourceViewerPage({
|
|
|
143
145
|
// Translations
|
|
144
146
|
const tw = useTranslations('ReferenceWizard');
|
|
145
147
|
const tg = useTranslations('ResourceGenerate');
|
|
146
|
-
const ta = useTranslations('AssistProgress');
|
|
147
148
|
|
|
148
149
|
const browser = useSemiont();
|
|
149
150
|
const session = useObservable(browser.activeSession$);
|
|
@@ -177,7 +178,7 @@ export function ResourceViewerPage({
|
|
|
177
178
|
const { theme } = useTheme();
|
|
178
179
|
const { showLineNumbers } = useLineNumbers();
|
|
179
180
|
const { hoverDelayMs } = useHoverDelay();
|
|
180
|
-
const { triggerSparkleAnimation,
|
|
181
|
+
const { triggerSparkleAnimation, clearSparkle, sparkleAnnotationIds } = useResourceAnnotations();
|
|
181
182
|
|
|
182
183
|
// Render mode chooses the content path: 'text' decodes inline; 'image'
|
|
183
184
|
// and 'pdf' go through the media-token (binary) path. 'none'/registry-miss
|
|
@@ -240,9 +241,27 @@ export function ResourceViewerPage({
|
|
|
240
241
|
const onScrollCompleted = stateUnit?.browse.onScrollCompleted;
|
|
241
242
|
const generationProgress = useObservable(stateUnit?.yield.progress$) ?? null;
|
|
242
243
|
const isGenerating = useObservable(stateUnit?.yield.isGenerating$) ?? false;
|
|
244
|
+
const generationOutcome = useObservable(stateUnit?.yield.outcome$) ?? null;
|
|
245
|
+
|
|
246
|
+
// GENERATION-ARRIVAL P2: a completion witnessed on this page reveals the
|
|
247
|
+
// derivation edge the worker minted — the annotations panel opens on
|
|
248
|
+
// References, scrolls to the provenance reference, and its sparkle is
|
|
249
|
+
// re-armed (the mark:added glow burned its window unseen). Never navigates
|
|
250
|
+
// (A2); a held outcome on remount stays quiet (D6, inside the hook).
|
|
251
|
+
const handleGenerationArrival = useCallback((annId: string) => {
|
|
252
|
+
browser.emit('panel:open', { panel: 'annotations', scrollToAnnotationId: annId, motivation: 'linking' });
|
|
253
|
+
triggerSparkleAnimation(annId);
|
|
254
|
+
}, [browser, triggerSparkleAnimation]);
|
|
255
|
+
useGenerationArrival(generationOutcome, annotations, handleGenerationArrival);
|
|
256
|
+
|
|
243
257
|
const gatherContext = useObservable(stateUnit?.gather.context$) ?? null;
|
|
244
258
|
const gatherLoading = useObservable(stateUnit?.gather.loading$) ?? false;
|
|
245
259
|
const gatherError = useObservable(stateUnit?.gather.error$) ?? null;
|
|
260
|
+
// Resource-gather slots (FLOW-LIFECYCLE-CONVERGENCE D2a: separate from the
|
|
261
|
+
// annotation slots above — the two gathers can be live at once).
|
|
262
|
+
const resourceGatherContext = useObservable(stateUnit?.gather.resourceContext$) ?? null;
|
|
263
|
+
const resourceGatherLoading = useObservable(stateUnit?.gather.resourceLoading$) ?? false;
|
|
264
|
+
const resourceGatherError = useObservable(stateUnit?.gather.resourceError$) ?? null;
|
|
246
265
|
const wizardState = useObservable(stateUnit?.wizard$);
|
|
247
266
|
const wizardOpen = wizardState?.open ?? false;
|
|
248
267
|
const wizardAnnotationId = wizardState?.annotationId ?? null;
|
|
@@ -256,35 +275,19 @@ export function ResourceViewerPage({
|
|
|
256
275
|
}, [stateUnit]);
|
|
257
276
|
|
|
258
277
|
const handleWizardGenerateSubmit = useCallback((referenceId: string, config: GenerationConfig) => {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
// The source resource is the one the user is viewing — fed into the
|
|
266
|
-
// prompt so the LLM understands the embedded context (selected
|
|
267
|
-
// passage, surrounding text) regardless of UI/target language.
|
|
268
|
-
sourceLanguage: getLanguage(resource),
|
|
269
|
-
temperature: config.temperature,
|
|
270
|
-
maxTokens: config.maxTokens,
|
|
271
|
-
});
|
|
272
|
-
}, [stateUnit, clearNewAnnotationId, resource]);
|
|
278
|
+
clearSparkle(annotationId(referenceId));
|
|
279
|
+
// D8: forwarded by spread in ONE place, so a knob added to the form is
|
|
280
|
+
// never dropped on the way to the wire. `sourceLanguage` is the viewed
|
|
281
|
+
// resource's language — a page fact the form cannot know.
|
|
282
|
+
stateUnit?.yield.generate(config.context, toGenerationOptions(config, getLanguage(resource)));
|
|
283
|
+
}, [stateUnit, clearSparkle, resource]);
|
|
273
284
|
|
|
274
285
|
// Resource-generate flow (GENERATE-FROM-BUTTON): drive the SAME yield progress$
|
|
275
286
|
// the annotation path uses so the full `AssistProgress` widget shows — NOT a
|
|
276
287
|
// toast. Both paths are one `generate(context, options)` now: the context's
|
|
277
288
|
// focus.kind (resource here, annotation above) decides the shape.
|
|
278
289
|
const handleResourceGenerateSubmit = useCallback((_resourceId: string, config: GenerationConfig) => {
|
|
279
|
-
stateUnit?.yield.generate(config.context,
|
|
280
|
-
title: config.title,
|
|
281
|
-
storageUri: config.storagePath,
|
|
282
|
-
...(config.prompt ? { prompt: config.prompt } : {}),
|
|
283
|
-
language: config.language,
|
|
284
|
-
sourceLanguage: getLanguage(resource),
|
|
285
|
-
temperature: config.temperature,
|
|
286
|
-
maxTokens: config.maxTokens,
|
|
287
|
-
});
|
|
290
|
+
stateUnit?.yield.generate(config.context, toGenerationOptions(config, getLanguage(resource)));
|
|
288
291
|
}, [stateUnit, resource]);
|
|
289
292
|
|
|
290
293
|
const handleWizardLinkResource = useCallback(async (referenceId: string, targetResourceId: string) => {
|
|
@@ -301,26 +304,43 @@ export function ResourceViewerPage({
|
|
|
301
304
|
}
|
|
302
305
|
}, [rUri, semiont, showSuccess, showError]);
|
|
303
306
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
307
|
+
// COMPOSE-IN-MODAL P3: create-and-link, in place. The old flow stashed the
|
|
308
|
+
// context in sessionStorage and navigated to the compose page; the modal
|
|
309
|
+
// already holds the context, so the side-channel dies with the mode.
|
|
310
|
+
// Text-only by design — uploads stay on the standalone compose page.
|
|
311
|
+
const handleWizardComposeSubmit = useCallback(async (referenceId: string, params: ComposeParams) => {
|
|
312
|
+
if (!semiont) throw new Error('No active session');
|
|
313
|
+
try {
|
|
314
|
+
const format = 'text/markdown';
|
|
315
|
+
const file = new File(
|
|
316
|
+
[new Blob([params.content], { type: format })],
|
|
317
|
+
params.name + extensionForMediaType(format),
|
|
318
|
+
{ type: format },
|
|
319
|
+
);
|
|
320
|
+
const newResourceId = await new Promise<ResourceId>((resolve, reject) => {
|
|
321
|
+
semiont.yield.resource({
|
|
322
|
+
name: params.name,
|
|
323
|
+
file,
|
|
324
|
+
format,
|
|
325
|
+
entityTypes: params.entityTypes,
|
|
326
|
+
language: params.language,
|
|
327
|
+
storageUri: params.storagePath,
|
|
328
|
+
}).subscribe({
|
|
329
|
+
next: (event) => { if (event.phase === 'finished') resolve(event.resourceId); },
|
|
330
|
+
error: reject,
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
await semiont.bind.body(
|
|
334
|
+
rUri,
|
|
335
|
+
annotationId(referenceId),
|
|
336
|
+
[{ op: 'add', item: { type: 'SpecificResource' as const, source: newResourceId, purpose: 'linking' as const } }],
|
|
337
|
+
);
|
|
338
|
+
showSuccess('Reference successfully linked to the new resource');
|
|
339
|
+
} catch (error) {
|
|
340
|
+
showError(`Failed to compose resource: ${error instanceof Error ? error.message : String(error)}`);
|
|
341
|
+
throw error; // ComposeStep re-enables its footer on rejection
|
|
342
|
+
}
|
|
343
|
+
}, [semiont, rUri, showSuccess, showError]);
|
|
324
344
|
|
|
325
345
|
// Add resource to open tabs when it loads, and record it as this KB's
|
|
326
346
|
// last-viewed for the /know landing route to resume from. Both are per-KB
|
|
@@ -329,7 +349,7 @@ export function ResourceViewerPage({
|
|
|
329
349
|
useEffect(() => {
|
|
330
350
|
if (resource && rUri) {
|
|
331
351
|
const mediaType = getPrimaryMediaType(resource);
|
|
332
|
-
browser.addOpenResource(rUri, resource.name, mediaType || undefined, resource
|
|
352
|
+
browser.addOpenResource(rUri, resource.name, mediaType || undefined, getStorageUri(resource));
|
|
333
353
|
browser.setLastViewedResource(rUri);
|
|
334
354
|
}
|
|
335
355
|
}, [resource, rUri, browser]);
|
|
@@ -392,6 +412,19 @@ export function ResourceViewerPage({
|
|
|
392
412
|
triggerSparkleAnimation(stored.payload.annotation.id);
|
|
393
413
|
}, [triggerSparkleAnimation]);
|
|
394
414
|
|
|
415
|
+
// RESOLUTION-SPARKLE D2: a reference resolving — Compose, Search → Link, a
|
|
416
|
+
// Generate job landing, or a remote collaborator — is a body update whose
|
|
417
|
+
// operations add a linking SpecificResource. Exactly that sparkles; an unlink
|
|
418
|
+
// (remove) or an entity-tag body change stays dark.
|
|
419
|
+
const handleAnnotationBodyUpdated = useCallback((stored: EventMap['mark:body-updated']) => {
|
|
420
|
+
const resolves = stored.payload.operations.some(
|
|
421
|
+
(op) => op.op === 'add' && op.item.type === 'SpecificResource' && op.item.purpose === 'linking',
|
|
422
|
+
);
|
|
423
|
+
if (resolves) {
|
|
424
|
+
triggerSparkleAnimation(stored.payload.annotationId);
|
|
425
|
+
}
|
|
426
|
+
}, [triggerSparkleAnimation]);
|
|
427
|
+
|
|
395
428
|
const handleResourceOpen = useCallback(({ resourceId }: { resourceId: string }) => {
|
|
396
429
|
if (routes.resourceDetail) {
|
|
397
430
|
const path = routes.resourceDetail(resourceId);
|
|
@@ -421,6 +454,7 @@ export function ResourceViewerPage({
|
|
|
421
454
|
'yield:clone': handleResourceClone,
|
|
422
455
|
'beckon:sparkle': handleAnnotationSparkle,
|
|
423
456
|
'mark:added': handleAnnotationAdded,
|
|
457
|
+
'mark:body-updated': handleAnnotationBodyUpdated,
|
|
424
458
|
'browse:resource-open': handleResourceOpen,
|
|
425
459
|
'browse:entity-type-clicked': handleEntityTypeClicked,
|
|
426
460
|
});
|
|
@@ -490,21 +524,6 @@ export function ResourceViewerPage({
|
|
|
490
524
|
</h2>
|
|
491
525
|
</div>
|
|
492
526
|
</div>
|
|
493
|
-
{/* Resource-generation progress (GENERATE-FROM-BUTTON P7) — no annotationId ⇒ a resource-gen job */}
|
|
494
|
-
{generationProgress && !generationProgress.annotationId && (
|
|
495
|
-
<AssistProgress
|
|
496
|
-
progress={generationProgress}
|
|
497
|
-
dataType="generation"
|
|
498
|
-
// The ending is the owner's fact and every owner must state it
|
|
499
|
-
// (CLEAN-PROGRESS D1). This call site used to pass neither
|
|
500
|
-
// `ended` nor `onDismiss`, so a finished generation stayed in its
|
|
501
|
-
// running form and then vanished on a timer.
|
|
502
|
-
ended={!isGenerating}
|
|
503
|
-
onCancel={() => session?.client.job.cancelRequest('generation')}
|
|
504
|
-
onDismiss={() => stateUnit?.yield.dismissProgress()}
|
|
505
|
-
translations={assistProgressTranslations(ta)}
|
|
506
|
-
/>
|
|
507
|
-
)}
|
|
508
527
|
{/* Scrollable body wrapper - contains document content, header is sibling above */}
|
|
509
528
|
<div className="semiont-document-viewer__scrollable-body" lang={getLanguage(resource) || undefined}>
|
|
510
529
|
<ErrorBoundary
|
|
@@ -544,7 +563,7 @@ export function ResourceViewerPage({
|
|
|
544
563
|
onSelectionMotivationChange={toolbarPrefs.setSelectionMotivation}
|
|
545
564
|
shape={toolbarPrefs.shape}
|
|
546
565
|
onShapeChange={toolbarPrefs.setShape}
|
|
547
|
-
|
|
566
|
+
sparkleAnnotationIds={sparkleAnnotationIds}
|
|
548
567
|
generatingReferenceId={generationProgress?.annotationId ?? null}
|
|
549
568
|
showLineNumbers={showLineNumbers}
|
|
550
569
|
hoverDelayMs={hoverDelayMs}
|
|
@@ -594,6 +613,7 @@ export function ResourceViewerPage({
|
|
|
594
613
|
onRetryAnnotations={stateUnit?.annotations.retry}
|
|
595
614
|
entityTypesError={entityTypesError}
|
|
596
615
|
generatingReferenceId={generationProgress?.annotationId ?? null}
|
|
616
|
+
sparkleAnnotationIds={sparkleAnnotationIds}
|
|
597
617
|
referencedBy={referencedBy}
|
|
598
618
|
referencedByLoading={referencedByLoading}
|
|
599
619
|
referencedByError={referencedByError}
|
|
@@ -604,7 +624,7 @@ export function ResourceViewerPage({
|
|
|
604
624
|
scrollToAnnotationId={scrollToAnnotationId}
|
|
605
625
|
hoveredAnnotationId={hoveredAnnotationId}
|
|
606
626
|
onScrollCompleted={onScrollCompleted}
|
|
607
|
-
initialTab={panelInitialTab?.tab
|
|
627
|
+
initialTab={panelInitialTab?.tab}
|
|
608
628
|
initialTabGeneration={panelInitialTab?.generation}
|
|
609
629
|
Link={Link}
|
|
610
630
|
routes={routes}
|
|
@@ -636,7 +656,7 @@ export function ResourceViewerPage({
|
|
|
636
656
|
documentLocale={getLanguage(resource)}
|
|
637
657
|
primaryMediaType={primaryMediaType}
|
|
638
658
|
primaryByteSize={primaryByteSize}
|
|
639
|
-
storageUri={resource
|
|
659
|
+
storageUri={getStorageUri(resource)}
|
|
640
660
|
isArchived={resource.archived ?? false}
|
|
641
661
|
dateCreated={resource.dateCreated}
|
|
642
662
|
dateModified={resource.dateModified}
|
|
@@ -644,6 +664,15 @@ export function ResourceViewerPage({
|
|
|
644
664
|
wasDerivedFrom={resource.wasDerivedFrom}
|
|
645
665
|
generator={resource.generator as components['schemas']['Agent'] | components['schemas']['Agent'][] | undefined}
|
|
646
666
|
onGenerate={() => setGenerateOpen(true)}
|
|
667
|
+
// The panel is generation's progress surface (GENERATE-FROM-
|
|
668
|
+
// RESOURCE D7); no annotationId ⇒ a resource-gen job — the
|
|
669
|
+
// annotation path's frame renders in the reference wizard.
|
|
670
|
+
isGenerating={isGenerating}
|
|
671
|
+
generationProgress={
|
|
672
|
+
generationProgress && !generationProgress.annotationId ? generationProgress : null
|
|
673
|
+
}
|
|
674
|
+
generationOutcome={generationOutcome}
|
|
675
|
+
onDismissProgress={() => stateUnit?.yield.dismissProgress()}
|
|
647
676
|
/>
|
|
648
677
|
)}
|
|
649
678
|
|
|
@@ -680,27 +709,35 @@ export function ResourceViewerPage({
|
|
|
680
709
|
annotationId={wizardAnnotationId}
|
|
681
710
|
resourceId={wizardResourceId}
|
|
682
711
|
defaultTitle={wizardDefaultTitle}
|
|
712
|
+
defaultFolder={folderOf(getStorageUri(resource))}
|
|
683
713
|
entityTypes={wizardEntityTypes}
|
|
714
|
+
resourceName={resource.name}
|
|
684
715
|
locale={locale}
|
|
685
716
|
context={gatherContext}
|
|
686
717
|
contextLoading={gatherLoading}
|
|
687
718
|
contextError={gatherError}
|
|
688
719
|
onGenerateSubmit={handleWizardGenerateSubmit}
|
|
689
720
|
onLinkResource={handleWizardLinkResource}
|
|
690
|
-
|
|
721
|
+
onComposeSubmit={handleWizardComposeSubmit}
|
|
722
|
+
entityTypeOptions={allEntityTypes}
|
|
723
|
+
hoverDelayMs={hoverDelayMs}
|
|
691
724
|
translations={{
|
|
692
|
-
|
|
693
|
-
configureGenerationTitle: tw('configureGenerationTitle'),
|
|
694
|
-
configureSearchTitle: tw('configureSearchTitle'),
|
|
695
|
-
searchResultsTitle: tw('searchResultsTitle'),
|
|
725
|
+
resolveTitle: tw('resolveTitle'),
|
|
696
726
|
sourceContextLabel: tw('sourceContextLabel'),
|
|
697
727
|
connectionsLabel: tw('connectionsLabel'),
|
|
698
728
|
citedByLabel: tw('citedByLabel'),
|
|
699
729
|
userHintLabel: tw('userHintLabel'),
|
|
730
|
+
userHintEffect: tw('userHintEffect'),
|
|
700
731
|
userHintPlaceholder: tw('userHintPlaceholder'),
|
|
732
|
+
graphPaneTitle: tw('graphPaneTitle'),
|
|
733
|
+
graphEmpty: tw('graphEmpty'),
|
|
734
|
+
resourceLinkLabel: tw('resourceLinkLabel'),
|
|
735
|
+
corpusPaneTitle: tw('corpusPaneTitle'),
|
|
736
|
+
corpusEmpty: tw('corpusEmpty'),
|
|
737
|
+
excludedReceipt: tw('excludedReceipt'),
|
|
738
|
+
machineRead: tw('machineRead'),
|
|
701
739
|
loadingContext: tw('loadingContext'),
|
|
702
740
|
failedContext: tw('failedContext'),
|
|
703
|
-
cancel: tw('cancel'),
|
|
704
741
|
search: tw('search'),
|
|
705
742
|
searching: tw('searching'),
|
|
706
743
|
generate: tw('generate'),
|
|
@@ -712,6 +749,7 @@ export function ResourceViewerPage({
|
|
|
712
749
|
noResults: tw('noResults'),
|
|
713
750
|
resourceTitle: tw('resourceTitle'),
|
|
714
751
|
resourceTitlePlaceholder: tw('resourceTitlePlaceholder'),
|
|
752
|
+
saveLocation: tw('saveLocation'),
|
|
715
753
|
additionalInstructions: tw('additionalInstructions'),
|
|
716
754
|
additionalInstructionsPlaceholder: tw('additionalInstructionsPlaceholder'),
|
|
717
755
|
language: tw('language'),
|
|
@@ -722,9 +760,19 @@ export function ResourceViewerPage({
|
|
|
722
760
|
maxLength: tw('maxLength'),
|
|
723
761
|
maxLengthHelp: tw('maxLengthHelp'),
|
|
724
762
|
maxLengthCeiling: tw('maxLengthCeiling'),
|
|
763
|
+
outputFormat: tw('outputFormat'),
|
|
764
|
+
formatExtensionMismatch: tw('formatExtensionMismatch'),
|
|
725
765
|
maxResults: tw('maxResults'),
|
|
726
766
|
semanticScoring: tw('semanticScoring'),
|
|
727
767
|
semanticScoringHelp: tw('semanticScoringHelp'),
|
|
768
|
+
searchFailed: tw('searchFailed'),
|
|
769
|
+
entityTypes: tw('entityTypes'),
|
|
770
|
+
contentLabel: tw('contentLabel'),
|
|
771
|
+
createAndLink: tw('createAndLink'),
|
|
772
|
+
creatingAndLinking: tw('creatingAndLinking'),
|
|
773
|
+
discardDraftPrompt: tw('discardDraftPrompt'),
|
|
774
|
+
discardDraft: tw('discardDraft'),
|
|
775
|
+
keepEditing: tw('keepEditing'),
|
|
728
776
|
}}
|
|
729
777
|
/>
|
|
730
778
|
|
|
@@ -734,31 +782,43 @@ export function ResourceViewerPage({
|
|
|
734
782
|
isOpen={generateOpen}
|
|
735
783
|
onClose={() => setGenerateOpen(false)}
|
|
736
784
|
resourceId={rUri}
|
|
737
|
-
|
|
785
|
+
// Seed the proposed title from the source resource's name (GFR D4/A4);
|
|
786
|
+
// the field stays editable and required.
|
|
787
|
+
defaultTitle={resource.name}
|
|
788
|
+
defaultFolder={folderOf(getStorageUri(resource))}
|
|
738
789
|
locale={locale}
|
|
739
790
|
entityTypeOptions={allEntityTypes}
|
|
740
791
|
onGenerateSubmit={handleResourceGenerateSubmit}
|
|
792
|
+
gatherContext={resourceGatherContext}
|
|
793
|
+
gatherLoading={resourceGatherLoading}
|
|
794
|
+
gatherError={resourceGatherError}
|
|
795
|
+
onGather={(options) => stateUnit?.gather.gatherResource(rUri, options)}
|
|
741
796
|
translations={{
|
|
742
|
-
|
|
743
|
-
reviewTitle: tg('reviewTitle'),
|
|
744
|
-
configureTitle: tg('configureTitle'),
|
|
745
|
-
next: tg('next'),
|
|
746
|
-
back: tg('back'),
|
|
747
|
-
cancel: tg('cancel'),
|
|
797
|
+
title: tg('title'),
|
|
748
798
|
gatherIntro: tg('gatherIntro'),
|
|
749
799
|
includeContent: tg('includeContent'),
|
|
750
800
|
includeSummary: tg('includeSummary'),
|
|
751
801
|
gatherDepth: tg('gatherDepth'),
|
|
752
802
|
gatherMaxResources: tg('gatherMaxResources'),
|
|
753
803
|
gatherButton: tg('gatherButton'),
|
|
754
|
-
|
|
804
|
+
editGather: tg('editGather'),
|
|
805
|
+
recallLabel: tg('recallLabel'),
|
|
755
806
|
loadingContext: tg('loadingContext'),
|
|
756
807
|
failedContext: tg('failedContext'),
|
|
757
808
|
sourceContextLabel: tg('sourceContextLabel'),
|
|
758
809
|
connectionsLabel: tg('connectionsLabel'),
|
|
759
810
|
citedByLabel: tg('citedByLabel'),
|
|
811
|
+
graphPaneTitle: tg('graphPaneTitle'),
|
|
812
|
+
graphEmpty: tg('graphEmpty'),
|
|
813
|
+
resourceLinkLabel: tg('resourceLinkLabel'),
|
|
814
|
+
corpusPaneTitle: tg('corpusPaneTitle'),
|
|
815
|
+
corpusEmpty: tg('corpusEmpty'),
|
|
816
|
+
excludedReceipt: tg('excludedReceipt'),
|
|
817
|
+
machineRead: tg('machineRead'),
|
|
818
|
+
score: tg('score'),
|
|
760
819
|
resourceTitle: tg('resourceTitle'),
|
|
761
820
|
resourceTitlePlaceholder: tg('resourceTitlePlaceholder'),
|
|
821
|
+
saveLocation: tg('saveLocation'),
|
|
762
822
|
additionalInstructions: tg('additionalInstructions'),
|
|
763
823
|
additionalInstructionsPlaceholder: tg('additionalInstructionsPlaceholder'),
|
|
764
824
|
language: tg('language'),
|
|
@@ -769,7 +829,12 @@ export function ResourceViewerPage({
|
|
|
769
829
|
maxLength: tg('maxLength'),
|
|
770
830
|
maxLengthHelp: tg('maxLengthHelp'),
|
|
771
831
|
maxLengthCeiling: tg('maxLengthCeiling'),
|
|
832
|
+
outputFormat: tg('outputFormat'),
|
|
833
|
+
formatExtensionMismatch: tg('formatExtensionMismatch'),
|
|
772
834
|
generate: tg('generate'),
|
|
835
|
+
discardDraftPrompt: tg('discardDraftPrompt'),
|
|
836
|
+
discardDraft: tg('discardDraft'),
|
|
837
|
+
keepEditing: tg('keepEditing'),
|
|
773
838
|
}}
|
|
774
839
|
/>
|
|
775
840
|
</div>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { GenerationOptions } from '@semiont/sdk';
|
|
2
|
+
import type { GenerationConfig } from '../../components/modals/ConfigureGenerationStep';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The submitted form config becomes generation options — in ONE place, by
|
|
6
|
+
* spread (GENERATION-OUTPUT-FORMAT D8).
|
|
7
|
+
*
|
|
8
|
+
* Both page handlers previously built this object field-by-field, so a knob
|
|
9
|
+
* the list didn't mention was silently dropped on the way to the wire. That
|
|
10
|
+
* is exactly how `outputMediaType` came to be unreachable one layer down
|
|
11
|
+
* (P1's finding), and copying the pattern here would have re-created it for
|
|
12
|
+
* every future knob. Spreading means the default is "forwarded"; only the two
|
|
13
|
+
* genuine transformations are spelled out:
|
|
14
|
+
*
|
|
15
|
+
* - `context` leaves the bag — it is `fromContext`'s positional argument.
|
|
16
|
+
* - `sourceLanguage` joins it — the language of the resource being VIEWED,
|
|
17
|
+
* which the form cannot know and the page can. Omitted entirely when
|
|
18
|
+
* unknown, so absence stays absence rather than becoming `''`.
|
|
19
|
+
*/
|
|
20
|
+
export function toGenerationOptions(
|
|
21
|
+
config: GenerationConfig,
|
|
22
|
+
sourceLanguage: string | undefined,
|
|
23
|
+
): GenerationOptions {
|
|
24
|
+
const { context: _positional, ...options } = config;
|
|
25
|
+
return {
|
|
26
|
+
...options,
|
|
27
|
+
...(sourceLanguage ? { sourceLanguage } : {}),
|
|
28
|
+
};
|
|
29
|
+
}
|