@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
|
@@ -23,6 +23,12 @@ interface Props {
|
|
|
23
23
|
wasAttributedTo?: Agent | Agent[] | undefined;
|
|
24
24
|
wasDerivedFrom?: string | string[] | undefined;
|
|
25
25
|
generator?: Agent | Agent[] | undefined;
|
|
26
|
+
/**
|
|
27
|
+
* Open the resource-generate flow. UI-only — the SDK isn't involved in
|
|
28
|
+
* *opening* the display (if collaborative gather ever lands, this graduates
|
|
29
|
+
* to a verb). Omit to hide the Generate action.
|
|
30
|
+
*/
|
|
31
|
+
onGenerate?: () => void;
|
|
26
32
|
}
|
|
27
33
|
|
|
28
34
|
/**
|
|
@@ -45,6 +51,7 @@ export function ResourceInfoPanel({
|
|
|
45
51
|
wasAttributedTo,
|
|
46
52
|
wasDerivedFrom,
|
|
47
53
|
generator,
|
|
54
|
+
onGenerate,
|
|
48
55
|
}: Props) {
|
|
49
56
|
const t = useTranslations('ResourceInfoPanel');
|
|
50
57
|
const session = useObservable(useSemiont().activeSession$);
|
|
@@ -177,6 +184,21 @@ export function ResourceInfoPanel({
|
|
|
177
184
|
</div>
|
|
178
185
|
)}
|
|
179
186
|
|
|
187
|
+
{/* Generate Action — opens the resource-generate flow (UI-only) */}
|
|
188
|
+
{onGenerate && (
|
|
189
|
+
<div className="semiont-resource-info-panel__action-section">
|
|
190
|
+
<button
|
|
191
|
+
onClick={onGenerate}
|
|
192
|
+
className="semiont-resource-button semiont-resource-button--secondary"
|
|
193
|
+
>
|
|
194
|
+
✨ {t('generate')}
|
|
195
|
+
</button>
|
|
196
|
+
<p className="semiont-resource-info-panel__description">
|
|
197
|
+
{t('generateDescription')}
|
|
198
|
+
</p>
|
|
199
|
+
</div>
|
|
200
|
+
)}
|
|
201
|
+
|
|
180
202
|
{/* Clone Action */}
|
|
181
203
|
<div className="semiont-resource-info-panel__action-section">
|
|
182
204
|
<button
|
|
@@ -20,6 +20,8 @@ vi.mock('../../../../contexts/TranslationContext', () => ({
|
|
|
20
20
|
storageUri: 'Storage',
|
|
21
21
|
clone: 'Clone',
|
|
22
22
|
cloneDescription: 'Generate a shareable clone link for this resource',
|
|
23
|
+
generate: 'Generate',
|
|
24
|
+
generateDescription: "Generate a new resource from this one's context",
|
|
23
25
|
archive: 'Archive',
|
|
24
26
|
archiveDescription: 'Move this resource to archived status',
|
|
25
27
|
unarchive: 'Unarchive',
|
|
@@ -269,6 +271,30 @@ describe('ResourceInfoPanel Component', () => {
|
|
|
269
271
|
});
|
|
270
272
|
});
|
|
271
273
|
|
|
274
|
+
describe('Generate Action', () => {
|
|
275
|
+
it('renders the Generate button when onGenerate is provided', () => {
|
|
276
|
+
renderWithEventBus(
|
|
277
|
+
<ResourceInfoPanel {...defaultProps} onGenerate={() => {}} />
|
|
278
|
+
);
|
|
279
|
+
expect(screen.getByRole('button', { name: /Generate/i })).toBeInTheDocument();
|
|
280
|
+
expect(screen.getByText("Generate a new resource from this one's context")).toBeInTheDocument();
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('hides the Generate button when onGenerate is omitted', () => {
|
|
284
|
+
renderWithEventBus(<ResourceInfoPanel {...defaultProps} />);
|
|
285
|
+
expect(screen.queryByRole('button', { name: /Generate/i })).not.toBeInTheDocument();
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it('calls onGenerate when clicked', () => {
|
|
289
|
+
const onGenerate = vi.fn();
|
|
290
|
+
renderWithEventBus(
|
|
291
|
+
<ResourceInfoPanel {...defaultProps} onGenerate={onGenerate} />
|
|
292
|
+
);
|
|
293
|
+
fireEvent.click(screen.getByRole('button', { name: /Generate/i }));
|
|
294
|
+
expect(onGenerate).toHaveBeenCalledTimes(1);
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
|
|
272
298
|
describe('Provenance Section', () => {
|
|
273
299
|
it('should not render provenance section when no provenance data', () => {
|
|
274
300
|
renderWithEventBus(<ResourceInfoPanel {...defaultProps} />);
|
|
@@ -2,6 +2,7 @@ import { describe, it, expect, vi } from 'vitest';
|
|
|
2
2
|
import { firstValueFrom, of, throwError } from 'rxjs';
|
|
3
3
|
import type { ShellStateUnit } from '../../../../state/shell-state-unit';
|
|
4
4
|
import { createExchangeStateUnit } from '../exchange-state-unit';
|
|
5
|
+
import { assertStateUnitAxioms, disposeProbe } from '@semiont/core/testing';
|
|
5
6
|
|
|
6
7
|
function mockBrowse(): ShellStateUnit {
|
|
7
8
|
return { dispose: vi.fn() } as unknown as ShellStateUnit;
|
|
@@ -169,3 +170,16 @@ describe('createExchangeStateUnit', () => {
|
|
|
169
170
|
stateUnit.dispose();
|
|
170
171
|
});
|
|
171
172
|
});
|
|
173
|
+
|
|
174
|
+
describe('ExchangeStateUnit — StateUnit axioms', () => {
|
|
175
|
+
it('satisfies the StateUnit axioms (incl. A7-passed: never disposes the injected browse)', () => {
|
|
176
|
+
assertStateUnitAxioms({
|
|
177
|
+
setup: () => {
|
|
178
|
+
const browse = disposeProbe();
|
|
179
|
+
return { unit: createExchangeStateUnit(browse as unknown as ShellStateUnit, vi.fn(), vi.fn()), passedIn: [browse] };
|
|
180
|
+
},
|
|
181
|
+
surfaces: (u) => [u.selectedFile$, u.preview$, u.importPhase$, u.importMessage$, u.importResult$, u.isExporting$, u.isImporting$],
|
|
182
|
+
invocations: (u) => [() => u.cancelImport(), () => u.doImport()],
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BehaviorSubject, lastValueFrom, type Observable } from 'rxjs';
|
|
2
2
|
import { tap } from 'rxjs/operators';
|
|
3
3
|
import { createDisposer } from '@semiont/sdk';
|
|
4
|
-
import type { StateUnit } from '@semiont/
|
|
4
|
+
import type { StateUnit } from '@semiont/core';
|
|
5
5
|
import type { ShellStateUnit } from '../../../state/shell-state-unit';
|
|
6
6
|
import type { BackendDownload, ProgressEvent } from '@semiont/core';
|
|
7
7
|
|
|
@@ -33,7 +33,9 @@ export function createExchangeStateUnit(
|
|
|
33
33
|
importFn: (file: File) => Observable<ProgressEvent>,
|
|
34
34
|
): ExchangeStateUnit {
|
|
35
35
|
const disposer = createDisposer();
|
|
36
|
-
|
|
36
|
+
// `browse` (ShellStateUnit) is a *passed-in* dependency owned by the caller
|
|
37
|
+
// (`useShellStateUnit`), not this unit — do NOT add it to the disposer (it's the
|
|
38
|
+
// shared, app-scoped shell). See packages/sdk/docs/STATE-UNITS.md (composition rule).
|
|
37
39
|
|
|
38
40
|
const selectedFile$ = new BehaviorSubject<File | null>(null);
|
|
39
41
|
const preview$ = new BehaviorSubject<ImportPreview | null>(null);
|
|
@@ -4,6 +4,7 @@ import { filter } from 'rxjs/operators';
|
|
|
4
4
|
import type { SemiontClient } from '@semiont/sdk';
|
|
5
5
|
import type { ShellStateUnit } from '../../../../state/shell-state-unit';
|
|
6
6
|
import { createAdminSecurityStateUnit } from '../admin-security-state-unit';
|
|
7
|
+
import { assertStateUnitAxioms, disposeProbe } from '@semiont/core/testing';
|
|
7
8
|
|
|
8
9
|
function mockBrowse(): ShellStateUnit {
|
|
9
10
|
return { dispose: vi.fn() } as unknown as ShellStateUnit;
|
|
@@ -66,3 +67,16 @@ describe('createAdminSecurityStateUnit', () => {
|
|
|
66
67
|
stateUnit.dispose();
|
|
67
68
|
});
|
|
68
69
|
});
|
|
70
|
+
|
|
71
|
+
describe('AdminSecurityStateUnit — StateUnit axioms', () => {
|
|
72
|
+
it('satisfies the StateUnit axioms (incl. A7-passed: never disposes the injected browse)', () => {
|
|
73
|
+
assertStateUnitAxioms({
|
|
74
|
+
setup: () => {
|
|
75
|
+
const browse = disposeProbe();
|
|
76
|
+
const client = mockClient(vi.fn().mockResolvedValue({ providers: [], allowedDomains: [] }));
|
|
77
|
+
return { unit: createAdminSecurityStateUnit(client, browse as unknown as ShellStateUnit), passedIn: [browse] };
|
|
78
|
+
},
|
|
79
|
+
surfaces: (u) => [u.providers$, u.allowedDomains$, u.isLoading$],
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BehaviorSubject, type Observable } from 'rxjs';
|
|
2
2
|
import { createDisposer } from '@semiont/sdk';
|
|
3
|
-
import type { StateUnit } from '@semiont/
|
|
3
|
+
import type { StateUnit } from '@semiont/core';
|
|
4
4
|
import type { ShellStateUnit } from '../../../state/shell-state-unit';
|
|
5
5
|
import type { SemiontClient } from '@semiont/sdk';
|
|
6
6
|
|
|
@@ -16,7 +16,9 @@ export function createAdminSecurityStateUnit(
|
|
|
16
16
|
browse: ShellStateUnit,
|
|
17
17
|
): AdminSecurityStateUnit {
|
|
18
18
|
const disposer = createDisposer();
|
|
19
|
-
|
|
19
|
+
// `browse` (ShellStateUnit) is a *passed-in* dependency owned by the caller
|
|
20
|
+
// (`useShellStateUnit`), not this unit — do NOT add it to the disposer (it's the
|
|
21
|
+
// shared, app-scoped shell). See packages/sdk/docs/STATE-UNITS.md (composition rule).
|
|
20
22
|
|
|
21
23
|
const providers$ = new BehaviorSubject<unknown[]>([]);
|
|
22
24
|
const allowedDomains$ = new BehaviorSubject<string[]>([]);
|
|
@@ -4,6 +4,7 @@ import { filter } from 'rxjs/operators';
|
|
|
4
4
|
import type { SemiontClient } from '@semiont/sdk';
|
|
5
5
|
import type { ShellStateUnit } from '../../../../state/shell-state-unit';
|
|
6
6
|
import { createAdminUsersStateUnit } from '../admin-users-state-unit';
|
|
7
|
+
import { assertStateUnitAxioms, disposeProbe } from '@semiont/core/testing';
|
|
7
8
|
|
|
8
9
|
function mockBrowse(): ShellStateUnit {
|
|
9
10
|
return { dispose: vi.fn() } as unknown as ShellStateUnit;
|
|
@@ -77,10 +78,16 @@ describe('createAdminUsersStateUnit', () => {
|
|
|
77
78
|
stateUnit.dispose();
|
|
78
79
|
});
|
|
79
80
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe('AdminUsersStateUnit — StateUnit axioms', () => {
|
|
84
|
+
it('satisfies the StateUnit axioms (incl. A7-passed: never disposes the injected browse)', () => {
|
|
85
|
+
assertStateUnitAxioms({
|
|
86
|
+
setup: () => {
|
|
87
|
+
const browse = disposeProbe();
|
|
88
|
+
return { unit: createAdminUsersStateUnit(mockClient(), browse as unknown as ShellStateUnit), passedIn: [browse] };
|
|
89
|
+
},
|
|
90
|
+
surfaces: (u) => [u.users$, u.stats$, u.usersLoading$, u.statsLoading$],
|
|
91
|
+
});
|
|
85
92
|
});
|
|
86
93
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BehaviorSubject, type Observable } from 'rxjs';
|
|
2
2
|
import { userDID } from '@semiont/core';
|
|
3
3
|
import { createDisposer } from '@semiont/sdk';
|
|
4
|
-
import type { StateUnit } from '@semiont/
|
|
4
|
+
import type { StateUnit } from '@semiont/core';
|
|
5
5
|
import type { ShellStateUnit } from '../../../state/shell-state-unit';
|
|
6
6
|
import type { SemiontClient } from '@semiont/sdk';
|
|
7
7
|
|
|
@@ -19,7 +19,9 @@ export function createAdminUsersStateUnit(
|
|
|
19
19
|
browse: ShellStateUnit,
|
|
20
20
|
): AdminUsersStateUnit {
|
|
21
21
|
const disposer = createDisposer();
|
|
22
|
-
|
|
22
|
+
// `browse` (ShellStateUnit) is a *passed-in* dependency owned by the caller
|
|
23
|
+
// (`useShellStateUnit`), not this unit — do NOT add it to the disposer (it's the
|
|
24
|
+
// shared, app-scoped shell). See packages/sdk/docs/STATE-UNITS.md (composition rule).
|
|
23
25
|
|
|
24
26
|
const users$ = new BehaviorSubject<unknown[]>([]);
|
|
25
27
|
const stats$ = new BehaviorSubject<unknown | null>(null);
|
|
@@ -3,6 +3,7 @@ import { firstValueFrom } from 'rxjs';
|
|
|
3
3
|
import { filter } from 'rxjs/operators';
|
|
4
4
|
import type { SemiontClient } from '@semiont/sdk';
|
|
5
5
|
import { createWelcomeStateUnit } from '../welcome-state-unit';
|
|
6
|
+
import { assertStateUnitAxioms } from '@semiont/core/testing';
|
|
6
7
|
|
|
7
8
|
function mockClient(overrides: {
|
|
8
9
|
getMe?: ReturnType<typeof vi.fn>;
|
|
@@ -84,3 +85,13 @@ describe('createWelcomeStateUnit', () => {
|
|
|
84
85
|
stateUnit.dispose();
|
|
85
86
|
});
|
|
86
87
|
});
|
|
88
|
+
|
|
89
|
+
describe('WelcomeStateUnit — StateUnit axioms', () => {
|
|
90
|
+
it('satisfies the StateUnit axioms', () => {
|
|
91
|
+
assertStateUnitAxioms({
|
|
92
|
+
setup: () => createWelcomeStateUnit(mockClient()),
|
|
93
|
+
surfaces: (u) => [u.userData$, u.isProcessing$],
|
|
94
|
+
invocations: (u) => [() => u.acceptTerms()],
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BehaviorSubject, type Observable } from 'rxjs';
|
|
2
2
|
import { createDisposer } from '@semiont/sdk';
|
|
3
|
-
import type { StateUnit } from '@semiont/
|
|
3
|
+
import type { StateUnit } from '@semiont/core';
|
|
4
4
|
import type { SemiontClient } from '@semiont/sdk';
|
|
5
5
|
|
|
6
6
|
export interface WelcomeStateUnit extends StateUnit {
|
|
@@ -4,6 +4,7 @@ import { filter } from 'rxjs/operators';
|
|
|
4
4
|
import type { SemiontClient } from '@semiont/sdk';
|
|
5
5
|
import type { ShellStateUnit } from '../../../../state/shell-state-unit';
|
|
6
6
|
import { createEntityTagsStateUnit } from '../entity-tags-state-unit';
|
|
7
|
+
import { assertStateUnitAxioms, disposeProbe } from '@semiont/core/testing';
|
|
7
8
|
|
|
8
9
|
function mockBrowse(): ShellStateUnit {
|
|
9
10
|
return { dispose: vi.fn() } as unknown as ShellStateUnit;
|
|
@@ -100,3 +101,16 @@ describe('createEntityTagsStateUnit', () => {
|
|
|
100
101
|
stateUnit.dispose();
|
|
101
102
|
});
|
|
102
103
|
});
|
|
104
|
+
|
|
105
|
+
describe('EntityTagsStateUnit — StateUnit axioms', () => {
|
|
106
|
+
it('satisfies the StateUnit axioms (incl. A7-passed: never disposes the injected browse)', () => {
|
|
107
|
+
assertStateUnitAxioms({
|
|
108
|
+
setup: () => {
|
|
109
|
+
const browse = disposeProbe();
|
|
110
|
+
return { unit: createEntityTagsStateUnit(mockClient(), browse as unknown as ShellStateUnit), passedIn: [browse] };
|
|
111
|
+
},
|
|
112
|
+
surfaces: (u) => [u.newTag$, u.error$, u.isAdding$],
|
|
113
|
+
invocations: (u) => [() => u.setNewTag(''), () => u.addTag()],
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { BehaviorSubject, type Observable, map } from 'rxjs';
|
|
2
2
|
import { createDisposer } from '@semiont/sdk';
|
|
3
|
-
import type { StateUnit } from '@semiont/
|
|
3
|
+
import type { StateUnit } from '@semiont/core';
|
|
4
4
|
import type { ShellStateUnit } from '../../../state/shell-state-unit';
|
|
5
5
|
import type { SemiontClient } from '@semiont/sdk';
|
|
6
6
|
|
|
@@ -20,7 +20,9 @@ export function createEntityTagsStateUnit(
|
|
|
20
20
|
browse: ShellStateUnit,
|
|
21
21
|
): EntityTagsStateUnit {
|
|
22
22
|
const disposer = createDisposer();
|
|
23
|
-
|
|
23
|
+
// `browse` (ShellStateUnit) is a *passed-in* dependency owned by the caller
|
|
24
|
+
// (`useShellStateUnit`), not this unit — do NOT add it to the disposer (it's the
|
|
25
|
+
// shared, app-scoped shell). See packages/sdk/docs/STATE-UNITS.md (composition rule).
|
|
24
26
|
|
|
25
27
|
const newTag$ = new BehaviorSubject<string>('');
|
|
26
28
|
const error$ = new BehaviorSubject<string>('');
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import React, { useState, useEffect } from 'react';
|
|
10
|
-
import type
|
|
10
|
+
import { deriveViews, type GatheredContext } from '@semiont/core';
|
|
11
11
|
import { capabilitiesOf, AUTHORABLE_MEDIA_TYPES, MEDIA_TYPES, mediaTypeForExtension, isSupportedMediaType, LOCALES } from '@semiont/core';
|
|
12
12
|
import type { UploadProgress } from '@semiont/sdk';
|
|
13
13
|
import { type CloneData, type ReferenceData } from '../state/compose-page-state-unit';
|
|
@@ -283,6 +283,12 @@ export function ResourceComposePage({
|
|
|
283
283
|
const isClone = mode === 'clone';
|
|
284
284
|
const isReferenceCompletion = mode === 'reference';
|
|
285
285
|
|
|
286
|
+
// Gathered-context panel — annotation-focus only for now.
|
|
287
|
+
const composeFocus = gatheredContext?.focus.kind === 'annotation' ? gatheredContext.focus : null;
|
|
288
|
+
const composeConnections = gatheredContext && composeFocus
|
|
289
|
+
? deriveViews(gatheredContext.graph, String(composeFocus.sourceResource.id)).connections
|
|
290
|
+
: [];
|
|
291
|
+
|
|
286
292
|
return (
|
|
287
293
|
<div className={`semiont-page${activePanel && COMMON_PANELS.includes(activePanel as ToolbarPanelType) ? ' semiont-page--panel-open' : ''}`}>
|
|
288
294
|
{/* Main Content Area */}
|
|
@@ -316,7 +322,7 @@ export function ResourceComposePage({
|
|
|
316
322
|
border: '1px solid var(--semiont-border-primary)',
|
|
317
323
|
}}>
|
|
318
324
|
<h3 className="semiont-form__label" style={{ marginBottom: '0.75rem' }}>Gathered Context</h3>
|
|
319
|
-
{
|
|
325
|
+
{composeFocus?.selected && (
|
|
320
326
|
<div style={{
|
|
321
327
|
padding: '0.75rem',
|
|
322
328
|
backgroundColor: 'var(--semiont-bg-primary)',
|
|
@@ -327,27 +333,27 @@ export function ResourceComposePage({
|
|
|
327
333
|
marginBottom: '0.5rem',
|
|
328
334
|
}}>
|
|
329
335
|
<div style={{ fontSize: 'var(--semiont-text-sm)', fontFamily: 'monospace', whiteSpace: 'pre-wrap', color: 'var(--semiont-text-secondary)' }}>
|
|
330
|
-
{
|
|
336
|
+
{composeFocus.selected.before && <span>{composeFocus.selected.before}</span>}
|
|
331
337
|
<span style={{
|
|
332
338
|
backgroundColor: 'var(--semiont-color-primary-100)',
|
|
333
339
|
padding: '0 0.25rem',
|
|
334
340
|
fontWeight: 600,
|
|
335
341
|
color: 'var(--semiont-color-primary-900)',
|
|
336
342
|
}}>
|
|
337
|
-
{
|
|
343
|
+
{composeFocus.selected.text}
|
|
338
344
|
</span>
|
|
339
|
-
{
|
|
345
|
+
{composeFocus.selected.after && <span>{composeFocus.selected.after}</span>}
|
|
340
346
|
</div>
|
|
341
347
|
</div>
|
|
342
348
|
)}
|
|
343
|
-
{
|
|
349
|
+
{composeConnections.length > 0 && (
|
|
344
350
|
<div style={{
|
|
345
351
|
padding: '0.5rem 0',
|
|
346
352
|
fontSize: 'var(--semiont-text-sm)',
|
|
347
353
|
color: 'var(--semiont-text-secondary)',
|
|
348
354
|
}}>
|
|
349
355
|
<span style={{ fontSize: 'var(--semiont-text-xs)', fontWeight: 500, marginRight: '0.5rem' }}>Connections:</span>
|
|
350
|
-
{
|
|
356
|
+
{composeConnections.map(conn => (
|
|
351
357
|
<span key={conn.resourceId} className="semiont-chip" style={{ fontSize: 'var(--semiont-text-xs)', padding: '0.125rem 0.5rem', marginRight: '0.25rem' }}>
|
|
352
358
|
{conn.resourceName}
|
|
353
359
|
</span>
|
|
@@ -4,6 +4,7 @@ import { filter } from 'rxjs/operators';
|
|
|
4
4
|
import type { SemiontClient } from '@semiont/sdk';
|
|
5
5
|
import type { ShellStateUnit } from '../../../../state/shell-state-unit';
|
|
6
6
|
import { createComposePageStateUnit } from '../compose-page-state-unit';
|
|
7
|
+
import { assertStateUnitAxioms, disposeProbe } from '@semiont/core/testing';
|
|
7
8
|
|
|
8
9
|
/** Build an `UploadObservable`-shaped mock that emits started → finished. */
|
|
9
10
|
function mockUpload(resourceId: string) {
|
|
@@ -185,3 +186,15 @@ describe('createComposePageStateUnit', () => {
|
|
|
185
186
|
stateUnit.dispose();
|
|
186
187
|
});
|
|
187
188
|
});
|
|
189
|
+
|
|
190
|
+
describe('ComposePageStateUnit — StateUnit axioms', () => {
|
|
191
|
+
it('satisfies the StateUnit axioms (incl. A7-passed: never disposes the injected browse)', () => {
|
|
192
|
+
assertStateUnitAxioms({
|
|
193
|
+
setup: () => {
|
|
194
|
+
const browse = disposeProbe();
|
|
195
|
+
return { unit: createComposePageStateUnit(mockClient(), browse as unknown as ShellStateUnit, {}), passedIn: [browse] };
|
|
196
|
+
},
|
|
197
|
+
surfaces: (u) => [u.mode$, u.loading$, u.cloneData$, u.referenceData$, u.gatheredContext$, u.uploadProgress$],
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
});
|
|
@@ -2,7 +2,7 @@ import { BehaviorSubject, type Observable, map } from 'rxjs';
|
|
|
2
2
|
import type { GatheredContext, AnnotationId, AccessToken, ResourceDescriptor, ResourceId } from '@semiont/core';
|
|
3
3
|
import { resourceId as makeResourceId, annotationId as makeAnnotationId } from '@semiont/core';
|
|
4
4
|
import { createDisposer } from '@semiont/sdk';
|
|
5
|
-
import type { StateUnit } from '@semiont/
|
|
5
|
+
import type { StateUnit } from '@semiont/core';
|
|
6
6
|
import type { ShellStateUnit } from '../../../state/shell-state-unit';
|
|
7
7
|
import type { SemiontClient } from '@semiont/sdk';
|
|
8
8
|
import { decodeWithCharset, extensionForMediaType } from '@semiont/core';
|
|
@@ -72,7 +72,9 @@ export function createComposePageStateUnit(
|
|
|
72
72
|
auth?: AccessToken,
|
|
73
73
|
): ComposePageStateUnit {
|
|
74
74
|
const disposer = createDisposer();
|
|
75
|
-
|
|
75
|
+
// `browse` (ShellStateUnit) is a *passed-in* dependency owned by the caller
|
|
76
|
+
// (`useShellStateUnit`), not this unit — do NOT add it to the disposer (it's the
|
|
77
|
+
// shared, app-scoped shell). See packages/sdk/docs/STATE-UNITS.md (composition rule).
|
|
76
78
|
|
|
77
79
|
const isReferenceMode = Boolean(params.annotationUri && params.sourceDocumentId && params.name);
|
|
78
80
|
const isCloneMode = params.mode === 'clone' && Boolean(params.token);
|
|
@@ -199,6 +201,7 @@ export function createComposePageStateUnit(
|
|
|
199
201
|
cloneData$.complete();
|
|
200
202
|
referenceData$.complete();
|
|
201
203
|
gatheredContext$.complete();
|
|
204
|
+
uploadProgress$.complete();
|
|
202
205
|
disposer.dispose();
|
|
203
206
|
},
|
|
204
207
|
};
|
|
@@ -4,6 +4,7 @@ import { filter, skip, take, toArray } from 'rxjs/operators';
|
|
|
4
4
|
import type { SemiontClient } from '@semiont/sdk';
|
|
5
5
|
import type { ShellStateUnit } from '../../../../state/shell-state-unit';
|
|
6
6
|
import { createDiscoverStateUnit } from '../discover-state-unit';
|
|
7
|
+
import { assertStateUnitAxioms, disposeProbe } from '@semiont/core/testing';
|
|
7
8
|
|
|
8
9
|
function mockBrowse(): ShellStateUnit {
|
|
9
10
|
return { dispose: vi.fn() } as unknown as ShellStateUnit;
|
|
@@ -100,15 +101,6 @@ describe('createDiscoverStateUnit', () => {
|
|
|
100
101
|
stateUnit.dispose();
|
|
101
102
|
});
|
|
102
103
|
|
|
103
|
-
it('disposes browse and search on dispose', () => {
|
|
104
|
-
const browse = mockBrowse();
|
|
105
|
-
const { client } = mockClient();
|
|
106
|
-
const stateUnit = createDiscoverStateUnit(client, browse);
|
|
107
|
-
stateUnit.dispose();
|
|
108
|
-
|
|
109
|
-
expect(browse.dispose).toHaveBeenCalled();
|
|
110
|
-
});
|
|
111
|
-
|
|
112
104
|
it('initial selectedEntityType$ is empty and recent fetch carries no entityType', async () => {
|
|
113
105
|
const { client, resourceCalls } = mockClient();
|
|
114
106
|
const stateUnit = createDiscoverStateUnit(client, mockBrowse());
|
|
@@ -267,3 +259,17 @@ describe('createDiscoverStateUnit', () => {
|
|
|
267
259
|
stateUnit.dispose();
|
|
268
260
|
});
|
|
269
261
|
});
|
|
262
|
+
|
|
263
|
+
describe('DiscoverStateUnit — StateUnit axioms', () => {
|
|
264
|
+
it('satisfies the StateUnit axioms (incl. A7-passed: never disposes the injected browse)', () => {
|
|
265
|
+
assertStateUnitAxioms({
|
|
266
|
+
setup: () => {
|
|
267
|
+
const browse = disposeProbe();
|
|
268
|
+
const { client } = mockClient();
|
|
269
|
+
return { unit: createDiscoverStateUnit(client, browse as unknown as ShellStateUnit), passedIn: [browse] };
|
|
270
|
+
},
|
|
271
|
+
surfaces: (u) => [u.selectedEntityType$],
|
|
272
|
+
invocations: (u) => [() => u.setSelectedEntityType('')],
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
});
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { BehaviorSubject, Subject, combineLatest, of, type Observable } from 'rxjs';
|
|
2
2
|
import { debounceTime, distinctUntilChanged, map, startWith, switchMap, shareReplay } from 'rxjs/operators';
|
|
3
3
|
import type { ResourceDescriptor } from '@semiont/core';
|
|
4
|
-
import type { SemiontClient
|
|
4
|
+
import type { SemiontClient } from '@semiont/sdk';
|
|
5
|
+
import type { StateUnit } from '@semiont/core';
|
|
5
6
|
import { createDisposer } from '@semiont/sdk';
|
|
6
7
|
import type { ShellStateUnit } from '../../../state/shell-state-unit';
|
|
7
8
|
|
|
@@ -30,7 +31,9 @@ export function createDiscoverStateUnit(
|
|
|
30
31
|
browse: ShellStateUnit,
|
|
31
32
|
): DiscoverStateUnit {
|
|
32
33
|
const disposer = createDisposer();
|
|
33
|
-
|
|
34
|
+
// `browse` (ShellStateUnit) is a *passed-in* dependency owned by the caller
|
|
35
|
+
// (`useShellStateUnit`), not this unit — do NOT add it to the disposer (it's the
|
|
36
|
+
// shared, app-scoped shell). See packages/sdk/docs/STATE-UNITS.md (composition rule).
|
|
34
37
|
|
|
35
38
|
// Selected entity-type chip on the Discover page. Drives both the
|
|
36
39
|
// `recent` list and the search results — filtering happens on the
|
|
@@ -35,6 +35,8 @@ import { useStateUnit } from '../../../hooks/useStateUnit';
|
|
|
35
35
|
import { useShellStateUnit } from '../../../hooks/useShellStateUnit';
|
|
36
36
|
import { useTranslations } from '../../../contexts/TranslationContext';
|
|
37
37
|
import { ReferenceWizardModal } from '../../../components/modals/ReferenceWizardModal';
|
|
38
|
+
import { ResourceGenerateModal } from '../../../components/modals/ResourceGenerateModal';
|
|
39
|
+
import { AnnotateReferencesProgressWidget } from '../../../components/AnnotateReferencesProgressWidget';
|
|
38
40
|
import type { GenerationConfig } from '../../../components/modals/ConfigureGenerationStep';
|
|
39
41
|
|
|
40
42
|
type SemiontResource = ResourceDescriptor;
|
|
@@ -128,6 +130,7 @@ export function ResourceViewerPage({
|
|
|
128
130
|
}: ResourceViewerPageProps) {
|
|
129
131
|
// Translations
|
|
130
132
|
const tw = useTranslations('ReferenceWizard');
|
|
133
|
+
const tg = useTranslations('ResourceGenerate');
|
|
131
134
|
|
|
132
135
|
const browser = useSemiont();
|
|
133
136
|
const session = useObservable(browser.activeSession$);
|
|
@@ -187,6 +190,7 @@ export function ResourceViewerPage({
|
|
|
187
190
|
const wizardResourceId = wizardState?.resourceId ?? null;
|
|
188
191
|
const wizardDefaultTitle = wizardState?.defaultTitle ?? '';
|
|
189
192
|
const wizardEntityTypes = wizardState?.entityTypes ?? [];
|
|
193
|
+
const [generateOpen, setGenerateOpen] = useState(false);
|
|
190
194
|
|
|
191
195
|
const handleWizardClose = useCallback(() => {
|
|
192
196
|
stateUnit.closeWizard();
|
|
@@ -209,6 +213,23 @@ export function ResourceViewerPage({
|
|
|
209
213
|
});
|
|
210
214
|
}, [stateUnit, clearNewAnnotationId, resource]);
|
|
211
215
|
|
|
216
|
+
// Resource-generate flow (GENERATE-FROM-BUTTON): drive the SAME yield progress$
|
|
217
|
+
// the annotation path uses so the full AnnotateReferencesProgressWidget shows —
|
|
218
|
+
// NOT a toast. `generateFromResource` is Phase 6 (the @semiont/sdk session);
|
|
219
|
+
// this is declared RED until that method lands. Do not re-impl it here.
|
|
220
|
+
const handleResourceGenerateSubmit = useCallback((_resourceId: string, config: GenerationConfig) => {
|
|
221
|
+
stateUnit.yield.generateFromResource({
|
|
222
|
+
title: config.title,
|
|
223
|
+
storageUri: config.storagePath,
|
|
224
|
+
...(config.prompt ? { prompt: config.prompt } : {}),
|
|
225
|
+
language: config.language,
|
|
226
|
+
sourceLanguage: getLanguage(resource),
|
|
227
|
+
temperature: config.temperature,
|
|
228
|
+
maxTokens: config.maxTokens,
|
|
229
|
+
context: config.context,
|
|
230
|
+
});
|
|
231
|
+
}, [stateUnit, resource]);
|
|
232
|
+
|
|
212
233
|
const handleWizardLinkResource = useCallback(async (referenceId: string, targetResourceId: string) => {
|
|
213
234
|
if (!semiont) return;
|
|
214
235
|
try {
|
|
@@ -452,6 +473,21 @@ export function ResourceViewerPage({
|
|
|
452
473
|
</h2>
|
|
453
474
|
</div>
|
|
454
475
|
</div>
|
|
476
|
+
{/* Resource-generation progress (GENERATE-FROM-BUTTON P7) — no annotationId ⇒ a resource-gen job */}
|
|
477
|
+
{generationProgress && !generationProgress.annotationId && (
|
|
478
|
+
<AnnotateReferencesProgressWidget
|
|
479
|
+
progress={generationProgress}
|
|
480
|
+
annotationType="generation"
|
|
481
|
+
cancelJobType="generation"
|
|
482
|
+
translations={{
|
|
483
|
+
title: tg('progressTitle'),
|
|
484
|
+
cancel: tg('progressCancel'),
|
|
485
|
+
inProgress: tg('progressInProgress'),
|
|
486
|
+
complete: tg('progressComplete'),
|
|
487
|
+
failed: tg('progressFailed'),
|
|
488
|
+
}}
|
|
489
|
+
/>
|
|
490
|
+
)}
|
|
455
491
|
{/* Scrollable body wrapper - contains document content, header is sibling above */}
|
|
456
492
|
<div className="semiont-document-viewer__scrollable-body" lang={getLanguage(resource) || undefined}>
|
|
457
493
|
<ErrorBoundary
|
|
@@ -565,6 +601,7 @@ export function ResourceViewerPage({
|
|
|
565
601
|
wasAttributedTo={resource.wasAttributedTo}
|
|
566
602
|
wasDerivedFrom={resource.wasDerivedFrom}
|
|
567
603
|
generator={resource.generator as components['schemas']['Agent'] | components['schemas']['Agent'][] | undefined}
|
|
604
|
+
onGenerate={() => setGenerateOpen(true)}
|
|
568
605
|
/>
|
|
569
606
|
)}
|
|
570
607
|
|
|
@@ -645,6 +682,48 @@ export function ResourceViewerPage({
|
|
|
645
682
|
semanticScoringHelp: tw('semanticScoringHelp'),
|
|
646
683
|
}}
|
|
647
684
|
/>
|
|
685
|
+
|
|
686
|
+
{/* Resource-generate flow (GENERATE-FROM-BUTTON) */}
|
|
687
|
+
<ResourceGenerateModal
|
|
688
|
+
isOpen={generateOpen}
|
|
689
|
+
onClose={() => setGenerateOpen(false)}
|
|
690
|
+
resourceId={rUri}
|
|
691
|
+
defaultTitle=""
|
|
692
|
+
locale={locale}
|
|
693
|
+
onGenerateSubmit={handleResourceGenerateSubmit}
|
|
694
|
+
translations={{
|
|
695
|
+
gatherTitle: tg('gatherTitle'),
|
|
696
|
+
reviewTitle: tg('reviewTitle'),
|
|
697
|
+
configureTitle: tg('configureTitle'),
|
|
698
|
+
next: tg('next'),
|
|
699
|
+
back: tg('back'),
|
|
700
|
+
cancel: tg('cancel'),
|
|
701
|
+
gatherIntro: tg('gatherIntro'),
|
|
702
|
+
includeContent: tg('includeContent'),
|
|
703
|
+
includeSummary: tg('includeSummary'),
|
|
704
|
+
gatherDepth: tg('gatherDepth'),
|
|
705
|
+
gatherMaxResources: tg('gatherMaxResources'),
|
|
706
|
+
gatherButton: tg('gatherButton'),
|
|
707
|
+
excludeLabel: tg('excludeLabel'),
|
|
708
|
+
loadingContext: tg('loadingContext'),
|
|
709
|
+
failedContext: tg('failedContext'),
|
|
710
|
+
sourceContextLabel: tg('sourceContextLabel'),
|
|
711
|
+
connectionsLabel: tg('connectionsLabel'),
|
|
712
|
+
citedByLabel: tg('citedByLabel'),
|
|
713
|
+
resourceTitle: tg('resourceTitle'),
|
|
714
|
+
resourceTitlePlaceholder: tg('resourceTitlePlaceholder'),
|
|
715
|
+
additionalInstructions: tg('additionalInstructions'),
|
|
716
|
+
additionalInstructionsPlaceholder: tg('additionalInstructionsPlaceholder'),
|
|
717
|
+
language: tg('language'),
|
|
718
|
+
languageHelp: tg('languageHelp'),
|
|
719
|
+
creativity: tg('creativity'),
|
|
720
|
+
creativityFocused: tg('creativityFocused'),
|
|
721
|
+
creativityCreative: tg('creativityCreative'),
|
|
722
|
+
maxLength: tg('maxLength'),
|
|
723
|
+
maxLengthHelp: tg('maxLengthHelp'),
|
|
724
|
+
generate: tg('generate'),
|
|
725
|
+
}}
|
|
726
|
+
/>
|
|
648
727
|
</div>
|
|
649
728
|
);
|
|
650
729
|
}
|
|
@@ -4,6 +4,7 @@ import { filter } from 'rxjs/operators';
|
|
|
4
4
|
import { resourceId as makeResourceId } from '@semiont/core';
|
|
5
5
|
import type { SemiontClient } from '@semiont/sdk';
|
|
6
6
|
import { createResourceLoaderStateUnit } from '../resource-loader-state-unit';
|
|
7
|
+
import { assertStateUnitAxioms } from '@semiont/core/testing';
|
|
7
8
|
|
|
8
9
|
const RID = makeResourceId('res-1');
|
|
9
10
|
|
|
@@ -44,3 +45,13 @@ describe('createResourceLoaderStateUnit', () => {
|
|
|
44
45
|
stateUnit.dispose();
|
|
45
46
|
});
|
|
46
47
|
});
|
|
48
|
+
|
|
49
|
+
describe('ResourceLoaderStateUnit — StateUnit axioms', () => {
|
|
50
|
+
it('satisfies the StateUnit axioms', () => {
|
|
51
|
+
// No owned surfaces: resource$/isLoading$ are derived from client.browse; dispose is a no-op.
|
|
52
|
+
assertStateUnitAxioms({
|
|
53
|
+
setup: () => createResourceLoaderStateUnit(mockClient(), RID),
|
|
54
|
+
invocations: (u) => [() => u.invalidate()],
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
});
|