@semiont/react-ui 0.5.27 → 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 (162) hide show
  1. package/dist/{PdfAnnotationCanvas.client-EVUPTUWZ.js → PdfAnnotationCanvas.client-C2IGG4SW.js} +31 -4
  2. package/dist/{ar-OU3DQGC6.js → ar-FABPTJVB.js} +39 -23
  3. package/dist/{bn-PHWCR6AZ.js → bn-LKP6BDMH.js} +39 -23
  4. package/dist/{chunk-PCRRBU7H.js → chunk-2GOHTTHW.js} +39 -23
  5. package/dist/{chunk-5VDTWRXJ.js → chunk-2YHEE63S.js} +31 -31
  6. package/dist/{cs-NEXYZ62I.js → cs-YQYNWHRG.js} +39 -23
  7. package/dist/{da-FAT43YCU.js → da-QHZOYKRY.js} +39 -23
  8. package/dist/{de-KRQ3WBCU.js → de-GKA7RD7J.js} +39 -23
  9. package/dist/{el-Q4IU3EKC.js → el-45T257AL.js} +39 -23
  10. package/dist/{en-2KADE7N4.js → en-RVW5EQ66.js} +2 -2
  11. package/dist/{es-2IO3R2MK.js → es-AVER7RU4.js} +39 -23
  12. package/dist/{fa-Q5GOQFXX.js → fa-K3VPK3O2.js} +39 -23
  13. package/dist/{fi-R6NMRWBE.js → fi-5NEKF7EB.js} +39 -23
  14. package/dist/{fr-QY4D5E6J.js → fr-FUMNDLBZ.js} +39 -23
  15. package/dist/{he-X6Y77XHY.js → he-3JGNA3PR.js} +39 -23
  16. package/dist/{hi-XL7IOEQM.js → hi-IJY375DF.js} +39 -23
  17. package/dist/{id-QZ4ZZS67.js → id-LWHKJ7ZD.js} +39 -23
  18. package/dist/index.css +279 -74
  19. package/dist/index.d.ts +170 -145
  20. package/dist/index.js +2743 -2365
  21. package/dist/{it-ME2Q7OB6.js → it-EBVOV4RW.js} +39 -23
  22. package/dist/{ja-HX7QASRI.js → ja-KWRIX2NF.js} +39 -23
  23. package/dist/{ko-EQO5AHAV.js → ko-L5CJMFDS.js} +39 -23
  24. package/dist/{ms-UILFSF3I.js → ms-LSNMJDR2.js} +39 -23
  25. package/dist/{nl-NNI45PMK.js → nl-ZB32MB7U.js} +39 -23
  26. package/dist/{no-MAL5F6P6.js → no-4GTNY7M5.js} +39 -23
  27. package/dist/{pl-4RMY6JAJ.js → pl-LUHBLTHB.js} +39 -23
  28. package/dist/{pt-VQH2X3QQ.js → pt-C4H3UNQ6.js} +39 -23
  29. package/dist/{ro-XXCQO2OG.js → ro-GGITS5V4.js} +39 -23
  30. package/dist/{sv-7Z6EJ6U4.js → sv-T56RTLBP.js} +39 -23
  31. package/dist/test-utils.js +2 -2
  32. package/dist/{th-L3DXXYVQ.js → th-22X2IWCB.js} +39 -23
  33. package/dist/{tr-P3DFVLFJ.js → tr-77TFG62H.js} +39 -23
  34. package/dist/{uk-7C5D3PXS.js → uk-MIIL6NXT.js} +39 -23
  35. package/dist/{vi-ZLTLEO7P.js → vi-7JYOIGNV.js} +39 -23
  36. package/dist/{zh-5FHVWDIN.js → zh-ID6DRYU4.js} +39 -23
  37. package/package.json +9 -8
  38. package/src/components/AssistProgress.tsx +36 -2
  39. package/src/components/CodeMirrorRenderer.tsx +8 -8
  40. package/src/components/__tests__/AssistProgress.test.tsx +59 -0
  41. package/src/components/modals/ComposeStep.tsx +218 -0
  42. package/src/components/modals/ConfigureGatherStep.tsx +10 -18
  43. package/src/components/modals/ConfigureGenerationStep.tsx +35 -19
  44. package/src/components/modals/ConfigureSearchStep.tsx +19 -0
  45. package/src/components/modals/ContextSummary.tsx +185 -72
  46. package/src/components/modals/CorpusPane.tsx +75 -0
  47. package/src/components/modals/DiscardPrompt.tsx +38 -0
  48. package/src/components/modals/GatherContextStep.tsx +237 -107
  49. package/src/components/modals/ReferenceWizardModal.tsx +238 -50
  50. package/src/components/modals/ResourceGenerateModal.tsx +216 -108
  51. package/src/components/modals/SearchModal.css +337 -101
  52. package/src/components/modals/SearchResultsStep.tsx +61 -68
  53. package/src/components/modals/WizardFooter.tsx +7 -1
  54. package/src/components/modals/__tests__/ComposeStep.test.tsx +152 -0
  55. package/src/components/modals/__tests__/ConfigureGatherStep.test.tsx +14 -26
  56. package/src/components/modals/__tests__/ConfigureGenerationStep.test.tsx +35 -0
  57. package/src/components/modals/__tests__/ConfigureSearchStep.test.tsx +24 -1
  58. package/src/components/modals/__tests__/GatherContextStep.resource.test.tsx +463 -25
  59. package/src/components/modals/__tests__/ReferenceWizardModal.test.tsx +417 -38
  60. package/src/components/modals/__tests__/ResourceGenerateModal.test.tsx +276 -85
  61. package/src/components/modals/__tests__/SearchResultsStep.test.tsx +61 -0
  62. package/src/components/modals/__tests__/WizardFooter.test.tsx +113 -4
  63. package/src/components/pdf-annotation/PdfAnnotationCanvas.tsx +34 -1
  64. package/src/components/pdf-annotation/__tests__/PdfAnnotationCanvas.test.tsx +38 -1
  65. package/src/components/resource/AnnotateView.tsx +3 -3
  66. package/src/components/resource/BrowseView.tsx +5 -5
  67. package/src/components/resource/ResourceViewer.tsx +4 -4
  68. package/src/components/resource/__tests__/AnnotateView.embeddable.test.tsx +1 -1
  69. package/src/components/resource/__tests__/BrowseView.embeddable.test.tsx +1 -1
  70. package/src/components/resource/__tests__/BrowseView.test.tsx +9 -9
  71. package/src/components/resource/annotate-renderers.tsx +3 -3
  72. package/src/components/resource/panels/AssistSection.css +35 -0
  73. package/src/components/resource/panels/ReferenceEntry.tsx +9 -2
  74. package/src/components/resource/panels/ReferencesPanel.tsx +4 -0
  75. package/src/components/resource/panels/ResourceInfoPanel.tsx +89 -20
  76. package/src/components/resource/panels/UnifiedAnnotationsPanel.tsx +7 -2
  77. package/src/components/resource/panels/__tests__/ReferenceEntry.test.tsx +41 -0
  78. package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +33 -1
  79. package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +148 -6
  80. package/src/features/resource-compose/__tests__/ResourceComposePage.test.tsx +0 -65
  81. package/src/features/resource-compose/components/ResourceComposePage.tsx +17 -130
  82. package/src/features/resource-compose/state/__tests__/compose-page-state-unit.test.ts +1 -100
  83. package/src/features/resource-compose/state/compose-page-state-unit.ts +5 -57
  84. package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +71 -15
  85. package/src/features/resource-viewer/components/ResourceViewerPage.tsx +133 -59
  86. package/src/styles/core/forms.css +35 -8
  87. package/src/styles/features/resource-viewer.css +0 -22
  88. package/src/styles/features/resource.css +0 -4
  89. package/translations/ar.json +38 -22
  90. package/translations/bn.json +38 -22
  91. package/translations/cs.json +38 -22
  92. package/translations/da.json +38 -22
  93. package/translations/de.json +38 -22
  94. package/translations/el.json +38 -22
  95. package/translations/en.json +38 -22
  96. package/translations/es.json +38 -22
  97. package/translations/fa.json +38 -22
  98. package/translations/fi.json +38 -22
  99. package/translations/fr.json +38 -22
  100. package/translations/he.json +38 -22
  101. package/translations/hi.json +38 -22
  102. package/translations/id.json +38 -22
  103. package/translations/it.json +38 -22
  104. package/translations/ja.json +38 -22
  105. package/translations/ko.json +38 -22
  106. package/translations/ms.json +38 -22
  107. package/translations/nl.json +38 -22
  108. package/translations/no.json +38 -22
  109. package/translations/pl.json +38 -22
  110. package/translations/pt.json +38 -22
  111. package/translations/ro.json +38 -22
  112. package/translations/sv.json +38 -22
  113. package/translations/th.json +38 -22
  114. package/translations/tr.json +38 -22
  115. package/translations/uk.json +38 -22
  116. package/translations/vi.json +38 -22
  117. package/translations/zh.json +38 -22
  118. package/dist/PdfAnnotationCanvas.client-EVUPTUWZ.js.map +0 -1
  119. package/dist/PdfAnnotationCanvas.client-TGDPPGXJ.css.map +0 -1
  120. package/dist/ar-OU3DQGC6.js.map +0 -1
  121. package/dist/bn-PHWCR6AZ.js.map +0 -1
  122. package/dist/chunk-5VDTWRXJ.js.map +0 -1
  123. package/dist/chunk-BTUG3VJJ.js.map +0 -1
  124. package/dist/chunk-O23TLLXW.js.map +0 -1
  125. package/dist/chunk-PCRRBU7H.js.map +0 -1
  126. package/dist/chunk-QT7LYM72.js.map +0 -1
  127. package/dist/chunk-VXASXU4K.js.map +0 -1
  128. package/dist/cs-NEXYZ62I.js.map +0 -1
  129. package/dist/da-FAT43YCU.js.map +0 -1
  130. package/dist/de-KRQ3WBCU.js.map +0 -1
  131. package/dist/el-Q4IU3EKC.js.map +0 -1
  132. package/dist/en-2KADE7N4.js.map +0 -1
  133. package/dist/es-2IO3R2MK.js.map +0 -1
  134. package/dist/fa-Q5GOQFXX.js.map +0 -1
  135. package/dist/fi-R6NMRWBE.js.map +0 -1
  136. package/dist/fr-QY4D5E6J.js.map +0 -1
  137. package/dist/he-X6Y77XHY.js.map +0 -1
  138. package/dist/hi-XL7IOEQM.js.map +0 -1
  139. package/dist/id-QZ4ZZS67.js.map +0 -1
  140. package/dist/index.css.map +0 -1
  141. package/dist/index.js.map +0 -1
  142. package/dist/integrations/css-modules-helper.js.map +0 -1
  143. package/dist/integrations/styled-components-theme.js.map +0 -1
  144. package/dist/it-ME2Q7OB6.js.map +0 -1
  145. package/dist/ja-HX7QASRI.js.map +0 -1
  146. package/dist/ko-EQO5AHAV.js.map +0 -1
  147. package/dist/ms-UILFSF3I.js.map +0 -1
  148. package/dist/nl-NNI45PMK.js.map +0 -1
  149. package/dist/no-MAL5F6P6.js.map +0 -1
  150. package/dist/pl-4RMY6JAJ.js.map +0 -1
  151. package/dist/pt-VQH2X3QQ.js.map +0 -1
  152. package/dist/ro-XXCQO2OG.js.map +0 -1
  153. package/dist/sv-7Z6EJ6U4.js.map +0 -1
  154. package/dist/test-utils.css.map +0 -1
  155. package/dist/test-utils.js.map +0 -1
  156. package/dist/th-L3DXXYVQ.js.map +0 -1
  157. package/dist/tr-P3DFVLFJ.js.map +0 -1
  158. package/dist/uk-7C5D3PXS.js.map +0 -1
  159. package/dist/vi-ZLTLEO7P.js.map +0 -1
  160. package/dist/zh-5FHVWDIN.js.map +0 -1
  161. package/src/components/modals/ResourceSearchModal.tsx +0 -224
  162. package/src/components/modals/__tests__/ResourceSearchModal.test.tsx +0 -163
@@ -1,6 +1,6 @@
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
5
  import type { GatheredContext, CollaboratorEntry } from '@semiont/core';
6
6
  import { uuidV4 } from '@semiont/core';
@@ -14,13 +14,18 @@ import { ConfigureSearchStep } from './ConfigureSearchStep';
14
14
  import type { SearchConfig } from './ConfigureSearchStep';
15
15
  import type { GenerationDraft } from './ConfigureGenerationStep';
16
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';
17
21
  import type { ScoredResult } from './SearchResultsStep';
18
22
 
19
23
  type WizardStep =
20
24
  | { step: 'gather' }
21
25
  | { step: 'configure-search' }
22
26
  | { step: 'search-results'; results: ScoredResult[] }
23
- | { step: 'configure-generation' };
27
+ | { step: 'configure-generation' }
28
+ | { step: 'compose' };
24
29
 
25
30
  export interface ReferenceWizardModalProps {
26
31
  /**
@@ -39,6 +44,12 @@ export interface ReferenceWizardModalProps {
39
44
  defaultTitle: string;
40
45
  /** Entity types from the annotation */
41
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;
42
53
  /** Current locale for generation defaults */
43
54
  locale: string;
44
55
  /** Gathered context state */
@@ -48,21 +59,36 @@ export interface ReferenceWizardModalProps {
48
59
  /** Callbacks */
49
60
  onGenerateSubmit: (referenceId: string, config: GenerationConfig) => void;
50
61
  onLinkResource: (referenceId: string, targetResourceId: string) => void;
51
- 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;
52
72
  /** Translation strings */
53
73
  translations: {
54
- gatherTitle: string;
55
- configureGenerationTitle: string;
56
- configureSearchTitle: string;
57
- 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;
58
77
  sourceContextLabel: string;
59
78
  connectionsLabel: string;
60
79
  citedByLabel: string;
80
+ graphPaneTitle: string;
81
+ graphEmpty: string;
82
+ resourceLinkLabel: string;
83
+ corpusPaneTitle: string;
84
+ corpusEmpty: string;
85
+ excludedReceipt: string;
86
+ machineRead: string;
61
87
  userHintLabel: string;
88
+ userHintEffect: string;
62
89
  userHintPlaceholder: string;
63
90
  loadingContext: string;
64
91
  failedContext: string;
65
- cancel: string;
66
92
  search: string;
67
93
  searching: string;
68
94
  generate: string;
@@ -74,6 +100,7 @@ export interface ReferenceWizardModalProps {
74
100
  noResults: string;
75
101
  resourceTitle: string;
76
102
  resourceTitlePlaceholder: string;
103
+ saveLocation: string;
77
104
  additionalInstructions: string;
78
105
  additionalInstructionsPlaceholder: string;
79
106
  language: string;
@@ -87,6 +114,14 @@ export interface ReferenceWizardModalProps {
87
114
  maxResults: string;
88
115
  semanticScoring: string;
89
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;
90
125
  };
91
126
  }
92
127
 
@@ -97,13 +132,16 @@ export function ReferenceWizardModal({
97
132
  resourceId,
98
133
  defaultTitle,
99
134
  entityTypes,
135
+ resourceName,
100
136
  locale,
101
137
  context,
102
138
  contextLoading,
103
139
  contextError,
104
140
  onGenerateSubmit,
105
141
  onLinkResource,
106
- onComposeNavigate,
142
+ onComposeSubmit,
143
+ entityTypeOptions = [],
144
+ hoverDelayMs = 300,
107
145
  translations: t,
108
146
  generationAgent,
109
147
  }: ReferenceWizardModalProps) {
@@ -118,11 +156,27 @@ export function ReferenceWizardModal({
118
156
  temperature: 0.7, maxTokensText: '500',
119
157
  });
120
158
  const [isSearching, setIsSearching] = useState(false);
159
+ const [searchError, setSearchError] = useState<string | null>(null);
121
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);
122
175
 
123
176
  // Reset to gather step when modal opens
124
177
  useEffect(() => {
125
178
  if (isOpen) {
179
+ seededTitle.current = defaultTitle;
126
180
  setWizardStep({ step: 'gather' });
127
181
  setSearchConfig({ limit: 10, useSemanticScoring: true });
128
182
  setGenerationDraft({
@@ -130,7 +184,10 @@ export function ReferenceWizardModal({
130
184
  temperature: 0.7, maxTokensText: '500',
131
185
  });
132
186
  setIsSearching(false);
187
+ setSearchError(null);
133
188
  setUserHint('');
189
+ setComposeDraft(freshComposeDraft());
190
+ setShowDiscardPrompt(false);
134
191
  }
135
192
  }, [isOpen]);
136
193
 
@@ -143,6 +200,17 @@ export function ReferenceWizardModal({
143
200
  }
144
201
  });
145
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
+
146
214
  const handleBind = useCallback(() => {
147
215
  setWizardStep({ step: 'configure-search' });
148
216
  }, []);
@@ -158,9 +226,9 @@ export function ReferenceWizardModal({
158
226
  * `focus.userHint` ("hint to supplement or replace the selected text for search and
159
227
  * generation"). So the hint was misplaced on every path, not just missing on one.
160
228
  *
161
- * NOTE: no production code reads `focus.userHint` yet this is correct plumbing
162
- * for a field nothing consumes. Making the hint actually steer search or generation
163
- * is a separate change (see WIZARD-NAVIGATION's execution log).
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).
164
232
  */
165
233
  const contextWithHint = !context
166
234
  ? null
@@ -173,18 +241,57 @@ export function ReferenceWizardModal({
173
241
  }, []);
174
242
 
175
243
  const handleCompose = useCallback(() => {
176
- if (!contextWithHint || !annotationId || !resourceId) return;
177
- onComposeNavigate(contextWithHint, annotationId, resourceId, defaultTitle, entityTypes);
244
+ if (!context || !annotationId || !resourceId) return;
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);
178
254
  onClose();
179
- }, [contextWithHint, annotationId, resourceId, defaultTitle, entityTypes, onComposeNavigate, 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
+ }
273
+ onClose();
274
+ }, [draftDirty, onClose]);
180
275
 
181
276
  const handleBackToGather = useCallback(() => {
182
277
  setWizardStep({ step: 'gather' });
183
278
  }, []);
184
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
+
185
291
  const handleSearchSubmit = useCallback((config: SearchConfig) => {
186
292
  if (!annotationId || !contextWithHint || !resourceId) return;
187
293
  setIsSearching(true);
294
+ setSearchError(null);
188
295
  session?.client.match.requestSearch({
189
296
  correlationId: uuidV4(),
190
297
  resourceId,
@@ -208,18 +315,29 @@ export function ReferenceWizardModal({
208
315
  onClose();
209
316
  }, [annotationId, onLinkResource, onClose]);
210
317
 
211
- // Determine title based on step
212
- const stepTitle = wizardStep.step === 'gather'
213
- ? t.gatherTitle
214
- : wizardStep.step === 'configure-generation'
215
- ? t.configureGenerationTitle
216
- : wizardStep.step === 'configure-search'
217
- ? t.configureSearchTitle
218
- : 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
+
219
337
 
220
338
  return (
221
339
  <Transition appear show={isOpen}>
222
- <Dialog as="div" className="semiont-search-modal" onClose={onClose}>
340
+ <Dialog as="div" className="semiont-search-modal" onClose={handleDismiss}>
223
341
  {/* Backdrop */}
224
342
  <TransitionChild
225
343
  enter="ease-out duration-200"
@@ -243,13 +361,15 @@ export function ReferenceWizardModal({
243
361
  leaveFrom="opacity-100 scale-100"
244
362
  leaveTo="opacity-0 scale-95"
245
363
  >
246
- <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">
247
367
  <div className="semiont-search-modal__header">
248
368
  <DialogTitle className="semiont-search-modal__title">
249
- {stepTitle}
369
+ {t.resolveTitle}
250
370
  </DialogTitle>
251
371
  <button
252
- onClick={onClose}
372
+ onClick={handleDismiss}
253
373
  className="semiont-search-modal__close-button"
254
374
  aria-label="Close"
255
375
  >
@@ -257,36 +377,87 @@ export function ReferenceWizardModal({
257
377
  </button>
258
378
  </div>
259
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
+
260
390
  {wizardStep.step === 'gather' && (
261
391
  <GatherContextStep
262
392
  context={context}
263
393
  contextLoading={contextLoading}
264
394
  contextError={contextError}
265
- userHint={userHint}
266
- onUserHintChange={setUserHint}
267
- onBind={handleBind}
268
- onGenerate={handleGenerate}
269
- 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}
270
434
  translations={{
271
- title: t.gatherTitle,
272
- sourceContextLabel: t.sourceContextLabel,
273
- connectionsLabel: t.connectionsLabel,
274
- citedByLabel: t.citedByLabel,
275
- userHintLabel: t.userHintLabel,
276
- userHintPlaceholder: t.userHintPlaceholder,
277
- loadingContext: t.loadingContext,
278
- failedContext: t.failedContext,
279
- search: t.search,
280
- generate: t.generate,
281
- compose: t.compose,
282
- 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,
283
444
  }}
284
445
  />
446
+ </div>
285
447
  )}
286
448
 
287
449
  {wizardStep.step === 'configure-generation' && context && (
450
+ <div className="semiont-wizard__step-scroll" ref={stepScrollRef}>
451
+ <GatherContextStep
452
+ context={context}
453
+ contextLoading={contextLoading}
454
+ contextError={contextError}
455
+ chosenStrategy={{ label: t.resolutionStrategyLabel, value: `✨ ${t.generate}` }}
456
+ translations={displayTranslations}
457
+ />
288
458
  <ConfigureGenerationStep
289
459
  {...(generationAgent ? { generationAgent } : {})}
460
+ {...(userHint ? { hintEcho: { label: t.userHintLabel, value: userHint } } : {})}
290
461
  context={contextWithHint ?? context}
291
462
  config={generationDraft}
292
463
  onConfigChange={setGenerationDraft}
@@ -295,6 +466,7 @@ export function ReferenceWizardModal({
295
466
  translations={{
296
467
  resourceTitle: t.resourceTitle,
297
468
  resourceTitlePlaceholder: t.resourceTitlePlaceholder,
469
+ saveLocation: t.saveLocation,
298
470
  additionalInstructions: t.additionalInstructions,
299
471
  additionalInstructionsPlaceholder: t.additionalInstructionsPlaceholder,
300
472
  language: t.language,
@@ -309,13 +481,24 @@ export function ReferenceWizardModal({
309
481
  generate: t.generate,
310
482
  }}
311
483
  />
484
+ </div>
312
485
  )}
313
486
 
314
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
+ />
315
496
  <ConfigureSearchStep
316
497
  config={searchConfig}
498
+ {...(userHint ? { hintEcho: { label: t.userHintLabel, value: userHint } } : {})}
317
499
  onConfigChange={setSearchConfig}
318
500
  isSearching={isSearching}
501
+ searchError={searchError}
319
502
  onBack={handleBackToGather}
320
503
  onSearch={handleSearchSubmit}
321
504
  translations={{
@@ -325,14 +508,23 @@ export function ReferenceWizardModal({
325
508
  back: t.back,
326
509
  search: t.search,
327
510
  searching: t.searching,
511
+ searchFailed: t.searchFailed,
328
512
  }}
329
513
  />
514
+ </div>
330
515
  )}
331
516
 
332
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
+ />
333
526
  <SearchResultsStep
334
527
  results={wizardStep.results}
335
- context={context}
336
528
  onLink={handleLink}
337
529
  onBack={handleBackToGather}
338
530
  translations={{
@@ -340,13 +532,9 @@ export function ReferenceWizardModal({
340
532
  link: t.link,
341
533
  back: t.back,
342
534
  score: t.score,
343
- sourceContextLabel: t.sourceContextLabel,
344
- connectionsLabel: t.connectionsLabel,
345
- citedByLabel: t.citedByLabel,
346
- userHintLabel: t.userHintLabel,
347
- userHintPlaceholder: t.userHintPlaceholder,
348
535
  }}
349
536
  />
537
+ </div>
350
538
  )}
351
539
  </DialogPanel>
352
540
  </TransitionChild>