@flowdrop/flowdrop 1.7.0 → 1.8.1
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 +10 -0
- package/dist/chat/responseParser.js +7 -0
- package/dist/commands/parser.js +12 -0
- package/dist/components/App.svelte +92 -54
- package/dist/components/App.svelte.d.ts +13 -0
- package/dist/components/ConfigModal.svelte +2 -1
- package/dist/components/ConfigPanel.svelte +3 -2
- package/dist/components/FlowDropZone.svelte +2 -1
- package/dist/components/LogsSidebar.svelte +3 -2
- package/dist/components/Navbar.svelte +10 -6
- package/dist/components/NodeSidebar.svelte +4 -3
- package/dist/components/NodeStatusOverlay.svelte +14 -7
- package/dist/components/NodeSwapPicker.svelte +2 -1
- package/dist/components/PipelineStatus.svelte +10 -7
- package/dist/components/ReadOnlyDetails.svelte +4 -2
- package/dist/components/SchemaForm.svelte +20 -9
- package/dist/components/SchemaForm.svelte.d.ts +2 -4
- package/dist/components/SettingsModal.svelte +4 -3
- package/dist/components/SettingsPanel.svelte +3 -2
- package/dist/components/SwapMappingEditor.svelte +2 -1
- package/dist/components/WorkflowEditor.svelte +3 -2
- package/dist/components/chat/AIChatPanel.svelte +33 -8
- package/dist/components/chat/AIChatPanel.svelte.d.ts +3 -0
- package/dist/components/chat/CommandPreview.svelte +10 -6
- package/dist/components/console/CommandConsole.svelte +4 -3
- package/dist/components/form/FormArray.svelte +33 -20
- package/dist/components/form/FormArray.svelte.d.ts +3 -1
- package/dist/components/form/FormAutocomplete.svelte +18 -7
- package/dist/components/form/FormCodeEditor.svelte +2 -1
- package/dist/components/form/FormFieldWrapper.svelte +2 -1
- package/dist/components/form/FormMarkdownEditor.svelte +152 -108
- package/dist/components/form/FormMarkdownEditor.svelte.d.ts +1 -1
- package/dist/components/form/FormTemplateEditor.svelte +2 -1
- package/dist/components/form/FormToggle.svelte +23 -5
- package/dist/components/form/FormToggle.svelte.d.ts +6 -2
- package/dist/components/interrupt/ChoicePrompt.svelte +14 -5
- package/dist/components/interrupt/ConfirmationPrompt.svelte +8 -5
- package/dist/components/interrupt/FormPrompt.svelte +28 -7
- package/dist/components/interrupt/InterruptBubble.svelte +27 -18
- package/dist/components/interrupt/ReviewPrompt.svelte +32 -22
- package/dist/components/interrupt/TextInputPrompt.svelte +12 -5
- package/dist/components/layouts/MainLayout.svelte +4 -3
- package/dist/components/nodes/GatewayNode.svelte +8 -3
- package/dist/components/nodes/IdeaNode.svelte +2 -1
- package/dist/components/nodes/NotesNode.svelte +18 -12
- package/dist/components/nodes/WorkflowNode.svelte +8 -3
- package/dist/components/playground/ChatPanel.svelte +36 -24
- package/dist/components/playground/MessageBubble.svelte +15 -7
- package/dist/components/playground/Playground.svelte +2 -1
- package/dist/components/playground/PlaygroundModal.svelte +2 -1
- package/dist/components/playground/SessionManager.svelte +14 -10
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +9 -0
- package/dist/editor/index.d.ts +1 -1
- package/dist/editor/index.js +1 -1
- package/dist/messages/context.d.ts +29 -0
- package/dist/messages/context.js +38 -0
- package/dist/messages/defaults.d.ts +396 -0
- package/dist/messages/defaults.js +356 -0
- package/dist/messages/deprecation.d.ts +20 -0
- package/dist/messages/deprecation.js +33 -0
- package/dist/messages/index.d.ts +11 -0
- package/dist/messages/index.js +10 -0
- package/dist/messages/merge.d.ts +28 -0
- package/dist/messages/merge.js +53 -0
- package/dist/messages/types.d.ts +29 -0
- package/dist/messages/types.js +13 -0
- package/dist/services/draftStorage.d.ts +13 -0
- package/dist/services/draftStorage.js +36 -0
- package/dist/styles/base.css +13 -4
- package/dist/svelte-app.d.ts +11 -0
- package/dist/svelte-app.js +11 -2
- package/package.json +1 -1
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
<script lang="ts">
|
|
13
13
|
import type { Snippet } from 'svelte';
|
|
14
|
+
import { m } from '../../messages/index.js';
|
|
14
15
|
|
|
15
16
|
interface Props {
|
|
16
17
|
/** Field identifier for label association */
|
|
@@ -42,7 +43,7 @@
|
|
|
42
43
|
{label}
|
|
43
44
|
</span>
|
|
44
45
|
{#if required}
|
|
45
|
-
<span class="form-field__required" aria-label=
|
|
46
|
+
<span class="form-field__required" aria-label={m().form.field.required}>*</span>
|
|
46
47
|
{/if}
|
|
47
48
|
</label>
|
|
48
49
|
|
|
@@ -18,20 +18,21 @@
|
|
|
18
18
|
|
|
19
19
|
<script lang="ts">
|
|
20
20
|
import { onMount, onDestroy } from 'svelte';
|
|
21
|
-
import { EditorView, lineNumbers, drawSelection, keymap } from '@codemirror/view';
|
|
21
|
+
import { EditorView, lineNumbers, drawSelection, keymap, placeholder } from '@codemirror/view';
|
|
22
22
|
import { EditorState, Compartment } from '@codemirror/state';
|
|
23
23
|
import { history, historyKeymap, defaultKeymap, indentWithTab } from '@codemirror/commands';
|
|
24
24
|
import { highlightSpecialChars, highlightActiveLine } from '@codemirror/view';
|
|
25
25
|
import { syntaxHighlighting, defaultHighlightStyle } from '@codemirror/language';
|
|
26
26
|
import { markdown } from '@codemirror/lang-markdown';
|
|
27
27
|
import { oneDark } from '@codemirror/theme-one-dark';
|
|
28
|
+
import { m } from '../../messages/index.js';
|
|
28
29
|
|
|
29
30
|
interface Props {
|
|
30
31
|
/** Field identifier */
|
|
31
32
|
id: string;
|
|
32
33
|
/** Current value (markdown string) */
|
|
33
34
|
value: string;
|
|
34
|
-
/** Placeholder text shown when empty */
|
|
35
|
+
/** Placeholder text shown when empty. Falls back to `messages.form.markdown.placeholder`. */
|
|
35
36
|
placeholder?: string;
|
|
36
37
|
/** Whether the field is required */
|
|
37
38
|
required?: boolean;
|
|
@@ -60,7 +61,7 @@
|
|
|
60
61
|
let {
|
|
61
62
|
id,
|
|
62
63
|
value = '',
|
|
63
|
-
placeholder
|
|
64
|
+
placeholder: placeholderProp,
|
|
64
65
|
required = false,
|
|
65
66
|
height = '300px',
|
|
66
67
|
showToolbar = true,
|
|
@@ -97,6 +98,12 @@
|
|
|
97
98
|
/** Theme compartment for dynamic theme switching */
|
|
98
99
|
const themeCompartment = new Compartment();
|
|
99
100
|
|
|
101
|
+
/** Placeholder compartment so locale changes can reconfigure without rebuilding the editor */
|
|
102
|
+
const placeholderCompartment = new Compartment();
|
|
103
|
+
|
|
104
|
+
/** aria-label compartment so locale changes update the editor's accessible name */
|
|
105
|
+
const ariaLabelCompartment = new Compartment();
|
|
106
|
+
|
|
100
107
|
// ── Toolbar actions ──────────────────────────────────────
|
|
101
108
|
|
|
102
109
|
type ToolbarAction = {
|
|
@@ -165,104 +172,110 @@
|
|
|
165
172
|
editorView.focus();
|
|
166
173
|
}
|
|
167
174
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
175
|
+
// Derived so `label` strings refresh whenever the consumer's i18n source
|
|
176
|
+
// changes locale. Action closures recreate alongside, but they're cheap —
|
|
177
|
+
// they only capture a stable reference to `editorView` (a state).
|
|
178
|
+
const toolbarActions: (ToolbarAction | '|')[] = $derived.by(() => {
|
|
179
|
+
const md = m().form.markdown;
|
|
180
|
+
return [
|
|
181
|
+
{
|
|
182
|
+
id: 'bold',
|
|
183
|
+
label: md.bold,
|
|
184
|
+
icon: 'B',
|
|
185
|
+
shortcut: 'Mod-b',
|
|
186
|
+
action: () => wrapSelection('**', '**')
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
id: 'italic',
|
|
190
|
+
label: md.italic,
|
|
191
|
+
icon: 'I',
|
|
192
|
+
shortcut: 'Mod-i',
|
|
193
|
+
action: () => wrapSelection('_', '_')
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
id: 'strikethrough',
|
|
197
|
+
label: md.strikethrough,
|
|
198
|
+
icon: 'S',
|
|
199
|
+
action: () => wrapSelection('~~', '~~')
|
|
200
|
+
},
|
|
201
|
+
'|',
|
|
202
|
+
{
|
|
203
|
+
id: 'heading-1',
|
|
204
|
+
label: md.heading1,
|
|
205
|
+
icon: 'H1',
|
|
206
|
+
action: () => prefixLine('# ')
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
id: 'heading-2',
|
|
210
|
+
label: md.heading2,
|
|
211
|
+
icon: 'H2',
|
|
212
|
+
action: () => prefixLine('## ')
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
id: 'heading-3',
|
|
216
|
+
label: md.heading3,
|
|
217
|
+
icon: 'H3',
|
|
218
|
+
action: () => prefixLine('### ')
|
|
219
|
+
},
|
|
220
|
+
'|',
|
|
221
|
+
{
|
|
222
|
+
id: 'quote',
|
|
223
|
+
label: md.quote,
|
|
224
|
+
icon: '"',
|
|
225
|
+
action: () => prefixLine('> ')
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
id: 'unordered-list',
|
|
229
|
+
label: md.unorderedList,
|
|
230
|
+
icon: '•',
|
|
231
|
+
action: () => prefixLine('- ')
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
id: 'ordered-list',
|
|
235
|
+
label: md.orderedList,
|
|
236
|
+
icon: '1.',
|
|
237
|
+
action: () => prefixLine('1. ')
|
|
238
|
+
},
|
|
239
|
+
'|',
|
|
240
|
+
{
|
|
241
|
+
id: 'link',
|
|
242
|
+
label: md.link,
|
|
243
|
+
icon: icons.link,
|
|
244
|
+
isSvg: true,
|
|
245
|
+
shortcut: 'Mod-k',
|
|
246
|
+
action: () => {
|
|
247
|
+
if (!editorView) return;
|
|
248
|
+
const { from, to } = editorView.state.selection.main;
|
|
249
|
+
const selected = editorView.state.sliceDoc(from, to);
|
|
250
|
+
const text = selected || 'link text';
|
|
251
|
+
const replacement = `[${text}](url)`;
|
|
252
|
+
editorView.dispatch({
|
|
253
|
+
changes: { from, to, insert: replacement },
|
|
254
|
+
selection: {
|
|
255
|
+
anchor: from + text.length + 3,
|
|
256
|
+
head: from + text.length + 6
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
editorView.focus();
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
id: 'image',
|
|
264
|
+
label: md.image,
|
|
265
|
+
icon: icons.image,
|
|
266
|
+
isSvg: true,
|
|
267
|
+
action: () => insertAtCursor('')
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
id: 'table',
|
|
271
|
+
label: md.table,
|
|
272
|
+
icon: icons.table,
|
|
273
|
+
isSvg: true,
|
|
274
|
+
action: () =>
|
|
275
|
+
insertAtCursor('\n| Header | Header |\n| ------ | ------ |\n| Cell | Cell |\n')
|
|
248
276
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
id: 'image',
|
|
252
|
-
label: 'Image',
|
|
253
|
-
icon: icons.image,
|
|
254
|
-
isSvg: true,
|
|
255
|
-
action: () => insertAtCursor('')
|
|
256
|
-
},
|
|
257
|
-
{
|
|
258
|
-
id: 'table',
|
|
259
|
-
label: 'Table',
|
|
260
|
-
icon: icons.table,
|
|
261
|
-
isSvg: true,
|
|
262
|
-
action: () =>
|
|
263
|
-
insertAtCursor('\n| Header | Header |\n| ------ | ------ |\n| Cell | Cell |\n')
|
|
264
|
-
}
|
|
265
|
-
];
|
|
277
|
+
];
|
|
278
|
+
});
|
|
266
279
|
|
|
267
280
|
// ── CM6 Keyboard shortcuts for toolbar actions ───────────
|
|
268
281
|
|
|
@@ -327,11 +340,14 @@
|
|
|
327
340
|
// ── Editor setup ─────────────────────────────────────────
|
|
328
341
|
|
|
329
342
|
function createExtensions() {
|
|
343
|
+
const placeholderText = placeholderProp ?? m().form.markdown.placeholder;
|
|
344
|
+
|
|
330
345
|
const extensions = [
|
|
331
346
|
lineNumbers(),
|
|
332
347
|
highlightSpecialChars(),
|
|
333
348
|
highlightActiveLine(),
|
|
334
349
|
drawSelection(),
|
|
350
|
+
placeholderCompartment.of(placeholder(placeholderText)),
|
|
335
351
|
|
|
336
352
|
// Editing features (skip when read-only)
|
|
337
353
|
...(disabled
|
|
@@ -394,10 +410,12 @@
|
|
|
394
410
|
EditorView.lineWrapping,
|
|
395
411
|
|
|
396
412
|
// Accessibility
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
413
|
+
ariaLabelCompartment.of(
|
|
414
|
+
EditorView.contentAttributes.of({
|
|
415
|
+
'aria-label': m().form.markdown.editor,
|
|
416
|
+
'aria-multiline': 'true'
|
|
417
|
+
})
|
|
418
|
+
)
|
|
401
419
|
];
|
|
402
420
|
|
|
403
421
|
return extensions;
|
|
@@ -462,6 +480,27 @@
|
|
|
462
480
|
updateStats(editorView.state.doc);
|
|
463
481
|
}
|
|
464
482
|
});
|
|
483
|
+
|
|
484
|
+
// Reconfigure CodeMirror-owned strings (placeholder, content aria-label)
|
|
485
|
+
// when the consumer's locale or `placeholder` prop changes. Toolbar and
|
|
486
|
+
// status-bar strings rerender via `$derived` in the template; CodeMirror
|
|
487
|
+
// lives outside Svelte's reactivity graph, so this effect bridges the gap.
|
|
488
|
+
$effect(() => {
|
|
489
|
+
const placeholderText = placeholderProp ?? m().form.markdown.placeholder;
|
|
490
|
+
const ariaLabel = m().form.markdown.editor;
|
|
491
|
+
if (!editorView) return;
|
|
492
|
+
editorView.dispatch({
|
|
493
|
+
effects: [
|
|
494
|
+
placeholderCompartment.reconfigure(placeholder(placeholderText)),
|
|
495
|
+
ariaLabelCompartment.reconfigure(
|
|
496
|
+
EditorView.contentAttributes.of({
|
|
497
|
+
'aria-label': ariaLabel,
|
|
498
|
+
'aria-multiline': 'true'
|
|
499
|
+
})
|
|
500
|
+
)
|
|
501
|
+
]
|
|
502
|
+
});
|
|
503
|
+
});
|
|
465
504
|
</script>
|
|
466
505
|
|
|
467
506
|
<div
|
|
@@ -481,7 +520,11 @@
|
|
|
481
520
|
|
|
482
521
|
<!-- Toolbar -->
|
|
483
522
|
{#if showToolbar && !disabled}
|
|
484
|
-
<div
|
|
523
|
+
<div
|
|
524
|
+
class="form-markdown-editor__toolbar"
|
|
525
|
+
role="toolbar"
|
|
526
|
+
aria-label={m().form.markdown.toolbar}
|
|
527
|
+
>
|
|
485
528
|
{#each toolbarActions as item}
|
|
486
529
|
{#if item === '|'}
|
|
487
530
|
<span class="form-markdown-editor__separator"></span>
|
|
@@ -514,10 +557,11 @@
|
|
|
514
557
|
|
|
515
558
|
<!-- Status bar -->
|
|
516
559
|
{#if showStatusBar}
|
|
560
|
+
{@const md = m().form.markdown}
|
|
517
561
|
<div class="form-markdown-editor__status">
|
|
518
|
-
<span>words: {wordCount}</span>
|
|
519
|
-
<span>lines: {lineCount}</span>
|
|
520
|
-
<span>characters: {charCount}</span>
|
|
562
|
+
<span>{md.words}: {wordCount}</span>
|
|
563
|
+
<span>{md.lines}: {lineCount}</span>
|
|
564
|
+
<span>{md.characters}: {charCount}</span>
|
|
521
565
|
</div>
|
|
522
566
|
{/if}
|
|
523
567
|
</div>
|
|
@@ -3,7 +3,7 @@ interface Props {
|
|
|
3
3
|
id: string;
|
|
4
4
|
/** Current value (markdown string) */
|
|
5
5
|
value: string;
|
|
6
|
-
/** Placeholder text shown when empty */
|
|
6
|
+
/** Placeholder text shown when empty. Falls back to `messages.form.markdown.placeholder`. */
|
|
7
7
|
placeholder?: string;
|
|
8
8
|
/** Whether the field is required */
|
|
9
9
|
required?: boolean;
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
import { createTemplateAutocomplete } from './templateAutocomplete.js';
|
|
47
47
|
import { getVariableSchema } from '../../services/variableService.js';
|
|
48
48
|
import { logger } from '../../utils/logger.js';
|
|
49
|
+
import { m } from '../../messages/index.js';
|
|
49
50
|
|
|
50
51
|
interface Props {
|
|
51
52
|
/** Field identifier */
|
|
@@ -506,7 +507,7 @@
|
|
|
506
507
|
class:form-template-editor__container--dark={darkTheme}
|
|
507
508
|
role="textbox"
|
|
508
509
|
aria-multiline="true"
|
|
509
|
-
aria-label=
|
|
510
|
+
aria-label={m().form.template.editor}
|
|
510
511
|
></div>
|
|
511
512
|
|
|
512
513
|
<!-- Loading banner (shown while fetching variables from API) -->
|
|
@@ -9,14 +9,20 @@
|
|
|
9
9
|
-->
|
|
10
10
|
|
|
11
11
|
<script lang="ts">
|
|
12
|
+
import { m, warnDeprecatedProp } from '../../messages/index.js';
|
|
13
|
+
|
|
12
14
|
interface Props {
|
|
13
15
|
/** Field identifier */
|
|
14
16
|
id: string;
|
|
15
17
|
/** Current value */
|
|
16
18
|
value: boolean;
|
|
17
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* @deprecated since v1.8 — use `messages.form.toggle.enabled`. Removed in v2.0.
|
|
21
|
+
*/
|
|
18
22
|
onLabel?: string;
|
|
19
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* @deprecated since v1.8 — use `messages.form.toggle.disabled`. Removed in v2.0.
|
|
25
|
+
*/
|
|
20
26
|
offLabel?: string;
|
|
21
27
|
/** Whether the field is disabled (read-only) */
|
|
22
28
|
disabled?: boolean;
|
|
@@ -29,13 +35,25 @@
|
|
|
29
35
|
let {
|
|
30
36
|
id,
|
|
31
37
|
value = false,
|
|
32
|
-
onLabel
|
|
33
|
-
offLabel
|
|
38
|
+
onLabel,
|
|
39
|
+
offLabel,
|
|
34
40
|
disabled = false,
|
|
35
41
|
ariaDescribedBy,
|
|
36
42
|
onChange
|
|
37
43
|
}: Props = $props();
|
|
38
44
|
|
|
45
|
+
// svelte-ignore state_referenced_locally — deprecation warns once per mount; later prop rebinds aren't relevant
|
|
46
|
+
if (onLabel !== undefined) {
|
|
47
|
+
warnDeprecatedProp('FormToggle', 'onLabel', 'messages.form.toggle.enabled');
|
|
48
|
+
}
|
|
49
|
+
// svelte-ignore state_referenced_locally
|
|
50
|
+
if (offLabel !== undefined) {
|
|
51
|
+
warnDeprecatedProp('FormToggle', 'offLabel', 'messages.form.toggle.disabled');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const resolvedOnLabel = $derived(onLabel ?? m().form.toggle.enabled);
|
|
55
|
+
const resolvedOffLabel = $derived(offLabel ?? m().form.toggle.disabled);
|
|
56
|
+
|
|
39
57
|
/**
|
|
40
58
|
* Handle toggle changes
|
|
41
59
|
*/
|
|
@@ -59,7 +77,7 @@
|
|
|
59
77
|
<span class="form-toggle__thumb"></span>
|
|
60
78
|
</span>
|
|
61
79
|
<span class="form-toggle__label">
|
|
62
|
-
{value ?
|
|
80
|
+
{value ? resolvedOnLabel : resolvedOffLabel}
|
|
63
81
|
</span>
|
|
64
82
|
</label>
|
|
65
83
|
|
|
@@ -3,9 +3,13 @@ interface Props {
|
|
|
3
3
|
id: string;
|
|
4
4
|
/** Current value */
|
|
5
5
|
value: boolean;
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* @deprecated since v1.8 — use `messages.form.toggle.enabled`. Removed in v2.0.
|
|
8
|
+
*/
|
|
7
9
|
onLabel?: string;
|
|
8
|
-
/**
|
|
10
|
+
/**
|
|
11
|
+
* @deprecated since v1.8 — use `messages.form.toggle.disabled`. Removed in v2.0.
|
|
12
|
+
*/
|
|
9
13
|
offLabel?: string;
|
|
10
14
|
/** Whether the field is disabled (read-only) */
|
|
11
15
|
disabled?: boolean;
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
<script lang="ts">
|
|
11
11
|
import Icon from '@iconify/svelte';
|
|
12
12
|
import type { ChoiceConfig, InterruptChoice } from '../../types/interrupt.js';
|
|
13
|
+
import { m } from '../../messages/index.js';
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Component props
|
|
@@ -41,6 +42,9 @@
|
|
|
41
42
|
onSubmit
|
|
42
43
|
}: Props = $props();
|
|
43
44
|
|
|
45
|
+
// Hoist the choice branch — counter, min, max, submit reads.
|
|
46
|
+
const t = $derived(m().interrupt.choice);
|
|
47
|
+
|
|
44
48
|
/** Local state for selected values */
|
|
45
49
|
let selectedValues = $state<Set<string>>(new Set());
|
|
46
50
|
|
|
@@ -165,12 +169,15 @@
|
|
|
165
169
|
{#if isMultiple && !isResolved}
|
|
166
170
|
<div class="choice-prompt__info">
|
|
167
171
|
<span>
|
|
168
|
-
{
|
|
172
|
+
{t.selectedCount({
|
|
173
|
+
n: selectedValues.size,
|
|
174
|
+
total: config.options.length
|
|
175
|
+
})}
|
|
169
176
|
{#if minSelections > 0}
|
|
170
|
-
(
|
|
177
|
+
{t.min({ n: minSelections })}
|
|
171
178
|
{/if}
|
|
172
179
|
{#if maxSelections < config.options.length}
|
|
173
|
-
(
|
|
180
|
+
{t.max({ n: maxSelections })}
|
|
174
181
|
{/if}
|
|
175
182
|
</span>
|
|
176
183
|
</div>
|
|
@@ -190,7 +197,7 @@
|
|
|
190
197
|
{:else}
|
|
191
198
|
<Icon icon="mdi:check" />
|
|
192
199
|
{/if}
|
|
193
|
-
<span>
|
|
200
|
+
<span>{t.submit}</span>
|
|
194
201
|
</button>
|
|
195
202
|
</div>
|
|
196
203
|
{/if}
|
|
@@ -200,7 +207,9 @@
|
|
|
200
207
|
<div class="choice-prompt__resolved-badge">
|
|
201
208
|
<Icon icon="mdi:check-circle" />
|
|
202
209
|
<span>
|
|
203
|
-
{resolvedByUserName
|
|
210
|
+
{resolvedByUserName
|
|
211
|
+
? m().interrupt.responseSubmittedBy({ name: resolvedByUserName })
|
|
212
|
+
: m().interrupt.responseSubmitted}
|
|
204
213
|
</span>
|
|
205
214
|
</div>
|
|
206
215
|
{/if}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
<script lang="ts">
|
|
11
11
|
import Icon from '@iconify/svelte';
|
|
12
12
|
import type { ConfirmationConfig } from '../../types/interrupt.js';
|
|
13
|
+
import { m } from '../../messages/index.js';
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Component props
|
|
@@ -44,11 +45,11 @@
|
|
|
44
45
|
onDecline
|
|
45
46
|
}: Props = $props();
|
|
46
47
|
|
|
47
|
-
/** Computed label for confirm button */
|
|
48
|
-
const confirmLabel = $derived(config.confirmLabel ??
|
|
48
|
+
/** Computed label for confirm button — config wins, falls back to messages tree. */
|
|
49
|
+
const confirmLabel = $derived(config.confirmLabel ?? m().interrupt.confirmation.yes);
|
|
49
50
|
|
|
50
|
-
/** Computed label for decline/cancel button */
|
|
51
|
-
const declineLabel = $derived(config.cancelLabel ??
|
|
51
|
+
/** Computed label for decline/cancel button — config wins, falls back to messages tree. */
|
|
52
|
+
const declineLabel = $derived(config.cancelLabel ?? m().interrupt.confirmation.no);
|
|
52
53
|
</script>
|
|
53
54
|
|
|
54
55
|
<div
|
|
@@ -119,7 +120,9 @@
|
|
|
119
120
|
<div class="confirmation-prompt__resolved-badge">
|
|
120
121
|
<Icon icon="mdi:check-circle" />
|
|
121
122
|
<span>
|
|
122
|
-
{resolvedByUserName
|
|
123
|
+
{resolvedByUserName
|
|
124
|
+
? m().interrupt.responseSubmittedBy({ name: resolvedByUserName })
|
|
125
|
+
: m().interrupt.responseSubmitted}
|
|
123
126
|
</span>
|
|
124
127
|
</div>
|
|
125
128
|
{/if}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import Icon from '@iconify/svelte';
|
|
12
12
|
import SchemaForm from '../SchemaForm.svelte';
|
|
13
13
|
import type { FormConfig } from '../../types/interrupt.js';
|
|
14
|
+
import { getMessages, m, mergeMessages, setMessages } from '../../messages/index.js';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Component props
|
|
@@ -49,6 +50,10 @@
|
|
|
49
50
|
/** Display values - either resolved or current form values */
|
|
50
51
|
const displayValues = $derived(isResolved ? (resolvedValue ?? {}) : formValues);
|
|
51
52
|
|
|
53
|
+
// Hoist the interrupt branch — six reads in the template, three of them
|
|
54
|
+
// inside `formatResolvedValue` which is called per `{#each schema.property}`.
|
|
55
|
+
const interrupt = $derived(m().interrupt);
|
|
56
|
+
|
|
52
57
|
/**
|
|
53
58
|
* Handle form value changes
|
|
54
59
|
*/
|
|
@@ -66,14 +71,30 @@
|
|
|
66
71
|
}
|
|
67
72
|
|
|
68
73
|
/**
|
|
69
|
-
* Format resolved value for display
|
|
74
|
+
* Format resolved value for display.
|
|
75
|
+
* Returns localized strings via the current messages tree.
|
|
70
76
|
*/
|
|
71
77
|
function formatResolvedValue(value: unknown): string {
|
|
72
|
-
if (value === null || value === undefined) return
|
|
73
|
-
if (typeof value === 'boolean') return value ?
|
|
78
|
+
if (value === null || value === undefined) return interrupt.form.empty;
|
|
79
|
+
if (typeof value === 'boolean') return value ? interrupt.form.yes : interrupt.form.no;
|
|
74
80
|
if (typeof value === 'object') return JSON.stringify(value, null, 2);
|
|
75
81
|
return String(value);
|
|
76
82
|
}
|
|
83
|
+
|
|
84
|
+
// Scope a messages override for the inner SchemaForm so its Save button reads
|
|
85
|
+
// the interrupt-specific submit label (e.g. "Submit"), and the cancel button
|
|
86
|
+
// remains empty — historical behavior that effectively hid it. Avoids passing
|
|
87
|
+
// deprecated `saveLabel` / `cancelLabel` props on SchemaForm.
|
|
88
|
+
// Merges over the parent's tree so consumer-supplied overrides higher up
|
|
89
|
+
// (e.g. translations from <FlowDrop messages={...} />) still apply.
|
|
90
|
+
const parentMessages = getMessages();
|
|
91
|
+
const scopedMessages = $derived.by(() => {
|
|
92
|
+
const base = parentMessages();
|
|
93
|
+
return mergeMessages(base, {
|
|
94
|
+
form: { schema: { save: base.interrupt.form.submit, cancel: '' } }
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
setMessages(() => scopedMessages);
|
|
77
98
|
</script>
|
|
78
99
|
|
|
79
100
|
<div
|
|
@@ -101,8 +122,6 @@
|
|
|
101
122
|
onChange={handleChange}
|
|
102
123
|
onSave={handleSave}
|
|
103
124
|
showActions={true}
|
|
104
|
-
saveLabel="Submit"
|
|
105
|
-
cancelLabel=""
|
|
106
125
|
loading={isSubmitting}
|
|
107
126
|
disabled={isResolved}
|
|
108
127
|
/>
|
|
@@ -110,7 +129,7 @@
|
|
|
110
129
|
{:else}
|
|
111
130
|
<!-- Resolved state: Show submitted values as read-only -->
|
|
112
131
|
<div class="form-prompt__resolved-values">
|
|
113
|
-
<h4 class="form-prompt__resolved-title">
|
|
132
|
+
<h4 class="form-prompt__resolved-title">{interrupt.form.submittedValuesTitle}</h4>
|
|
114
133
|
<div class="form-prompt__values-list">
|
|
115
134
|
{#each Object.entries(config.schema.properties ?? {}) as [key, field]}
|
|
116
135
|
{@const value = displayValues[key]}
|
|
@@ -131,7 +150,9 @@
|
|
|
131
150
|
<div class="form-prompt__resolved-badge">
|
|
132
151
|
<Icon icon="mdi:check-circle" />
|
|
133
152
|
<span>
|
|
134
|
-
{resolvedByUserName
|
|
153
|
+
{resolvedByUserName
|
|
154
|
+
? interrupt.responseSubmittedBy({ name: resolvedByUserName })
|
|
155
|
+
: interrupt.responseSubmitted}
|
|
135
156
|
</span>
|
|
136
157
|
</div>
|
|
137
158
|
{/if}
|