@semiont/react-ui 0.5.25 → 0.5.27
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-EVUPTUWZ.js +677 -0
- package/dist/PdfAnnotationCanvas.client-EVUPTUWZ.js.map +1 -0
- package/dist/PdfAnnotationCanvas.client-TGDPPGXJ.css +185 -0
- package/dist/PdfAnnotationCanvas.client-TGDPPGXJ.css.map +1 -0
- package/dist/{ar-CULRHLNG.js → ar-OU3DQGC6.js} +65 -7
- package/dist/ar-OU3DQGC6.js.map +1 -0
- package/dist/{bn-HYR7CEL2.js → bn-PHWCR6AZ.js} +65 -7
- package/dist/bn-PHWCR6AZ.js.map +1 -0
- package/dist/chunk-5VDTWRXJ.js +287 -0
- package/dist/chunk-5VDTWRXJ.js.map +1 -0
- package/dist/{chunk-TG2XQEC4.js → chunk-BTUG3VJJ.js} +18 -298
- package/dist/chunk-BTUG3VJJ.js.map +1 -0
- package/dist/{chunk-NH6L2GIL.js → chunk-PCRRBU7H.js} +65 -7
- package/dist/chunk-PCRRBU7H.js.map +1 -0
- package/dist/{cs-YBRWY7NH.js → cs-NEXYZ62I.js} +65 -7
- package/dist/cs-NEXYZ62I.js.map +1 -0
- package/dist/{da-PHDEOBV3.js → da-FAT43YCU.js} +65 -7
- package/dist/da-FAT43YCU.js.map +1 -0
- package/dist/{de-O4UCCVLJ.js → de-KRQ3WBCU.js} +65 -7
- package/dist/de-KRQ3WBCU.js.map +1 -0
- package/dist/{el-MVGO6WZV.js → el-Q4IU3EKC.js} +65 -7
- package/dist/el-Q4IU3EKC.js.map +1 -0
- package/dist/{en-2KQILFKQ.js → en-2KADE7N4.js} +6 -2
- package/dist/{es-USSLXQY7.js → es-2IO3R2MK.js} +65 -7
- package/dist/es-2IO3R2MK.js.map +1 -0
- package/dist/{fa-WMAREFWY.js → fa-Q5GOQFXX.js} +65 -7
- package/dist/fa-Q5GOQFXX.js.map +1 -0
- package/dist/{fi-B3NBNOTS.js → fi-R6NMRWBE.js} +65 -7
- package/dist/fi-R6NMRWBE.js.map +1 -0
- package/dist/{fr-TXYK5T5W.js → fr-QY4D5E6J.js} +65 -7
- package/dist/fr-QY4D5E6J.js.map +1 -0
- package/dist/{he-JCJACW5D.js → he-X6Y77XHY.js} +65 -7
- package/dist/he-X6Y77XHY.js.map +1 -0
- package/dist/{hi-VOQLZNJU.js → hi-XL7IOEQM.js} +65 -7
- package/dist/hi-XL7IOEQM.js.map +1 -0
- package/dist/{id-BFN5RZNZ.js → id-QZ4ZZS67.js} +65 -7
- package/dist/id-QZ4ZZS67.js.map +1 -0
- package/dist/index.css +241 -508
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +155 -54
- package/dist/index.js +1815 -1642
- package/dist/index.js.map +1 -1
- package/dist/integrations/css-modules-helper.js +1 -3
- package/dist/integrations/css-modules-helper.js.map +1 -1
- package/dist/{it-TWWDHKKM.js → it-ME2Q7OB6.js} +65 -7
- package/dist/it-ME2Q7OB6.js.map +1 -0
- package/dist/{ja-YRQLEBFM.js → ja-HX7QASRI.js} +65 -7
- package/dist/ja-HX7QASRI.js.map +1 -0
- package/dist/{ko-PG6B3CRB.js → ko-EQO5AHAV.js} +65 -7
- package/dist/ko-EQO5AHAV.js.map +1 -0
- package/dist/{ms-7EZECVKM.js → ms-UILFSF3I.js} +65 -7
- package/dist/ms-UILFSF3I.js.map +1 -0
- package/dist/{nl-HP753YAP.js → nl-NNI45PMK.js} +65 -7
- package/dist/nl-NNI45PMK.js.map +1 -0
- package/dist/{no-JUD5WX3N.js → no-MAL5F6P6.js} +65 -7
- package/dist/no-MAL5F6P6.js.map +1 -0
- package/dist/{pl-RVUL4CBM.js → pl-4RMY6JAJ.js} +65 -7
- package/dist/pl-4RMY6JAJ.js.map +1 -0
- package/dist/{pt-RPOVD4QM.js → pt-VQH2X3QQ.js} +65 -7
- package/dist/pt-VQH2X3QQ.js.map +1 -0
- package/dist/{ro-DMYWQTRD.js → ro-XXCQO2OG.js} +65 -7
- package/dist/ro-XXCQO2OG.js.map +1 -0
- package/dist/{sv-DMZ4FGXD.js → sv-7Z6EJ6U4.js} +65 -7
- package/dist/sv-7Z6EJ6U4.js.map +1 -0
- package/dist/test-utils.css +1 -1
- package/dist/test-utils.css.map +1 -1
- package/dist/test-utils.d.ts +1 -1
- package/dist/test-utils.js +5 -3
- package/dist/test-utils.js.map +1 -1
- package/dist/{th-4HVBJBQ2.js → th-L3DXXYVQ.js} +65 -7
- package/dist/th-L3DXXYVQ.js.map +1 -0
- package/dist/{tr-6BDPU5ML.js → tr-P3DFVLFJ.js} +65 -7
- package/dist/tr-P3DFVLFJ.js.map +1 -0
- package/dist/{uk-TBZBT2TL.js → uk-7C5D3PXS.js} +65 -7
- package/dist/uk-7C5D3PXS.js.map +1 -0
- package/dist/{vi-UMKZKZL3.js → vi-ZLTLEO7P.js} +65 -7
- package/dist/vi-ZLTLEO7P.js.map +1 -0
- package/dist/{zh-YBJWJJYB.js → zh-5FHVWDIN.js} +65 -7
- package/dist/zh-5FHVWDIN.js.map +1 -0
- package/package.json +7 -7
- package/src/components/AssistProgress.tsx +144 -134
- package/src/components/{EntityFoundLog.tsx → ItemFoundLog.tsx} +12 -7
- package/src/components/ProtectedErrorBoundary.css +3 -1
- package/src/components/StatusDisplay.css +35 -35
- package/src/components/Toast.css +3 -1
- package/src/components/__tests__/AssistProgress.test.tsx +216 -148
- package/src/components/annotation/annotation-entries.css +0 -4
- package/src/components/annotation/annotations.css +1 -1
- 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/ConfigureGenerationStep.tsx +109 -48
- package/src/components/modals/ConfigureSearchStep.tsx +23 -40
- package/src/components/modals/ReferenceWizardModal.tsx +55 -16
- package/src/components/modals/ResourceGenerateModal.tsx +24 -8
- package/src/components/modals/ResourceSearchModal.tsx +1 -1
- package/src/components/modals/SearchModal.css +5 -17
- package/src/components/modals/SearchModal.tsx +1 -1
- package/src/components/modals/SearchResultsStep.tsx +2 -20
- package/src/components/modals/WizardFooter.tsx +63 -0
- package/src/components/modals/__tests__/ConfigureGenerationStep.test.tsx +255 -0
- package/src/components/modals/__tests__/ConfigureSearchStep.test.tsx +72 -0
- package/src/components/modals/__tests__/ReferenceWizardModal.test.tsx +256 -0
- package/src/components/modals/__tests__/ResourceGenerateModal.test.tsx +1 -0
- package/src/components/modals/__tests__/ResourceSearchModal.test.tsx +6 -2
- package/src/components/modals/__tests__/SearchModal.search-wiring.test.tsx +6 -2
- package/src/components/modals/__tests__/WizardFooter.test.tsx +235 -0
- package/src/components/modals/modals.css +16 -42
- package/src/components/navigation/NavigationTabs.css +0 -20
- package/src/components/pdf-annotation/PdfAnnotationCanvas.css +162 -6
- package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +623 -232
- package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +764 -7
- package/src/components/pdf-annotation/__tests__/estimate-slot-height.test.ts +53 -0
- package/src/components/pdf-annotation/estimate-slot-height.ts +31 -0
- package/src/components/resource/AnnotateView.tsx +16 -5
- package/src/components/resource/AnnotationHistory.tsx +2 -0
- package/src/components/resource/BrowseView.tsx +15 -4
- package/src/components/resource/ResourceViewer.tsx +23 -24
- 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.test.tsx +29 -3
- package/src/components/resource/__tests__/ResourceViewer.embeddable.test.tsx +47 -3
- package/src/components/resource/__tests__/browse-renderers.dispatch.test.tsx +33 -0
- package/src/components/resource/annotate-renderers.tsx +10 -1
- package/src/components/resource/browse-renderers.tsx +10 -1
- 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 +68 -271
- 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 +1 -1
- package/src/components/resource/panels/ReferencesPanel.css +0 -12
- package/src/components/resource/panels/ReferencesPanel.tsx +12 -20
- package/src/components/resource/panels/ResourceInfoPanel.css +0 -32
- package/src/components/resource/panels/ResourceInfoPanel.tsx +1 -1
- 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/__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 +0 -1
- package/src/components/resource/panels/__tests__/ReferencesPanel.headless.test.tsx +1 -1
- package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +34 -53
- package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +37 -1
- 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 +9 -33
- package/src/features/resource-compose/state/__tests__/compose-page-state-unit.test.ts +25 -1
- package/src/features/resource-compose/state/compose-page-state-unit.ts +11 -2
- 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 +26 -0
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +42 -21
- 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 +0 -21
- package/src/styles/core/indicators.css +0 -4
- package/src/styles/core/inputs.css +1 -5
- 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/compose.css +1 -1
- package/src/styles/features/resource-discovery.css +13 -0
- package/src/styles/features/resource-viewer.css +1 -21
- package/src/styles/features/static-pages.css +11 -11
- package/src/styles/motivations/motivation-assessment.css +2 -2
- package/src/styles/motivations/motivation-comment.css +2 -2
- package/src/styles/motivations/motivation-reference.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 +4 -36
- package/src/styles/utilities/motion.css +15 -15
- package/src/styles/variables.css +29 -1
- package/translations/ar.json +59 -5
- package/translations/bn.json +59 -5
- package/translations/cs.json +59 -5
- package/translations/da.json +59 -5
- package/translations/de.json +59 -5
- package/translations/el.json +59 -5
- package/translations/en.json +59 -5
- package/translations/es.json +59 -5
- package/translations/fa.json +59 -5
- package/translations/fi.json +59 -5
- package/translations/fr.json +59 -5
- package/translations/he.json +59 -5
- package/translations/hi.json +59 -5
- package/translations/id.json +59 -5
- package/translations/it.json +59 -5
- package/translations/ja.json +59 -5
- package/translations/ko.json +59 -5
- package/translations/ms.json +59 -5
- package/translations/nl.json +59 -5
- package/translations/no.json +59 -5
- package/translations/pl.json +59 -5
- package/translations/pt.json +59 -5
- package/translations/ro.json +59 -5
- package/translations/sv.json +59 -5
- package/translations/th.json +59 -5
- package/translations/tr.json +59 -5
- package/translations/uk.json +59 -5
- package/translations/vi.json +59 -5
- package/translations/zh.json +59 -5
- package/dist/PdfAnnotationCanvas.client-EHQWF6BR.js +0 -462
- package/dist/PdfAnnotationCanvas.client-EHQWF6BR.js.map +0 -1
- package/dist/PdfAnnotationCanvas.client-SU5KNT7G.css +0 -98
- package/dist/PdfAnnotationCanvas.client-SU5KNT7G.css.map +0 -1
- package/dist/ar-CULRHLNG.js.map +0 -1
- package/dist/bn-HYR7CEL2.js.map +0 -1
- package/dist/chunk-NH6L2GIL.js.map +0 -1
- package/dist/chunk-TG2XQEC4.js.map +0 -1
- package/dist/chunk-XUDKYAVC.js +0 -21
- package/dist/chunk-XUDKYAVC.js.map +0 -1
- package/dist/cs-YBRWY7NH.js.map +0 -1
- package/dist/da-PHDEOBV3.js.map +0 -1
- package/dist/de-O4UCCVLJ.js.map +0 -1
- package/dist/el-MVGO6WZV.js.map +0 -1
- package/dist/es-USSLXQY7.js.map +0 -1
- package/dist/fa-WMAREFWY.js.map +0 -1
- package/dist/fi-B3NBNOTS.js.map +0 -1
- package/dist/fr-TXYK5T5W.js.map +0 -1
- package/dist/he-JCJACW5D.js.map +0 -1
- package/dist/hi-VOQLZNJU.js.map +0 -1
- package/dist/id-BFN5RZNZ.js.map +0 -1
- package/dist/it-TWWDHKKM.js.map +0 -1
- package/dist/ja-YRQLEBFM.js.map +0 -1
- package/dist/ko-PG6B3CRB.js.map +0 -1
- package/dist/ms-7EZECVKM.js.map +0 -1
- package/dist/nl-HP753YAP.js.map +0 -1
- package/dist/no-JUD5WX3N.js.map +0 -1
- package/dist/pl-RVUL4CBM.js.map +0 -1
- package/dist/pt-RPOVD4QM.js.map +0 -1
- package/dist/ro-DMYWQTRD.js.map +0 -1
- package/dist/sv-DMZ4FGXD.js.map +0 -1
- package/dist/th-4HVBJBQ2.js.map +0 -1
- package/dist/tr-6BDPU5ML.js.map +0 -1
- package/dist/uk-TBZBT2TL.js.map +0 -1
- package/dist/vi-UMKZKZL3.js.map +0 -1
- package/dist/zh-YBJWJJYB.js.map +0 -1
- package/src/integrations/tailwind-plugin.cjs +0 -224
- /package/dist/{en-2KQILFKQ.js.map → en-2KADE7N4.js.map} +0 -0
|
@@ -211,11 +211,11 @@
|
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
.semiont-search-modal__result-icon--entity {
|
|
214
|
-
color: var(--semiont-color-
|
|
214
|
+
color: var(--semiont-color-green-500);
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
[data-theme="dark"] .semiont-search-modal__result-icon--entity {
|
|
218
|
-
color: var(--semiont-color-
|
|
218
|
+
color: var(--semiont-color-green-400);
|
|
219
219
|
}
|
|
220
220
|
|
|
221
221
|
/* Result content */
|
|
@@ -244,13 +244,13 @@
|
|
|
244
244
|
font-size: 0.75rem;
|
|
245
245
|
font-weight: 500;
|
|
246
246
|
border-radius: 9999px;
|
|
247
|
-
background-color: var(--semiont-color-
|
|
248
|
-
color: var(--semiont-color-
|
|
247
|
+
background-color: var(--semiont-color-green-100);
|
|
248
|
+
color: var(--semiont-color-green-700);
|
|
249
249
|
}
|
|
250
250
|
|
|
251
251
|
[data-theme="dark"] .semiont-search-modal__result-badge {
|
|
252
252
|
background-color: rgba(34, 197, 94, 0.3);
|
|
253
|
-
color: var(--semiont-color-
|
|
253
|
+
color: var(--semiont-color-green-300);
|
|
254
254
|
}
|
|
255
255
|
|
|
256
256
|
.semiont-search-modal__result-description {
|
|
@@ -503,10 +503,6 @@
|
|
|
503
503
|
font-weight: 500;
|
|
504
504
|
}
|
|
505
505
|
|
|
506
|
-
[data-theme="dark"] .semiont-gather__loading-text {
|
|
507
|
-
color: var(--semiont-text-secondary);
|
|
508
|
-
}
|
|
509
|
-
|
|
510
506
|
.semiont-gather__loading-dot {
|
|
511
507
|
display: inline-block;
|
|
512
508
|
width: 0.625rem;
|
|
@@ -599,10 +595,6 @@
|
|
|
599
595
|
margin-bottom: 0.5rem;
|
|
600
596
|
}
|
|
601
597
|
|
|
602
|
-
[data-theme="dark"] .semiont-gather__footer-label {
|
|
603
|
-
color: var(--semiont-text-tertiary);
|
|
604
|
-
}
|
|
605
|
-
|
|
606
598
|
.semiont-gather__actions {
|
|
607
599
|
display: flex;
|
|
608
600
|
flex-flow: row nowrap;
|
|
@@ -665,10 +657,6 @@
|
|
|
665
657
|
color: var(--semiont-color-primary-600);
|
|
666
658
|
}
|
|
667
659
|
|
|
668
|
-
[data-theme="dark"] .semiont-gather__expand-btn {
|
|
669
|
-
color: var(--semiont-text-tertiary);
|
|
670
|
-
}
|
|
671
|
-
|
|
672
660
|
[data-theme="dark"] .semiont-gather__expand-btn:hover {
|
|
673
661
|
color: var(--semiont-color-primary-400);
|
|
674
662
|
}
|
|
@@ -74,7 +74,7 @@ export function SearchModal({
|
|
|
74
74
|
(q) =>
|
|
75
75
|
semiontRef.current!.browse.resources({ search: q, limit: SEARCH_LIMIT }).pipe(
|
|
76
76
|
map((st) => {
|
|
77
|
-
const resources = readyValue(st);
|
|
77
|
+
const resources = readyValue(st)?.resources;
|
|
78
78
|
if (resources === undefined) return undefined;
|
|
79
79
|
return resources
|
|
80
80
|
.map((resource): SearchResult | null => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
'use client';
|
|
2
|
+
import { WizardFooter } from './WizardFooter';
|
|
2
3
|
|
|
3
4
|
import type { GatheredContext, ResourceDescriptor } from '@semiont/core';
|
|
4
5
|
import { ContextSummary } from './ContextSummary';
|
|
@@ -14,12 +15,10 @@ export interface SearchResultsStepProps {
|
|
|
14
15
|
context: GatheredContext;
|
|
15
16
|
onLink: (resourceId: string) => void;
|
|
16
17
|
onBack: () => void;
|
|
17
|
-
onCancel: () => void;
|
|
18
18
|
translations: {
|
|
19
19
|
noResults: string;
|
|
20
20
|
link: string;
|
|
21
21
|
back: string;
|
|
22
|
-
cancel: string;
|
|
23
22
|
score: string;
|
|
24
23
|
} & ContextSummaryTranslations;
|
|
25
24
|
}
|
|
@@ -29,7 +28,6 @@ export function SearchResultsStep({
|
|
|
29
28
|
context,
|
|
30
29
|
onLink,
|
|
31
30
|
onBack,
|
|
32
|
-
onCancel,
|
|
33
31
|
translations: t,
|
|
34
32
|
}: SearchResultsStepProps) {
|
|
35
33
|
return (
|
|
@@ -102,23 +100,7 @@ export function SearchResultsStep({
|
|
|
102
100
|
</div>
|
|
103
101
|
</div>
|
|
104
102
|
|
|
105
|
-
{
|
|
106
|
-
<div className="semiont-modal__actions" style={{ paddingTop: '0.5rem' }}>
|
|
107
|
-
<button
|
|
108
|
-
type="button"
|
|
109
|
-
onClick={onCancel}
|
|
110
|
-
className="semiont-button--secondary semiont-button--flex"
|
|
111
|
-
>
|
|
112
|
-
✕ {t.cancel}
|
|
113
|
-
</button>
|
|
114
|
-
<button
|
|
115
|
-
type="button"
|
|
116
|
-
onClick={onBack}
|
|
117
|
-
className="semiont-button--secondary semiont-button--flex"
|
|
118
|
-
>
|
|
119
|
-
◀ {t.back}
|
|
120
|
-
</button>
|
|
121
|
-
</div>
|
|
103
|
+
<WizardFooter backLabel={t.back} onBack={onBack} />
|
|
122
104
|
</>
|
|
123
105
|
);
|
|
124
106
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The one wizard footer (WIZARD-NAVIGATION D7).
|
|
5
|
+
*
|
|
6
|
+
* Four steps used to hand-build this markup, which is how four different footers
|
|
7
|
+
* happened: three carried a `✕ Cancel` that duplicated the corner control, one had no
|
|
8
|
+
* footer at all, and every button stretched to equal width so "leave" read as a peer of
|
|
9
|
+
* "do the thing".
|
|
10
|
+
*
|
|
11
|
+
* The grammar, settled in D1/D2:
|
|
12
|
+
* • Dismissal is NOT here. The corner ✕, Esc and the backdrop are the way out, and
|
|
13
|
+
* they are the same on every step including the one with no footer.
|
|
14
|
+
* • Retreat is chrome: quiet, left, no emoji.
|
|
15
|
+
* • Advance is the decision: filled, right, one verb.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
export interface WizardFooterPrimary {
|
|
19
|
+
label: string;
|
|
20
|
+
/** Shown while `pending`; falls back to `label`. */
|
|
21
|
+
pendingLabel?: string;
|
|
22
|
+
pending?: boolean;
|
|
23
|
+
type: 'submit' | 'button';
|
|
24
|
+
onClick?: () => void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface WizardFooterProps {
|
|
28
|
+
/** Omit both to render no retreat — correct on the first step. */
|
|
29
|
+
backLabel?: string;
|
|
30
|
+
onBack?: () => void;
|
|
31
|
+
/** Omit on steps whose action lives elsewhere (search results links per row). */
|
|
32
|
+
primary?: WizardFooterPrimary;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function WizardFooter({ backLabel, onBack, primary }: WizardFooterProps) {
|
|
36
|
+
const pending = primary?.pending ?? false;
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<div className="semiont-modal__actions semiont-modal__actions--wizard">
|
|
40
|
+
{onBack && backLabel && (
|
|
41
|
+
<button
|
|
42
|
+
type="button"
|
|
43
|
+
onClick={onBack}
|
|
44
|
+
className="semiont-button--secondary semiont-wizard-footer__back"
|
|
45
|
+
disabled={pending}
|
|
46
|
+
>
|
|
47
|
+
◀ {backLabel}
|
|
48
|
+
</button>
|
|
49
|
+
)}
|
|
50
|
+
{primary && (
|
|
51
|
+
<button
|
|
52
|
+
type={primary.type}
|
|
53
|
+
{...(primary.onClick ? { onClick: primary.onClick } : {})}
|
|
54
|
+
className="semiont-button--primary"
|
|
55
|
+
disabled={pending}
|
|
56
|
+
data-pending={pending ? 'true' : 'false'}
|
|
57
|
+
>
|
|
58
|
+
{pending ? (primary.pendingLabel ?? primary.label) : primary.label}
|
|
59
|
+
</button>
|
|
60
|
+
)}
|
|
61
|
+
</div>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* INFERENCE-LIMITS-EXPOSURE P3b — the max-length control is bounded by the
|
|
3
|
+
* generation-serving agent's discovered output ceiling.
|
|
4
|
+
*
|
|
5
|
+
* The control was NOT previously unbounded: it shipped `min="100" max="4000"`.
|
|
6
|
+
* So this phase replaces a hardcoded 4000 with the real ceiling, which for any
|
|
7
|
+
* model above 4000 RAISES a cap users hit today, and for a smaller model
|
|
8
|
+
* lowers it to something the provider will actually honour.
|
|
9
|
+
*/
|
|
10
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
11
|
+
import { useState } from 'react';
|
|
12
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
13
|
+
import '@testing-library/jest-dom';
|
|
14
|
+
import type { CollaboratorEntry, GatheredContext } from '@semiont/core';
|
|
15
|
+
import { ConfigureGenerationStep } from '../ConfigureGenerationStep';
|
|
16
|
+
|
|
17
|
+
const translations = {
|
|
18
|
+
resourceTitle: 'Title',
|
|
19
|
+
resourceTitlePlaceholder: 'Title…',
|
|
20
|
+
additionalInstructions: 'Instructions',
|
|
21
|
+
additionalInstructionsPlaceholder: 'Instructions…',
|
|
22
|
+
language: 'Language',
|
|
23
|
+
languageHelp: 'Language help',
|
|
24
|
+
creativity: 'Creativity',
|
|
25
|
+
creativityFocused: 'Focused',
|
|
26
|
+
creativityCreative: 'Creative',
|
|
27
|
+
maxLength: 'Max length',
|
|
28
|
+
maxLengthHelp: 'How long the generated resource may be.',
|
|
29
|
+
// Named model + ceiling, so the bound is legible rather than mysterious (D6).
|
|
30
|
+
maxLengthCeiling: 'Limited to {{maxOutputTokens}} tokens by {{model}}.',
|
|
31
|
+
back: 'Back',
|
|
32
|
+
generate: 'Generate',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const context = { resources: [], annotations: [] } as unknown as GatheredContext;
|
|
36
|
+
|
|
37
|
+
/** The wizard's initial draft (WIZARD-NAVIGATION D3). */
|
|
38
|
+
const DRAFT = {
|
|
39
|
+
title: 'Untitled', storagePath: '', prompt: '', language: 'en',
|
|
40
|
+
temperature: 0.7, maxTokensText: '500',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const agentWithCeiling = (maxOutputTokens: number): CollaboratorEntry =>
|
|
44
|
+
({
|
|
45
|
+
agent: {
|
|
46
|
+
'@type': 'Software',
|
|
47
|
+
name: 'Claude Sonnet 5',
|
|
48
|
+
provider: 'anthropic',
|
|
49
|
+
model: 'claude-sonnet-5',
|
|
50
|
+
},
|
|
51
|
+
servesJobTypes: ['generation'],
|
|
52
|
+
limits: { contextTokens: 200_000, maxOutputTokens },
|
|
53
|
+
}) as unknown as CollaboratorEntry;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The step is CONTROLLED since WIZARD-NAVIGATION D3 — the wizard owns the draft so
|
|
57
|
+
* Back cannot discard it. These tests therefore mount a tiny stateful harness rather
|
|
58
|
+
* than the bare component: a `vi.fn()` for `onConfigChange` would swallow every edit
|
|
59
|
+
* and quietly turn each assertion below into a test of nothing.
|
|
60
|
+
*/
|
|
61
|
+
function Harness({ generationAgent }: { generationAgent?: CollaboratorEntry }) {
|
|
62
|
+
const [draft, setDraft] = useState(DRAFT);
|
|
63
|
+
return (
|
|
64
|
+
<ConfigureGenerationStep
|
|
65
|
+
config={draft}
|
|
66
|
+
onConfigChange={setDraft}
|
|
67
|
+
context={context}
|
|
68
|
+
onBack={vi.fn()}
|
|
69
|
+
onGenerate={onGenerateSpy}
|
|
70
|
+
translations={translations}
|
|
71
|
+
{...(generationAgent ? { generationAgent } : {})}
|
|
72
|
+
/>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let onGenerateSpy = vi.fn();
|
|
77
|
+
|
|
78
|
+
function renderStep(generationAgent?: CollaboratorEntry) {
|
|
79
|
+
onGenerateSpy = vi.fn();
|
|
80
|
+
const utils = render(<Harness {...(generationAgent ? { generationAgent } : {})} />);
|
|
81
|
+
const input = () => screen.getByLabelText('Max length') as HTMLInputElement;
|
|
82
|
+
return { ...utils, input, props: { onGenerate: onGenerateSpy } };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function fillRequired() {
|
|
86
|
+
fireEvent.change(screen.getByLabelText('Save location'), {
|
|
87
|
+
target: { value: 'generated/out.md' },
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
describe('ConfigureGenerationStep — ceiling awareness', () => {
|
|
92
|
+
it('bounds the control by the agent ceiling and names it in help text', () => {
|
|
93
|
+
const { input } = renderStep(agentWithCeiling(64_000));
|
|
94
|
+
|
|
95
|
+
expect(input().max).toBe('64000');
|
|
96
|
+
expect(screen.getByText(/Limited to 64000 tokens by claude-sonnet-5\./)).toBeInTheDocument();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('will not commit a value above the ceiling', () => {
|
|
100
|
+
// `max` on a number input does not prevent TYPING an over-value — it only
|
|
101
|
+
// marks the field invalid. D6 says the value cannot be entered, so assert
|
|
102
|
+
// the committed state, never the attribute alone.
|
|
103
|
+
const { input } = renderStep(agentWithCeiling(2_000));
|
|
104
|
+
|
|
105
|
+
fireEvent.change(input(), { target: { value: '9999' } });
|
|
106
|
+
|
|
107
|
+
expect(input().value).toBe('2000');
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('clamps the 500 default down when the ceiling is smaller', () => {
|
|
111
|
+
const { input } = renderStep(agentWithCeiling(300));
|
|
112
|
+
|
|
113
|
+
expect(input().value).toBe('300');
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('re-bounds and clamps when the ceiling arrives AFTER mount', () => {
|
|
117
|
+
// The case that decides whether this feature works in the app at all:
|
|
118
|
+
// `browse.agents()` is asynchronous, so `generationAgent` is undefined on
|
|
119
|
+
// first render and arrives later. An implementation that only consults the
|
|
120
|
+
// ceiling in the `useState` initializer passes every test above and does
|
|
121
|
+
// nothing in production.
|
|
122
|
+
const { input, rerender } = renderStep(undefined);
|
|
123
|
+
|
|
124
|
+
expect(input().max).toBe('4000');
|
|
125
|
+
fireEvent.change(input(), { target: { value: '3000' } });
|
|
126
|
+
expect(input().value).toBe('3000');
|
|
127
|
+
|
|
128
|
+
rerender(<Harness generationAgent={agentWithCeiling(1_000)} />);
|
|
129
|
+
|
|
130
|
+
expect(input().max).toBe('1000');
|
|
131
|
+
// 3000 is now out of range and must not survive as a submittable value.
|
|
132
|
+
expect(input().value).toBe('1000');
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('keeps a value that is still within a newly arrived ceiling', () => {
|
|
136
|
+
const { input, rerender } = renderStep(undefined);
|
|
137
|
+
fireEvent.change(input(), { target: { value: '800' } });
|
|
138
|
+
|
|
139
|
+
rerender(<Harness generationAgent={agentWithCeiling(4_000)} />);
|
|
140
|
+
|
|
141
|
+
expect(input().value).toBe('800');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('falls back to today\'s bounds and copy without an agent (D3/D6 degradation)', () => {
|
|
145
|
+
const { input } = renderStep(undefined);
|
|
146
|
+
|
|
147
|
+
expect(input().min).toBe('100');
|
|
148
|
+
expect(input().max).toBe('4000');
|
|
149
|
+
expect(screen.getByText('How long the generated resource may be.')).toBeInTheDocument();
|
|
150
|
+
expect(screen.queryByText(/Limited to/)).not.toBeInTheDocument();
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('falls back when the agent is present but discovery could not answer', () => {
|
|
154
|
+
const noLimits = { ...agentWithCeiling(64_000) } as Record<string, unknown>;
|
|
155
|
+
delete noLimits.limits;
|
|
156
|
+
const { input } = renderStep(noLimits as unknown as CollaboratorEntry);
|
|
157
|
+
|
|
158
|
+
expect(input().max).toBe('4000');
|
|
159
|
+
expect(screen.queryByText(/Limited to/)).not.toBeInTheDocument();
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('submits the clamped value, not the typed one', () => {
|
|
163
|
+
const { props } = renderStep(agentWithCeiling(1_500));
|
|
164
|
+
const onGenerate = props.onGenerate;
|
|
165
|
+
|
|
166
|
+
fillRequired();
|
|
167
|
+
const input = screen.getByLabelText('Max length') as HTMLInputElement;
|
|
168
|
+
fireEvent.change(input, { target: { value: '8000' } });
|
|
169
|
+
fireEvent.click(screen.getByText(/Generate/));
|
|
170
|
+
|
|
171
|
+
expect(onGenerate).toHaveBeenCalledTimes(1);
|
|
172
|
+
expect(onGenerate.mock.calls[0]![0].maxTokens).toBe(1_500);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('lets the field be cleared, and never submits NaN for it', () => {
|
|
176
|
+
// Pre-existing hazard the clamp must not inherit: the handler was
|
|
177
|
+
// `parseInt(e.target.value)` and `parseInt('')` is NaN, which React then
|
|
178
|
+
// warns about and which would travel into the job config. Clearing must
|
|
179
|
+
// stay possible (you cannot retype a number otherwise), so the contract is
|
|
180
|
+
// about what SUBMITS, not about forbidding the empty state.
|
|
181
|
+
const { props } = renderStep(agentWithCeiling(4_000));
|
|
182
|
+
const onGenerate = props.onGenerate;
|
|
183
|
+
const input = screen.getByLabelText('Max length') as HTMLInputElement;
|
|
184
|
+
|
|
185
|
+
fillRequired();
|
|
186
|
+
fireEvent.change(input, { target: { value: '' } });
|
|
187
|
+
expect(input.value).toBe('');
|
|
188
|
+
|
|
189
|
+
fireEvent.click(screen.getByText(/Generate/));
|
|
190
|
+
|
|
191
|
+
const submitted = onGenerate.mock.calls[0]![0].maxTokens;
|
|
192
|
+
expect(Number.isFinite(submitted)).toBe(true);
|
|
193
|
+
expect(submitted).toBeGreaterThanOrEqual(100);
|
|
194
|
+
expect(submitted).toBeLessThanOrEqual(4_000);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// The rest of the draft, same contract as the ceiling: every field reports to
|
|
199
|
+
// the owner (WIZARD-NAVIGATION D3). A field that kept its own state would look
|
|
200
|
+
// right until someone pressed Back.
|
|
201
|
+
describe('ConfigureGenerationStep — the draft is the owner\'s', () => {
|
|
202
|
+
it('reports edits to title, instructions, language and creativity', async () => {
|
|
203
|
+
const { input } = renderStep(agentWithCeiling(4_000));
|
|
204
|
+
expect(input()).toBeInTheDocument();
|
|
205
|
+
|
|
206
|
+
fireEvent.change(screen.getByLabelText(translations.resourceTitle), { target: { value: 'Caspian Sea' } });
|
|
207
|
+
expect((screen.getByLabelText(translations.resourceTitle) as HTMLInputElement).value).toBe('Caspian Sea');
|
|
208
|
+
|
|
209
|
+
fireEvent.change(screen.getByLabelText(translations.additionalInstructions), { target: { value: 'hydrology only' } });
|
|
210
|
+
expect((screen.getByLabelText(translations.additionalInstructions) as HTMLTextAreaElement).value).toBe('hydrology only');
|
|
211
|
+
|
|
212
|
+
// The label carries the live value — "Creativity (0.7)" — which is itself
|
|
213
|
+
// the proof the slider is controlled: it can only update if the owner's
|
|
214
|
+
// draft came back down.
|
|
215
|
+
const slider = () => screen.getByLabelText(new RegExp(`^${translations.creativity}`)) as HTMLInputElement;
|
|
216
|
+
fireEvent.change(slider(), { target: { value: '0.2' } });
|
|
217
|
+
expect(slider().value).toBe('0.2');
|
|
218
|
+
expect(screen.getByText(/Creativity \(0\.2\)/)).toBeInTheDocument();
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('lets the max-length field go empty while retyping', () => {
|
|
222
|
+
// Clearing is a legitimate mid-edit state — you cannot retype a number
|
|
223
|
+
// otherwise — so the field must accept '' and let submission clamp it.
|
|
224
|
+
const { input } = renderStep(agentWithCeiling(4_000));
|
|
225
|
+
fireEvent.change(input(), { target: { value: '' } });
|
|
226
|
+
expect(input().value).toBe('');
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('reports a language change to the owner', () => {
|
|
230
|
+
renderStep(agentWithCeiling(4_000));
|
|
231
|
+
const select = () => screen.getByLabelText(translations.language) as HTMLSelectElement;
|
|
232
|
+
const other = Array.from(select().options).map((o) => o.value).find((v) => v !== select().value);
|
|
233
|
+
fireEvent.change(select(), { target: { value: other } });
|
|
234
|
+
expect(select().value).toBe(other);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('carries the typed instructions into the submitted config, trimmed', () => {
|
|
238
|
+
const { props } = renderStep(agentWithCeiling(4_000));
|
|
239
|
+
fillRequired();
|
|
240
|
+
fireEvent.change(screen.getByLabelText(translations.additionalInstructions), { target: { value: ' hydrology only ' } });
|
|
241
|
+
fireEvent.click(screen.getByText(new RegExp(translations.generate)));
|
|
242
|
+
|
|
243
|
+
expect(props.onGenerate).toHaveBeenCalledTimes(1);
|
|
244
|
+
expect(props.onGenerate.mock.calls[0]![0].prompt).toBe('hydrology only');
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('omits the prompt entirely when the field is blank', () => {
|
|
248
|
+
// `prompt` is optional on the wire; sending "" would be a claim the user
|
|
249
|
+
// never made, and the worker would prepend an empty instruction block.
|
|
250
|
+
const { props } = renderStep(agentWithCeiling(4_000));
|
|
251
|
+
fillRequired();
|
|
252
|
+
fireEvent.click(screen.getByText(new RegExp(translations.generate)));
|
|
253
|
+
expect(props.onGenerate.mock.calls[0]![0]).not.toHaveProperty('prompt');
|
|
254
|
+
});
|
|
255
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The search step is CONTROLLED (WIZARD-NAVIGATION D3) — the wizard owns
|
|
3
|
+
* `limit` and `useSemanticScoring` so stepping Back cannot discard them.
|
|
4
|
+
*
|
|
5
|
+
* That makes these tests about the contract rather than the widget: every edit
|
|
6
|
+
* must reach `onConfigChange` (an uncontrolled field would appear to work and
|
|
7
|
+
* silently revert on Back), and submit must send the CURRENT config rather than
|
|
8
|
+
* a stale copy.
|
|
9
|
+
*/
|
|
10
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
11
|
+
import { render, screen } from '@testing-library/react';
|
|
12
|
+
import userEvent from '@testing-library/user-event';
|
|
13
|
+
import '@testing-library/jest-dom';
|
|
14
|
+
import { ConfigureSearchStep, type SearchConfig } from '../ConfigureSearchStep';
|
|
15
|
+
|
|
16
|
+
const T = {
|
|
17
|
+
maxResults: 'Max Results', semanticScoring: 'Semantic Scoring',
|
|
18
|
+
semanticScoringHelp: 'Use AI to score results by semantic relevance',
|
|
19
|
+
back: 'Back', search: 'Search', searching: 'Searching…',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const CONFIG: SearchConfig = { limit: 10, useSemanticScoring: true };
|
|
23
|
+
|
|
24
|
+
function renderStep(config: SearchConfig = CONFIG) {
|
|
25
|
+
const onConfigChange = vi.fn();
|
|
26
|
+
const onSearch = vi.fn();
|
|
27
|
+
const onBack = vi.fn();
|
|
28
|
+
render(
|
|
29
|
+
<ConfigureSearchStep
|
|
30
|
+
config={config}
|
|
31
|
+
onConfigChange={onConfigChange}
|
|
32
|
+
onBack={onBack}
|
|
33
|
+
onSearch={onSearch}
|
|
34
|
+
translations={T}
|
|
35
|
+
/>,
|
|
36
|
+
);
|
|
37
|
+
return { onConfigChange, onSearch, onBack };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
describe('ConfigureSearchStep', () => {
|
|
41
|
+
it('reports a new result limit to the owner rather than keeping it', async () => {
|
|
42
|
+
const { onConfigChange } = renderStep();
|
|
43
|
+
await userEvent.selectOptions(screen.getByLabelText(T.maxResults), '20');
|
|
44
|
+
expect(onConfigChange).toHaveBeenCalledWith({ limit: 20, useSemanticScoring: true });
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('reports the semantic-scoring toggle the same way', async () => {
|
|
48
|
+
const { onConfigChange } = renderStep();
|
|
49
|
+
await userEvent.click(screen.getByLabelText(new RegExp(T.semanticScoring)));
|
|
50
|
+
expect(onConfigChange).toHaveBeenCalledWith({ limit: 10, useSemanticScoring: false });
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('submits whatever the owner currently holds, not a default', async () => {
|
|
54
|
+
// The point of controlled state: a step that kept its own copy could submit
|
|
55
|
+
// something the wizard never saw.
|
|
56
|
+
const { onSearch } = renderStep({ limit: 5, useSemanticScoring: false });
|
|
57
|
+
await userEvent.click(screen.getByRole('button', { name: T.search }));
|
|
58
|
+
expect(onSearch).toHaveBeenCalledWith({ limit: 5, useSemanticScoring: false });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('renders the owner\'s values, so Back-then-forward shows them again', () => {
|
|
62
|
+
renderStep({ limit: 20, useSemanticScoring: false });
|
|
63
|
+
expect((screen.getByLabelText(T.maxResults) as HTMLSelectElement).value).toBe('20');
|
|
64
|
+
expect((screen.getByLabelText(new RegExp(T.semanticScoring)) as HTMLInputElement).checked).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('retreats when Back is pressed', async () => {
|
|
68
|
+
const { onBack } = renderStep();
|
|
69
|
+
await userEvent.click(screen.getByRole('button', { name: /Back/ }));
|
|
70
|
+
expect(onBack).toHaveBeenCalledTimes(1);
|
|
71
|
+
});
|
|
72
|
+
});
|