@semiont/react-ui 0.5.22 → 0.5.24
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/{ar-ZHKO4XDR.js → ar-JRTKCMTF.js} +11 -4
- package/dist/ar-JRTKCMTF.js.map +1 -0
- package/dist/{bn-3BUDKAK5.js → bn-LO6EDSCC.js} +11 -4
- package/dist/bn-LO6EDSCC.js.map +1 -0
- package/dist/{chunk-PFUXQPWJ.js → chunk-4QMTEPQ3.js} +92 -60
- package/dist/chunk-4QMTEPQ3.js.map +1 -0
- package/dist/{chunk-R52YAGHK.js → chunk-5Y5JDCKC.js} +11 -4
- package/dist/chunk-5Y5JDCKC.js.map +1 -0
- package/dist/{cs-2EFEUKTW.js → cs-LCHHOR4O.js} +11 -4
- package/dist/cs-LCHHOR4O.js.map +1 -0
- package/dist/{da-SUD3Q44Q.js → da-TARGPMJR.js} +11 -4
- package/dist/da-TARGPMJR.js.map +1 -0
- package/dist/{de-6IC5QRX7.js → de-SBH4QBPJ.js} +11 -4
- package/dist/de-SBH4QBPJ.js.map +1 -0
- package/dist/{el-Q4NAXFRC.js → el-2D2OLGWB.js} +11 -4
- package/dist/el-2D2OLGWB.js.map +1 -0
- package/dist/{en-P2L4X27N.js → en-QFIEG3CA.js} +2 -2
- package/dist/{es-YUKZBD4W.js → es-U6YK2LX6.js} +11 -4
- package/dist/es-U6YK2LX6.js.map +1 -0
- package/dist/{fa-OJ47LILE.js → fa-S7SZE37M.js} +11 -4
- package/dist/fa-S7SZE37M.js.map +1 -0
- package/dist/{fi-QFFL5CUB.js → fi-CBL3NIXN.js} +11 -4
- package/dist/fi-CBL3NIXN.js.map +1 -0
- package/dist/{fr-BOQZZWP4.js → fr-EABUHV6S.js} +11 -4
- package/dist/fr-EABUHV6S.js.map +1 -0
- package/dist/{he-LXDY3TFA.js → he-THWD244Y.js} +11 -4
- package/dist/he-THWD244Y.js.map +1 -0
- package/dist/{hi-HOJER77V.js → hi-GHGFZJ44.js} +11 -4
- package/dist/hi-GHGFZJ44.js.map +1 -0
- package/dist/{id-I5WQ6EWD.js → id-POZOUMDW.js} +11 -4
- package/dist/id-POZOUMDW.js.map +1 -0
- package/dist/index.d.ts +269 -69
- package/dist/index.js +2080 -1861
- package/dist/index.js.map +1 -1
- package/dist/{it-UYH642IJ.js → it-KDYJADSH.js} +11 -4
- package/dist/it-KDYJADSH.js.map +1 -0
- package/dist/{ja-JTVWAC4L.js → ja-Z5Z6ZF3P.js} +11 -4
- package/dist/ja-Z5Z6ZF3P.js.map +1 -0
- package/dist/{ko-IHXFMOIZ.js → ko-7X3XUXRN.js} +11 -4
- package/dist/ko-7X3XUXRN.js.map +1 -0
- package/dist/{ms-JH25VFLO.js → ms-BTWEIW43.js} +11 -4
- package/dist/ms-BTWEIW43.js.map +1 -0
- package/dist/{nl-SURXW7IQ.js → nl-IS2CGF5I.js} +11 -4
- package/dist/nl-IS2CGF5I.js.map +1 -0
- package/dist/{no-HXIKQFDZ.js → no-AAMEVA26.js} +11 -4
- package/dist/no-AAMEVA26.js.map +1 -0
- package/dist/{pl-7A4KU6XK.js → pl-PVXUQYFT.js} +11 -4
- package/dist/pl-PVXUQYFT.js.map +1 -0
- package/dist/{pt-6MGYP7Q5.js → pt-3ITQ76TL.js} +11 -4
- package/dist/pt-3ITQ76TL.js.map +1 -0
- package/dist/{ro-IROCHNIA.js → ro-FRB4IIVS.js} +11 -4
- package/dist/ro-FRB4IIVS.js.map +1 -0
- package/dist/{sv-WDJD75CV.js → sv-DS3RO24M.js} +11 -4
- package/dist/sv-DS3RO24M.js.map +1 -0
- package/dist/test-utils.js +9 -3
- package/dist/test-utils.js.map +1 -1
- package/dist/{th-RIO2E6B3.js → th-AX66MWWB.js} +11 -4
- package/dist/th-AX66MWWB.js.map +1 -0
- package/dist/{tr-TZ7ERCIN.js → tr-7KTB67TT.js} +11 -4
- package/dist/tr-7KTB67TT.js.map +1 -0
- package/dist/{uk-ZFWDKLUE.js → uk-SMQS2MAQ.js} +11 -4
- package/dist/uk-SMQS2MAQ.js.map +1 -0
- package/dist/{vi-UIOSP2JG.js → vi-D3JXL2EZ.js} +11 -4
- package/dist/vi-D3JXL2EZ.js.map +1 -0
- package/dist/{zh-6R6YUY46.js → zh-S754AWFS.js} +11 -4
- package/dist/zh-S754AWFS.js.map +1 -0
- package/package.json +4 -4
- package/src/components/annotation-popups/JsonLdView.tsx +1 -1
- package/src/components/annotation-popups/__tests__/JsonLdView.test.tsx +8 -2
- package/src/components/modals/ReferenceWizardModal.tsx +2 -1
- package/src/components/modals/ResourceGenerateModal.tsx +10 -4
- package/src/components/modals/ResourceSearchModal.tsx +3 -1
- package/src/components/modals/SearchModal.tsx +3 -1
- package/src/components/modals/__tests__/ResourceGenerateModal.test.tsx +5 -0
- package/src/components/modals/__tests__/ResourceSearchModal.test.tsx +2 -1
- package/src/components/modals/__tests__/SearchModal.search-wiring.test.tsx +2 -1
- package/src/components/resource/AnnotateView.tsx +70 -138
- package/src/components/resource/AnnotationHistory.tsx +53 -17
- package/src/components/resource/BrowseView.tsx +3 -2
- package/src/components/resource/ResourceViewer.tsx +39 -22
- package/src/components/resource/__tests__/AnnotateView.registry.test.tsx +114 -0
- package/src/components/resource/__tests__/AnnotationHistory.test.tsx +63 -41
- package/src/components/resource/__tests__/BrowseView.reference-hover.test.tsx +2 -1
- package/src/components/resource/__tests__/ResourceViewer.multi-mount-invalidation.test.tsx +140 -0
- package/src/components/resource/__tests__/ResourceViewer.registry.test.tsx +89 -0
- package/src/components/resource/annotate-renderers.tsx +155 -0
- package/src/components/resource/panels/JsonLdPanel.tsx +1 -1
- package/src/components/resource/panels/ReferencesPanel.tsx +38 -3
- package/src/components/resource/panels/TagEntry.tsx +5 -1
- package/src/components/resource/panels/TaggingPanel.tsx +6 -1
- package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +41 -0
- package/src/components/resource/panels/__tests__/JsonLdPanel.test.tsx +8 -2
- package/src/components/resource/panels/__tests__/ReferencesPanel.headless.test.tsx +90 -0
- package/src/components/resource/panels/__tests__/TagEntry.test.tsx +2 -2
- package/src/components/resource/panels/__tests__/TaggingPanel.test.tsx +2 -2
- package/src/components/resource/panels/__tests__/UnifiedAnnotationsPanel.failure.test.tsx +78 -0
- package/src/components/settings/SettingsPanel.tsx +6 -2
- package/src/components/settings/__tests__/SettingsPanel.test.tsx +28 -3
- package/src/features/admin-devops/__tests__/AdminDevOpsPage.test.tsx +3 -11
- package/src/features/admin-devops/components/AdminDevOpsPage.tsx +0 -3
- package/src/features/admin-exchange/__tests__/AdminExchangePage.test.tsx +0 -1
- package/src/features/admin-exchange/components/AdminExchangePage.tsx +0 -3
- package/src/features/admin-exchange/state/__tests__/exchange-state-unit.test.ts +1 -1
- package/src/features/admin-security/__tests__/AdminSecurityPage.test.tsx +0 -8
- package/src/features/admin-security/components/AdminSecurityPage.tsx +0 -3
- package/src/features/admin-security/state/__tests__/admin-security-state-unit.test.ts +10 -9
- package/src/features/admin-security/state/admin-security-state-unit.ts +3 -2
- package/src/features/admin-users/__tests__/AdminUsersPage.test.tsx +0 -1
- package/src/features/admin-users/components/AdminUsersPage.tsx +0 -3
- package/src/features/admin-users/state/__tests__/admin-users-state-unit.test.ts +7 -6
- package/src/features/admin-users/state/admin-users-state-unit.ts +3 -2
- package/src/features/auth-welcome/state/__tests__/welcome-state-unit.test.ts +36 -26
- package/src/features/auth-welcome/state/welcome-state-unit.ts +7 -2
- package/src/features/moderate-entity-tags/__tests__/EntityTagsPage.test.tsx +0 -2
- package/src/features/moderate-entity-tags/components/EntityTagsPage.tsx +0 -3
- package/src/features/moderate-entity-tags/state/__tests__/entity-tags-state-unit.test.ts +37 -12
- package/src/features/moderate-entity-tags/state/entity-tags-state-unit.ts +9 -10
- package/src/features/moderate-recent/__tests__/RecentDocumentsPage.test.tsx +3 -5
- package/src/features/moderate-recent/components/RecentDocumentsPage.tsx +0 -3
- package/src/features/moderate-tag-schemas/__tests__/TagSchemasPage.test.tsx +3 -5
- package/src/features/moderate-tag-schemas/components/TagSchemasPage.tsx +0 -3
- package/src/features/moderation-linked-data/__tests__/LinkedDataPage.test.tsx +0 -1
- package/src/features/moderation-linked-data/components/LinkedDataPage.tsx +0 -3
- package/src/features/resource-compose/__tests__/ResourceComposePage.test.tsx +17 -7
- package/src/features/resource-compose/components/ResourceComposePage.tsx +5 -3
- package/src/features/resource-compose/state/__tests__/compose-page-state-unit.test.ts +33 -12
- package/src/features/resource-compose/state/compose-page-state-unit.ts +9 -8
- package/src/features/resource-discovery/__tests__/ResourceDiscoveryPage.test.tsx +3 -2
- package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +25 -3
- package/src/features/resource-discovery/state/__tests__/discover-state-unit.test.ts +90 -25
- package/src/features/resource-discovery/state/discover-state-unit.ts +30 -30
- package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +9 -5
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +70 -40
- package/src/features/resource-viewer/state/__tests__/resource-loader-state-unit.test.ts +111 -8
- package/src/features/resource-viewer/state/__tests__/resource-viewer-page-state-unit.test.ts +167 -27
- package/src/features/resource-viewer/state/resource-loader-state-unit.ts +74 -9
- package/src/features/resource-viewer/state/resource-viewer-page-state-unit.ts +29 -28
- package/translations/ar.json +10 -3
- package/translations/bn.json +10 -3
- package/translations/cs.json +10 -3
- package/translations/da.json +10 -3
- package/translations/de.json +10 -3
- package/translations/el.json +10 -3
- package/translations/en.json +10 -3
- package/translations/es.json +10 -3
- package/translations/fa.json +10 -3
- package/translations/fi.json +10 -3
- package/translations/fr.json +10 -3
- package/translations/he.json +10 -3
- package/translations/hi.json +10 -3
- package/translations/id.json +10 -3
- package/translations/it.json +10 -3
- package/translations/ja.json +10 -3
- package/translations/ko.json +10 -3
- package/translations/ms.json +10 -3
- package/translations/nl.json +10 -3
- package/translations/no.json +10 -3
- package/translations/pl.json +10 -3
- package/translations/pt.json +10 -3
- package/translations/ro.json +10 -3
- package/translations/sv.json +10 -3
- package/translations/th.json +10 -3
- package/translations/tr.json +10 -3
- package/translations/uk.json +10 -3
- package/translations/vi.json +10 -3
- package/translations/zh.json +10 -3
- package/dist/ar-ZHKO4XDR.js.map +0 -1
- package/dist/bn-3BUDKAK5.js.map +0 -1
- package/dist/chunk-PFUXQPWJ.js.map +0 -1
- package/dist/chunk-R52YAGHK.js.map +0 -1
- package/dist/cs-2EFEUKTW.js.map +0 -1
- package/dist/da-SUD3Q44Q.js.map +0 -1
- package/dist/de-6IC5QRX7.js.map +0 -1
- package/dist/el-Q4NAXFRC.js.map +0 -1
- package/dist/es-YUKZBD4W.js.map +0 -1
- package/dist/fa-OJ47LILE.js.map +0 -1
- package/dist/fi-QFFL5CUB.js.map +0 -1
- package/dist/fr-BOQZZWP4.js.map +0 -1
- package/dist/he-LXDY3TFA.js.map +0 -1
- package/dist/hi-HOJER77V.js.map +0 -1
- package/dist/id-I5WQ6EWD.js.map +0 -1
- package/dist/it-UYH642IJ.js.map +0 -1
- package/dist/ja-JTVWAC4L.js.map +0 -1
- package/dist/ko-IHXFMOIZ.js.map +0 -1
- package/dist/ms-JH25VFLO.js.map +0 -1
- package/dist/nl-SURXW7IQ.js.map +0 -1
- package/dist/no-HXIKQFDZ.js.map +0 -1
- package/dist/pl-7A4KU6XK.js.map +0 -1
- package/dist/pt-6MGYP7Q5.js.map +0 -1
- package/dist/ro-IROCHNIA.js.map +0 -1
- package/dist/sv-WDJD75CV.js.map +0 -1
- package/dist/th-RIO2E6B3.js.map +0 -1
- package/dist/tr-TZ7ERCIN.js.map +0 -1
- package/dist/uk-ZFWDKLUE.js.map +0 -1
- package/dist/vi-UIOSP2JG.js.map +0 -1
- package/dist/zh-6R6YUY46.js.map +0 -1
- /package/dist/{en-P2L4X27N.js.map → en-QFIEG3CA.js.map} +0 -0
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { asStates } from '../../../../__tests__/test-client';
|
|
3
|
+
import { sessionOf } from '../../../../__tests__/test-client';
|
|
2
4
|
import { BehaviorSubject, firstValueFrom } from 'rxjs';
|
|
3
5
|
import { filter } from 'rxjs/operators';
|
|
4
6
|
import type { SemiontClient } from '@semiont/sdk';
|
|
5
7
|
import type { ShellStateUnit } from '../../../../state/shell-state-unit';
|
|
6
8
|
import { createEntityTagsStateUnit } from '../entity-tags-state-unit';
|
|
7
|
-
import { assertStateUnitAxioms, disposeProbe } from '@semiont/core/testing';
|
|
9
|
+
import { assertStateUnitAxioms, disposeProbe } from '@semiont/core/testing/axioms';
|
|
8
10
|
|
|
9
11
|
function mockBrowse(): ShellStateUnit {
|
|
10
12
|
return { dispose: vi.fn() } as unknown as ShellStateUnit;
|
|
@@ -17,7 +19,7 @@ function mockClient(overrides: {
|
|
|
17
19
|
const entityTypes$ = overrides.entityTypes$ ?? new BehaviorSubject<string[] | undefined>(['Person', 'Place']);
|
|
18
20
|
return {
|
|
19
21
|
browse: {
|
|
20
|
-
entityTypes: () => entityTypes$.asObservable(),
|
|
22
|
+
entityTypes: () => asStates(entityTypes$.asObservable()),
|
|
21
23
|
},
|
|
22
24
|
frame: {
|
|
23
25
|
addEntityType: overrides.addEntityType ?? vi.fn().mockResolvedValue(undefined),
|
|
@@ -27,9 +29,9 @@ function mockClient(overrides: {
|
|
|
27
29
|
|
|
28
30
|
describe('createEntityTagsStateUnit', () => {
|
|
29
31
|
it('exposes entity types from browse namespace', async () => {
|
|
30
|
-
const stateUnit = createEntityTagsStateUnit(mockClient(), mockBrowse());
|
|
32
|
+
const stateUnit = createEntityTagsStateUnit(sessionOf(mockClient()), mockBrowse());
|
|
31
33
|
|
|
32
|
-
const types = await firstValueFrom(stateUnit.entityTypes$);
|
|
34
|
+
const types = await firstValueFrom(stateUnit.entityTypes.value$);
|
|
33
35
|
expect(types).toEqual(['Person', 'Place']);
|
|
34
36
|
|
|
35
37
|
stateUnit.dispose();
|
|
@@ -37,20 +39,20 @@ describe('createEntityTagsStateUnit', () => {
|
|
|
37
39
|
|
|
38
40
|
it('reports loading when entity types are undefined', async () => {
|
|
39
41
|
const entityTypes$ = new BehaviorSubject<string[] | undefined>(undefined);
|
|
40
|
-
const stateUnit = createEntityTagsStateUnit(mockClient({ entityTypes$ }), mockBrowse());
|
|
42
|
+
const stateUnit = createEntityTagsStateUnit(sessionOf(mockClient({ entityTypes$ })), mockBrowse());
|
|
41
43
|
|
|
42
|
-
const loading = await firstValueFrom(stateUnit.
|
|
44
|
+
const loading = await firstValueFrom(stateUnit.entityTypes.loading$);
|
|
43
45
|
expect(loading).toBe(true);
|
|
44
46
|
|
|
45
47
|
entityTypes$.next(['Tag']);
|
|
46
|
-
const loaded = await firstValueFrom(stateUnit.
|
|
48
|
+
const loaded = await firstValueFrom(stateUnit.entityTypes.loading$.pipe(filter((l) => !l)));
|
|
47
49
|
expect(loaded).toBe(false);
|
|
48
50
|
|
|
49
51
|
stateUnit.dispose();
|
|
50
52
|
});
|
|
51
53
|
|
|
52
54
|
it('setNewTag updates newTag$', async () => {
|
|
53
|
-
const stateUnit = createEntityTagsStateUnit(mockClient(), mockBrowse());
|
|
55
|
+
const stateUnit = createEntityTagsStateUnit(sessionOf(mockClient()), mockBrowse());
|
|
54
56
|
|
|
55
57
|
stateUnit.setNewTag('Organization');
|
|
56
58
|
const tag = await firstValueFrom(stateUnit.newTag$);
|
|
@@ -61,7 +63,7 @@ describe('createEntityTagsStateUnit', () => {
|
|
|
61
63
|
|
|
62
64
|
it('addTag calls client and clears newTag$', async () => {
|
|
63
65
|
const addEntityType = vi.fn().mockResolvedValue(undefined);
|
|
64
|
-
const stateUnit = createEntityTagsStateUnit(mockClient({ addEntityType }), mockBrowse());
|
|
66
|
+
const stateUnit = createEntityTagsStateUnit(sessionOf(mockClient({ addEntityType })), mockBrowse());
|
|
65
67
|
|
|
66
68
|
stateUnit.setNewTag('Event');
|
|
67
69
|
await stateUnit.addTag();
|
|
@@ -75,7 +77,7 @@ describe('createEntityTagsStateUnit', () => {
|
|
|
75
77
|
|
|
76
78
|
it('addTag ignores empty/whitespace input', async () => {
|
|
77
79
|
const addEntityType = vi.fn();
|
|
78
|
-
const stateUnit = createEntityTagsStateUnit(mockClient({ addEntityType }), mockBrowse());
|
|
80
|
+
const stateUnit = createEntityTagsStateUnit(sessionOf(mockClient({ addEntityType })), mockBrowse());
|
|
79
81
|
|
|
80
82
|
stateUnit.setNewTag(' ');
|
|
81
83
|
await stateUnit.addTag();
|
|
@@ -87,7 +89,7 @@ describe('createEntityTagsStateUnit', () => {
|
|
|
87
89
|
|
|
88
90
|
it('addTag sets error on failure', async () => {
|
|
89
91
|
const addEntityType = vi.fn().mockRejectedValue(new Error('duplicate'));
|
|
90
|
-
const stateUnit = createEntityTagsStateUnit(mockClient({ addEntityType }), mockBrowse());
|
|
92
|
+
const stateUnit = createEntityTagsStateUnit(sessionOf(mockClient({ addEntityType })), mockBrowse());
|
|
91
93
|
|
|
92
94
|
stateUnit.setNewTag('Person');
|
|
93
95
|
await stateUnit.addTag();
|
|
@@ -107,10 +109,33 @@ describe('EntityTagsStateUnit — StateUnit axioms', () => {
|
|
|
107
109
|
assertStateUnitAxioms({
|
|
108
110
|
setup: () => {
|
|
109
111
|
const browse = disposeProbe();
|
|
110
|
-
return { unit: createEntityTagsStateUnit(mockClient(), browse as unknown as ShellStateUnit), passedIn: [browse] };
|
|
112
|
+
return { unit: createEntityTagsStateUnit(sessionOf(mockClient()), browse as unknown as ShellStateUnit), passedIn: [browse] };
|
|
111
113
|
},
|
|
112
114
|
surfaces: (u) => [u.newTag$, u.error$, u.isAdding$],
|
|
113
115
|
invocations: (u) => [() => u.setNewTag(''), () => u.addTag()],
|
|
114
116
|
});
|
|
115
117
|
});
|
|
116
118
|
});
|
|
119
|
+
|
|
120
|
+
describe('createEntityTagsStateUnit — terminal load failure', () => {
|
|
121
|
+
// `isLoading$` was `raw$.pipe(map(e => e === undefined))` and
|
|
122
|
+
// moderate/entity-tags/page.tsx returns a full-page loading screen off it,
|
|
123
|
+
// so a failed load froze the route. The existing `error$` is the ADD-tag
|
|
124
|
+
// error and is deliberately left alone.
|
|
125
|
+
// See .plans/PANEL-FAILURE-STATES.md
|
|
126
|
+
|
|
127
|
+
it('stops loading and surfaces the load failure, distinct from the add-tag error', async () => {
|
|
128
|
+
const entityTypes$ = new BehaviorSubject<string[] | undefined>(undefined);
|
|
129
|
+
const unit = createEntityTagsStateUnit(sessionOf(mockClient({ entityTypes$ })), mockBrowse());
|
|
130
|
+
|
|
131
|
+
expect(await firstValueFrom(unit.entityTypes.loading$)).toBe(true);
|
|
132
|
+
entityTypes$.next({ status: 'failed', error: new Error('Resource not found') } as never);
|
|
133
|
+
|
|
134
|
+
expect(await firstValueFrom(unit.entityTypes.loading$)).toBe(false);
|
|
135
|
+
expect(await firstValueFrom(unit.entityTypes.error$)).not.toBeNull();
|
|
136
|
+
// The add-tag error channel is untouched by a load failure.
|
|
137
|
+
expect(await firstValueFrom(unit.error$)).toBe('');
|
|
138
|
+
|
|
139
|
+
unit.dispose();
|
|
140
|
+
});
|
|
141
|
+
});
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { BehaviorSubject, type Observable
|
|
1
|
+
import { BehaviorSubject, type Observable } from 'rxjs';
|
|
2
2
|
import { createDisposer } from '@semiont/sdk';
|
|
3
3
|
import type { StateUnit } from '@semiont/core';
|
|
4
4
|
import type { ShellStateUnit } from '../../../state/shell-state-unit';
|
|
5
|
-
import type
|
|
5
|
+
import { trackList, type ListState } from '../../../state/list-state';
|
|
6
|
+
import type { SemiontSession } from '@semiont/sdk';
|
|
6
7
|
|
|
7
8
|
export interface EntityTagsStateUnit extends StateUnit {
|
|
8
9
|
browse: ShellStateUnit;
|
|
9
|
-
entityTypes
|
|
10
|
-
isLoading$: Observable<boolean>;
|
|
10
|
+
entityTypes: ListState<string[]>;
|
|
11
11
|
newTag$: Observable<string>;
|
|
12
12
|
error$: Observable<string>;
|
|
13
13
|
isAdding$: Observable<boolean>;
|
|
@@ -16,9 +16,10 @@ export interface EntityTagsStateUnit extends StateUnit {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
export function createEntityTagsStateUnit(
|
|
19
|
-
|
|
19
|
+
session: SemiontSession,
|
|
20
20
|
browse: ShellStateUnit,
|
|
21
21
|
): EntityTagsStateUnit {
|
|
22
|
+
const { client } = session;
|
|
22
23
|
const disposer = createDisposer();
|
|
23
24
|
// `browse` (ShellStateUnit) is a *passed-in* dependency owned by the caller
|
|
24
25
|
// (`useShellStateUnit`), not this unit — do NOT add it to the disposer (it's the
|
|
@@ -28,9 +29,8 @@ export function createEntityTagsStateUnit(
|
|
|
28
29
|
const error$ = new BehaviorSubject<string>('');
|
|
29
30
|
const isAdding$ = new BehaviorSubject<boolean>(false);
|
|
30
31
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
const isLoading$: Observable<boolean> = raw$.pipe(map((e) => e === undefined));
|
|
32
|
+
const entityTypes = trackList<string[]>(() => client.browse.entityTypes(), []);
|
|
33
|
+
disposer.add(entityTypes.dispose);
|
|
34
34
|
|
|
35
35
|
const addTag = async (): Promise<void> => {
|
|
36
36
|
const tag = newTag$.getValue().trim();
|
|
@@ -49,8 +49,7 @@ export function createEntityTagsStateUnit(
|
|
|
49
49
|
|
|
50
50
|
return {
|
|
51
51
|
browse,
|
|
52
|
-
entityTypes
|
|
53
|
-
isLoading$,
|
|
52
|
+
entityTypes: entityTypes.state,
|
|
54
53
|
newTag$: newTag$.asObservable(),
|
|
55
54
|
error$: error$.asObservable(),
|
|
56
55
|
isAdding$: isAdding$.asObservable(),
|
|
@@ -14,7 +14,6 @@ const createMockProps = (overrides?: Partial<RecentDocumentsPageProps>): RecentD
|
|
|
14
14
|
hasDocuments: false,
|
|
15
15
|
isLoading: false,
|
|
16
16
|
theme: 'light',
|
|
17
|
-
showLineNumbers: false,
|
|
18
17
|
activePanel: null,
|
|
19
18
|
translations: {
|
|
20
19
|
pageTitle: 'Recent Documents',
|
|
@@ -205,13 +204,13 @@ describe('RecentDocumentsPage', () => {
|
|
|
205
204
|
);
|
|
206
205
|
});
|
|
207
206
|
|
|
208
|
-
it('
|
|
207
|
+
it('does not thread showLineNumbers to toolbar panels — the setting is context-backed', () => {
|
|
209
208
|
const ToolbarPanels = vi.fn(() => <div data-testid="toolbar-panels" />);
|
|
210
|
-
const props = createMockProps({
|
|
209
|
+
const props = createMockProps({ToolbarPanels });
|
|
211
210
|
render(<RecentDocumentsPage {...props} />);
|
|
212
211
|
|
|
213
212
|
expect(ToolbarPanels).toHaveBeenCalledWith(
|
|
214
|
-
expect.objectContaining({ showLineNumbers:
|
|
213
|
+
expect.not.objectContaining({ showLineNumbers: expect.anything() }),
|
|
215
214
|
undefined,
|
|
216
215
|
);
|
|
217
216
|
});
|
|
@@ -342,7 +341,6 @@ describe('RecentDocumentsPage', () => {
|
|
|
342
341
|
const props = createMockProps({
|
|
343
342
|
hasDocuments: true,
|
|
344
343
|
theme: 'dark',
|
|
345
|
-
showLineNumbers: true,
|
|
346
344
|
activePanel: 'settings',
|
|
347
345
|
});
|
|
348
346
|
render(<RecentDocumentsPage {...props} />);
|
|
@@ -15,7 +15,6 @@ export interface RecentDocumentsPageProps {
|
|
|
15
15
|
|
|
16
16
|
// UI state
|
|
17
17
|
theme: 'light' | 'dark' | 'system';
|
|
18
|
-
showLineNumbers: boolean;
|
|
19
18
|
activePanel: string | null;
|
|
20
19
|
|
|
21
20
|
// Translations
|
|
@@ -37,7 +36,6 @@ export interface RecentDocumentsPageProps {
|
|
|
37
36
|
export function RecentDocumentsPage({
|
|
38
37
|
isLoading,
|
|
39
38
|
theme,
|
|
40
|
-
showLineNumbers,
|
|
41
39
|
activePanel,
|
|
42
40
|
translations: t,
|
|
43
41
|
ToolbarPanels,
|
|
@@ -95,7 +93,6 @@ export function RecentDocumentsPage({
|
|
|
95
93
|
<ToolbarPanels
|
|
96
94
|
activePanel={activePanel}
|
|
97
95
|
theme={theme}
|
|
98
|
-
showLineNumbers={showLineNumbers}
|
|
99
96
|
/>
|
|
100
97
|
|
|
101
98
|
<Toolbar
|
|
@@ -61,7 +61,6 @@ const createMockProps = (overrides?: Partial<TagSchemasPageProps>): TagSchemasPa
|
|
|
61
61
|
schemas: mockSchemas,
|
|
62
62
|
isLoading: false,
|
|
63
63
|
theme: 'light',
|
|
64
|
-
showLineNumbers: false,
|
|
65
64
|
activePanel: null,
|
|
66
65
|
translations: {
|
|
67
66
|
pageTitle: 'Tag Schemas',
|
|
@@ -276,13 +275,13 @@ describe('TagSchemasPage', () => {
|
|
|
276
275
|
);
|
|
277
276
|
});
|
|
278
277
|
|
|
279
|
-
it('
|
|
278
|
+
it('does not thread showLineNumbers to toolbar panels — the setting is context-backed', () => {
|
|
280
279
|
const ToolbarPanels = vi.fn(() => <div data-testid="toolbar-panels" />);
|
|
281
|
-
const props = createMockProps({
|
|
280
|
+
const props = createMockProps({ToolbarPanels });
|
|
282
281
|
render(<TagSchemasPage {...props} />);
|
|
283
282
|
|
|
284
283
|
expect(ToolbarPanels).toHaveBeenCalledWith(
|
|
285
|
-
expect.objectContaining({ showLineNumbers:
|
|
284
|
+
expect.not.objectContaining({ showLineNumbers: expect.anything() }),
|
|
286
285
|
undefined,
|
|
287
286
|
);
|
|
288
287
|
});
|
|
@@ -420,7 +419,6 @@ describe('TagSchemasPage', () => {
|
|
|
420
419
|
it('handles all props being defined', () => {
|
|
421
420
|
const props = createMockProps({
|
|
422
421
|
theme: 'dark',
|
|
423
|
-
showLineNumbers: true,
|
|
424
422
|
activePanel: 'settings',
|
|
425
423
|
});
|
|
426
424
|
render(<TagSchemasPage {...props} />);
|
|
@@ -22,7 +22,6 @@ export interface TagSchemasPageProps {
|
|
|
22
22
|
|
|
23
23
|
// UI state
|
|
24
24
|
theme: 'light' | 'dark' | 'system';
|
|
25
|
-
showLineNumbers: boolean;
|
|
26
25
|
activePanel: string | null;
|
|
27
26
|
|
|
28
27
|
// Translations
|
|
@@ -54,7 +53,6 @@ export function TagSchemasPage({
|
|
|
54
53
|
schemas,
|
|
55
54
|
isLoading,
|
|
56
55
|
theme,
|
|
57
|
-
showLineNumbers,
|
|
58
56
|
activePanel,
|
|
59
57
|
translations: t,
|
|
60
58
|
ToolbarPanels,
|
|
@@ -153,7 +151,6 @@ export function TagSchemasPage({
|
|
|
153
151
|
<ToolbarPanels
|
|
154
152
|
activePanel={activePanel}
|
|
155
153
|
theme={theme}
|
|
156
|
-
showLineNumbers={showLineNumbers}
|
|
157
154
|
/>
|
|
158
155
|
|
|
159
156
|
<Toolbar
|
|
@@ -39,7 +39,6 @@ export interface LinkedDataPageProps {
|
|
|
39
39
|
|
|
40
40
|
// UI state
|
|
41
41
|
theme: 'light' | 'dark' | 'system';
|
|
42
|
-
showLineNumbers: boolean;
|
|
43
42
|
activePanel: string | null;
|
|
44
43
|
|
|
45
44
|
// Translations
|
|
@@ -63,7 +62,6 @@ export function LinkedDataPage({
|
|
|
63
62
|
importMessage,
|
|
64
63
|
importResult,
|
|
65
64
|
theme,
|
|
66
|
-
showLineNumbers,
|
|
67
65
|
activePanel,
|
|
68
66
|
translations: t,
|
|
69
67
|
ToolbarPanels,
|
|
@@ -109,7 +107,6 @@ export function LinkedDataPage({
|
|
|
109
107
|
<ToolbarPanels
|
|
110
108
|
activePanel={activePanel}
|
|
111
109
|
theme={theme}
|
|
112
|
-
showLineNumbers={showLineNumbers}
|
|
113
110
|
/>
|
|
114
111
|
<Toolbar
|
|
115
112
|
context="simple"
|
|
@@ -10,12 +10,14 @@ import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
|
10
10
|
import { ResourceComposePage } from '../components/ResourceComposePage';
|
|
11
11
|
import type { ResourceComposePageProps } from '../components/ResourceComposePage';
|
|
12
12
|
import { createTestSemiontWrapper } from '../../../test-utils';
|
|
13
|
+
import { LineNumbersProvider } from '../../../contexts/LineNumbersContext';
|
|
13
14
|
|
|
14
15
|
// Mock CodeMirrorRenderer to avoid CodeMirror dependencies
|
|
15
16
|
vi.mock('../../../components/CodeMirrorRenderer', () => ({
|
|
16
|
-
CodeMirrorRenderer: ({ content, onChange, editable }: any) => (
|
|
17
|
+
CodeMirrorRenderer: ({ content, onChange, editable, showLineNumbers }: any) => (
|
|
17
18
|
<textarea
|
|
18
19
|
data-testid="code-editor"
|
|
20
|
+
data-line-numbers={String(showLineNumbers)}
|
|
19
21
|
value={content}
|
|
20
22
|
onChange={(e) => editable && onChange?.(e.target.value)}
|
|
21
23
|
disabled={!editable}
|
|
@@ -62,7 +64,6 @@ const createMockProps = (overrides?: Partial<ResourceComposePageProps>): Resourc
|
|
|
62
64
|
availableEntityTypes: ['Document', 'Article', 'Report'],
|
|
63
65
|
initialLocale: 'en',
|
|
64
66
|
theme: 'light',
|
|
65
|
-
showLineNumbers: false,
|
|
66
67
|
hoverDelayMs: 0,
|
|
67
68
|
activePanel: null,
|
|
68
69
|
onSaveResource: vi.fn().mockResolvedValue(undefined),
|
|
@@ -77,7 +78,11 @@ const createMockProps = (overrides?: Partial<ResourceComposePageProps>): Resourc
|
|
|
77
78
|
// component reaches for via useSemiont/useObservable).
|
|
78
79
|
const renderWithProviders = (ui: React.ReactElement) => {
|
|
79
80
|
const { SemiontWrapper } = createTestSemiontWrapper();
|
|
80
|
-
return render(
|
|
81
|
+
return render(
|
|
82
|
+
<LineNumbersProvider>
|
|
83
|
+
<SemiontWrapper>{ui}</SemiontWrapper>
|
|
84
|
+
</LineNumbersProvider>,
|
|
85
|
+
);
|
|
81
86
|
};
|
|
82
87
|
|
|
83
88
|
describe('ResourceComposePage', () => {
|
|
@@ -500,11 +505,16 @@ describe('ResourceComposePage', () => {
|
|
|
500
505
|
expect(editor.value).toBe('New content');
|
|
501
506
|
});
|
|
502
507
|
|
|
503
|
-
it('
|
|
504
|
-
|
|
505
|
-
|
|
508
|
+
it('feeds the editor the shared line-numbers setting from context', () => {
|
|
509
|
+
localStorage.setItem('showLineNumbers', 'true');
|
|
510
|
+
try {
|
|
511
|
+
const props = createMockProps({});
|
|
512
|
+
renderWithProviders(<ResourceComposePage {...props} />);
|
|
506
513
|
|
|
507
|
-
|
|
514
|
+
expect(screen.getByTestId('code-editor')).toHaveAttribute('data-line-numbers', 'true');
|
|
515
|
+
} finally {
|
|
516
|
+
localStorage.removeItem('showLineNumbers');
|
|
517
|
+
}
|
|
508
518
|
});
|
|
509
519
|
});
|
|
510
520
|
});
|
|
@@ -15,6 +15,7 @@ import { COMMON_PANELS, type ToolbarPanelType } from '../../../state/shell-state
|
|
|
15
15
|
import { buttonStyles } from '../../../lib/button-styles';
|
|
16
16
|
import { CodeMirrorRenderer } from '../../../components/CodeMirrorRenderer';
|
|
17
17
|
import { useFormAnnouncements } from '../../../components/LiveRegion';
|
|
18
|
+
import { useLineNumbers } from '../../../contexts/LineNumbersContext';
|
|
18
19
|
import { UploadProgressBar } from './UploadProgressBar';
|
|
19
20
|
|
|
20
21
|
/**
|
|
@@ -54,7 +55,6 @@ export interface ResourceComposePageProps {
|
|
|
54
55
|
|
|
55
56
|
// UI state
|
|
56
57
|
theme: 'light' | 'dark';
|
|
57
|
-
showLineNumbers: boolean;
|
|
58
58
|
hoverDelayMs: number;
|
|
59
59
|
activePanel: string | null;
|
|
60
60
|
|
|
@@ -133,7 +133,6 @@ export function ResourceComposePage({
|
|
|
133
133
|
availableEntityTypes,
|
|
134
134
|
initialLocale,
|
|
135
135
|
theme,
|
|
136
|
-
showLineNumbers,
|
|
137
136
|
hoverDelayMs,
|
|
138
137
|
activePanel,
|
|
139
138
|
onSaveResource,
|
|
@@ -146,6 +145,10 @@ export function ResourceComposePage({
|
|
|
146
145
|
// Form announcements
|
|
147
146
|
const { announceFormSubmitting, announceFormSuccess, announceFormError } = useFormAnnouncements();
|
|
148
147
|
|
|
148
|
+
// Shared display setting (LineNumbersContext) — was a prop threaded from
|
|
149
|
+
// the route, which desynced it from the Settings toggle.
|
|
150
|
+
const { showLineNumbers } = useLineNumbers();
|
|
151
|
+
|
|
149
152
|
// Form state
|
|
150
153
|
const [newResourceName, setNewResourceName] = useState('');
|
|
151
154
|
const [newResourceContent, setNewResourceContent] = useState('');
|
|
@@ -746,7 +749,6 @@ export function ResourceComposePage({
|
|
|
746
749
|
<ToolbarPanels
|
|
747
750
|
activePanel={activePanel}
|
|
748
751
|
theme={theme}
|
|
749
|
-
showLineNumbers={showLineNumbers}
|
|
750
752
|
/>
|
|
751
753
|
|
|
752
754
|
{/* Toolbar - Always visible on the right */}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { asStates } from '../../../../__tests__/test-client';
|
|
3
|
+
import { sessionOf } from '../../../../__tests__/test-client';
|
|
2
4
|
import { BehaviorSubject, Observable, firstValueFrom } from 'rxjs';
|
|
3
5
|
import { filter } from 'rxjs/operators';
|
|
4
6
|
import type { SemiontClient } from '@semiont/sdk';
|
|
5
7
|
import type { ShellStateUnit } from '../../../../state/shell-state-unit';
|
|
6
8
|
import { createComposePageStateUnit } from '../compose-page-state-unit';
|
|
7
|
-
import { assertStateUnitAxioms, disposeProbe } from '@semiont/core/testing';
|
|
9
|
+
import { assertStateUnitAxioms, disposeProbe } from '@semiont/core/testing/axioms';
|
|
8
10
|
|
|
9
11
|
/** Build an `UploadObservable`-shaped mock that emits started → finished. */
|
|
10
12
|
function mockUpload(resourceId: string) {
|
|
@@ -32,7 +34,7 @@ function mockClient(overrides: {
|
|
|
32
34
|
const entityTypes$ = overrides.entityTypes$ ?? new BehaviorSubject<string[] | undefined>(['Person']);
|
|
33
35
|
return {
|
|
34
36
|
browse: {
|
|
35
|
-
entityTypes: () => entityTypes$.asObservable(),
|
|
37
|
+
entityTypes: () => asStates(entityTypes$.asObservable()),
|
|
36
38
|
},
|
|
37
39
|
yield: {
|
|
38
40
|
fromToken: overrides.fromToken ?? vi.fn().mockResolvedValue({ '@id': 'src-1', representations: [{ mediaType: 'text/plain' }] }),
|
|
@@ -51,7 +53,7 @@ function mockClient(overrides: {
|
|
|
51
53
|
|
|
52
54
|
describe('createComposePageStateUnit', () => {
|
|
53
55
|
it('defaults to "new" mode', async () => {
|
|
54
|
-
const stateUnit = createComposePageStateUnit(mockClient(), mockBrowse(), {});
|
|
56
|
+
const stateUnit = createComposePageStateUnit(sessionOf(mockClient()), mockBrowse(), {});
|
|
55
57
|
|
|
56
58
|
const mode = await firstValueFrom(stateUnit.mode$);
|
|
57
59
|
expect(mode).toBe('new');
|
|
@@ -63,7 +65,7 @@ describe('createComposePageStateUnit', () => {
|
|
|
63
65
|
});
|
|
64
66
|
|
|
65
67
|
it('detects reference mode from params', async () => {
|
|
66
|
-
const stateUnit = createComposePageStateUnit(mockClient(), mockBrowse(), {
|
|
68
|
+
const stateUnit = createComposePageStateUnit(sessionOf(mockClient()), mockBrowse(), {
|
|
67
69
|
annotationUri: 'ann-1',
|
|
68
70
|
sourceDocumentId: 'doc-1',
|
|
69
71
|
name: 'Reference Doc',
|
|
@@ -82,7 +84,7 @@ describe('createComposePageStateUnit', () => {
|
|
|
82
84
|
|
|
83
85
|
it('parses storedContext in reference mode', async () => {
|
|
84
86
|
const context = { annotation: { id: 'ann-1' }, sourceContext: 'text' };
|
|
85
|
-
const stateUnit = createComposePageStateUnit(mockClient(), mockBrowse(), {
|
|
87
|
+
const stateUnit = createComposePageStateUnit(sessionOf(mockClient()), mockBrowse(), {
|
|
86
88
|
annotationUri: 'ann-1',
|
|
87
89
|
sourceDocumentId: 'doc-1',
|
|
88
90
|
name: 'Ref',
|
|
@@ -96,7 +98,7 @@ describe('createComposePageStateUnit', () => {
|
|
|
96
98
|
});
|
|
97
99
|
|
|
98
100
|
it('ignores malformed storedContext', async () => {
|
|
99
|
-
const stateUnit = createComposePageStateUnit(mockClient(), mockBrowse(), {
|
|
101
|
+
const stateUnit = createComposePageStateUnit(sessionOf(mockClient()), mockBrowse(), {
|
|
100
102
|
annotationUri: 'ann-1',
|
|
101
103
|
sourceDocumentId: 'doc-1',
|
|
102
104
|
name: 'Ref',
|
|
@@ -113,9 +115,9 @@ describe('createComposePageStateUnit', () => {
|
|
|
113
115
|
});
|
|
114
116
|
|
|
115
117
|
it('exposes entity types', async () => {
|
|
116
|
-
const stateUnit = createComposePageStateUnit(mockClient(), mockBrowse(), {});
|
|
118
|
+
const stateUnit = createComposePageStateUnit(sessionOf(mockClient()), mockBrowse(), {});
|
|
117
119
|
|
|
118
|
-
const types = await firstValueFrom(stateUnit.entityTypes$);
|
|
120
|
+
const types = await firstValueFrom(stateUnit.entityTypes.value$);
|
|
119
121
|
expect(types).toEqual(['Person']);
|
|
120
122
|
|
|
121
123
|
stateUnit.dispose();
|
|
@@ -123,7 +125,7 @@ describe('createComposePageStateUnit', () => {
|
|
|
123
125
|
|
|
124
126
|
it('save in new mode calls yield.resource', async () => {
|
|
125
127
|
const resource = mockUpload('new-3');
|
|
126
|
-
const stateUnit = createComposePageStateUnit(mockClient({ resource }), mockBrowse(), {});
|
|
128
|
+
const stateUnit = createComposePageStateUnit(sessionOf(mockClient({ resource })), mockBrowse(), {});
|
|
127
129
|
|
|
128
130
|
const id = await stateUnit.save({
|
|
129
131
|
mode: 'new',
|
|
@@ -143,7 +145,7 @@ describe('createComposePageStateUnit', () => {
|
|
|
143
145
|
it('save in reference mode calls yield.resource then bind.body', async () => {
|
|
144
146
|
const resource = mockUpload('new-4');
|
|
145
147
|
const body = vi.fn().mockResolvedValue(undefined);
|
|
146
|
-
const stateUnit = createComposePageStateUnit(mockClient({ resource, body }), mockBrowse(), {
|
|
148
|
+
const stateUnit = createComposePageStateUnit(sessionOf(mockClient({ resource, body })), mockBrowse(), {
|
|
147
149
|
annotationUri: 'ann-1',
|
|
148
150
|
sourceDocumentId: 'doc-1',
|
|
149
151
|
name: 'Ref',
|
|
@@ -167,7 +169,7 @@ describe('createComposePageStateUnit', () => {
|
|
|
167
169
|
|
|
168
170
|
it('save in clone mode calls yield.createFromToken', async () => {
|
|
169
171
|
const createFromToken = vi.fn().mockResolvedValue({ resourceId: 'cloned-1' });
|
|
170
|
-
const stateUnit = createComposePageStateUnit(mockClient({ createFromToken }), mockBrowse(), {
|
|
172
|
+
const stateUnit = createComposePageStateUnit(sessionOf(mockClient({ createFromToken })), mockBrowse(), {
|
|
171
173
|
mode: 'clone',
|
|
172
174
|
token: 'tok-abc',
|
|
173
175
|
});
|
|
@@ -192,9 +194,28 @@ describe('ComposePageStateUnit — StateUnit axioms', () => {
|
|
|
192
194
|
assertStateUnitAxioms({
|
|
193
195
|
setup: () => {
|
|
194
196
|
const browse = disposeProbe();
|
|
195
|
-
return { unit: createComposePageStateUnit(mockClient(), browse as unknown as ShellStateUnit, {}), passedIn: [browse] };
|
|
197
|
+
return { unit: createComposePageStateUnit(sessionOf(mockClient()), browse as unknown as ShellStateUnit, {}), passedIn: [browse] };
|
|
196
198
|
},
|
|
197
199
|
surfaces: (u) => [u.mode$, u.loading$, u.cloneData$, u.referenceData$, u.gatheredContext$, u.uploadProgress$],
|
|
198
200
|
});
|
|
199
201
|
});
|
|
200
202
|
});
|
|
203
|
+
|
|
204
|
+
describe('createComposePageStateUnit — entity types load failure', () => {
|
|
205
|
+
// Lower severity than the two hangs: compose defaults to an empty picker,
|
|
206
|
+
// which misrepresents a failed load as "this KB has no entity types".
|
|
207
|
+
// See .plans/PANEL-FAILURE-STATES.md
|
|
208
|
+
|
|
209
|
+
it('surfaces the failure rather than presenting an empty picker as fact', async () => {
|
|
210
|
+
const entityTypes$ = new BehaviorSubject<string[] | undefined>(undefined);
|
|
211
|
+
const stateUnit = createComposePageStateUnit(sessionOf(mockClient({ entityTypes$ })), mockBrowse(), {});
|
|
212
|
+
|
|
213
|
+
entityTypes$.next({ status: 'failed', error: new Error('boom') } as never);
|
|
214
|
+
|
|
215
|
+
expect(await firstValueFrom(stateUnit.entityTypes.loading$)).toBe(false);
|
|
216
|
+
expect(await firstValueFrom(stateUnit.entityTypes.error$)).not.toBeNull();
|
|
217
|
+
expect(await firstValueFrom(stateUnit.entityTypes.value$)).toEqual([]);
|
|
218
|
+
|
|
219
|
+
stateUnit.dispose();
|
|
220
|
+
});
|
|
221
|
+
});
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { BehaviorSubject, type Observable
|
|
1
|
+
import { BehaviorSubject, type Observable } 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
5
|
import type { StateUnit } from '@semiont/core';
|
|
6
6
|
import type { ShellStateUnit } from '../../../state/shell-state-unit';
|
|
7
|
-
import type
|
|
7
|
+
import { trackList, type ListState } from '../../../state/list-state';
|
|
8
|
+
import type { SemiontSession } from '@semiont/sdk';
|
|
8
9
|
import { decodeWithCharset, extensionForMediaType } from '@semiont/core';
|
|
9
10
|
import type { UploadProgress } from '@semiont/sdk';
|
|
10
11
|
|
|
@@ -54,7 +55,7 @@ export interface ComposePageStateUnit extends StateUnit {
|
|
|
54
55
|
cloneData$: Observable<CloneData | null>;
|
|
55
56
|
referenceData$: Observable<ReferenceData | null>;
|
|
56
57
|
gatheredContext$: Observable<GatheredContext | null>;
|
|
57
|
-
entityTypes
|
|
58
|
+
entityTypes: ListState<string[]>;
|
|
58
59
|
/**
|
|
59
60
|
* Live upload-progress for the in-flight `save(...)` call. Emits the
|
|
60
61
|
* full `UploadProgress` lifecycle (started → finished) while a save is
|
|
@@ -66,11 +67,12 @@ export interface ComposePageStateUnit extends StateUnit {
|
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
export function createComposePageStateUnit(
|
|
69
|
-
|
|
70
|
+
session: SemiontSession,
|
|
70
71
|
browse: ShellStateUnit,
|
|
71
72
|
params: ComposeParams,
|
|
72
73
|
auth?: AccessToken,
|
|
73
74
|
): ComposePageStateUnit {
|
|
75
|
+
const { client } = session;
|
|
74
76
|
const disposer = createDisposer();
|
|
75
77
|
// `browse` (ShellStateUnit) is a *passed-in* dependency owned by the caller
|
|
76
78
|
// (`useShellStateUnit`), not this unit — do NOT add it to the disposer (it's the
|
|
@@ -87,9 +89,8 @@ export function createComposePageStateUnit(
|
|
|
87
89
|
const gatheredContext$ = new BehaviorSubject<GatheredContext | null>(null);
|
|
88
90
|
const uploadProgress$ = new BehaviorSubject<UploadProgress | null>(null);
|
|
89
91
|
|
|
90
|
-
const entityTypes
|
|
91
|
-
|
|
92
|
-
);
|
|
92
|
+
const entityTypes = trackList<string[]>(() => client.browse.entityTypes(), []);
|
|
93
|
+
disposer.add(entityTypes.dispose);
|
|
93
94
|
|
|
94
95
|
// Initialize based on mode
|
|
95
96
|
if (isReferenceMode) {
|
|
@@ -192,7 +193,7 @@ export function createComposePageStateUnit(
|
|
|
192
193
|
cloneData$: cloneData$.asObservable(),
|
|
193
194
|
referenceData$: referenceData$.asObservable(),
|
|
194
195
|
gatheredContext$: gatheredContext$.asObservable(),
|
|
195
|
-
entityTypes
|
|
196
|
+
entityTypes: entityTypes.state,
|
|
196
197
|
uploadProgress$: uploadProgress$.asObservable(),
|
|
197
198
|
save,
|
|
198
199
|
dispose: () => {
|
|
@@ -36,7 +36,6 @@ const createMockProps = (overrides?: Partial<ResourceDiscoveryPageProps>): Resou
|
|
|
36
36
|
selectedEntityType: '',
|
|
37
37
|
onSelectedEntityTypeChange: vi.fn(),
|
|
38
38
|
theme: 'light',
|
|
39
|
-
showLineNumbers: false,
|
|
40
39
|
activePanel: null,
|
|
41
40
|
onNavigateToResource: vi.fn(),
|
|
42
41
|
onNavigateToCompose: vi.fn(),
|
|
@@ -56,7 +55,9 @@ const createMockProps = (overrides?: Partial<ResourceDiscoveryPageProps>): Resou
|
|
|
56
55
|
composeFirstResource: 'Compose First Resource',
|
|
57
56
|
archived: 'Archived',
|
|
58
57
|
created: 'Created:',
|
|
59
|
-
|
|
58
|
+
recentFailed: 'Could not load resources.',
|
|
59
|
+
retry: 'Try again',
|
|
60
|
+
loadingKnowledgeBase: 'Loading knowledge base...',
|
|
60
61
|
},
|
|
61
62
|
ToolbarPanels: ({ children }: any) => <div data-testid="toolbar-panels">{children}</div>,
|
|
62
63
|
...overrides,
|