@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,21 +1,17 @@
1
1
  'use client';
2
2
 
3
- import { useState, useEffect, useCallback } from 'react';
4
- import type { CollaboratorEntry } from '@semiont/core';
3
+ import { useState, useEffect, useCallback, useRef } from 'react';
4
+ import type { CollaboratorEntry, GatheredContext } from '@semiont/core';
5
+ import type { ResourceGatherOptions } from '@semiont/sdk';
5
6
  import { Dialog, DialogPanel, DialogTitle, Transition, TransitionChild } from '@headlessui/react';
6
- import { useResourceGather } from '../../hooks/useResourceGather';
7
7
  import { ConfigureGatherStep, type ResourceGatherConfig } from './ConfigureGatherStep';
8
+ import { DiscardPrompt } from './DiscardPrompt';
8
9
  import { GatherContextStep } from './GatherContextStep';
9
10
  import { ConfigureGenerationStep, type GenerationConfig, type GenerationDraft } from './ConfigureGenerationStep';
10
11
 
11
12
  export interface ResourceGenerateModalTranslations {
12
- // Step titles + nav
13
- gatherTitle: string;
14
- reviewTitle: string;
15
- configureTitle: string;
16
- next: string;
17
- back: string;
18
- cancel: string;
13
+ /** The ONE modal title — names the flow, not a step. */
14
+ title: string;
19
15
  // ConfigureGatherStep
20
16
  gatherIntro: string;
21
17
  includeContent: string;
@@ -23,16 +19,28 @@ export interface ResourceGenerateModalTranslations {
23
19
  gatherDepth: string;
24
20
  gatherMaxResources: string;
25
21
  gatherButton: string;
26
- excludeLabel: string;
22
+ /** Labels the recall chips: every type included until deselected. */
23
+ recallLabel: string;
24
+ /** Affordance on the post-gather receipt row (title/tooltip). */
25
+ editGather: string;
27
26
  // GatherContextStep display
28
27
  loadingContext: string;
29
28
  failedContext: string;
30
29
  sourceContextLabel: string;
31
30
  connectionsLabel: string;
32
31
  citedByLabel: string;
32
+ graphPaneTitle: string;
33
+ graphEmpty: string;
34
+ resourceLinkLabel: string;
35
+ corpusPaneTitle: string;
36
+ corpusEmpty: string;
37
+ excludedReceipt: string;
38
+ machineRead: string;
39
+ score: string;
33
40
  // ConfigureGenerationStep
34
41
  resourceTitle: string;
35
42
  resourceTitlePlaceholder: string;
43
+ saveLocation: string;
36
44
  additionalInstructions: string;
37
45
  additionalInstructionsPlaceholder: string;
38
46
  language: string;
@@ -44,6 +52,10 @@ export interface ResourceGenerateModalTranslations {
44
52
  maxLengthHelp: string;
45
53
  maxLengthCeiling: string;
46
54
  generate: string;
55
+ // DiscardPrompt (GATHER-AT-THE-TOP P1)
56
+ discardDraftPrompt: string;
57
+ discardDraft: string;
58
+ keepEditing: string;
47
59
  }
48
60
 
49
61
  export interface ResourceGenerateModalProps {
@@ -58,7 +70,6 @@ export interface ResourceGenerateModalProps {
58
70
  resourceId: string;
59
71
  defaultTitle: string;
60
72
  locale: string;
61
- gatherDefaults?: Partial<ResourceGatherConfig>;
62
73
  /**
63
74
  * Entity types offered in the exclusion picker. Owner-supplied so the
64
75
  * modal cannot present a failed load as an empty vocabulary.
@@ -70,15 +81,34 @@ export interface ResourceGenerateModalProps {
70
81
  * annotation wizard delegates generation to its parent.
71
82
  */
72
83
  onGenerateSubmit: (resourceId: string, config: GenerationConfig) => void;
84
+ /**
85
+ * Resource-gather state, verbatim from the SDK's gather unit slots
86
+ * (`gather.resourceContext$` / `resourceLoading$` / `resourceError$`) — the
87
+ * page reads the observables and threads them here, the same shape the
88
+ * reference wizard has always had (FLOW-LIFECYCLE-CONVERGENCE D3/A6). The
89
+ * modal renders this state; it never owns it.
90
+ */
91
+ gatherContext: GatheredContext | null;
92
+ gatherLoading: boolean;
93
+ gatherError: Error | null;
94
+ /**
95
+ * Run the gather — the page wires `gather.gatherResource(resourceId, …)`.
96
+ * No resourceId in the payload: the owner already holds it. `gatherResource`
97
+ * clears its slots at start, so no reset threading is needed.
98
+ */
99
+ onGather: (options: ResourceGatherOptions) => void;
73
100
  translations: ResourceGenerateModalTranslations;
74
101
  }
75
102
 
76
- type Step = 'configure-gather' | 'review' | 'configure-generation';
77
-
78
103
  /**
79
- * Resource-generate flow (GENERATE-FROM-BUTTON): configure gather options →
80
- * `gather.resource` review the gathered `GatheredContext` configure
81
- * generation → emit. Reuses the kind-aware `GatherContextStep` for the review.
104
+ * Resource-generate flow (GENERATE-FROM-BUTTON, folded by GATHER-AT-THE-TOP):
105
+ * one composite stack in a single scroll pane — the gather controls at the
106
+ * top, the gathered `GatheredContext` below them once gather fires, and the
107
+ * generation params beneath the evidence once context arrives. Re-gathering
108
+ * is scroll-up → tweak → Gather; `gatherResource` clears its slots at start,
109
+ * so the evidence refreshes in place. [gather zone] → [evidence] → [act
110
+ * zone]: the same grammar the resolve wizard expresses, whose gather zone
111
+ * happens to be automatic and whose act zone has a strategy choice.
82
112
  */
83
113
  export function ResourceGenerateModal({
84
114
  isOpen,
@@ -86,54 +116,128 @@ export function ResourceGenerateModal({
86
116
  resourceId,
87
117
  defaultTitle,
88
118
  locale,
89
- gatherDefaults,
90
119
  entityTypeOptions = [],
91
120
  onGenerateSubmit,
121
+ gatherContext,
122
+ gatherLoading,
123
+ gatherError,
124
+ onGather,
92
125
  translations: t,
93
126
  generationAgent,
94
127
  }: ResourceGenerateModalProps) {
95
128
  // Same draft ownership as the wizard (WIZARD-NAVIGATION D3): the step is
96
129
  // controlled, so stepping back through this modal keeps what was typed.
97
- const [generationDraft, setGenerationDraft] = useState<GenerationDraft>({
130
+ const freshDraft = (): GenerationDraft => ({
98
131
  title: defaultTitle, storagePath: '', prompt: '', language: locale,
99
132
  temperature: 0.7, maxTokensText: '500',
100
133
  });
134
+ const [generationDraft, setGenerationDraft] = useState<GenerationDraft>(freshDraft);
101
135
 
102
- const [step, setStep] = useState<Step>('configure-gather');
103
- const { context, loading, error, gather, reset } = useResourceGather();
136
+ // "Has THIS run gathered yet?" — the page's gather slots survive across
137
+ // opens, so a context prop can be stale at open time. Nothing renders below
138
+ // the controls until the user fires a gather in this run.
139
+ const [gatherFired, setGatherFired] = useState(false);
140
+ // The fired snapshot feeds the receipt: what THIS gather actually asked
141
+ // for, exclusions included — not the form's live state, which the user can
142
+ // edit without re-firing.
143
+ const [lastGather, setLastGather] = useState<(ResourceGatherConfig & { excludeEntityTypes: string[] }) | null>(null);
144
+ const [editingGather, setEditingGather] = useState(false);
145
+ const [showDiscardPrompt, setShowDiscardPrompt] = useState(false);
104
146
  // Supplied by the owner, which already tracks the list with its failure
105
147
  // state. Fetching it here could only model (value | not-yet), so a failed
106
148
  // load would render an empty exclusion picker as though the KB had no
107
149
  // entity types. See .plans/PANEL-FAILURE-STATES.md
108
150
  const [excludeEntityTypes, setExcludeEntityTypes] = useState<string[]>([]);
109
151
 
110
- // Reset to the first step whenever the modal (re)opens.
152
+ // Reset to the first step ON OPENING — and re-seed the draft, because
153
+ // `defaultTitle` is the source resource's name and loads asynchronously: the
154
+ // useState initializer ran at mount, which for the real page was before the
155
+ // name existed (GFR A4). Guarded to the false→true transition so a name
156
+ // arriving mid-flow cannot clobber what the user has typed.
157
+ const wasOpen = useRef(false);
158
+ // The dirty baseline is what was SEEDED, not the live prop: defaultTitle
159
+ // can move while the modal is open (the source resource's name loads
160
+ // asynchronously), and an untouched draft must not read as dirty because
161
+ // the baseline moved under it.
162
+ const seededTitle = useRef(defaultTitle);
111
163
  useEffect(() => {
112
- if (isOpen) {
113
- setStep('configure-gather');
164
+ if (isOpen && !wasOpen.current) {
165
+ setGatherFired(false);
166
+ setLastGather(null);
167
+ setEditingGather(false);
114
168
  setExcludeEntityTypes([]);
115
- reset();
169
+ setGenerationDraft(freshDraft());
170
+ setShowDiscardPrompt(false);
171
+ seededTitle.current = defaultTitle;
116
172
  }
117
- }, [isOpen, reset]);
173
+ wasOpen.current = isOpen;
174
+ // freshDraft reads defaultTitle/locale at call time; the effect keys on the
175
+ // OPENING, not on their drift while open.
176
+ // eslint-disable-next-line react-hooks/exhaustive-deps
177
+ }, [isOpen]);
118
178
 
119
179
  const handleGather = useCallback((config: ResourceGatherConfig) => {
120
- setStep('review');
121
- void gather(resourceId, {
180
+ setGatherFired(true);
181
+ setLastGather({ ...config, excludeEntityTypes });
182
+ setEditingGather(false);
183
+ onGather({
122
184
  ...config,
123
185
  ...(excludeEntityTypes.length ? { excludeEntityTypes } : {}),
124
186
  });
125
- }, [gather, resourceId, excludeEntityTypes]);
187
+ }, [onGather, excludeEntityTypes]);
126
188
 
127
189
  const handleGenerate = useCallback((config: GenerationConfig) => {
128
190
  onGenerateSubmit(resourceId, config);
129
191
  onClose();
130
192
  }, [onGenerateSubmit, resourceId, onClose]);
131
193
 
132
- const stepTitle = step === 'configure-gather' ? t.gatherTitle : step === 'review' ? t.reviewTitle : t.configureTitle;
194
+ // D4/D5 (GATHER-AT-THE-TOP P1): typed text must not die with a dismissed
195
+ // modal. Toggles, depth, and exclusion picks are cheap to redo and never
196
+ // nag. Generate's completion path above bypasses this — the guard protects
197
+ // dismissal, not completion.
198
+ const draftDirty =
199
+ generationDraft.title !== seededTitle.current ||
200
+ generationDraft.storagePath.trim() !== '' ||
201
+ generationDraft.prompt.trim() !== '';
202
+ const handleDismiss = useCallback(() => {
203
+ if (draftDirty) {
204
+ setShowDiscardPrompt(true);
205
+ return;
206
+ }
207
+ onClose();
208
+ }, [draftDirty, onClose]);
209
+
210
+ // The whole stack is ONE scroll pane; when the params zone appears (context
211
+ // arrival — including each re-gather), enter at the BOTTOM so the
212
+ // parameters show in full, the evidence and controls tucked up under the
213
+ // modal top. jsdom has no layout (scrollHeight 0) — no-op in tests.
214
+ const stepScrollRef = useRef<HTMLDivElement>(null);
215
+ useEffect(() => {
216
+ const el = stepScrollRef.current;
217
+ if (el && gatherContext) el.scrollTop = el.scrollHeight;
218
+ }, [gatherContext]);
219
+
220
+ // The evidence zone: what the generation will be grounded in stays in view
221
+ // above the form.
222
+ const displayTranslations = {
223
+ loadingContext: t.loadingContext,
224
+ failedContext: t.failedContext,
225
+ sourceContextLabel: t.sourceContextLabel,
226
+ connectionsLabel: t.connectionsLabel,
227
+ citedByLabel: t.citedByLabel,
228
+ graphPaneTitle: t.graphPaneTitle,
229
+ graphEmpty: t.graphEmpty,
230
+ resourceLinkLabel: t.resourceLinkLabel,
231
+ corpusPaneTitle: t.corpusPaneTitle,
232
+ corpusEmpty: t.corpusEmpty,
233
+ excludedReceipt: t.excludedReceipt,
234
+ machineRead: t.machineRead,
235
+ score: t.score,
236
+ };
133
237
 
134
238
  return (
135
239
  <Transition appear show={isOpen}>
136
- <Dialog as="div" className="semiont-search-modal" onClose={onClose}>
240
+ <Dialog as="div" className="semiont-search-modal" onClose={handleDismiss}>
137
241
  <TransitionChild
138
242
  enter="ease-out duration-200"
139
243
  enterFrom="opacity-0"
@@ -155,21 +259,53 @@ export function ResourceGenerateModal({
155
259
  leaveFrom="opacity-100 scale-100"
156
260
  leaveTo="opacity-0 scale-95"
157
261
  >
158
- <DialogPanel className="semiont-search-modal__panel semiont-search-modal__panel--with-border semiont-search-modal__panel--gather">
262
+ <DialogPanel className="semiont-search-modal__panel semiont-search-modal__panel--with-border semiont-search-modal__panel--gather semiont-search-modal__panel--wide">
159
263
  <div className="semiont-search-modal__header">
160
- <DialogTitle className="semiont-search-modal__title">{stepTitle}</DialogTitle>
161
- <button onClick={onClose} className="semiont-search-modal__close-button" aria-label="Close">
264
+ <DialogTitle className="semiont-search-modal__title">{t.title}</DialogTitle>
265
+ <button onClick={handleDismiss} className="semiont-search-modal__close-button" aria-label="Close">
162
266
 
163
267
  </button>
164
268
  </div>
165
269
 
166
- {step === 'configure-gather' && (
270
+ {showDiscardPrompt && (
271
+ <DiscardPrompt
272
+ prompt={t.discardDraftPrompt}
273
+ discardLabel={t.discardDraft}
274
+ keepLabel={t.keepEditing}
275
+ onDiscard={() => { setShowDiscardPrompt(false); onClose(); }}
276
+ onKeepEditing={() => setShowDiscardPrompt(false)}
277
+ />
278
+ )}
279
+
280
+ <div className="semiont-wizard__step-scroll" ref={stepScrollRef}>
281
+ {/* Once fired, the spent controls fold into a receipt of
282
+ what THIS gather asked for — the evidence leads the
283
+ pane, the parameters stay visible and revisable. The
284
+ form below is HIDDEN, not unmounted, so its values
285
+ survive for the expand path. */}
286
+ {gatherFired && !editingGather && lastGather && (
287
+ <button
288
+ type="button"
289
+ className="semiont-gather-receipt"
290
+ title={t.editGather}
291
+ onClick={() => setEditingGather(true)}
292
+ >
293
+ {[
294
+ `${t.gatherDepth} ${lastGather.depth}`,
295
+ `${t.gatherMaxResources} ${lastGather.maxResources}`,
296
+ ...(lastGather.includeContent ? [t.includeContent] : []),
297
+ ...(lastGather.includeSummary ? [t.includeSummary] : []),
298
+ ...(lastGather.excludeEntityTypes.length
299
+ ? [t.excludedReceipt.replace('{{types}}', lastGather.excludeEntityTypes.join(', '))]
300
+ : []),
301
+ ].join(' · ')}
302
+ <span aria-hidden="true"> ✎</span>
303
+ </button>
304
+ )}
305
+ <div hidden={gatherFired && !editingGather}>
167
306
  <ConfigureGatherStep
168
- defaults={gatherDefaults}
169
307
  onGather={handleGather}
170
- onCancel={onClose}
171
308
  translations={{
172
- cancel: t.cancel,
173
309
  intro: t.gatherIntro,
174
310
  includeContent: t.includeContent,
175
311
  includeSummary: t.includeSummary,
@@ -180,17 +316,21 @@ export function ResourceGenerateModal({
180
316
  >
181
317
  {entityTypeOptions.length > 0 && (
182
318
  <div className="semiont-form__field semiont-form__entity-types">
183
- <label className="semiont-form__label">{t.excludeLabel}</label>
184
- <div className="semiont-form__entity-type-buttons">
319
+ <label className="semiont-form__label">{t.recallLabel}</label>
320
+ {/* Recall chips — the exclude wire, inverted for the
321
+ UI: every type is IN the recall until crossed off,
322
+ so the state is still the exclude list and a chip
323
+ is included when absent from it. */}
324
+ <div className="semiont-form__recall-chips">
185
325
  {entityTypeOptions.map((et) => {
186
- const isSelected = excludeEntityTypes.includes(et);
326
+ const isIncluded = !excludeEntityTypes.includes(et);
187
327
  return (
188
328
  <button
189
329
  key={et}
190
330
  type="button"
191
- className="semiont-form__entity-type-button"
192
- data-selected={isSelected}
193
- aria-pressed={isSelected}
331
+ className="semiont-form__recall-chip"
332
+ data-included={isIncluded}
333
+ aria-pressed={isIncluded}
194
334
  onClick={() => setExcludeEntityTypes(prev => prev.includes(et) ? prev.filter(x => x !== et) : [...prev, et])}
195
335
  >
196
336
  {et}
@@ -201,75 +341,43 @@ export function ResourceGenerateModal({
201
341
  </div>
202
342
  )}
203
343
  </ConfigureGatherStep>
204
- )}
344
+ </div>
205
345
 
206
- {step === 'review' && (
207
- <>
346
+ {gatherFired && (
208
347
  <GatherContextStep
209
- context={context}
210
- contextLoading={loading}
211
- contextError={error}
348
+ context={gatherContext}
349
+ contextLoading={gatherLoading}
350
+ contextError={gatherError}
351
+ translations={displayTranslations}
352
+ />
353
+ )}
354
+
355
+ {gatherFired && gatherContext && (
356
+ <ConfigureGenerationStep
357
+ {...(generationAgent ? { generationAgent } : {})}
358
+ context={gatherContext}
359
+ config={generationDraft}
360
+ onConfigChange={setGenerationDraft}
361
+ onGenerate={handleGenerate}
212
362
  translations={{
213
- title: '',
214
- loadingContext: t.loadingContext,
215
- failedContext: t.failedContext,
216
- search: '',
217
- generate: '',
218
- compose: '',
219
- resolutionStrategyLabel: '',
220
- sourceContextLabel: t.sourceContextLabel,
221
- connectionsLabel: t.connectionsLabel,
222
- citedByLabel: t.citedByLabel,
223
- userHintLabel: '',
224
- userHintPlaceholder: '',
363
+ resourceTitle: t.resourceTitle,
364
+ resourceTitlePlaceholder: t.resourceTitlePlaceholder,
365
+ saveLocation: t.saveLocation,
366
+ additionalInstructions: t.additionalInstructions,
367
+ additionalInstructionsPlaceholder: t.additionalInstructionsPlaceholder,
368
+ language: t.language,
369
+ languageHelp: t.languageHelp,
370
+ creativity: t.creativity,
371
+ creativityFocused: t.creativityFocused,
372
+ creativityCreative: t.creativityCreative,
373
+ maxLength: t.maxLength,
374
+ maxLengthHelp: t.maxLengthHelp,
375
+ maxLengthCeiling: t.maxLengthCeiling,
376
+ generate: t.generate,
225
377
  }}
226
378
  />
227
- <div className="semiont-modal__actions" style={{ paddingTop: '0.5rem' }}>
228
- <button
229
- type="button"
230
- onClick={() => setStep('configure-gather')}
231
- className="semiont-button--secondary semiont-button--flex"
232
- >
233
- ◀ {t.back}
234
- </button>
235
- <button
236
- type="button"
237
- onClick={() => setStep('configure-generation')}
238
- disabled={!context}
239
- className="semiont-button--primary semiont-button--flex"
240
- >
241
- {t.next} ▶
242
- </button>
243
- </div>
244
- </>
245
- )}
246
-
247
- {step === 'configure-generation' && context && (
248
- <ConfigureGenerationStep
249
- {...(generationAgent ? { generationAgent } : {})}
250
- context={context}
251
- config={generationDraft}
252
- onConfigChange={setGenerationDraft}
253
- onBack={() => setStep('review')}
254
- onGenerate={handleGenerate}
255
- translations={{
256
- resourceTitle: t.resourceTitle,
257
- resourceTitlePlaceholder: t.resourceTitlePlaceholder,
258
- additionalInstructions: t.additionalInstructions,
259
- additionalInstructionsPlaceholder: t.additionalInstructionsPlaceholder,
260
- language: t.language,
261
- languageHelp: t.languageHelp,
262
- creativity: t.creativity,
263
- creativityFocused: t.creativityFocused,
264
- creativityCreative: t.creativityCreative,
265
- maxLength: t.maxLength,
266
- maxLengthHelp: t.maxLengthHelp,
267
- maxLengthCeiling: t.maxLengthCeiling,
268
- back: t.back,
269
- generate: t.generate,
270
- }}
271
- />
272
- )}
379
+ )}
380
+ </div>
273
381
  </DialogPanel>
274
382
  </TransitionChild>
275
383
  </div>