@semiont/react-ui 0.5.26 → 0.5.28
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 +28 -4
- package/dist/{PdfAnnotationCanvas.client-6ZSF6ZTJ.js → PdfAnnotationCanvas.client-C2IGG4SW.js} +33 -6
- package/dist/{PdfAnnotationCanvas.client-JCJMXWWJ.css → PdfAnnotationCanvas.client-TGDPPGXJ.css} +6 -1
- package/dist/{ar-YTUUGK2O.js → ar-FABPTJVB.js} +88 -26
- package/dist/{bn-ASGNGPMD.js → bn-LKP6BDMH.js} +88 -26
- package/dist/{chunk-L3WKRWP3.js → chunk-2GOHTTHW.js} +88 -26
- package/dist/{chunk-OBQOZCJE.js → chunk-2YHEE63S.js} +31 -31
- package/dist/{cs-WMYVAOZY.js → cs-YQYNWHRG.js} +88 -26
- package/dist/{da-FSHHN4D6.js → da-QHZOYKRY.js} +88 -26
- package/dist/{de-FJFO3TPT.js → de-GKA7RD7J.js} +88 -26
- package/dist/{el-XOH4QIAG.js → el-45T257AL.js} +88 -26
- package/dist/{en-3FCBW3YD.js → en-RVW5EQ66.js} +4 -2
- package/dist/{es-HDX4QI7E.js → es-AVER7RU4.js} +88 -26
- package/dist/{fa-DJ6WTFTJ.js → fa-K3VPK3O2.js} +88 -26
- package/dist/{fi-4S7H7W7U.js → fi-5NEKF7EB.js} +88 -26
- package/dist/{fr-6UJSYHXC.js → fr-FUMNDLBZ.js} +88 -26
- package/dist/{he-ZZJOHJX2.js → he-3JGNA3PR.js} +88 -26
- package/dist/{hi-HZJTAW7U.js → hi-IJY375DF.js} +88 -26
- package/dist/{id-E3I6ILEM.js → id-LWHKJ7ZD.js} +88 -26
- package/dist/index.css +380 -524
- package/dist/index.d.ts +318 -192
- package/dist/index.js +2960 -2421
- package/dist/integrations/css-modules-helper.js +1 -3
- package/dist/{it-ULAPAD7U.js → it-EBVOV4RW.js} +88 -26
- package/dist/{ja-SAHZQ4CZ.js → ja-KWRIX2NF.js} +88 -26
- package/dist/{ko-DFU2ADQ4.js → ko-L5CJMFDS.js} +88 -26
- package/dist/{ms-NWL76XNT.js → ms-LSNMJDR2.js} +88 -26
- package/dist/{nl-ATCM76WN.js → nl-ZB32MB7U.js} +88 -26
- package/dist/{no-UIAQ5RJF.js → no-4GTNY7M5.js} +88 -26
- package/dist/{pl-NOECQDXD.js → pl-LUHBLTHB.js} +88 -26
- package/dist/{pt-SVWIIUPC.js → pt-C4H3UNQ6.js} +88 -26
- package/dist/{ro-NR2DRTZG.js → ro-GGITS5V4.js} +88 -26
- package/dist/{sv-STVJ37JP.js → sv-T56RTLBP.js} +88 -26
- package/dist/test-utils.css +1 -1
- package/dist/test-utils.d.ts +1 -1
- package/dist/test-utils.js +2 -2
- package/dist/{th-UWQR4K54.js → th-22X2IWCB.js} +88 -26
- package/dist/{tr-WNXZSD27.js → tr-77TFG62H.js} +88 -26
- package/dist/{uk-SJRMB47Y.js → uk-MIIL6NXT.js} +88 -26
- package/dist/{vi-TMRQFES2.js → vi-7JYOIGNV.js} +88 -26
- package/dist/{zh-QOL3LYEM.js → zh-ID6DRYU4.js} +88 -26
- package/package.json +11 -10
- package/src/components/AssistProgress.tsx +178 -134
- package/src/components/CodeMirrorRenderer.tsx +8 -8
- package/src/components/{EntityFoundLog.tsx → ItemFoundLog.tsx} +12 -7
- package/src/components/ProtectedErrorBoundary.css +3 -1
- package/src/components/StatusDisplay.css +1 -1
- package/src/components/Toast.css +3 -1
- package/src/components/__tests__/AssistProgress.test.tsx +268 -141
- package/src/components/annotation/annotation-entries.css +0 -4
- package/src/components/annotation/references.css +0 -4
- package/src/components/image-annotation/AnnotationOverlay.tsx +6 -6
- package/src/components/image-annotation/SvgDrawingCanvas.tsx +1 -1
- package/src/components/image-annotation/__tests__/AnnotationOverlay.click.test.tsx +7 -9
- package/src/components/image-annotation/__tests__/SvgDrawingCanvas.click.test.tsx +1 -2
- package/src/components/layout/Header.css +0 -4
- package/src/components/layout/LeftSidebar.css +0 -4
- package/src/components/modals/ComposeStep.tsx +218 -0
- package/src/components/modals/ConfigureGatherStep.tsx +10 -18
- package/src/components/modals/ConfigureGenerationStep.tsx +131 -54
- package/src/components/modals/ConfigureSearchStep.tsx +42 -40
- 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 +287 -60
- package/src/components/modals/ResourceGenerateModal.tsx +233 -109
- package/src/components/modals/SearchModal.css +335 -111
- package/src/components/modals/SearchModal.tsx +1 -1
- package/src/components/modals/SearchResultsStep.tsx +63 -88
- package/src/components/modals/WizardFooter.tsx +69 -0
- 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 +290 -0
- package/src/components/modals/__tests__/ConfigureSearchStep.test.tsx +95 -0
- package/src/components/modals/__tests__/GatherContextStep.resource.test.tsx +463 -25
- package/src/components/modals/__tests__/ReferenceWizardModal.test.tsx +635 -0
- package/src/components/modals/__tests__/ResourceGenerateModal.test.tsx +277 -85
- package/src/components/modals/__tests__/SearchModal.search-wiring.test.tsx +6 -2
- package/src/components/modals/__tests__/SearchResultsStep.test.tsx +61 -0
- package/src/components/modals/__tests__/WizardFooter.test.tsx +344 -0
- package/src/components/modals/modals.css +16 -42
- package/src/components/navigation/NavigationTabs.css +0 -20
- package/src/components/pdf-annotation/PdfAnnotationCanvas.css +21 -2
- package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +36 -3
- package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +41 -6
- package/src/components/resource/AnnotateView.tsx +19 -8
- package/src/components/resource/AnnotationHistory.tsx +2 -0
- package/src/components/resource/BrowseView.tsx +20 -9
- package/src/components/resource/ResourceViewer.tsx +27 -28
- package/src/components/resource/__tests__/AnnotateView.embeddable.test.tsx +1 -1
- package/src/components/resource/__tests__/AnnotateView.focus.test.tsx +50 -0
- package/src/components/resource/__tests__/AnnotationHistory.test.tsx +2 -0
- package/src/components/resource/__tests__/BrowseView.embeddable.test.tsx +1 -1
- package/src/components/resource/__tests__/BrowseView.test.tsx +38 -12
- package/src/components/resource/__tests__/ResourceViewer.embeddable.test.tsx +47 -3
- package/src/components/resource/annotate-renderers.tsx +3 -3
- package/src/components/resource/event-formatting.ts +0 -3
- package/src/components/resource/panels/AssessmentEntry.tsx +1 -1
- package/src/components/resource/panels/AssessmentPanel.css +5 -6
- package/src/components/resource/panels/AssistSection.css +95 -263
- package/src/components/resource/panels/AssistSection.tsx +4 -4
- package/src/components/resource/panels/AssistShell.tsx +12 -4
- package/src/components/resource/panels/CollaborationPanel.css +47 -22
- package/src/components/resource/panels/CollaborationPanel.tsx +93 -2
- package/src/components/resource/panels/CommentEntry.tsx +1 -1
- package/src/components/resource/panels/CommentsPanel.css +0 -16
- package/src/components/resource/panels/HighlightEntry.tsx +1 -1
- package/src/components/resource/panels/HighlightPanel.css +0 -7
- package/src/components/resource/panels/JsonLdPanel.css +0 -4
- package/src/components/resource/panels/ReferenceEntry.tsx +10 -3
- package/src/components/resource/panels/ReferencesPanel.css +0 -12
- package/src/components/resource/panels/ReferencesPanel.tsx +16 -20
- package/src/components/resource/panels/ResourceInfoPanel.css +0 -32
- package/src/components/resource/panels/ResourceInfoPanel.tsx +89 -20
- package/src/components/resource/panels/StatisticsPanel.css +0 -20
- package/src/components/resource/panels/TagEntry.tsx +1 -1
- package/src/components/resource/panels/TaggingPanel.css +0 -4
- package/src/components/resource/panels/TaggingPanel.tsx +2 -10
- package/src/components/resource/panels/UnifiedAnnotationsPanel.css +0 -4
- package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +7 -2
- package/src/components/resource/panels/__tests__/AssessmentEntry.test.tsx +0 -1
- package/src/components/resource/panels/__tests__/AssistSection.test.tsx +48 -50
- package/src/components/resource/panels/__tests__/AssistShell.test.tsx +11 -6
- package/src/components/resource/panels/__tests__/CollaborationPanel.test.tsx +89 -0
- package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +1 -2
- package/src/components/resource/panels/__tests__/HighlightEntry.test.tsx +0 -1
- package/src/components/resource/panels/__tests__/HighlightPanel.annotationProgress.test.tsx +44 -36
- package/src/components/resource/panels/__tests__/ReferenceEntry.test.tsx +41 -1
- package/src/components/resource/panels/__tests__/ReferencesPanel.headless.test.tsx +1 -1
- package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +67 -54
- package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +185 -7
- package/src/components/resource/panels/__tests__/TagEntry.test.tsx +0 -1
- package/src/components/settings/SettingsPanel.css +0 -4
- package/src/features/auth/auth.css +2 -26
- package/src/features/resource-compose/__tests__/ResourceComposePage.test.tsx +0 -65
- package/src/features/resource-compose/components/ResourceComposePage.tsx +17 -130
- package/src/features/resource-compose/state/__tests__/compose-page-state-unit.test.ts +1 -76
- package/src/features/resource-compose/state/compose-page-state-unit.ts +5 -48
- package/src/features/resource-discovery/__tests__/ResourceDiscoveryPage.test.tsx +55 -0
- package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +29 -0
- package/src/features/resource-discovery/state/__tests__/discover-state-unit.test.ts +53 -3
- package/src/features/resource-discovery/state/discover-state-unit.ts +29 -8
- package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +71 -15
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +166 -71
- package/src/features/resource-viewer/hooks/__tests__/useResourceViewedReport.test.tsx +63 -0
- package/src/features/resource-viewer/hooks/useResourceViewedReport.ts +33 -0
- package/src/features/resource-viewer/state/__tests__/resource-viewer-page-state-unit.test.ts +2 -2
- package/src/features/resource-viewer/state/resource-viewer-page-state-unit.ts +1 -1
- package/src/styles/core/buttons.css +3 -1
- package/src/styles/core/forms.css +35 -29
- package/src/styles/core/indicators.css +0 -4
- package/src/styles/core/inputs.css +0 -4
- package/src/styles/core/progress.css +2 -34
- package/src/styles/core/sliders.css +0 -4
- package/src/styles/core/toggles.css +0 -4
- package/src/styles/features/resource-discovery.css +13 -0
- package/src/styles/features/resource-viewer.css +0 -42
- package/src/styles/features/resource.css +0 -4
- package/src/styles/motivations/motivation-assessment.css +2 -2
- package/src/styles/panels/history-panel.css +0 -40
- package/src/styles/panels/user-panel.css +0 -3
- package/src/styles/patterns/panel-helpers.css +0 -27
- package/src/styles/patterns/panel-sections.css +0 -59
- package/src/styles/patterns/panels-base.css +0 -32
- package/translations/ar.json +84 -24
- package/translations/bn.json +84 -24
- package/translations/cs.json +84 -24
- package/translations/da.json +84 -24
- package/translations/de.json +84 -24
- package/translations/el.json +84 -24
- package/translations/en.json +84 -24
- package/translations/es.json +84 -24
- package/translations/fa.json +84 -24
- package/translations/fi.json +84 -24
- package/translations/fr.json +84 -24
- package/translations/he.json +84 -24
- package/translations/hi.json +84 -24
- package/translations/id.json +84 -24
- package/translations/it.json +84 -24
- package/translations/ja.json +84 -24
- package/translations/ko.json +84 -24
- package/translations/ms.json +84 -24
- package/translations/nl.json +84 -24
- package/translations/no.json +84 -24
- package/translations/pl.json +84 -24
- package/translations/pt.json +84 -24
- package/translations/ro.json +84 -24
- package/translations/sv.json +84 -24
- package/translations/th.json +84 -24
- package/translations/tr.json +84 -24
- package/translations/uk.json +84 -24
- package/translations/vi.json +84 -24
- package/translations/zh.json +84 -24
- package/dist/PdfAnnotationCanvas.client-6ZSF6ZTJ.js.map +0 -1
- package/dist/PdfAnnotationCanvas.client-JCJMXWWJ.css.map +0 -1
- package/dist/ar-YTUUGK2O.js.map +0 -1
- package/dist/bn-ASGNGPMD.js.map +0 -1
- package/dist/chunk-BTUG3VJJ.js.map +0 -1
- package/dist/chunk-L3WKRWP3.js.map +0 -1
- package/dist/chunk-O23TLLXW.js.map +0 -1
- package/dist/chunk-OBQOZCJE.js.map +0 -1
- package/dist/chunk-QT7LYM72.js.map +0 -1
- package/dist/chunk-VXASXU4K.js.map +0 -1
- package/dist/chunk-XUDKYAVC.js +0 -21
- package/dist/chunk-XUDKYAVC.js.map +0 -1
- package/dist/cs-WMYVAOZY.js.map +0 -1
- package/dist/da-FSHHN4D6.js.map +0 -1
- package/dist/de-FJFO3TPT.js.map +0 -1
- package/dist/el-XOH4QIAG.js.map +0 -1
- package/dist/en-3FCBW3YD.js.map +0 -1
- package/dist/es-HDX4QI7E.js.map +0 -1
- package/dist/fa-DJ6WTFTJ.js.map +0 -1
- package/dist/fi-4S7H7W7U.js.map +0 -1
- package/dist/fr-6UJSYHXC.js.map +0 -1
- package/dist/he-ZZJOHJX2.js.map +0 -1
- package/dist/hi-HZJTAW7U.js.map +0 -1
- package/dist/id-E3I6ILEM.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-ULAPAD7U.js.map +0 -1
- package/dist/ja-SAHZQ4CZ.js.map +0 -1
- package/dist/ko-DFU2ADQ4.js.map +0 -1
- package/dist/ms-NWL76XNT.js.map +0 -1
- package/dist/nl-ATCM76WN.js.map +0 -1
- package/dist/no-UIAQ5RJF.js.map +0 -1
- package/dist/pl-NOECQDXD.js.map +0 -1
- package/dist/pt-SVWIIUPC.js.map +0 -1
- package/dist/ro-NR2DRTZG.js.map +0 -1
- package/dist/sv-STVJ37JP.js.map +0 -1
- package/dist/test-utils.css.map +0 -1
- package/dist/test-utils.js.map +0 -1
- package/dist/th-UWQR4K54.js.map +0 -1
- package/dist/tr-WNXZSD27.js.map +0 -1
- package/dist/uk-SJRMB47Y.js.map +0 -1
- package/dist/vi-TMRQFES2.js.map +0 -1
- package/dist/zh-QOL3LYEM.js.map +0 -1
- package/src/components/modals/ResourceSearchModal.tsx +0 -224
- package/src/components/modals/__tests__/ResourceSearchModal.test.tsx +0 -159
- package/src/integrations/tailwind-plugin.cjs +0 -224
|
@@ -2,7 +2,7 @@ import { describe, it, expect, vi } from 'vitest';
|
|
|
2
2
|
import { asStates } from '../../../../__tests__/test-client';
|
|
3
3
|
import { sessionOf } from '../../../../__tests__/test-client';
|
|
4
4
|
import { BehaviorSubject, firstValueFrom } from 'rxjs';
|
|
5
|
-
import { filter, skip, take, toArray } from 'rxjs/operators';
|
|
5
|
+
import { filter, map, skip, take, toArray } from 'rxjs/operators';
|
|
6
6
|
import type { SemiontClient } from '@semiont/sdk';
|
|
7
7
|
import type { ShellStateUnit } from '../../../../state/shell-state-unit';
|
|
8
8
|
import { createDiscoverStateUnit } from '../discover-state-unit';
|
|
@@ -23,6 +23,8 @@ function mockClient(overrides: {
|
|
|
23
23
|
resources$?: BehaviorSubject<unknown[] | undefined>;
|
|
24
24
|
entityTypes$?: BehaviorSubject<string[] | undefined>;
|
|
25
25
|
resourcesFn?: (filters: BrowseFilters) => BehaviorSubject<unknown[] | undefined>;
|
|
26
|
+
/** What the stubbed list envelope reports as its match kind. */
|
|
27
|
+
matchKind?: 'lexical' | 'semantic';
|
|
26
28
|
} = {}): { client: SemiontClient; resourceCalls: BrowseFilters[] } {
|
|
27
29
|
const resourceCalls: BrowseFilters[] = [];
|
|
28
30
|
const defaultResources$ =
|
|
@@ -36,7 +38,11 @@ function mockClient(overrides: {
|
|
|
36
38
|
browse: {
|
|
37
39
|
resources: (filters: BrowseFilters = {}) => {
|
|
38
40
|
resourceCalls.push(filters);
|
|
39
|
-
return asStates(resourcesFn(filters).asObservable()
|
|
41
|
+
return asStates(resourcesFn(filters).asObservable().pipe(
|
|
42
|
+
// Arrays get the list envelope `resources()` now emits; explicit
|
|
43
|
+
// status objects (B15 fixtures) and `undefined` pass through.
|
|
44
|
+
map((v) => (Array.isArray(v) ? { resources: v, total: v.length, offset: 0, limit: 20, matchKind: overrides.matchKind ?? 'lexical' } : v)),
|
|
45
|
+
));
|
|
40
46
|
},
|
|
41
47
|
entityTypes: () => asStates(entityTypes$.asObservable()),
|
|
42
48
|
},
|
|
@@ -45,6 +51,50 @@ function mockClient(overrides: {
|
|
|
45
51
|
return { client, resourceCalls };
|
|
46
52
|
}
|
|
47
53
|
|
|
54
|
+
describe('createDiscoverStateUnit — search match kind (SEMANTIC-FALLBACK P3b)', () => {
|
|
55
|
+
// S10 is tier-agnostic: the label and the resources it describes must arrive
|
|
56
|
+
// as ONE value. Exposing `matchKind$` as a second observable beside
|
|
57
|
+
// `state$.results` would satisfy the component and still let a render pair
|
|
58
|
+
// this query's label with the previous query's list.
|
|
59
|
+
it('emits matchKind in the SAME value as the results it labels', async () => {
|
|
60
|
+
vi.useFakeTimers();
|
|
61
|
+
try {
|
|
62
|
+
const results$ = new BehaviorSubject<unknown[] | undefined>([{ '@id': 'sem-1' }]);
|
|
63
|
+
const { client } = mockClient({ resourcesFn: () => results$, matchKind: 'semantic' });
|
|
64
|
+
const stateUnit = createDiscoverStateUnit(sessionOf(client), mockBrowse());
|
|
65
|
+
|
|
66
|
+
// Subscribe BEFORE setQuery: `queryInput$` is a plain Subject, so a value
|
|
67
|
+
// pushed with nothing listening is gone.
|
|
68
|
+
const collected: Array<{ results: unknown[]; matchKind?: string }> = [];
|
|
69
|
+
const sub = stateUnit.search.state$.subscribe((st) => collected.push(st));
|
|
70
|
+
|
|
71
|
+
stateUnit.search.setQuery('kitten');
|
|
72
|
+
await vi.advanceTimersByTimeAsync(300);
|
|
73
|
+
|
|
74
|
+
const last = collected.at(-1)!;
|
|
75
|
+
expect(last.results).toEqual([{ '@id': 'sem-1' }]);
|
|
76
|
+
expect(last.matchKind).toBe('semantic');
|
|
77
|
+
|
|
78
|
+
sub.unsubscribe();
|
|
79
|
+
stateUnit.dispose();
|
|
80
|
+
} finally {
|
|
81
|
+
vi.useRealTimers();
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('carries no matchKind on the empty-query view', async () => {
|
|
86
|
+
// Clearing the box returns to `recent`, which no label describes.
|
|
87
|
+
const { client } = mockClient();
|
|
88
|
+
const stateUnit = createDiscoverStateUnit(sessionOf(client), mockBrowse());
|
|
89
|
+
|
|
90
|
+
const first = await firstValueFrom(stateUnit.search.state$);
|
|
91
|
+
expect(first.results).toEqual([]);
|
|
92
|
+
expect(first.matchKind).toBeUndefined();
|
|
93
|
+
|
|
94
|
+
stateUnit.dispose();
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
48
98
|
describe('createDiscoverStateUnit', () => {
|
|
49
99
|
it('exposes recent resources from browse namespace', async () => {
|
|
50
100
|
const { client } = mockClient();
|
|
@@ -201,7 +251,7 @@ describe('createDiscoverStateUnit', () => {
|
|
|
201
251
|
stateUnit.search.setQuery('lincoln');
|
|
202
252
|
await vi.advanceTimersByTimeAsync(300);
|
|
203
253
|
|
|
204
|
-
expect(collected.at(-1)).toEqual({ results: [{ '@id': 'hit' }], isSearching: false });
|
|
254
|
+
expect(collected.at(-1)).toEqual({ results: [{ '@id': 'hit' }], isSearching: false, matchKind: 'lexical' });
|
|
205
255
|
|
|
206
256
|
sub.unsubscribe();
|
|
207
257
|
stateUnit.dispose();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BehaviorSubject, Subject, combineLatest, of, type Observable } from 'rxjs';
|
|
2
|
-
import { readyValue } from '@semiont/sdk';
|
|
2
|
+
import { readyValue, type CacheState } from '@semiont/sdk';
|
|
3
3
|
import { debounceTime, distinctUntilChanged, map, startWith, switchMap, shareReplay } from 'rxjs/operators';
|
|
4
4
|
import type { ResourceDescriptor } from '@semiont/core';
|
|
5
5
|
import type { SemiontSession } from '@semiont/sdk';
|
|
@@ -14,7 +14,18 @@ const DEBOUNCE_MS = 250;
|
|
|
14
14
|
|
|
15
15
|
export interface DiscoverSearchPipeline {
|
|
16
16
|
query$: Observable<string>;
|
|
17
|
-
|
|
17
|
+
/**
|
|
18
|
+
* The page AND the label that describes it, as one value. `matchKind` is
|
|
19
|
+
* carried here rather than beside it because SEMANTIC-FALLBACK S10 is
|
|
20
|
+
* tier-agnostic: two separately-observable pieces of state let a render pair
|
|
21
|
+
* this query's label with the previous query's list. Undefined only before
|
|
22
|
+
* the first answer (and while a query is empty), never as a third kind.
|
|
23
|
+
*/
|
|
24
|
+
state$: Observable<{
|
|
25
|
+
results: ResourceDescriptor[];
|
|
26
|
+
isSearching: boolean;
|
|
27
|
+
matchKind?: 'lexical' | 'semantic';
|
|
28
|
+
}>;
|
|
18
29
|
setQuery(value: string): void;
|
|
19
30
|
}
|
|
20
31
|
|
|
@@ -58,7 +69,11 @@ export function createDiscoverStateUnit(
|
|
|
58
69
|
limit: RECENT_LIMIT,
|
|
59
70
|
archived: false,
|
|
60
71
|
...(et ? { entityType: et } : {}),
|
|
61
|
-
})
|
|
72
|
+
}).pipe(
|
|
73
|
+
// This unit renders only the page; project it out of the list
|
|
74
|
+
// envelope (`matchKind` rendering is SEMANTIC-FALLBACK P3b's).
|
|
75
|
+
map((st): CacheState<ResourceDescriptor[]> => (st.status === 'ready' ? { status: 'ready', value: st.value.resources } : st)),
|
|
76
|
+
),
|
|
62
77
|
),
|
|
63
78
|
),
|
|
64
79
|
[],
|
|
@@ -74,11 +89,13 @@ export function createDiscoverStateUnit(
|
|
|
74
89
|
distinctUntilChanged(),
|
|
75
90
|
);
|
|
76
91
|
|
|
77
|
-
const state$:
|
|
92
|
+
const state$: DiscoverSearchPipeline['state$'] =
|
|
78
93
|
combineLatest([debouncedQuery$, selectedEntityType$]).pipe(
|
|
79
94
|
switchMap(([q, et]) => {
|
|
80
95
|
const trimmed = q.trim();
|
|
81
96
|
if (!trimmed) {
|
|
97
|
+
// No query, no answer to label: `recent` is what renders, and no
|
|
98
|
+
// matchKind describes it.
|
|
82
99
|
return of({ results: [] as ResourceDescriptor[], isSearching: false });
|
|
83
100
|
}
|
|
84
101
|
return client.browse
|
|
@@ -88,10 +105,14 @@ export function createDiscoverStateUnit(
|
|
|
88
105
|
...(et ? { entityType: et } : {}),
|
|
89
106
|
})
|
|
90
107
|
.pipe(
|
|
91
|
-
map((st) =>
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
108
|
+
map((st) => {
|
|
109
|
+
const ready = readyValue(st);
|
|
110
|
+
return {
|
|
111
|
+
results: ready?.resources ?? [],
|
|
112
|
+
isSearching: st.status === 'pending',
|
|
113
|
+
...(ready ? { matchKind: ready.matchKind } : {}),
|
|
114
|
+
};
|
|
115
|
+
}),
|
|
95
116
|
startWith({ results: [] as ResourceDescriptor[], isSearching: true }),
|
|
96
117
|
);
|
|
97
118
|
}),
|
|
@@ -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', {
|
|
@@ -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
|
});
|