@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
@@ -1,8 +1,8 @@
1
1
  'use client';
2
2
 
3
- import { useState, useEffect, useCallback } from 'react';
3
+ import { useState, useEffect, useCallback, useRef } from 'react';
4
4
  import { Dialog, DialogPanel, DialogTitle, Transition, TransitionChild } from '@headlessui/react';
5
- import type { GatheredContext } from '@semiont/core';
5
+ import type { GatheredContext, CollaboratorEntry } from '@semiont/core';
6
6
  import { uuidV4 } from '@semiont/core';
7
7
  import { useSemiont } from '../../session/SemiontProvider';
8
8
  import { useObservable } from '../../hooks/useObservable';
@@ -12,16 +12,28 @@ import { ConfigureGenerationStep } from './ConfigureGenerationStep';
12
12
  import type { GenerationConfig } from './ConfigureGenerationStep';
13
13
  import { ConfigureSearchStep } from './ConfigureSearchStep';
14
14
  import type { SearchConfig } from './ConfigureSearchStep';
15
+ import type { GenerationDraft } from './ConfigureGenerationStep';
15
16
  import { SearchResultsStep } from './SearchResultsStep';
17
+ import { ComposeStep } from './ComposeStep';
18
+ import type { ComposeDraft, ComposeParams } from './ComposeStep';
19
+ import { DiscardPrompt } from './DiscardPrompt';
20
+ import { useLineNumbers } from '../../contexts/LineNumbersContext';
16
21
  import type { ScoredResult } from './SearchResultsStep';
17
22
 
18
23
  type WizardStep =
19
24
  | { step: 'gather' }
20
25
  | { step: 'configure-search' }
21
26
  | { step: 'search-results'; results: ScoredResult[] }
22
- | { step: 'configure-generation' };
27
+ | { step: 'configure-generation' }
28
+ | { step: 'compose' };
23
29
 
24
30
  export interface ReferenceWizardModalProps {
31
+ /**
32
+ * Roster entry serving `generation`, forwarded to ConfigureGenerationStep so
33
+ * the max-length control is bounded by the model's real output ceiling.
34
+ * Optional: absent means today's default bounds (INFERENCE-LIMITS-EXPOSURE D3).
35
+ */
36
+ generationAgent?: CollaboratorEntry;
25
37
  isOpen: boolean;
26
38
  onClose: () => void;
27
39
  /** The annotation being resolved */
@@ -32,6 +44,12 @@ export interface ReferenceWizardModalProps {
32
44
  defaultTitle: string;
33
45
  /** Entity types from the annotation */
34
46
  entityTypes: string[];
47
+ /**
48
+ * The source resource's display name — a page fact (the wizard opens FROM
49
+ * that resource), shown in the gather skeleton's source line before the
50
+ * gathered context can say it itself.
51
+ */
52
+ resourceName?: string;
35
53
  /** Current locale for generation defaults */
36
54
  locale: string;
37
55
  /** Gathered context state */
@@ -41,21 +59,36 @@ export interface ReferenceWizardModalProps {
41
59
  /** Callbacks */
42
60
  onGenerateSubmit: (referenceId: string, config: GenerationConfig) => void;
43
61
  onLinkResource: (referenceId: string, targetResourceId: string) => void;
44
- onComposeNavigate: (context: GatheredContext, annotationId: string, resourceId: string, title: string, entityTypes: string[]) => void;
62
+ /**
63
+ * Create-and-link (COMPOSE-IN-MODAL): the host runs `yield.resource` then
64
+ * `bind.body` and settles the promise; rejection keeps the modal open with
65
+ * the compose footer re-enabled. Replaces the old navigate-to-page flow.
66
+ */
67
+ onComposeSubmit: (referenceId: string, params: ComposeParams) => Promise<void>;
68
+ /** Picker vocabulary for compose when the reference fixed no entity types. */
69
+ entityTypeOptions?: string[];
70
+ /** Editor hover delay for the compose step's CodeMirror. */
71
+ hoverDelayMs?: number;
45
72
  /** Translation strings */
46
73
  translations: {
47
- gatherTitle: string;
48
- configureGenerationTitle: string;
49
- configureSearchTitle: string;
50
- searchResultsTitle: string;
74
+ /** The ONE title, all steps — the flow is resolving a reference; the
75
+ strategy band below says where in it you are. */
76
+ resolveTitle: string;
51
77
  sourceContextLabel: string;
52
78
  connectionsLabel: string;
53
79
  citedByLabel: string;
80
+ graphPaneTitle: string;
81
+ graphEmpty: string;
82
+ resourceLinkLabel: string;
83
+ corpusPaneTitle: string;
84
+ corpusEmpty: string;
85
+ excludedReceipt: string;
86
+ machineRead: string;
54
87
  userHintLabel: string;
88
+ userHintEffect: string;
55
89
  userHintPlaceholder: string;
56
90
  loadingContext: string;
57
91
  failedContext: string;
58
- cancel: string;
59
92
  search: string;
60
93
  searching: string;
61
94
  generate: string;
@@ -67,6 +100,7 @@ export interface ReferenceWizardModalProps {
67
100
  noResults: string;
68
101
  resourceTitle: string;
69
102
  resourceTitlePlaceholder: string;
103
+ saveLocation: string;
70
104
  additionalInstructions: string;
71
105
  additionalInstructionsPlaceholder: string;
72
106
  language: string;
@@ -76,9 +110,18 @@ export interface ReferenceWizardModalProps {
76
110
  creativityCreative: string;
77
111
  maxLength: string;
78
112
  maxLengthHelp: string;
113
+ maxLengthCeiling: string;
79
114
  maxResults: string;
80
115
  semanticScoring: string;
81
116
  semanticScoringHelp: string;
117
+ searchFailed: string;
118
+ entityTypes: string;
119
+ contentLabel: string;
120
+ createAndLink: string;
121
+ creatingAndLinking: string;
122
+ discardDraftPrompt: string;
123
+ discardDraft: string;
124
+ keepEditing: string;
82
125
  };
83
126
  }
84
127
 
@@ -89,26 +132,62 @@ export function ReferenceWizardModal({
89
132
  resourceId,
90
133
  defaultTitle,
91
134
  entityTypes,
135
+ resourceName,
92
136
  locale,
93
137
  context,
94
138
  contextLoading,
95
139
  contextError,
96
140
  onGenerateSubmit,
97
141
  onLinkResource,
98
- onComposeNavigate,
142
+ onComposeSubmit,
143
+ entityTypeOptions = [],
144
+ hoverDelayMs = 300,
99
145
  translations: t,
146
+ generationAgent,
100
147
  }: ReferenceWizardModalProps) {
101
148
  const session = useObservable(useSemiont().activeSession$);
102
149
  const [wizardStep, setWizardStep] = useState<WizardStep>({ step: 'gather' });
150
+ // Both step drafts live HERE, not in the steps (WIZARD-NAVIGATION D3). Stepping
151
+ // back unmounts a step; if the step owned its values, Back would silently discard
152
+ // everything typed — which is exactly what it used to do.
153
+ const [searchConfig, setSearchConfig] = useState<SearchConfig>({ limit: 10, useSemanticScoring: true });
154
+ const [generationDraft, setGenerationDraft] = useState<GenerationDraft>({
155
+ title: defaultTitle, storagePath: '', prompt: '', language: locale,
156
+ temperature: 0.7, maxTokensText: '500',
157
+ });
103
158
  const [isSearching, setIsSearching] = useState(false);
159
+ const [searchError, setSearchError] = useState<string | null>(null);
104
160
  const [userHint, setUserHint] = useState('');
161
+ const freshComposeDraft = (): ComposeDraft => ({
162
+ name: defaultTitle, storagePath: '', content: '', entityTypes: [], language: locale,
163
+ });
164
+ const [composeDraft, setComposeDraft] = useState<ComposeDraft>(freshComposeDraft);
165
+ const [showDiscardPrompt, setShowDiscardPrompt] = useState(false);
166
+ // The editor consumes the shared display setting; the step itself stays
167
+ // provider-free, so the wizard resolves it and passes it down.
168
+ const { showLineNumbers } = useLineNumbers();
169
+
170
+ // The dirty baseline is what was SEEDED, not the live prop: defaultTitle
171
+ // derives from observable wizard state and can re-emit while the modal is
172
+ // open; an untouched draft must not read as dirty because the baseline
173
+ // moved under it.
174
+ const seededTitle = useRef(defaultTitle);
105
175
 
106
176
  // Reset to gather step when modal opens
107
177
  useEffect(() => {
108
178
  if (isOpen) {
179
+ seededTitle.current = defaultTitle;
109
180
  setWizardStep({ step: 'gather' });
181
+ setSearchConfig({ limit: 10, useSemanticScoring: true });
182
+ setGenerationDraft({
183
+ title: defaultTitle, storagePath: '', prompt: '', language: locale,
184
+ temperature: 0.7, maxTokensText: '500',
185
+ });
110
186
  setIsSearching(false);
187
+ setSearchError(null);
111
188
  setUserHint('');
189
+ setComposeDraft(freshComposeDraft());
190
+ setShowDiscardPrompt(false);
112
191
  }
113
192
  }, [isOpen]);
114
193
 
@@ -121,29 +200,98 @@ export function ReferenceWizardModal({
121
200
  }
122
201
  });
123
202
 
203
+ // …and to failures, same scoping. A refused emit (/bus/emit 4xx), a matcher
204
+ // error, and the match unit's timeout all land here — without this
205
+ // subscription every one of them left the button on "Searching…" forever.
206
+ useEventSubscription('match:search-failed', (event) => {
207
+ if (!isOpen) return;
208
+ if (annotationId && event.referenceId === annotationId) {
209
+ setIsSearching(false);
210
+ setSearchError(event.error);
211
+ }
212
+ });
213
+
124
214
  const handleBind = useCallback(() => {
125
215
  setWizardStep({ step: 'configure-search' });
126
216
  }, []);
127
217
 
218
+ /**
219
+ * The gather step's Hint, placed where the schema says it lives.
220
+ *
221
+ * Two defects, one line. (1) Search and compose each built this inline and
222
+ * generation did not, so picking the AI path silently discarded what the user had
223
+ * typed — and then showed them an empty "Additional Instructions" box.
224
+ * (2) Both existing sites wrote `{ ...context, userHint }`, a TOP-LEVEL key
225
+ * `GatheredContext` does not define; `GatheredContext.json` puts it at
226
+ * `focus.userHint` ("hint to supplement or replace the selected text for search and
227
+ * generation"). So the hint was misplaced on every path, not just missing on one.
228
+ *
229
+ * Both consumers read it: the matcher folds it into its search term and its
230
+ * LLM-scoring passage (matcher.ts, since #911), and generation's prompt builder
231
+ * renders it in the annotation section (resource-generation.ts).
232
+ */
233
+ const contextWithHint = !context
234
+ ? null
235
+ : userHint
236
+ ? { ...context, focus: { ...context.focus, userHint } }
237
+ : context;
238
+
128
239
  const handleGenerate = useCallback(() => {
129
240
  setWizardStep({ step: 'configure-generation' });
130
241
  }, []);
131
242
 
132
243
  const handleCompose = useCallback(() => {
133
244
  if (!context || !annotationId || !resourceId) return;
134
- const contextWithHint = userHint ? { ...context, userHint } : context;
135
- onComposeNavigate(contextWithHint, annotationId, resourceId, defaultTitle, entityTypes);
245
+ setWizardStep({ step: 'compose' });
246
+ }, [context, annotationId, resourceId]);
247
+
248
+ const handleComposeSubmit = useCallback(async (params: ComposeParams) => {
249
+ if (!annotationId) return;
250
+ // Rejection propagates to ComposeStep (footer re-enables); the host
251
+ // surfaces the error. Success closes UNCONDITIONALLY — the dirty guard
252
+ // protects dismissal, not completion.
253
+ await onComposeSubmit(annotationId, params);
254
+ onClose();
255
+ }, [annotationId, onComposeSubmit, onClose]);
256
+
257
+ // D4 (widened by GATHER-AT-THE-TOP): a modal dies on ✕/Escape/backdrop, but
258
+ // typed work must not die with it — WHEREVER the user currently is (drafts
259
+ // survive stepping away, so the guard must too). Typed text only (D5):
260
+ // seeded titles, toggles, and sliders never nag.
261
+ const draftDirty =
262
+ composeDraft.content.trim() !== '' ||
263
+ composeDraft.storagePath.trim() !== '' ||
264
+ composeDraft.name !== seededTitle.current ||
265
+ generationDraft.title !== seededTitle.current ||
266
+ generationDraft.storagePath.trim() !== '' ||
267
+ generationDraft.prompt.trim() !== '';
268
+ const handleDismiss = useCallback(() => {
269
+ if (draftDirty) {
270
+ setShowDiscardPrompt(true);
271
+ return;
272
+ }
136
273
  onClose();
137
- }, [context, annotationId, resourceId, defaultTitle, entityTypes, onComposeNavigate, onClose, userHint]);
274
+ }, [draftDirty, onClose]);
138
275
 
139
276
  const handleBackToGather = useCallback(() => {
140
277
  setWizardStep({ step: 'gather' });
141
278
  }, []);
142
279
 
280
+ // The strategy steps stack evidence + step content in ONE scroll pane. On
281
+ // entry, start at the BOTTOM: the step's own content (form, results) shows
282
+ // in full, the evidence tucks up under the modal top, reachable by
283
+ // scrolling up. jsdom has no layout (scrollHeight 0), so this is a no-op
284
+ // in tests — the same posture as the viewer's guarded scrolls.
285
+ const stepScrollRef = useRef<HTMLDivElement>(null);
286
+ useEffect(() => {
287
+ const el = stepScrollRef.current;
288
+ if (el) el.scrollTop = el.scrollHeight;
289
+ }, [wizardStep.step]);
290
+
143
291
  const handleSearchSubmit = useCallback((config: SearchConfig) => {
144
- if (!annotationId || !context || !resourceId) return;
292
+ if (!annotationId || !contextWithHint || !resourceId) return;
145
293
  setIsSearching(true);
146
- const contextWithHint = userHint ? { ...context, userHint } : context;
294
+ setSearchError(null);
147
295
  session?.client.match.requestSearch({
148
296
  correlationId: uuidV4(),
149
297
  resourceId,
@@ -153,7 +301,7 @@ export function ReferenceWizardModal({
153
301
  useSemanticScoring: config.useSemanticScoring,
154
302
  });
155
303
  // Stay on configure-search until results arrive (subscription above handles transition)
156
- }, [annotationId, resourceId, context, session, userHint]);
304
+ }, [annotationId, resourceId, contextWithHint, session]);
157
305
 
158
306
  const handleGenerateSubmit = useCallback((config: GenerationConfig) => {
159
307
  if (!annotationId) return;
@@ -167,18 +315,29 @@ export function ReferenceWizardModal({
167
315
  onClose();
168
316
  }, [annotationId, onLinkResource, onClose]);
169
317
 
170
- // Determine title based on step
171
- const stepTitle = wizardStep.step === 'gather'
172
- ? t.gatherTitle
173
- : wizardStep.step === 'configure-generation'
174
- ? t.configureGenerationTitle
175
- : wizardStep.step === 'configure-search'
176
- ? t.configureSearchTitle
177
- : t.searchResultsTitle;
318
+ // The evidence display's translations — shared by the gather step and the
319
+ // strategy steps, which keep the context in view (display-only, GFR A2)
320
+ // above their forms rather than navigating away from it.
321
+ const displayTranslations = {
322
+ loadingContext: t.loadingContext,
323
+ failedContext: t.failedContext,
324
+ sourceContextLabel: t.sourceContextLabel,
325
+ connectionsLabel: t.connectionsLabel,
326
+ citedByLabel: t.citedByLabel,
327
+ graphPaneTitle: t.graphPaneTitle,
328
+ graphEmpty: t.graphEmpty,
329
+ resourceLinkLabel: t.resourceLinkLabel,
330
+ corpusPaneTitle: t.corpusPaneTitle,
331
+ corpusEmpty: t.corpusEmpty,
332
+ excludedReceipt: t.excludedReceipt,
333
+ machineRead: t.machineRead,
334
+ score: t.score,
335
+ };
336
+
178
337
 
179
338
  return (
180
339
  <Transition appear show={isOpen}>
181
- <Dialog as="div" className="semiont-search-modal" onClose={onClose}>
340
+ <Dialog as="div" className="semiont-search-modal" onClose={handleDismiss}>
182
341
  {/* Backdrop */}
183
342
  <TransitionChild
184
343
  enter="ease-out duration-200"
@@ -202,13 +361,15 @@ export function ReferenceWizardModal({
202
361
  leaveFrom="opacity-100 scale-100"
203
362
  leaveTo="opacity-0 scale-95"
204
363
  >
205
- <DialogPanel className={`semiont-search-modal__panel semiont-search-modal__panel--with-border${wizardStep.step === 'search-results' ? ' semiont-search-modal__panel--wide' : ''}${wizardStep.step === 'gather' ? ' semiont-search-modal__panel--gather' : ''}`}>
364
+ {/* Every step shows the evidence display now, so every step is
365
+ wide and gets the gather layout (flex column, capped height). */}
366
+ <DialogPanel className="semiont-search-modal__panel semiont-search-modal__panel--with-border semiont-search-modal__panel--gather semiont-search-modal__panel--wide">
206
367
  <div className="semiont-search-modal__header">
207
368
  <DialogTitle className="semiont-search-modal__title">
208
- {stepTitle}
369
+ {t.resolveTitle}
209
370
  </DialogTitle>
210
371
  <button
211
- onClick={onClose}
372
+ onClick={handleDismiss}
212
373
  className="semiont-search-modal__close-button"
213
374
  aria-label="Close"
214
375
  >
@@ -216,44 +377,96 @@ export function ReferenceWizardModal({
216
377
  </button>
217
378
  </div>
218
379
 
380
+ {showDiscardPrompt && (
381
+ <DiscardPrompt
382
+ prompt={t.discardDraftPrompt}
383
+ discardLabel={t.discardDraft}
384
+ keepLabel={t.keepEditing}
385
+ onDiscard={() => { setShowDiscardPrompt(false); onClose(); }}
386
+ onKeepEditing={() => setShowDiscardPrompt(false)}
387
+ />
388
+ )}
389
+
219
390
  {wizardStep.step === 'gather' && (
220
391
  <GatherContextStep
221
392
  context={context}
222
393
  contextLoading={contextLoading}
223
394
  contextError={contextError}
224
- userHint={userHint}
225
- onUserHintChange={setUserHint}
226
- onBind={handleBind}
227
- onGenerate={handleGenerate}
228
- onCompose={handleCompose}
395
+ pending={{ exact: defaultTitle, entityTypes, ...(resourceName ? { resourceName } : {}) }}
396
+ annotate={{
397
+ userHint,
398
+ onUserHintChange: setUserHint,
399
+ onBind: handleBind,
400
+ onGenerate: handleGenerate,
401
+ onCompose: handleCompose,
402
+ translations: {
403
+ search: t.search,
404
+ generate: t.generate,
405
+ compose: t.compose,
406
+ resolutionStrategyLabel: t.resolutionStrategyLabel,
407
+ userHintLabel: t.userHintLabel,
408
+ userHintEffect: t.userHintEffect,
409
+ userHintPlaceholder: t.userHintPlaceholder,
410
+ },
411
+ }}
412
+ translations={displayTranslations}
413
+ />
414
+ )}
415
+
416
+ {wizardStep.step === 'compose' && context && (
417
+ <div className="semiont-wizard__step-scroll" ref={stepScrollRef}>
418
+ <GatherContextStep
419
+ context={context}
420
+ contextLoading={contextLoading}
421
+ contextError={contextError}
422
+ chosenStrategy={{ label: t.resolutionStrategyLabel, value: `✍️ ${t.compose}` }}
423
+ translations={displayTranslations}
424
+ />
425
+ <ComposeStep
426
+ draft={composeDraft}
427
+ onDraftChange={(patch) => setComposeDraft((d) => ({ ...d, ...patch }))}
428
+ referenceEntityTypes={entityTypes}
429
+ entityTypeOptions={entityTypeOptions}
430
+ showLineNumbers={showLineNumbers}
431
+ hoverDelayMs={hoverDelayMs}
432
+ onBack={handleBackToGather}
433
+ onCompose={handleComposeSubmit}
229
434
  translations={{
230
- title: t.gatherTitle,
231
- sourceContextLabel: t.sourceContextLabel,
232
- connectionsLabel: t.connectionsLabel,
233
- citedByLabel: t.citedByLabel,
234
- userHintLabel: t.userHintLabel,
235
- userHintPlaceholder: t.userHintPlaceholder,
236
- loadingContext: t.loadingContext,
237
- failedContext: t.failedContext,
238
- search: t.search,
239
- generate: t.generate,
240
- compose: t.compose,
241
- resolutionStrategyLabel: t.resolutionStrategyLabel,
435
+ resourceTitle: t.resourceTitle,
436
+ resourceTitlePlaceholder: t.resourceTitlePlaceholder,
437
+ saveLocation: t.saveLocation,
438
+ entityTypes: t.entityTypes,
439
+ language: t.language,
440
+ contentLabel: t.contentLabel,
441
+ back: t.back,
442
+ createAndLink: t.createAndLink,
443
+ creatingAndLinking: t.creatingAndLinking,
242
444
  }}
243
445
  />
446
+ </div>
244
447
  )}
245
448
 
246
449
  {wizardStep.step === 'configure-generation' && context && (
247
- <ConfigureGenerationStep
248
- defaultTitle={defaultTitle}
249
- locale={locale}
450
+ <div className="semiont-wizard__step-scroll" ref={stepScrollRef}>
451
+ <GatherContextStep
250
452
  context={context}
453
+ contextLoading={contextLoading}
454
+ contextError={contextError}
455
+ chosenStrategy={{ label: t.resolutionStrategyLabel, value: `✨ ${t.generate}` }}
456
+ translations={displayTranslations}
457
+ />
458
+ <ConfigureGenerationStep
459
+ {...(generationAgent ? { generationAgent } : {})}
460
+ {...(userHint ? { hintEcho: { label: t.userHintLabel, value: userHint } } : {})}
461
+ context={contextWithHint ?? context}
462
+ config={generationDraft}
463
+ onConfigChange={setGenerationDraft}
251
464
  onBack={handleBackToGather}
252
- onCancel={onClose}
253
465
  onGenerate={handleGenerateSubmit}
254
466
  translations={{
255
467
  resourceTitle: t.resourceTitle,
256
468
  resourceTitlePlaceholder: t.resourceTitlePlaceholder,
469
+ saveLocation: t.saveLocation,
257
470
  additionalInstructions: t.additionalInstructions,
258
471
  additionalInstructionsPlaceholder: t.additionalInstructionsPlaceholder,
259
472
  language: t.language,
@@ -263,51 +476,65 @@ export function ReferenceWizardModal({
263
476
  creativityCreative: t.creativityCreative,
264
477
  maxLength: t.maxLength,
265
478
  maxLengthHelp: t.maxLengthHelp,
266
- cancel: t.cancel,
479
+ maxLengthCeiling: t.maxLengthCeiling,
267
480
  back: t.back,
268
481
  generate: t.generate,
269
482
  }}
270
483
  />
484
+ </div>
271
485
  )}
272
486
 
273
487
  {wizardStep.step === 'configure-search' && (
488
+ <div className="semiont-wizard__step-scroll" ref={stepScrollRef}>
489
+ <GatherContextStep
490
+ context={context}
491
+ contextLoading={contextLoading}
492
+ contextError={contextError}
493
+ chosenStrategy={{ label: t.resolutionStrategyLabel, value: `🔍 ${t.search}` }}
494
+ translations={displayTranslations}
495
+ />
274
496
  <ConfigureSearchStep
497
+ config={searchConfig}
498
+ {...(userHint ? { hintEcho: { label: t.userHintLabel, value: userHint } } : {})}
499
+ onConfigChange={setSearchConfig}
275
500
  isSearching={isSearching}
501
+ searchError={searchError}
276
502
  onBack={handleBackToGather}
277
- onCancel={onClose}
278
503
  onSearch={handleSearchSubmit}
279
504
  translations={{
280
505
  maxResults: t.maxResults,
281
506
  semanticScoring: t.semanticScoring,
282
507
  semanticScoringHelp: t.semanticScoringHelp,
283
- cancel: t.cancel,
284
508
  back: t.back,
285
509
  search: t.search,
286
510
  searching: t.searching,
511
+ searchFailed: t.searchFailed,
287
512
  }}
288
513
  />
514
+ </div>
289
515
  )}
290
516
 
291
517
  {wizardStep.step === 'search-results' && context && (
518
+ <div className="semiont-wizard__step-scroll" ref={stepScrollRef}>
519
+ <GatherContextStep
520
+ context={context}
521
+ contextLoading={contextLoading}
522
+ contextError={contextError}
523
+ chosenStrategy={{ label: t.resolutionStrategyLabel, value: `🔍 ${t.search}` }}
524
+ translations={displayTranslations}
525
+ />
292
526
  <SearchResultsStep
293
527
  results={wizardStep.results}
294
- context={context}
295
528
  onLink={handleLink}
296
529
  onBack={handleBackToGather}
297
- onCancel={onClose}
298
530
  translations={{
299
531
  noResults: t.noResults,
300
532
  link: t.link,
301
533
  back: t.back,
302
- cancel: t.cancel,
303
534
  score: t.score,
304
- sourceContextLabel: t.sourceContextLabel,
305
- connectionsLabel: t.connectionsLabel,
306
- citedByLabel: t.citedByLabel,
307
- userHintLabel: t.userHintLabel,
308
- userHintPlaceholder: t.userHintPlaceholder,
309
535
  }}
310
536
  />
537
+ </div>
311
538
  )}
312
539
  </DialogPanel>
313
540
  </TransitionChild>