@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.
Files changed (242) hide show
  1. package/README.md +28 -4
  2. package/dist/{PdfAnnotationCanvas.client-6ZSF6ZTJ.js → PdfAnnotationCanvas.client-C2IGG4SW.js} +33 -6
  3. package/dist/{PdfAnnotationCanvas.client-JCJMXWWJ.css → PdfAnnotationCanvas.client-TGDPPGXJ.css} +6 -1
  4. package/dist/{ar-YTUUGK2O.js → ar-FABPTJVB.js} +88 -26
  5. package/dist/{bn-ASGNGPMD.js → bn-LKP6BDMH.js} +88 -26
  6. package/dist/{chunk-L3WKRWP3.js → chunk-2GOHTTHW.js} +88 -26
  7. package/dist/{chunk-OBQOZCJE.js → chunk-2YHEE63S.js} +31 -31
  8. package/dist/{cs-WMYVAOZY.js → cs-YQYNWHRG.js} +88 -26
  9. package/dist/{da-FSHHN4D6.js → da-QHZOYKRY.js} +88 -26
  10. package/dist/{de-FJFO3TPT.js → de-GKA7RD7J.js} +88 -26
  11. package/dist/{el-XOH4QIAG.js → el-45T257AL.js} +88 -26
  12. package/dist/{en-3FCBW3YD.js → en-RVW5EQ66.js} +4 -2
  13. package/dist/{es-HDX4QI7E.js → es-AVER7RU4.js} +88 -26
  14. package/dist/{fa-DJ6WTFTJ.js → fa-K3VPK3O2.js} +88 -26
  15. package/dist/{fi-4S7H7W7U.js → fi-5NEKF7EB.js} +88 -26
  16. package/dist/{fr-6UJSYHXC.js → fr-FUMNDLBZ.js} +88 -26
  17. package/dist/{he-ZZJOHJX2.js → he-3JGNA3PR.js} +88 -26
  18. package/dist/{hi-HZJTAW7U.js → hi-IJY375DF.js} +88 -26
  19. package/dist/{id-E3I6ILEM.js → id-LWHKJ7ZD.js} +88 -26
  20. package/dist/index.css +380 -524
  21. package/dist/index.d.ts +318 -192
  22. package/dist/index.js +2960 -2421
  23. package/dist/integrations/css-modules-helper.js +1 -3
  24. package/dist/{it-ULAPAD7U.js → it-EBVOV4RW.js} +88 -26
  25. package/dist/{ja-SAHZQ4CZ.js → ja-KWRIX2NF.js} +88 -26
  26. package/dist/{ko-DFU2ADQ4.js → ko-L5CJMFDS.js} +88 -26
  27. package/dist/{ms-NWL76XNT.js → ms-LSNMJDR2.js} +88 -26
  28. package/dist/{nl-ATCM76WN.js → nl-ZB32MB7U.js} +88 -26
  29. package/dist/{no-UIAQ5RJF.js → no-4GTNY7M5.js} +88 -26
  30. package/dist/{pl-NOECQDXD.js → pl-LUHBLTHB.js} +88 -26
  31. package/dist/{pt-SVWIIUPC.js → pt-C4H3UNQ6.js} +88 -26
  32. package/dist/{ro-NR2DRTZG.js → ro-GGITS5V4.js} +88 -26
  33. package/dist/{sv-STVJ37JP.js → sv-T56RTLBP.js} +88 -26
  34. package/dist/test-utils.css +1 -1
  35. package/dist/test-utils.d.ts +1 -1
  36. package/dist/test-utils.js +2 -2
  37. package/dist/{th-UWQR4K54.js → th-22X2IWCB.js} +88 -26
  38. package/dist/{tr-WNXZSD27.js → tr-77TFG62H.js} +88 -26
  39. package/dist/{uk-SJRMB47Y.js → uk-MIIL6NXT.js} +88 -26
  40. package/dist/{vi-TMRQFES2.js → vi-7JYOIGNV.js} +88 -26
  41. package/dist/{zh-QOL3LYEM.js → zh-ID6DRYU4.js} +88 -26
  42. package/package.json +11 -10
  43. package/src/components/AssistProgress.tsx +178 -134
  44. package/src/components/CodeMirrorRenderer.tsx +8 -8
  45. package/src/components/{EntityFoundLog.tsx → ItemFoundLog.tsx} +12 -7
  46. package/src/components/ProtectedErrorBoundary.css +3 -1
  47. package/src/components/StatusDisplay.css +1 -1
  48. package/src/components/Toast.css +3 -1
  49. package/src/components/__tests__/AssistProgress.test.tsx +268 -141
  50. package/src/components/annotation/annotation-entries.css +0 -4
  51. package/src/components/annotation/references.css +0 -4
  52. package/src/components/image-annotation/AnnotationOverlay.tsx +6 -6
  53. package/src/components/image-annotation/SvgDrawingCanvas.tsx +1 -1
  54. package/src/components/image-annotation/__tests__/AnnotationOverlay.click.test.tsx +7 -9
  55. package/src/components/image-annotation/__tests__/SvgDrawingCanvas.click.test.tsx +1 -2
  56. package/src/components/layout/Header.css +0 -4
  57. package/src/components/layout/LeftSidebar.css +0 -4
  58. package/src/components/modals/ComposeStep.tsx +218 -0
  59. package/src/components/modals/ConfigureGatherStep.tsx +10 -18
  60. package/src/components/modals/ConfigureGenerationStep.tsx +131 -54
  61. package/src/components/modals/ConfigureSearchStep.tsx +42 -40
  62. package/src/components/modals/ContextSummary.tsx +185 -72
  63. package/src/components/modals/CorpusPane.tsx +75 -0
  64. package/src/components/modals/DiscardPrompt.tsx +38 -0
  65. package/src/components/modals/GatherContextStep.tsx +237 -107
  66. package/src/components/modals/ReferenceWizardModal.tsx +287 -60
  67. package/src/components/modals/ResourceGenerateModal.tsx +233 -109
  68. package/src/components/modals/SearchModal.css +335 -111
  69. package/src/components/modals/SearchModal.tsx +1 -1
  70. package/src/components/modals/SearchResultsStep.tsx +63 -88
  71. package/src/components/modals/WizardFooter.tsx +69 -0
  72. package/src/components/modals/__tests__/ComposeStep.test.tsx +152 -0
  73. package/src/components/modals/__tests__/ConfigureGatherStep.test.tsx +14 -26
  74. package/src/components/modals/__tests__/ConfigureGenerationStep.test.tsx +290 -0
  75. package/src/components/modals/__tests__/ConfigureSearchStep.test.tsx +95 -0
  76. package/src/components/modals/__tests__/GatherContextStep.resource.test.tsx +463 -25
  77. package/src/components/modals/__tests__/ReferenceWizardModal.test.tsx +635 -0
  78. package/src/components/modals/__tests__/ResourceGenerateModal.test.tsx +277 -85
  79. package/src/components/modals/__tests__/SearchModal.search-wiring.test.tsx +6 -2
  80. package/src/components/modals/__tests__/SearchResultsStep.test.tsx +61 -0
  81. package/src/components/modals/__tests__/WizardFooter.test.tsx +344 -0
  82. package/src/components/modals/modals.css +16 -42
  83. package/src/components/navigation/NavigationTabs.css +0 -20
  84. package/src/components/pdf-annotation/PdfAnnotationCanvas.css +21 -2
  85. package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +36 -3
  86. package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +41 -6
  87. package/src/components/resource/AnnotateView.tsx +19 -8
  88. package/src/components/resource/AnnotationHistory.tsx +2 -0
  89. package/src/components/resource/BrowseView.tsx +20 -9
  90. package/src/components/resource/ResourceViewer.tsx +27 -28
  91. package/src/components/resource/__tests__/AnnotateView.embeddable.test.tsx +1 -1
  92. package/src/components/resource/__tests__/AnnotateView.focus.test.tsx +50 -0
  93. package/src/components/resource/__tests__/AnnotationHistory.test.tsx +2 -0
  94. package/src/components/resource/__tests__/BrowseView.embeddable.test.tsx +1 -1
  95. package/src/components/resource/__tests__/BrowseView.test.tsx +38 -12
  96. package/src/components/resource/__tests__/ResourceViewer.embeddable.test.tsx +47 -3
  97. package/src/components/resource/annotate-renderers.tsx +3 -3
  98. package/src/components/resource/event-formatting.ts +0 -3
  99. package/src/components/resource/panels/AssessmentEntry.tsx +1 -1
  100. package/src/components/resource/panels/AssessmentPanel.css +5 -6
  101. package/src/components/resource/panels/AssistSection.css +95 -263
  102. package/src/components/resource/panels/AssistSection.tsx +4 -4
  103. package/src/components/resource/panels/AssistShell.tsx +12 -4
  104. package/src/components/resource/panels/CollaborationPanel.css +47 -22
  105. package/src/components/resource/panels/CollaborationPanel.tsx +93 -2
  106. package/src/components/resource/panels/CommentEntry.tsx +1 -1
  107. package/src/components/resource/panels/CommentsPanel.css +0 -16
  108. package/src/components/resource/panels/HighlightEntry.tsx +1 -1
  109. package/src/components/resource/panels/HighlightPanel.css +0 -7
  110. package/src/components/resource/panels/JsonLdPanel.css +0 -4
  111. package/src/components/resource/panels/ReferenceEntry.tsx +10 -3
  112. package/src/components/resource/panels/ReferencesPanel.css +0 -12
  113. package/src/components/resource/panels/ReferencesPanel.tsx +16 -20
  114. package/src/components/resource/panels/ResourceInfoPanel.css +0 -32
  115. package/src/components/resource/panels/ResourceInfoPanel.tsx +89 -20
  116. package/src/components/resource/panels/StatisticsPanel.css +0 -20
  117. package/src/components/resource/panels/TagEntry.tsx +1 -1
  118. package/src/components/resource/panels/TaggingPanel.css +0 -4
  119. package/src/components/resource/panels/TaggingPanel.tsx +2 -10
  120. package/src/components/resource/panels/UnifiedAnnotationsPanel.css +0 -4
  121. package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +7 -2
  122. package/src/components/resource/panels/__tests__/AssessmentEntry.test.tsx +0 -1
  123. package/src/components/resource/panels/__tests__/AssistSection.test.tsx +48 -50
  124. package/src/components/resource/panels/__tests__/AssistShell.test.tsx +11 -6
  125. package/src/components/resource/panels/__tests__/CollaborationPanel.test.tsx +89 -0
  126. package/src/components/resource/panels/__tests__/CommentEntry.test.tsx +1 -2
  127. package/src/components/resource/panels/__tests__/HighlightEntry.test.tsx +0 -1
  128. package/src/components/resource/panels/__tests__/HighlightPanel.annotationProgress.test.tsx +44 -36
  129. package/src/components/resource/panels/__tests__/ReferenceEntry.test.tsx +41 -1
  130. package/src/components/resource/panels/__tests__/ReferencesPanel.headless.test.tsx +1 -1
  131. package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +67 -54
  132. package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +185 -7
  133. package/src/components/resource/panels/__tests__/TagEntry.test.tsx +0 -1
  134. package/src/components/settings/SettingsPanel.css +0 -4
  135. package/src/features/auth/auth.css +2 -26
  136. package/src/features/resource-compose/__tests__/ResourceComposePage.test.tsx +0 -65
  137. package/src/features/resource-compose/components/ResourceComposePage.tsx +17 -130
  138. package/src/features/resource-compose/state/__tests__/compose-page-state-unit.test.ts +1 -76
  139. package/src/features/resource-compose/state/compose-page-state-unit.ts +5 -48
  140. package/src/features/resource-discovery/__tests__/ResourceDiscoveryPage.test.tsx +55 -0
  141. package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +29 -0
  142. package/src/features/resource-discovery/state/__tests__/discover-state-unit.test.ts +53 -3
  143. package/src/features/resource-discovery/state/discover-state-unit.ts +29 -8
  144. package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +71 -15
  145. package/src/features/resource-viewer/components/ResourceViewerPage.tsx +166 -71
  146. package/src/features/resource-viewer/hooks/__tests__/useResourceViewedReport.test.tsx +63 -0
  147. package/src/features/resource-viewer/hooks/useResourceViewedReport.ts +33 -0
  148. package/src/features/resource-viewer/state/__tests__/resource-viewer-page-state-unit.test.ts +2 -2
  149. package/src/features/resource-viewer/state/resource-viewer-page-state-unit.ts +1 -1
  150. package/src/styles/core/buttons.css +3 -1
  151. package/src/styles/core/forms.css +35 -29
  152. package/src/styles/core/indicators.css +0 -4
  153. package/src/styles/core/inputs.css +0 -4
  154. package/src/styles/core/progress.css +2 -34
  155. package/src/styles/core/sliders.css +0 -4
  156. package/src/styles/core/toggles.css +0 -4
  157. package/src/styles/features/resource-discovery.css +13 -0
  158. package/src/styles/features/resource-viewer.css +0 -42
  159. package/src/styles/features/resource.css +0 -4
  160. package/src/styles/motivations/motivation-assessment.css +2 -2
  161. package/src/styles/panels/history-panel.css +0 -40
  162. package/src/styles/panels/user-panel.css +0 -3
  163. package/src/styles/patterns/panel-helpers.css +0 -27
  164. package/src/styles/patterns/panel-sections.css +0 -59
  165. package/src/styles/patterns/panels-base.css +0 -32
  166. package/translations/ar.json +84 -24
  167. package/translations/bn.json +84 -24
  168. package/translations/cs.json +84 -24
  169. package/translations/da.json +84 -24
  170. package/translations/de.json +84 -24
  171. package/translations/el.json +84 -24
  172. package/translations/en.json +84 -24
  173. package/translations/es.json +84 -24
  174. package/translations/fa.json +84 -24
  175. package/translations/fi.json +84 -24
  176. package/translations/fr.json +84 -24
  177. package/translations/he.json +84 -24
  178. package/translations/hi.json +84 -24
  179. package/translations/id.json +84 -24
  180. package/translations/it.json +84 -24
  181. package/translations/ja.json +84 -24
  182. package/translations/ko.json +84 -24
  183. package/translations/ms.json +84 -24
  184. package/translations/nl.json +84 -24
  185. package/translations/no.json +84 -24
  186. package/translations/pl.json +84 -24
  187. package/translations/pt.json +84 -24
  188. package/translations/ro.json +84 -24
  189. package/translations/sv.json +84 -24
  190. package/translations/th.json +84 -24
  191. package/translations/tr.json +84 -24
  192. package/translations/uk.json +84 -24
  193. package/translations/vi.json +84 -24
  194. package/translations/zh.json +84 -24
  195. package/dist/PdfAnnotationCanvas.client-6ZSF6ZTJ.js.map +0 -1
  196. package/dist/PdfAnnotationCanvas.client-JCJMXWWJ.css.map +0 -1
  197. package/dist/ar-YTUUGK2O.js.map +0 -1
  198. package/dist/bn-ASGNGPMD.js.map +0 -1
  199. package/dist/chunk-BTUG3VJJ.js.map +0 -1
  200. package/dist/chunk-L3WKRWP3.js.map +0 -1
  201. package/dist/chunk-O23TLLXW.js.map +0 -1
  202. package/dist/chunk-OBQOZCJE.js.map +0 -1
  203. package/dist/chunk-QT7LYM72.js.map +0 -1
  204. package/dist/chunk-VXASXU4K.js.map +0 -1
  205. package/dist/chunk-XUDKYAVC.js +0 -21
  206. package/dist/chunk-XUDKYAVC.js.map +0 -1
  207. package/dist/cs-WMYVAOZY.js.map +0 -1
  208. package/dist/da-FSHHN4D6.js.map +0 -1
  209. package/dist/de-FJFO3TPT.js.map +0 -1
  210. package/dist/el-XOH4QIAG.js.map +0 -1
  211. package/dist/en-3FCBW3YD.js.map +0 -1
  212. package/dist/es-HDX4QI7E.js.map +0 -1
  213. package/dist/fa-DJ6WTFTJ.js.map +0 -1
  214. package/dist/fi-4S7H7W7U.js.map +0 -1
  215. package/dist/fr-6UJSYHXC.js.map +0 -1
  216. package/dist/he-ZZJOHJX2.js.map +0 -1
  217. package/dist/hi-HZJTAW7U.js.map +0 -1
  218. package/dist/id-E3I6ILEM.js.map +0 -1
  219. package/dist/index.css.map +0 -1
  220. package/dist/index.js.map +0 -1
  221. package/dist/integrations/css-modules-helper.js.map +0 -1
  222. package/dist/integrations/styled-components-theme.js.map +0 -1
  223. package/dist/it-ULAPAD7U.js.map +0 -1
  224. package/dist/ja-SAHZQ4CZ.js.map +0 -1
  225. package/dist/ko-DFU2ADQ4.js.map +0 -1
  226. package/dist/ms-NWL76XNT.js.map +0 -1
  227. package/dist/nl-ATCM76WN.js.map +0 -1
  228. package/dist/no-UIAQ5RJF.js.map +0 -1
  229. package/dist/pl-NOECQDXD.js.map +0 -1
  230. package/dist/pt-SVWIIUPC.js.map +0 -1
  231. package/dist/ro-NR2DRTZG.js.map +0 -1
  232. package/dist/sv-STVJ37JP.js.map +0 -1
  233. package/dist/test-utils.css.map +0 -1
  234. package/dist/test-utils.js.map +0 -1
  235. package/dist/th-UWQR4K54.js.map +0 -1
  236. package/dist/tr-WNXZSD27.js.map +0 -1
  237. package/dist/uk-SJRMB47Y.js.map +0 -1
  238. package/dist/vi-TMRQFES2.js.map +0 -1
  239. package/dist/zh-QOL3LYEM.js.map +0 -1
  240. package/src/components/modals/ResourceSearchModal.tsx +0 -224
  241. package/src/components/modals/__tests__/ResourceSearchModal.test.tsx +0 -159
  242. package/src/integrations/tailwind-plugin.cjs +0 -224
@@ -2,15 +2,15 @@
2
2
  * ResourceComposePage Component
3
3
  *
4
4
  * Pure React component for creating and editing resources.
5
- * Supports three modes: new resource, clone, and reference completion.
5
+ * Supports two modes: new resource and clone. (Reference completion moved
6
+ * into the resolve wizard — COMPOSE-IN-MODAL.)
6
7
  * All dependencies passed as props - no Next.js hooks!
7
8
  */
8
9
 
9
10
  import React, { useState, useEffect } from 'react';
10
- import { deriveViews, type GatheredContext } from '@semiont/core';
11
11
  import { capabilitiesOf, AUTHORABLE_MEDIA_TYPES, MEDIA_TYPES, mediaTypeForExtension, isSupportedMediaType, LOCALES } from '@semiont/core';
12
12
  import type { UploadProgress } from '@semiont/sdk';
13
- import { type CloneData, type ReferenceData } from '../state/compose-page-state-unit';
13
+ import { type CloneData } from '../state/compose-page-state-unit';
14
14
  import { COMMON_PANELS, type ToolbarPanelType } from '../../../state/shell-state-unit';
15
15
  import { buttonStyles } from '../../../lib/button-styles';
16
16
  import { CodeMirrorRenderer } from '../../../components/CodeMirrorRenderer';
@@ -42,10 +42,8 @@ function detectUploadMediaType(file: File): string {
42
42
  }
43
43
 
44
44
  export interface ResourceComposePageProps {
45
- mode: 'new' | 'clone' | 'reference';
45
+ mode: 'new' | 'clone';
46
46
  cloneData?: CloneData | null;
47
- referenceData?: ReferenceData | null;
48
- gatheredContext?: GatheredContext | null;
49
47
 
50
48
  // Available options
51
49
  availableEntityTypes: string[];
@@ -74,10 +72,7 @@ export interface ResourceComposePageProps {
74
72
  translations: {
75
73
  title: string;
76
74
  titleEditClone: string;
77
- titleCompleteReference: string;
78
75
  subtitleClone: string;
79
- subtitleReference: string;
80
- linkedNoticePrefix: string;
81
76
  resourceName: string;
82
77
  resourceNamePlaceholder: string;
83
78
  entityTypes: string;
@@ -99,9 +94,7 @@ export interface ResourceComposePageProps {
99
94
  cancel: string;
100
95
  saving: string;
101
96
  creating: string;
102
- creatingAndLinking: string;
103
97
  saveClonedResource: string;
104
- createAndLinkResource: string;
105
98
  createResource: string;
106
99
  };
107
100
 
@@ -111,7 +104,7 @@ export interface ResourceComposePageProps {
111
104
  }
112
105
 
113
106
  export interface SaveResourceParams {
114
- mode: 'new' | 'clone' | 'reference';
107
+ mode: 'new' | 'clone';
115
108
  name: string;
116
109
  storageUri: string;
117
110
  content?: string;
@@ -121,15 +114,11 @@ export interface SaveResourceParams {
121
114
  entityTypes?: string[];
122
115
  language: string;
123
116
  archiveOriginal?: boolean;
124
- annotationUri?: string;
125
- sourceDocumentId?: string;
126
117
  }
127
118
 
128
119
  export function ResourceComposePage({
129
120
  mode,
130
121
  cloneData,
131
- referenceData,
132
- gatheredContext,
133
122
  availableEntityTypes,
134
123
  initialLocale,
135
124
  theme,
@@ -184,13 +173,8 @@ export function ResourceComposePage({
184
173
  if (mode === 'clone' && cloneData) {
185
174
  setNewResourceName(cloneData.sourceResource.name);
186
175
  setNewResourceContent(cloneData.sourceContent);
187
- } else if (mode === 'reference' && referenceData) {
188
- setNewResourceName(referenceData.name);
189
- if (referenceData.entityTypes.length > 0) {
190
- setSelectedEntityTypes(referenceData.entityTypes);
191
- }
192
176
  }
193
- }, [mode, cloneData, referenceData]);
177
+ }, [mode, cloneData]);
194
178
 
195
179
  // Handle file upload
196
180
  const handleFileUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
@@ -266,20 +250,12 @@ export function ResourceComposePage({
266
250
  if (mode === 'clone') {
267
251
  params.archiveOriginal = archiveOriginal;
268
252
  }
269
- if (referenceData?.annotationUri) {
270
- params.annotationUri = referenceData.annotationUri;
271
- }
272
- if (referenceData?.sourceDocumentId) {
273
- params.sourceDocumentId = referenceData.sourceDocumentId;
274
- }
275
253
 
276
254
  await onSaveResource(params);
277
255
 
278
256
  // Announce success based on mode
279
257
  const successMessage = mode === 'clone'
280
258
  ? 'Resource cloned successfully'
281
- : mode === 'reference'
282
- ? 'Reference completed successfully'
283
259
  : 'Resource created successfully';
284
260
  announceFormSuccess(successMessage);
285
261
  } catch (error) {
@@ -291,13 +267,6 @@ export function ResourceComposePage({
291
267
  };
292
268
 
293
269
  const isClone = mode === 'clone';
294
- const isReferenceCompletion = mode === 'reference';
295
-
296
- // Gathered-context panel — annotation-focus only for now.
297
- const composeFocus = gatheredContext?.focus.kind === 'annotation' ? gatheredContext.focus : null;
298
- const composeConnections = gatheredContext && composeFocus
299
- ? deriveViews(gatheredContext.graph, String(composeFocus.sourceResource.id)).connections
300
- : [];
301
270
 
302
271
  return (
303
272
  <div className={`semiont-page${activePanel && COMMON_PANELS.includes(activePanel as ToolbarPanelType) ? ' semiont-page--panel-open' : ''}`}>
@@ -306,73 +275,15 @@ export function ResourceComposePage({
306
275
  {/* Page Title */}
307
276
  <div className="semiont-page__header">
308
277
  <h1 className="semiont-page__title">
309
- {isClone ? t.titleEditClone : isReferenceCompletion ? t.titleCompleteReference : t.title}
278
+ {isClone ? t.titleEditClone : t.title}
310
279
  </h1>
311
- {(isClone || isReferenceCompletion) && (
280
+ {isClone && (
312
281
  <p className="semiont-page__subtitle">
313
- {isClone ? t.subtitleClone : t.subtitleReference}
282
+ {t.subtitleClone}
314
283
  </p>
315
284
  )}
316
- {isReferenceCompletion && (
317
- <div className="semiont-page__reference-notice">
318
- <p className="semiont-page__reference-notice-text">
319
- {t.linkedNoticePrefix}
320
- </p>
321
- </div>
322
- )}
323
285
  </div>
324
286
 
325
- {/* Gathered Context Panel (from wizard) */}
326
- {gatheredContext && (
327
- <div className="semiont-form__field" style={{
328
- marginBottom: '1.5rem',
329
- padding: '1rem',
330
- backgroundColor: 'var(--semiont-bg-secondary)',
331
- borderRadius: 'var(--semiont-radius-md)',
332
- border: '1px solid var(--semiont-border-primary)',
333
- }}>
334
- <h3 className="semiont-form__label" style={{ marginBottom: '0.75rem' }}>Gathered Context</h3>
335
- {composeFocus?.selected && (
336
- <div style={{
337
- padding: '0.75rem',
338
- backgroundColor: 'var(--semiont-bg-primary)',
339
- borderRadius: 'var(--semiont-radius-md)',
340
- border: '1px solid var(--semiont-border-primary)',
341
- maxHeight: '150px',
342
- overflowY: 'auto',
343
- marginBottom: '0.5rem',
344
- }}>
345
- <div style={{ fontSize: 'var(--semiont-text-sm)', fontFamily: 'monospace', whiteSpace: 'pre-wrap', color: 'var(--semiont-text-secondary)' }}>
346
- {composeFocus.selected.before && <span>{composeFocus.selected.before}</span>}
347
- <span style={{
348
- backgroundColor: 'var(--semiont-color-primary-100)',
349
- padding: '0 0.25rem',
350
- fontWeight: 600,
351
- color: 'var(--semiont-color-primary-900)',
352
- }}>
353
- {composeFocus.selected.text}
354
- </span>
355
- {composeFocus.selected.after && <span>{composeFocus.selected.after}</span>}
356
- </div>
357
- </div>
358
- )}
359
- {composeConnections.length > 0 && (
360
- <div style={{
361
- padding: '0.5rem 0',
362
- fontSize: 'var(--semiont-text-sm)',
363
- color: 'var(--semiont-text-secondary)',
364
- }}>
365
- <span style={{ fontSize: 'var(--semiont-text-xs)', fontWeight: 500, marginRight: '0.5rem' }}>Connections:</span>
366
- {composeConnections.map(conn => (
367
- <span key={conn.resourceId} className="semiont-chip" style={{ fontSize: 'var(--semiont-text-xs)', padding: '0.125rem 0.5rem', marginRight: '0.25rem' }}>
368
- {conn.resourceName}
369
- </span>
370
- ))}
371
- </div>
372
- )}
373
- </div>
374
- )}
375
-
376
287
  {/* Create Form */}
377
288
  <div className="semiont-form">
378
289
  <form onSubmit={handleSaveResource} className="semiont-form__fields semiont-compose-grid">
@@ -416,7 +327,7 @@ export function ResourceComposePage({
416
327
  </div>
417
328
 
418
329
  {/* Entity Types Selection */}
419
- {(!isReferenceCompletion || selectedEntityTypes.length === 0) && (
330
+ {(
420
331
  <div className="semiont-form__field semiont-form__entity-types">
421
332
  <div className="semiont-form__label">
422
333
  {t.entityTypes}
@@ -462,30 +373,6 @@ export function ResourceComposePage({
462
373
  </div>
463
374
  )}
464
375
 
465
- {/* Entity Types Display for reference completion */}
466
- {isReferenceCompletion && selectedEntityTypes.length > 0 && (
467
- <div className="semiont-form__field semiont-form__entity-types-display" role="region" aria-labelledby="selected-entity-types-label">
468
- <h3 id="selected-entity-types-label" className="semiont-form__label">
469
- {t.entityTypes}
470
- </h3>
471
- <div className="semiont-form__entity-type-tags" role="list">
472
- {selectedEntityTypes.map((type) => (
473
- <span
474
- key={type}
475
- role="listitem"
476
- className="semiont-form__entity-type-tag"
477
- aria-label={`Entity type: ${type}`}
478
- >
479
- {type}
480
- </span>
481
- ))}
482
- </div>
483
- <p className="semiont-form__helper-text" id="reference-entity-types-description">
484
- These entity types were selected when creating the reference
485
- </p>
486
- </div>
487
- )}
488
-
489
376
  {/* Language Selector */}
490
377
  <div className="semiont-form__field">
491
378
  <label htmlFor="language-select" className="semiont-form__label">
@@ -512,7 +399,7 @@ export function ResourceComposePage({
512
399
  <div className="semiont-compose-grid__content">
513
400
 
514
401
  {/* Content Source Toggle - only show for new resources */}
515
- {!isClone && !isReferenceCompletion && (
402
+ {!isClone && (
516
403
  <div className="semiont-form__field">
517
404
  <label className="semiont-form__label">
518
405
  {t.contentSource}
@@ -554,7 +441,7 @@ export function ResourceComposePage({
554
441
  )}
555
442
 
556
443
  {/* Upload File Section */}
557
- {!isClone && !isReferenceCompletion && inputMethod === 'upload' && (
444
+ {!isClone && inputMethod === 'upload' && (
558
445
  <div className="semiont-form__upload-section">
559
446
  <div>
560
447
  <div className="semiont-form__upload-container">
@@ -631,10 +518,10 @@ export function ResourceComposePage({
631
518
  )}
632
519
 
633
520
  {/* Write Content Section */}
634
- {(isClone || isReferenceCompletion || inputMethod === 'write') && (
521
+ {(isClone || inputMethod === 'write') && (
635
522
  <div className="semiont-form__write-section">
636
523
  {/* Format Selector - only for manual entry, not clones */}
637
- {!isClone && !isReferenceCompletion && (
524
+ {!isClone && (
638
525
  <div className="semiont-form__field">
639
526
  <label htmlFor="format-select" className="semiont-form__label">
640
527
  {t.format}
@@ -673,7 +560,7 @@ export function ResourceComposePage({
673
560
  </div>
674
561
 
675
562
  {/* Encoding Selector - only for manual text entry */}
676
- {!isClone && !isReferenceCompletion && (
563
+ {!isClone && (
677
564
  <div className="semiont-form__field">
678
565
  <label htmlFor="charset-select" className="semiont-form__label">
679
566
  {t.encoding}
@@ -731,8 +618,8 @@ export function ResourceComposePage({
731
618
  className={buttonStyles.primary.base}
732
619
  >
733
620
  {isCreating
734
- ? (isClone ? t.saving : isReferenceCompletion ? t.creatingAndLinking : t.creating)
735
- : (isClone ? t.saveClonedResource : isReferenceCompletion ? t.createAndLinkResource : t.createResource)}
621
+ ? (isClone ? t.saving : t.creating)
622
+ : (isClone ? t.saveClonedResource : t.createResource)}
736
623
  </button>
737
624
  </div>
738
625
 
@@ -64,56 +64,6 @@ describe('createComposePageStateUnit', () => {
64
64
  stateUnit.dispose();
65
65
  });
66
66
 
67
- it('detects reference mode from params', async () => {
68
- const stateUnit = createComposePageStateUnit(sessionOf(mockClient()), mockBrowse(), {
69
- annotationUri: 'ann-1',
70
- sourceDocumentId: 'doc-1',
71
- name: 'Reference Doc',
72
- entityTypes: 'Person,Place',
73
- });
74
-
75
- const mode = await firstValueFrom(stateUnit.mode$);
76
- expect(mode).toBe('reference');
77
-
78
- const ref = await firstValueFrom(stateUnit.referenceData$.pipe(filter((r) => r !== null)));
79
- expect(ref!.annotationUri).toBe('ann-1');
80
- expect(ref!.entityTypes).toEqual(['Person', 'Place']);
81
-
82
- stateUnit.dispose();
83
- });
84
-
85
- it('parses storedContext in reference mode', async () => {
86
- const context = { annotation: { id: 'ann-1' }, sourceContext: 'text' };
87
- const stateUnit = createComposePageStateUnit(sessionOf(mockClient()), mockBrowse(), {
88
- annotationUri: 'ann-1',
89
- sourceDocumentId: 'doc-1',
90
- name: 'Ref',
91
- storedContext: JSON.stringify(context),
92
- });
93
-
94
- const gathered = await firstValueFrom(stateUnit.gatheredContext$.pipe(filter((g) => g !== null)));
95
- expect(gathered).toEqual(context);
96
-
97
- stateUnit.dispose();
98
- });
99
-
100
- it('ignores malformed storedContext', async () => {
101
- const stateUnit = createComposePageStateUnit(sessionOf(mockClient()), mockBrowse(), {
102
- annotationUri: 'ann-1',
103
- sourceDocumentId: 'doc-1',
104
- name: 'Ref',
105
- storedContext: 'not-json{{{',
106
- });
107
-
108
- const loading = await firstValueFrom(stateUnit.loading$.pipe(filter((l) => !l)));
109
- expect(loading).toBe(false);
110
-
111
- const gathered = await firstValueFrom(stateUnit.gatheredContext$);
112
- expect(gathered).toBeNull();
113
-
114
- stateUnit.dispose();
115
- });
116
-
117
67
  it('exposes entity types', async () => {
118
68
  const stateUnit = createComposePageStateUnit(sessionOf(mockClient()), mockBrowse(), {});
119
69
 
@@ -142,31 +92,6 @@ describe('createComposePageStateUnit', () => {
142
92
  stateUnit.dispose();
143
93
  });
144
94
 
145
- it('save in reference mode calls yield.resource then bind.body', async () => {
146
- const resource = mockUpload('new-4');
147
- const body = vi.fn().mockResolvedValue(undefined);
148
- const stateUnit = createComposePageStateUnit(sessionOf(mockClient({ resource, body })), mockBrowse(), {
149
- annotationUri: 'ann-1',
150
- sourceDocumentId: 'doc-1',
151
- name: 'Ref',
152
- });
153
-
154
- const id = await stateUnit.save({
155
- mode: 'reference',
156
- name: 'Ref Doc',
157
- storageUri: '/docs/ref.md',
158
- content: 'content',
159
- language: 'en',
160
- annotationUri: 'ann-1',
161
- sourceDocumentId: 'doc-1',
162
- });
163
-
164
- expect(id).toBe('new-4');
165
- expect(body).toHaveBeenCalledOnce();
166
-
167
- stateUnit.dispose();
168
- });
169
-
170
95
  it('save in clone mode calls yield.createFromToken', async () => {
171
96
  const createFromToken = vi.fn().mockResolvedValue({ resourceId: 'cloned-1' });
172
97
  const stateUnit = createComposePageStateUnit(sessionOf(mockClient({ createFromToken })), mockBrowse(), {
@@ -196,7 +121,7 @@ describe('ComposePageStateUnit — StateUnit axioms', () => {
196
121
  const browse = disposeProbe();
197
122
  return { unit: createComposePageStateUnit(sessionOf(mockClient()), browse as unknown as ShellStateUnit, {}), passedIn: [browse] };
198
123
  },
199
- surfaces: (u) => [u.mode$, u.loading$, u.cloneData$, u.referenceData$, u.gatheredContext$, u.uploadProgress$],
124
+ surfaces: (u) => [u.mode$, u.loading$, u.cloneData$, u.uploadProgress$],
200
125
  });
201
126
  });
202
127
  });
@@ -1,6 +1,6 @@
1
1
  import { BehaviorSubject, type Observable } from 'rxjs';
2
- import type { GatheredContext, AnnotationId, AccessToken, ResourceDescriptor, ResourceId } from '@semiont/core';
3
- import { resourceId as makeResourceId, annotationId as makeAnnotationId } from '@semiont/core';
2
+ import type { AccessToken, ResourceDescriptor, ResourceId } from '@semiont/core';
3
+ import { resourceId as makeResourceId } 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';
@@ -9,16 +9,11 @@ import type { SemiontSession } from '@semiont/sdk';
9
9
  import { decodeWithCharset, extensionForMediaType } from '@semiont/core';
10
10
  import type { UploadProgress } from '@semiont/sdk';
11
11
 
12
- export type ComposeMode = 'new' | 'clone' | 'reference';
12
+ export type ComposeMode = 'new' | 'clone';
13
13
 
14
14
  export interface ComposeParams {
15
15
  mode?: string | undefined;
16
16
  token?: string | undefined;
17
- annotationUri?: string | undefined;
18
- sourceDocumentId?: string | undefined;
19
- name?: string | undefined;
20
- entityTypes?: string | undefined;
21
- storedContext?: string | undefined;
22
17
  }
23
18
 
24
19
  export interface CloneData {
@@ -26,13 +21,6 @@ export interface CloneData {
26
21
  sourceContent: string;
27
22
  }
28
23
 
29
- export interface ReferenceData {
30
- annotationUri: string;
31
- sourceDocumentId: string;
32
- name: string;
33
- entityTypes: string[];
34
- }
35
-
36
24
  export interface SaveResourceParams {
37
25
  mode: ComposeMode;
38
26
  name: string;
@@ -44,8 +32,6 @@ export interface SaveResourceParams {
44
32
  entityTypes?: string[];
45
33
  language: string;
46
34
  archiveOriginal?: boolean;
47
- annotationUri?: string;
48
- sourceDocumentId?: string;
49
35
  }
50
36
 
51
37
  export interface ComposePageStateUnit extends StateUnit {
@@ -53,8 +39,6 @@ export interface ComposePageStateUnit extends StateUnit {
53
39
  mode$: Observable<ComposeMode>;
54
40
  loading$: Observable<boolean>;
55
41
  cloneData$: Observable<CloneData | null>;
56
- referenceData$: Observable<ReferenceData | null>;
57
- gatheredContext$: Observable<GatheredContext | null>;
58
42
  entityTypes: ListState<string[]>;
59
43
  /**
60
44
  * Live upload-progress for the in-flight `save(...)` call. Emits the
@@ -78,34 +62,19 @@ export function createComposePageStateUnit(
78
62
  // (`useShellStateUnit`), not this unit — do NOT add it to the disposer (it's the
79
63
  // shared, app-scoped shell). See packages/sdk/docs/STATE-UNITS.md (composition rule).
80
64
 
81
- const isReferenceMode = Boolean(params.annotationUri && params.sourceDocumentId && params.name);
82
65
  const isCloneMode = params.mode === 'clone' && Boolean(params.token);
83
- const pageMode: ComposeMode = isCloneMode ? 'clone' : isReferenceMode ? 'reference' : 'new';
66
+ const pageMode: ComposeMode = isCloneMode ? 'clone' : 'new';
84
67
 
85
68
  const mode$ = new BehaviorSubject<ComposeMode>(pageMode);
86
69
  const loading$ = new BehaviorSubject<boolean>(true);
87
70
  const cloneData$ = new BehaviorSubject<CloneData | null>(null);
88
- const referenceData$ = new BehaviorSubject<ReferenceData | null>(null);
89
- const gatheredContext$ = new BehaviorSubject<GatheredContext | null>(null);
90
71
  const uploadProgress$ = new BehaviorSubject<UploadProgress | null>(null);
91
72
 
92
73
  const entityTypes = trackList<string[]>(() => client.browse.entityTypes(), []);
93
74
  disposer.add(entityTypes.dispose);
94
75
 
95
76
  // Initialize based on mode
96
- if (isReferenceMode) {
97
- const entityTypes = params.entityTypes ? params.entityTypes.split(',') : [];
98
- referenceData$.next({
99
- annotationUri: params.annotationUri!,
100
- sourceDocumentId: params.sourceDocumentId!,
101
- name: params.name!,
102
- entityTypes,
103
- });
104
- if (params.storedContext) {
105
- try { gatheredContext$.next(JSON.parse(params.storedContext)); } catch { /* ignore malformed */ }
106
- }
107
- loading$.next(false);
108
- } else if (isCloneMode) {
77
+ if (isCloneMode) {
109
78
  void (async () => {
110
79
  try {
111
80
  const tokenResult = await client.yield.fromToken(params.token!);
@@ -175,14 +144,6 @@ export function createComposePageStateUnit(
175
144
  });
176
145
  });
177
146
 
178
- if (saveParams.mode === 'reference' && saveParams.annotationUri && saveParams.sourceDocumentId) {
179
- await client.bind.body(
180
- makeResourceId(saveParams.sourceDocumentId),
181
- makeAnnotationId(saveParams.annotationUri) as AnnotationId,
182
- [{ op: 'add', item: { type: 'SpecificResource' as const, source: newResourceId, purpose: 'linking' as const } }],
183
- );
184
- }
185
-
186
147
  return newResourceId;
187
148
  };
188
149
 
@@ -191,8 +152,6 @@ export function createComposePageStateUnit(
191
152
  mode$: mode$.asObservable(),
192
153
  loading$: loading$.asObservable(),
193
154
  cloneData$: cloneData$.asObservable(),
194
- referenceData$: referenceData$.asObservable(),
195
- gatheredContext$: gatheredContext$.asObservable(),
196
155
  entityTypes: entityTypes.state,
197
156
  uploadProgress$: uploadProgress$.asObservable(),
198
157
  save,
@@ -200,8 +159,6 @@ export function createComposePageStateUnit(
200
159
  mode$.complete();
201
160
  loading$.complete();
202
161
  cloneData$.complete();
203
- referenceData$.complete();
204
- gatheredContext$.complete();
205
162
  uploadProgress$.complete();
206
163
  disposer.dispose();
207
164
  },
@@ -45,6 +45,7 @@ const createMockProps = (overrides?: Partial<ResourceDiscoveryPageProps>): Resou
45
45
  searchPlaceholder: 'Search resources...',
46
46
  searchButton: 'Search',
47
47
  searching: 'Searching...',
48
+ semanticFallbackNotice: 'No title matches — showing related documents.',
48
49
  filterByEntityType: 'Filter by type',
49
50
  all: 'All',
50
51
  recentResources: 'Recent Resources',
@@ -70,6 +71,60 @@ const renderWithProviders = (ui: React.ReactElement) => {
70
71
  };
71
72
 
72
73
  describe('ResourceDiscoveryPage', () => {
74
+ // SEMANTIC-FALLBACK P3b / S11. The distinction is asserted through a stable
75
+ // hook, never the sentence: decision 3 set the wording and it will be revised
76
+ // from use, so a copy-pinned test would rot on the first edit and teach the
77
+ // next reader to weaken it.
78
+ describe('semantic fallback notice', () => {
79
+ const NOTICE = 'semiont-semantic-match-notice';
80
+
81
+ it('marks a semantic result set as distinct from a lexical one', () => {
82
+ const props = createMockProps({
83
+ searchQuery: 'kitten',
84
+ searchDocuments: [createMockResource('r1', 'All about cats')],
85
+ searchMatchKind: 'semantic',
86
+ });
87
+ renderWithProviders(<ResourceDiscoveryPage {...props} />);
88
+
89
+ expect(screen.getByTestId(NOTICE)).toBeTruthy();
90
+ });
91
+
92
+ it('says nothing when the answer was lexical', () => {
93
+ const props = createMockProps({
94
+ searchQuery: 'cats',
95
+ searchDocuments: [createMockResource('r1', 'All about cats')],
96
+ searchMatchKind: 'lexical',
97
+ });
98
+ renderWithProviders(<ResourceDiscoveryPage {...props} />);
99
+
100
+ expect(screen.queryByTestId(NOTICE)).toBeNull();
101
+ });
102
+
103
+ it('does not carry a stale label onto the no-query view', () => {
104
+ // Clearing the box returns to "recent", which no label describes; a
105
+ // leftover `semantic` must not annotate that list.
106
+ const props = createMockProps({
107
+ searchQuery: '',
108
+ searchDocuments: [],
109
+ searchMatchKind: 'semantic',
110
+ });
111
+ renderWithProviders(<ResourceDiscoveryPage {...props} />);
112
+
113
+ expect(screen.queryByTestId(NOTICE)).toBeNull();
114
+ });
115
+
116
+ it('renders exactly as today when the prop is absent', () => {
117
+ const props = createMockProps({
118
+ searchQuery: 'cats',
119
+ searchDocuments: [createMockResource('r1', 'All about cats')],
120
+ });
121
+ renderWithProviders(<ResourceDiscoveryPage {...props} />);
122
+
123
+ expect(screen.queryByTestId(NOTICE)).toBeNull();
124
+ });
125
+ });
126
+
127
+
73
128
  beforeEach(() => {
74
129
  });
75
130
 
@@ -28,6 +28,16 @@ export interface ResourceDiscoveryPageProps {
28
28
  recentError?: Error | null;
29
29
  onRetryRecent?: () => void;
30
30
  isSearching: boolean;
31
+ /**
32
+ * How the current search answer was produced — `'lexical'` (title/metadata
33
+ * matching) or `'semantic'` (the vector fallback that runs only when lexical
34
+ * matching returned nothing). Undefined before the first answer, and on the
35
+ * no-query view where `recent` renders and no label describes it.
36
+ *
37
+ * Optional so hosts that never wire it — including external consumers —
38
+ * render exactly as before (SEMANTIC-FALLBACK P3b).
39
+ */
40
+ searchMatchKind?: 'lexical' | 'semantic';
31
41
 
32
42
  // Controlled search state
33
43
  searchQuery: string;
@@ -53,6 +63,7 @@ export interface ResourceDiscoveryPageProps {
53
63
  searchPlaceholder: string;
54
64
  searchButton: string;
55
65
  searching: string;
66
+ semanticFallbackNotice: string;
56
67
  filterByEntityType: string;
57
68
  all: string;
58
69
  recentResources: string;
@@ -80,6 +91,7 @@ export function ResourceDiscoveryPage({
80
91
  recentError = null,
81
92
  onRetryRecent,
82
93
  isSearching,
94
+ searchMatchKind,
83
95
  searchQuery,
84
96
  onSearchQueryChange,
85
97
  selectedEntityType,
@@ -232,6 +244,23 @@ export function ResourceDiscoveryPage({
232
244
  }
233
245
  </h3>
234
246
 
247
+ {/* Decision 3: label it, minimally. Someone who searches "kitten"
248
+ and gets documents about cats otherwise has no way to know why,
249
+ and an unexplained answer is a worse trust outcome than the
250
+ empty page this replaced. Only ever appears where the
251
+ alternative was that empty page (S5), so it never competes with
252
+ a better answer. `aria-live` because the results it explains
253
+ arrive without any control being activated. */}
254
+ {hasSearchQuery && searchMatchKind === 'semantic' && searchDocuments.length > 0 && (
255
+ <p
256
+ className="semiont-card__documents-note"
257
+ data-testid="semiont-semantic-match-notice"
258
+ aria-live="polite"
259
+ >
260
+ {t.semanticFallbackNotice}
261
+ </p>
262
+ )}
263
+
235
264
  {showNoResultsWarning && (
236
265
  <div className="semiont-card__warning">
237
266
  <p className="semiont-card__warning-text">