@semiont/react-ui 0.5.28 → 0.5.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/{PdfAnnotationCanvas.client-C2IGG4SW.js → PdfAnnotationCanvas.client-LSH3PZPY.js} +4 -4
- package/dist/{ar-FABPTJVB.js → ar-AZBRADEB.js} +5 -1
- package/dist/{bn-LKP6BDMH.js → bn-CCEW6AOS.js} +5 -1
- package/dist/{chunk-VXASXU4K.js → chunk-3ZBKCWUZ.js} +1 -1
- package/dist/{chunk-2YHEE63S.js → chunk-G4VHL4UA.js} +31 -31
- package/dist/{chunk-2GOHTTHW.js → chunk-SDKFZLEX.js} +5 -1
- package/dist/{cs-YQYNWHRG.js → cs-HXPF2CTE.js} +5 -1
- package/dist/{da-QHZOYKRY.js → da-K5NTK7LH.js} +5 -1
- package/dist/{de-GKA7RD7J.js → de-GMRQEDZI.js} +5 -1
- package/dist/{el-45T257AL.js → el-EUB4CI4G.js} +5 -1
- package/dist/{en-RVW5EQ66.js → en-R5XA26NF.js} +2 -2
- package/dist/{es-AVER7RU4.js → es-Z32DAZ6W.js} +5 -1
- package/dist/{fa-K3VPK3O2.js → fa-G7HN6B32.js} +5 -1
- package/dist/{fi-5NEKF7EB.js → fi-3YBLZO7I.js} +5 -1
- package/dist/{fr-FUMNDLBZ.js → fr-VUBH7BBS.js} +5 -1
- package/dist/{he-3JGNA3PR.js → he-WQQEY26B.js} +5 -1
- package/dist/{hi-IJY375DF.js → hi-HJZ6W4JB.js} +5 -1
- package/dist/{id-LWHKJ7ZD.js → id-ZOGZ37MI.js} +5 -1
- package/dist/index.d.ts +157 -257
- package/dist/index.js +763 -1209
- package/dist/{it-EBVOV4RW.js → it-DT6UW66F.js} +5 -1
- package/dist/{ja-KWRIX2NF.js → ja-RUKQCOLO.js} +5 -1
- package/dist/{ko-L5CJMFDS.js → ko-ZH5ADKZM.js} +5 -1
- package/dist/{ms-LSNMJDR2.js → ms-Y2AFPZTU.js} +5 -1
- package/dist/{nl-ZB32MB7U.js → nl-OQEWIXNA.js} +5 -1
- package/dist/{no-4GTNY7M5.js → no-KPAYJZWY.js} +5 -1
- package/dist/{pl-LUHBLTHB.js → pl-WSXK2UUD.js} +5 -1
- package/dist/{pt-C4H3UNQ6.js → pt-NWRJRUCK.js} +5 -1
- package/dist/{ro-GGITS5V4.js → ro-3CGVMOC6.js} +5 -1
- package/dist/{sv-T56RTLBP.js → sv-HGSEBA7S.js} +5 -1
- package/dist/test-utils.js +16 -7
- package/dist/{th-22X2IWCB.js → th-NZ4G352Z.js} +5 -1
- package/dist/{tr-77TFG62H.js → tr-LGGS4DLW.js} +5 -1
- package/dist/{uk-MIIL6NXT.js → uk-AIX536L4.js} +5 -1
- package/dist/{vi-7JYOIGNV.js → vi-V6CVQZLQ.js} +5 -1
- package/dist/{zh-ID6DRYU4.js → zh-AGKPGI3L.js} +5 -1
- package/package.json +12 -12
- package/src/components/StatusDisplay.tsx +13 -13
- package/src/components/__tests__/StatusDisplay.test.tsx +9 -9
- package/src/components/modals/ComposeStep.tsx +19 -4
- package/src/components/modals/ConfigureGenerationStep.tsx +143 -15
- package/src/components/modals/ReferenceWizardModal.tsx +14 -9
- package/src/components/modals/ResourceGenerateModal.tsx +10 -5
- package/src/components/modals/__tests__/ConfigureGenerationStep.test.tsx +171 -8
- package/src/components/modals/__tests__/ReferenceWizardModal.test.tsx +25 -1
- package/src/components/modals/__tests__/ResourceGenerateModal.test.tsx +24 -1
- package/src/components/modals/__tests__/WizardFooter.test.tsx +3 -5
- package/src/components/resource/BrowseView.tsx +3 -3
- package/src/components/resource/DownloadFileLink.tsx +1 -1
- package/src/components/resource/__tests__/BrowseView.reference-hover.test.tsx +1 -1
- package/src/components/resource/__tests__/BrowseView.test.tsx +2 -2
- package/src/components/resource/__tests__/download-link.test.tsx +6 -6
- package/src/components/resource/panels/ReferenceEntry.tsx +2 -2
- package/src/components/resource/panels/ReferencesPanel.tsx +5 -1
- package/src/components/resource/panels/__tests__/ReferencesPanel.observable-flow.test.tsx +1 -1
- package/src/components/resource/panels/__tests__/ReferencesPanel.test.tsx +51 -2
- package/src/components/resource/panels/__tests__/ResourceInfoPanel.test.tsx +1 -1
- package/src/features/auth/__tests__/SignInForm.a11y.test.tsx +12 -12
- package/src/features/auth/components/SignInForm.tsx +53 -53
- package/src/features/auth-welcome/state/__tests__/welcome-state-unit.test.ts +2 -2
- package/src/features/resource-compose/components/ResourceComposePage.tsx +18 -3
- package/src/features/resource-discovery/__tests__/ResourceDiscoveryPage.test.tsx +1 -1
- package/src/features/resource-discovery/components/ResourceDiscoveryPage.tsx +2 -2
- package/src/features/resource-discovery/state/discover-state-unit.ts +1 -1
- package/src/features/resource-viewer/__tests__/ResourceViewerPage.test.tsx +2 -2
- package/src/features/resource-viewer/__tests__/generation-options.test.ts +53 -0
- package/src/features/resource-viewer/components/ResourceViewerPage.tsx +16 -25
- package/src/features/resource-viewer/generation-options.ts +29 -0
- package/src/styles/index.css +0 -1
- package/translations/ar.json +4 -0
- package/translations/bn.json +4 -0
- package/translations/cs.json +4 -0
- package/translations/da.json +4 -0
- package/translations/de.json +4 -0
- package/translations/el.json +4 -0
- package/translations/en.json +4 -0
- package/translations/es.json +4 -0
- package/translations/fa.json +4 -0
- package/translations/fi.json +4 -0
- package/translations/fr.json +4 -0
- package/translations/he.json +4 -0
- package/translations/hi.json +4 -0
- package/translations/id.json +4 -0
- package/translations/it.json +4 -0
- package/translations/ja.json +4 -0
- package/translations/ko.json +4 -0
- package/translations/ms.json +4 -0
- package/translations/nl.json +4 -0
- package/translations/no.json +4 -0
- package/translations/pl.json +4 -0
- package/translations/pt.json +4 -0
- package/translations/ro.json +4 -0
- package/translations/sv.json +4 -0
- package/translations/th.json +4 -0
- package/translations/tr.json +4 -0
- package/translations/uk.json +4 -0
- package/translations/vi.json +4 -0
- package/translations/zh.json +4 -0
- package/src/features/admin-exchange/__tests__/AdminExchangePage.test.tsx +0 -137
- package/src/features/admin-exchange/__tests__/ExportCard.test.tsx +0 -41
- package/src/features/admin-exchange/__tests__/ImportCard.test.tsx +0 -148
- package/src/features/admin-exchange/__tests__/ImportProgress.test.tsx +0 -117
- package/src/features/admin-exchange/components/AdminExchangePage.tsx +0 -117
- package/src/features/admin-exchange/components/ExportCard.tsx +0 -35
- package/src/features/admin-exchange/components/ImportCard.tsx +0 -183
- package/src/features/admin-exchange/components/ImportProgress.tsx +0 -80
- package/src/features/admin-exchange/state/__tests__/exchange-state-unit.test.ts +0 -185
- package/src/features/admin-exchange/state/exchange-state-unit.ts +0 -133
- package/src/features/moderation-linked-data/__tests__/LinkedDataPage.test.tsx +0 -118
- package/src/features/moderation-linked-data/components/LinkedDataPage.tsx +0 -118
- package/src/styles/features/exchange.css +0 -374
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React, { useState } from 'react';
|
|
4
|
-
import { LOCALES } from '@semiont/core';
|
|
4
|
+
import { LOCALES, proposeStoragePath } from '@semiont/core';
|
|
5
5
|
import { CodeMirrorRenderer } from '../CodeMirrorRenderer';
|
|
6
6
|
import { WizardFooter } from './WizardFooter';
|
|
7
7
|
|
|
@@ -37,6 +37,12 @@ export interface ComposeParams {
|
|
|
37
37
|
export interface ComposeStepProps {
|
|
38
38
|
draft: ComposeDraft;
|
|
39
39
|
onDraftChange: (patch: Partial<ComposeDraft>) => void;
|
|
40
|
+
/**
|
|
41
|
+
* Folder of the resource being composed FROM, so the new file lands beside
|
|
42
|
+
* its source (D11). Compose is always `text/markdown`, so the proposal's
|
|
43
|
+
* extension is fixed.
|
|
44
|
+
*/
|
|
45
|
+
defaultFolder?: string;
|
|
40
46
|
/** Entity types fixed when the reference was created — read-only tags when
|
|
41
47
|
* non-empty (D6): they are not this step's to change. */
|
|
42
48
|
referenceEntityTypes: string[];
|
|
@@ -65,6 +71,7 @@ export interface ComposeStepProps {
|
|
|
65
71
|
export function ComposeStep({
|
|
66
72
|
draft,
|
|
67
73
|
onDraftChange,
|
|
74
|
+
defaultFolder = '',
|
|
68
75
|
referenceEntityTypes,
|
|
69
76
|
entityTypeOptions,
|
|
70
77
|
showLineNumbers,
|
|
@@ -73,17 +80,25 @@ export function ComposeStep({
|
|
|
73
80
|
onCompose,
|
|
74
81
|
translations: t,
|
|
75
82
|
}: ComposeStepProps) {
|
|
83
|
+
// D11 — the Save location starts filled and follows the title until the
|
|
84
|
+
// user takes it over; `pathTouched` is derived (see ConfigureGenerationStep),
|
|
85
|
+
// so clearing the field restores the proposal. Compose writes markdown.
|
|
86
|
+
const pathTouched = draft.storagePath !== '';
|
|
87
|
+
const effectivePath = pathTouched
|
|
88
|
+
? draft.storagePath
|
|
89
|
+
: proposeStoragePath(defaultFolder, draft.name, 'text/markdown');
|
|
90
|
+
|
|
76
91
|
const [isCreating, setIsCreating] = useState(false);
|
|
77
92
|
const fixedTypes = referenceEntityTypes.length > 0;
|
|
78
93
|
|
|
79
94
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
80
95
|
e.preventDefault();
|
|
81
|
-
if (!draft.name.trim() || !
|
|
96
|
+
if (!draft.name.trim() || !effectivePath.trim()) return;
|
|
82
97
|
setIsCreating(true);
|
|
83
98
|
try {
|
|
84
99
|
await onCompose({
|
|
85
100
|
name: draft.name,
|
|
86
|
-
storagePath: `file://${
|
|
101
|
+
storagePath: `file://${effectivePath}`,
|
|
87
102
|
content: draft.content,
|
|
88
103
|
entityTypes: fixedTypes ? referenceEntityTypes : draft.entityTypes,
|
|
89
104
|
language: draft.language,
|
|
@@ -126,7 +141,7 @@ export function ComposeStep({
|
|
|
126
141
|
<input
|
|
127
142
|
id="compose-storagePath"
|
|
128
143
|
type="text"
|
|
129
|
-
value={
|
|
144
|
+
value={effectivePath}
|
|
130
145
|
onChange={(e) => onDraftChange({ storagePath: e.target.value })}
|
|
131
146
|
placeholder="people/my-resource.md"
|
|
132
147
|
required
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import React, { useEffect } from 'react';
|
|
4
4
|
import { WizardFooter } from './WizardFooter';
|
|
5
|
-
import type { CollaboratorEntry, GatheredContext } from '@semiont/core';
|
|
6
|
-
import {
|
|
5
|
+
import type { CollaboratorEntry, GatheredContext, SupportedMediaType } from '@semiont/core';
|
|
6
|
+
import type { GenerationOptions } from '@semiont/sdk';
|
|
7
|
+
import { LOCALES, GENERATABLE_MEDIA_TYPES, capabilitiesOf, proposeStoragePath } from '@semiont/core';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Bounds for the max-length control when no ceiling is known. These are the
|
|
@@ -15,23 +16,82 @@ const DEFAULT_MAX_TOKENS_CEILING = 4000;
|
|
|
15
16
|
/** The step's form values, owned by the wizard so Back cannot discard them (D3). */
|
|
16
17
|
export interface GenerationDraft {
|
|
17
18
|
title: string;
|
|
19
|
+
/**
|
|
20
|
+
* The path FRAGMENT the user types beside the `file://` prefix — not a URI.
|
|
21
|
+
* (`GenerationConfig.storageUri` is the assembled one; the names differ
|
|
22
|
+
* because the things differ.)
|
|
23
|
+
*/
|
|
18
24
|
storagePath: string;
|
|
19
25
|
prompt: string;
|
|
20
26
|
language: string;
|
|
21
27
|
temperature: number;
|
|
22
28
|
/** Text, not number: the field is user-editable and may be mid-edit or empty. */
|
|
23
29
|
maxTokensText: string;
|
|
30
|
+
/** The artifact's media type. Seeded `text/markdown` by both hosts (D2). */
|
|
31
|
+
outputMediaType: SupportedMediaType;
|
|
24
32
|
}
|
|
25
33
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
/**
|
|
35
|
+
* What this form fills in. Named here, but TYPED by the SDK (P2b).
|
|
36
|
+
*
|
|
37
|
+
* `storageUri` is on this list rather than a local `storagePath: string`
|
|
38
|
+
* because the two were the same thing under different names — a divergence
|
|
39
|
+
* that survived precisely because nothing tied them together (D8).
|
|
40
|
+
*/
|
|
41
|
+
type FormFilled =
|
|
42
|
+
| 'title'
|
|
43
|
+
| 'storageUri'
|
|
44
|
+
| 'language'
|
|
45
|
+
| 'temperature'
|
|
46
|
+
| 'maxTokens'
|
|
47
|
+
| 'outputMediaType';
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The submitted payload: the SDK's own generation options, DERIVED rather
|
|
51
|
+
* than restated (P2b), plus the grounding context.
|
|
52
|
+
*
|
|
53
|
+
* This was a hand-written interface listing the same fields with independent
|
|
54
|
+
* types — the shape `## Duplicated Shape` forbids — and it drifted exactly as
|
|
55
|
+
* predicted: `storagePath: string` held a `storageUri`, so every consumer
|
|
56
|
+
* re-mapped it by hand and silently dropped whatever the mapping forgot.
|
|
57
|
+
* Deriving means a rename or retype in `GenerationOptions` is a compile
|
|
58
|
+
* error here instead of a quiet gap at the wire.
|
|
59
|
+
*
|
|
60
|
+
* NARROWED on purpose: the SDK also carries `task`, `structure`, `cite` and
|
|
61
|
+
* `stallDeadlineMs`, which this form does not collect, and a payload type
|
|
62
|
+
* that advertised them would promise more than the UI delivers. Surfacing one
|
|
63
|
+
* later is a one-word edit to `FormFilled` plus its control — the door is
|
|
64
|
+
* unlocked, not open.
|
|
65
|
+
*
|
|
66
|
+
* `Required` because the form always fills these, even where the SDK lets
|
|
67
|
+
* them be absent; `prompt` stays optional, because a blank instruction field
|
|
68
|
+
* is omitted rather than sent empty.
|
|
69
|
+
*/
|
|
70
|
+
export type GenerationConfig =
|
|
71
|
+
& Required<Pick<GenerationOptions, FormFilled>>
|
|
72
|
+
& Pick<GenerationOptions, 'prompt'>
|
|
73
|
+
& { context: GatheredContext };
|
|
74
|
+
|
|
75
|
+
/** The one place a format's required extension is read (D7). */
|
|
76
|
+
const extensionFor = (format: SupportedMediaType): string =>
|
|
77
|
+
capabilitiesOf(format)?.extension ?? '';
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* A fresh draft, defined ONCE for both hosts (D8b). The two modals carried
|
|
81
|
+
* byte-identical literals — magic numbers included — so every new field meant
|
|
82
|
+
* two edits and an opportunity to seed only one of them.
|
|
83
|
+
*/
|
|
84
|
+
export const freshGenerationDraft = (title: string, locale: string): GenerationDraft => ({
|
|
85
|
+
title,
|
|
86
|
+
storagePath: '',
|
|
87
|
+
prompt: '',
|
|
88
|
+
language: locale,
|
|
89
|
+
temperature: 0.7,
|
|
90
|
+
maxTokensText: '500',
|
|
91
|
+
// D2: markdown is the DEFAULT, not an assumption — the worker would also
|
|
92
|
+
// default to it, but a control must send what it shows.
|
|
93
|
+
outputMediaType: 'text/markdown',
|
|
94
|
+
});
|
|
35
95
|
|
|
36
96
|
export interface ConfigureGenerationStepProps {
|
|
37
97
|
context: GatheredContext;
|
|
@@ -43,6 +103,12 @@ export interface ConfigureGenerationStepProps {
|
|
|
43
103
|
/** Absent in a single-stack host (GATHER-AT-THE-TOP D6) — the footer then renders no retreat. */
|
|
44
104
|
onBack?: () => void;
|
|
45
105
|
onGenerate: (config: GenerationConfig) => void;
|
|
106
|
+
/**
|
|
107
|
+
* Folder of the resource being generated FROM, so the artifact lands beside
|
|
108
|
+
* its source (D11). The page derives it; this component stays
|
|
109
|
+
* presentational, exactly as it already receives the default title.
|
|
110
|
+
*/
|
|
111
|
+
defaultFolder?: string;
|
|
46
112
|
translations: {
|
|
47
113
|
resourceTitle: string;
|
|
48
114
|
resourceTitlePlaceholder: string;
|
|
@@ -62,6 +128,15 @@ export interface ConfigureGenerationStepProps {
|
|
|
62
128
|
* and `{{model}}`.
|
|
63
129
|
*/
|
|
64
130
|
maxLengthCeiling: string;
|
|
131
|
+
/** Label for the output-format select. */
|
|
132
|
+
outputFormat: string;
|
|
133
|
+
/**
|
|
134
|
+
* Shown when the Save location's extension contradicts the chosen format,
|
|
135
|
+
* which the form REFUSES to submit (D7 — the worker is faithful and will
|
|
136
|
+
* write a PDF to a `.md` path, so this is the only gate). Interpolates
|
|
137
|
+
* `{{extension}}`.
|
|
138
|
+
*/
|
|
139
|
+
formatExtensionMismatch: string;
|
|
65
140
|
back?: string;
|
|
66
141
|
generate: string;
|
|
67
142
|
};
|
|
@@ -82,6 +157,7 @@ export function ConfigureGenerationStep({
|
|
|
82
157
|
onConfigChange,
|
|
83
158
|
onBack,
|
|
84
159
|
onGenerate,
|
|
160
|
+
defaultFolder = '',
|
|
85
161
|
translations: t,
|
|
86
162
|
generationAgent,
|
|
87
163
|
}: ConfigureGenerationStepProps) {
|
|
@@ -90,9 +166,28 @@ export function ConfigureGenerationStep({
|
|
|
90
166
|
// both sliders. `maxTokensText` stays TEXT rather than a number so the field can
|
|
91
167
|
// be cleared mid-edit — `parseInt` on an empty field yields NaN, which React
|
|
92
168
|
// warns about and which used to travel into the job config.
|
|
93
|
-
const { title, storagePath, prompt, language, temperature, maxTokensText } = config;
|
|
169
|
+
const { title, storagePath, prompt, language, temperature, maxTokensText, outputMediaType } = config;
|
|
94
170
|
const set = (patch: Partial<GenerationDraft>) => onConfigChange({ ...config, ...patch });
|
|
95
171
|
|
|
172
|
+
// D11 — the Save location starts filled and FOLLOWS the title and format
|
|
173
|
+
// until the user takes it over. `pathTouched` is DERIVED rather than stored:
|
|
174
|
+
// it is exactly "the field holds the user's own text", which makes clearing
|
|
175
|
+
// the field un-touch it for free, keeps it reset-on-open and Back-safe
|
|
176
|
+
// because `storagePath` already is, and leaves no second field to desync.
|
|
177
|
+
const pathTouched = storagePath !== '';
|
|
178
|
+
const proposedPath = proposeStoragePath(defaultFolder, title, outputMediaType);
|
|
179
|
+
const effectivePath = pathTouched ? storagePath : proposedPath;
|
|
180
|
+
|
|
181
|
+
// D7 — the GUI is where a format/extension mismatch is caught, because it is
|
|
182
|
+
// where the person who can fix it is standing. An EMPTY path is not a
|
|
183
|
+
// mismatch: the field is `required`, so emptiness is already refused, and
|
|
184
|
+
// telling an untouched form it is wrong is not a welcome. Under D11 a
|
|
185
|
+
// PROPOSED path always matches, so this now fires only on hand-edits.
|
|
186
|
+
const requiredExtension = extensionFor(outputMediaType);
|
|
187
|
+
const extensionMismatch =
|
|
188
|
+
effectivePath !== '' &&
|
|
189
|
+
!effectivePath.toLowerCase().endsWith(requiredExtension.toLowerCase());
|
|
190
|
+
|
|
96
191
|
const ceiling = generationAgent?.limits?.maxOutputTokens ?? DEFAULT_MAX_TOKENS_CEILING;
|
|
97
192
|
|
|
98
193
|
/**
|
|
@@ -119,14 +214,16 @@ export function ConfigureGenerationStep({
|
|
|
119
214
|
|
|
120
215
|
const handleSubmit = (e: React.FormEvent) => {
|
|
121
216
|
e.preventDefault();
|
|
217
|
+
if (extensionMismatch) return;
|
|
122
218
|
const trimmedPrompt = prompt.trim();
|
|
123
219
|
onGenerate({
|
|
124
220
|
title,
|
|
125
|
-
|
|
221
|
+
storageUri: `file://${effectivePath}`,
|
|
126
222
|
...(trimmedPrompt ? { prompt: trimmedPrompt } : {}),
|
|
127
223
|
language,
|
|
128
224
|
temperature,
|
|
129
225
|
maxTokens: submittedMaxTokens,
|
|
226
|
+
outputMediaType,
|
|
130
227
|
context,
|
|
131
228
|
});
|
|
132
229
|
};
|
|
@@ -167,13 +264,44 @@ export function ConfigureGenerationStep({
|
|
|
167
264
|
<input
|
|
168
265
|
id="wizard-storagePath"
|
|
169
266
|
type="text"
|
|
170
|
-
value={
|
|
267
|
+
value={effectivePath}
|
|
171
268
|
onChange={(e) => set({ storagePath: e.target.value })}
|
|
172
269
|
required
|
|
270
|
+
aria-invalid={extensionMismatch}
|
|
271
|
+
{...(extensionMismatch ? { 'aria-describedby': 'wizard-format-mismatch' } : {})}
|
|
173
272
|
className="semiont-input semiont-input--addon"
|
|
174
273
|
placeholder="generated/my-resource.md"
|
|
175
274
|
/>
|
|
176
275
|
</div>
|
|
276
|
+
{/* The reason sits with the field, not on the button: the message
|
|
277
|
+
belongs where the fix is. */}
|
|
278
|
+
{extensionMismatch && (
|
|
279
|
+
<p id="wizard-format-mismatch" className="semiont-form__error" role="alert">
|
|
280
|
+
{t.formatExtensionMismatch.replace('{{extension}}', requiredExtension)}
|
|
281
|
+
</p>
|
|
282
|
+
)}
|
|
283
|
+
</div>
|
|
284
|
+
|
|
285
|
+
{/* Output format — with the artifact's IDENTITY (title, location), not
|
|
286
|
+
among the model knobs below (D5). The options are DERIVED from the
|
|
287
|
+
registry (D1): promote a fourth row to `generatable` and it appears
|
|
288
|
+
here, with no list to forget to update. */}
|
|
289
|
+
<div className="semiont-form__field">
|
|
290
|
+
<label htmlFor="wizard-outputFormat" className="semiont-form__label">
|
|
291
|
+
{t.outputFormat}
|
|
292
|
+
</label>
|
|
293
|
+
<select
|
|
294
|
+
id="wizard-outputFormat"
|
|
295
|
+
value={outputMediaType}
|
|
296
|
+
onChange={(e) => set({ outputMediaType: e.target.value as SupportedMediaType })}
|
|
297
|
+
className="semiont-select"
|
|
298
|
+
>
|
|
299
|
+
{GENERATABLE_MEDIA_TYPES.map((format) => (
|
|
300
|
+
<option key={format} value={format}>
|
|
301
|
+
{capabilitiesOf(format)?.label ?? format}
|
|
302
|
+
</option>
|
|
303
|
+
))}
|
|
304
|
+
</select>
|
|
177
305
|
</div>
|
|
178
306
|
|
|
179
307
|
{/* Additional Instructions */}
|
|
@@ -275,7 +403,7 @@ export function ConfigureGenerationStep({
|
|
|
275
403
|
|
|
276
404
|
<WizardFooter
|
|
277
405
|
{...(onBack && t.back ? { backLabel: t.back, onBack } : {})}
|
|
278
|
-
primary={{ label: t.generate, type: 'submit' }}
|
|
406
|
+
primary={{ label: t.generate, type: 'submit', disabled: extensionMismatch }}
|
|
279
407
|
/>
|
|
280
408
|
</form>
|
|
281
409
|
);
|
|
@@ -8,7 +8,7 @@ import { useSemiont } from '../../session/SemiontProvider';
|
|
|
8
8
|
import { useObservable } from '../../hooks/useObservable';
|
|
9
9
|
import { useEventSubscription } from '../../contexts/useEventSubscription';
|
|
10
10
|
import { GatherContextStep } from './GatherContextStep';
|
|
11
|
-
import { ConfigureGenerationStep } from './ConfigureGenerationStep';
|
|
11
|
+
import { ConfigureGenerationStep, freshGenerationDraft } from './ConfigureGenerationStep';
|
|
12
12
|
import type { GenerationConfig } from './ConfigureGenerationStep';
|
|
13
13
|
import { ConfigureSearchStep } from './ConfigureSearchStep';
|
|
14
14
|
import type { SearchConfig } from './ConfigureSearchStep';
|
|
@@ -42,6 +42,8 @@ export interface ReferenceWizardModalProps {
|
|
|
42
42
|
resourceId: string | null;
|
|
43
43
|
/** Default title (selected text) */
|
|
44
44
|
defaultTitle: string;
|
|
45
|
+
/** Folder of the source resource, so new files land beside it (D11). */
|
|
46
|
+
defaultFolder?: string;
|
|
45
47
|
/** Entity types from the annotation */
|
|
46
48
|
entityTypes: string[];
|
|
47
49
|
/**
|
|
@@ -111,6 +113,8 @@ export interface ReferenceWizardModalProps {
|
|
|
111
113
|
maxLength: string;
|
|
112
114
|
maxLengthHelp: string;
|
|
113
115
|
maxLengthCeiling: string;
|
|
116
|
+
outputFormat: string;
|
|
117
|
+
formatExtensionMismatch: string;
|
|
114
118
|
maxResults: string;
|
|
115
119
|
semanticScoring: string;
|
|
116
120
|
semanticScoringHelp: string;
|
|
@@ -131,6 +135,7 @@ export function ReferenceWizardModal({
|
|
|
131
135
|
annotationId,
|
|
132
136
|
resourceId,
|
|
133
137
|
defaultTitle,
|
|
138
|
+
defaultFolder,
|
|
134
139
|
entityTypes,
|
|
135
140
|
resourceName,
|
|
136
141
|
locale,
|
|
@@ -151,10 +156,9 @@ export function ReferenceWizardModal({
|
|
|
151
156
|
// back unmounts a step; if the step owned its values, Back would silently discard
|
|
152
157
|
// everything typed — which is exactly what it used to do.
|
|
153
158
|
const [searchConfig, setSearchConfig] = useState<SearchConfig>({ limit: 10, useSemanticScoring: true });
|
|
154
|
-
const [generationDraft, setGenerationDraft] = useState<GenerationDraft>(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
});
|
|
159
|
+
const [generationDraft, setGenerationDraft] = useState<GenerationDraft>(
|
|
160
|
+
() => freshGenerationDraft(defaultTitle, locale),
|
|
161
|
+
);
|
|
158
162
|
const [isSearching, setIsSearching] = useState(false);
|
|
159
163
|
const [searchError, setSearchError] = useState<string | null>(null);
|
|
160
164
|
const [userHint, setUserHint] = useState('');
|
|
@@ -179,10 +183,7 @@ export function ReferenceWizardModal({
|
|
|
179
183
|
seededTitle.current = defaultTitle;
|
|
180
184
|
setWizardStep({ step: 'gather' });
|
|
181
185
|
setSearchConfig({ limit: 10, useSemanticScoring: true });
|
|
182
|
-
setGenerationDraft(
|
|
183
|
-
title: defaultTitle, storagePath: '', prompt: '', language: locale,
|
|
184
|
-
temperature: 0.7, maxTokensText: '500',
|
|
185
|
-
});
|
|
186
|
+
setGenerationDraft(freshGenerationDraft(defaultTitle, locale));
|
|
186
187
|
setIsSearching(false);
|
|
187
188
|
setSearchError(null);
|
|
188
189
|
setUserHint('');
|
|
@@ -425,6 +426,7 @@ export function ReferenceWizardModal({
|
|
|
425
426
|
<ComposeStep
|
|
426
427
|
draft={composeDraft}
|
|
427
428
|
onDraftChange={(patch) => setComposeDraft((d) => ({ ...d, ...patch }))}
|
|
429
|
+
defaultFolder={defaultFolder}
|
|
428
430
|
referenceEntityTypes={entityTypes}
|
|
429
431
|
entityTypeOptions={entityTypeOptions}
|
|
430
432
|
showLineNumbers={showLineNumbers}
|
|
@@ -461,6 +463,7 @@ export function ReferenceWizardModal({
|
|
|
461
463
|
context={contextWithHint ?? context}
|
|
462
464
|
config={generationDraft}
|
|
463
465
|
onConfigChange={setGenerationDraft}
|
|
466
|
+
defaultFolder={defaultFolder}
|
|
464
467
|
onBack={handleBackToGather}
|
|
465
468
|
onGenerate={handleGenerateSubmit}
|
|
466
469
|
translations={{
|
|
@@ -477,6 +480,8 @@ export function ReferenceWizardModal({
|
|
|
477
480
|
maxLength: t.maxLength,
|
|
478
481
|
maxLengthHelp: t.maxLengthHelp,
|
|
479
482
|
maxLengthCeiling: t.maxLengthCeiling,
|
|
483
|
+
outputFormat: t.outputFormat,
|
|
484
|
+
formatExtensionMismatch: t.formatExtensionMismatch,
|
|
480
485
|
back: t.back,
|
|
481
486
|
generate: t.generate,
|
|
482
487
|
}}
|
|
@@ -7,7 +7,7 @@ import { Dialog, DialogPanel, DialogTitle, Transition, TransitionChild } from '@
|
|
|
7
7
|
import { ConfigureGatherStep, type ResourceGatherConfig } from './ConfigureGatherStep';
|
|
8
8
|
import { DiscardPrompt } from './DiscardPrompt';
|
|
9
9
|
import { GatherContextStep } from './GatherContextStep';
|
|
10
|
-
import { ConfigureGenerationStep, type GenerationConfig, type GenerationDraft } from './ConfigureGenerationStep';
|
|
10
|
+
import { ConfigureGenerationStep, freshGenerationDraft, type GenerationConfig, type GenerationDraft } from './ConfigureGenerationStep';
|
|
11
11
|
|
|
12
12
|
export interface ResourceGenerateModalTranslations {
|
|
13
13
|
/** The ONE modal title — names the flow, not a step. */
|
|
@@ -51,6 +51,8 @@ export interface ResourceGenerateModalTranslations {
|
|
|
51
51
|
maxLength: string;
|
|
52
52
|
maxLengthHelp: string;
|
|
53
53
|
maxLengthCeiling: string;
|
|
54
|
+
outputFormat: string;
|
|
55
|
+
formatExtensionMismatch: string;
|
|
54
56
|
generate: string;
|
|
55
57
|
// DiscardPrompt (GATHER-AT-THE-TOP P1)
|
|
56
58
|
discardDraftPrompt: string;
|
|
@@ -69,6 +71,8 @@ export interface ResourceGenerateModalProps {
|
|
|
69
71
|
onClose: () => void;
|
|
70
72
|
resourceId: string;
|
|
71
73
|
defaultTitle: string;
|
|
74
|
+
/** Folder of the source resource, so the artifact lands beside it (D11). */
|
|
75
|
+
defaultFolder?: string;
|
|
72
76
|
locale: string;
|
|
73
77
|
/**
|
|
74
78
|
* Entity types offered in the exclusion picker. Owner-supplied so the
|
|
@@ -115,6 +119,7 @@ export function ResourceGenerateModal({
|
|
|
115
119
|
onClose,
|
|
116
120
|
resourceId,
|
|
117
121
|
defaultTitle,
|
|
122
|
+
defaultFolder,
|
|
118
123
|
locale,
|
|
119
124
|
entityTypeOptions = [],
|
|
120
125
|
onGenerateSubmit,
|
|
@@ -127,10 +132,7 @@ export function ResourceGenerateModal({
|
|
|
127
132
|
}: ResourceGenerateModalProps) {
|
|
128
133
|
// Same draft ownership as the wizard (WIZARD-NAVIGATION D3): the step is
|
|
129
134
|
// controlled, so stepping back through this modal keeps what was typed.
|
|
130
|
-
const freshDraft = (): GenerationDraft => (
|
|
131
|
-
title: defaultTitle, storagePath: '', prompt: '', language: locale,
|
|
132
|
-
temperature: 0.7, maxTokensText: '500',
|
|
133
|
-
});
|
|
135
|
+
const freshDraft = (): GenerationDraft => freshGenerationDraft(defaultTitle, locale);
|
|
134
136
|
const [generationDraft, setGenerationDraft] = useState<GenerationDraft>(freshDraft);
|
|
135
137
|
|
|
136
138
|
// "Has THIS run gathered yet?" — the page's gather slots survive across
|
|
@@ -358,6 +360,7 @@ export function ResourceGenerateModal({
|
|
|
358
360
|
context={gatherContext}
|
|
359
361
|
config={generationDraft}
|
|
360
362
|
onConfigChange={setGenerationDraft}
|
|
363
|
+
defaultFolder={defaultFolder}
|
|
361
364
|
onGenerate={handleGenerate}
|
|
362
365
|
translations={{
|
|
363
366
|
resourceTitle: t.resourceTitle,
|
|
@@ -373,6 +376,8 @@ export function ResourceGenerateModal({
|
|
|
373
376
|
maxLength: t.maxLength,
|
|
374
377
|
maxLengthHelp: t.maxLengthHelp,
|
|
375
378
|
maxLengthCeiling: t.maxLengthCeiling,
|
|
379
|
+
outputFormat: t.outputFormat,
|
|
380
|
+
formatExtensionMismatch: t.formatExtensionMismatch,
|
|
376
381
|
generate: t.generate,
|
|
377
382
|
}}
|
|
378
383
|
/>
|