@flowdrop/flowdrop 1.15.0 → 2.0.0-beta.2
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/CHANGELOG.md +508 -0
- package/MIGRATION-2.0.md +629 -0
- package/README.md +23 -23
- package/dist/adapters/WorkflowAdapter.d.ts +1 -1
- package/dist/adapters/WorkflowAdapter.js +14 -8
- package/dist/adapters/agentspec/AgentSpecAdapter.js +7 -7
- package/dist/api/enhanced-client.js +6 -11
- package/dist/chat/batchFeedback.d.ts +39 -0
- package/dist/chat/batchFeedback.js +51 -0
- package/dist/commands/executor.js +15 -1
- package/dist/commands/storeIntegration.svelte.d.ts +4 -1
- package/dist/commands/storeIntegration.svelte.js +26 -21
- package/dist/commands/types.d.ts +2 -0
- package/dist/components/App.svelte +163 -192
- package/dist/components/App.svelte.d.ts +47 -8
- package/dist/components/ConfigForm.svelte +77 -49
- package/dist/components/ConfigModal.svelte +7 -2
- package/dist/components/ConnectionLine.svelte +4 -2
- package/dist/components/Navbar.svelte +61 -1
- package/dist/components/NodeSidebar.svelte +27 -45
- package/dist/components/NodeStatusOverlay.svelte +94 -6
- package/dist/components/NodeSwapPicker.svelte +10 -8
- package/dist/components/PipelineStatus.svelte +22 -68
- package/dist/components/PipelineStatus.svelte.d.ts +3 -0
- package/dist/components/PortCoordinateTracker.svelte +5 -6
- package/dist/components/SchemaForm.stories.svelte +1 -3
- package/dist/components/SchemaForm.svelte +22 -27
- package/dist/components/SchemaForm.svelte.d.ts +0 -8
- package/dist/components/SettingsModal.svelte +8 -3
- package/dist/components/SettingsPanel.svelte +20 -4
- package/dist/components/SwapMappingEditor.svelte +67 -49
- package/dist/components/SwapMappingEditor.svelte.d.ts +0 -2
- package/dist/components/UniversalNode.svelte +9 -7
- package/dist/components/WorkflowEditor.svelte +121 -111
- package/dist/components/WorkflowEditor.svelte.d.ts +21 -10
- package/dist/components/chat/AIChatPanel.svelte +98 -89
- package/dist/components/chat/AIChatPanel.svelte.d.ts +0 -4
- package/dist/components/chat/CommandPreview.svelte +2 -1
- package/dist/components/console/CommandConsole.svelte +7 -5
- package/dist/components/console/ConsoleAutocomplete.svelte +10 -11
- package/dist/components/console/ConsoleAutocomplete.svelte.d.ts +6 -0
- package/dist/components/console/ConsoleInput.svelte +15 -6
- package/dist/components/console/ConsoleOutput.svelte +2 -1
- package/dist/components/form/FormArray.svelte +5 -9
- package/dist/components/form/FormArray.svelte.d.ts +2 -1
- package/dist/components/form/FormAutocomplete.svelte +16 -15
- package/dist/components/form/FormField.svelte +4 -2
- package/dist/components/form/FormFieldLight.svelte +34 -3
- package/dist/components/form/FormFieldLight.svelte.d.ts +12 -0
- package/dist/components/form/FormMarkdownEditor.svelte +9 -4
- package/dist/components/form/FormRangeField.svelte +1 -0
- package/dist/components/form/FormTemplateEditor.svelte +11 -3
- package/dist/components/form/FormToggle.svelte +5 -12
- package/dist/components/form/FormToggle.svelte.d.ts +4 -2
- package/dist/components/form/FormUISchemaRenderer.svelte +3 -1
- package/dist/components/form/templateAutocomplete.js +1 -5
- package/dist/components/form/types.d.ts +1 -14
- package/dist/components/interrupt/FormPrompt.svelte +3 -2
- package/dist/components/interrupt/InterruptBubble.svelte +25 -17
- package/dist/components/interrupt/ReviewPrompt.svelte +10 -3
- package/dist/components/interrupt/TextInputPrompt.svelte +2 -1
- package/dist/components/layouts/MainLayout.svelte +20 -13
- package/dist/components/layouts/MainLayout.svelte.d.ts +4 -0
- package/dist/components/nodes/AtomNode.svelte +17 -5
- package/dist/components/nodes/GatewayNode.svelte +19 -10
- package/dist/components/nodes/IdeaNode.svelte +7 -0
- package/dist/components/nodes/SimpleNode.svelte +11 -6
- package/dist/components/nodes/SquareNode.svelte +15 -8
- package/dist/components/nodes/TerminalNode.svelte +9 -4
- package/dist/components/nodes/ToolNode.svelte +7 -1
- package/dist/components/nodes/WorkflowNode.svelte +16 -7
- package/dist/components/playground/ChatInput.svelte +11 -14
- package/dist/components/playground/ChatPanel.svelte +6 -49
- package/dist/components/playground/ChatPanel.svelte.d.ts +0 -14
- package/dist/components/playground/ControlPanel.svelte +134 -123
- package/dist/components/playground/ControlPanel.svelte.d.ts +3 -0
- package/dist/components/playground/ExecutionLogs.svelte +11 -9
- package/dist/components/playground/InputCollector.svelte +11 -9
- package/dist/components/playground/MessageStream.svelte +17 -23
- package/dist/components/playground/PipelineKanbanView.svelte +69 -8
- package/dist/components/playground/PipelineKanbanView.svelte.d.ts +2 -0
- package/dist/components/playground/PipelinePanel.svelte +31 -8
- package/dist/components/playground/PipelinePanel.svelte.d.ts +2 -0
- package/dist/components/playground/PipelineTableView.svelte +188 -44
- package/dist/components/playground/PipelineTableView.svelte.d.ts +2 -0
- package/dist/components/playground/Playground.svelte +154 -105
- package/dist/components/playground/Playground.svelte.d.ts +5 -0
- package/dist/components/playground/PlaygroundApp.svelte +11 -1
- package/dist/components/playground/PlaygroundApp.svelte.d.ts +6 -0
- package/dist/components/playground/PlaygroundModal.svelte +18 -3
- package/dist/components/playground/PlaygroundModal.svelte.d.ts +6 -0
- package/dist/components/playground/PlaygroundStudio.svelte +40 -32
- package/dist/components/playground/PlaygroundStudio.svelte.d.ts +6 -0
- package/dist/components/playground/SessionManager.svelte +9 -12
- package/dist/components/playground/pipelineViewUtils.svelte.d.ts +30 -1
- package/dist/components/playground/pipelineViewUtils.svelte.js +40 -3
- package/dist/config/endpoints.d.ts +23 -7
- package/dist/config/endpoints.js +30 -10
- package/dist/core/index.d.ts +5 -6
- package/dist/core/index.js +8 -12
- package/dist/display/index.d.ts +6 -3
- package/dist/display/index.js +7 -5
- package/dist/editor/index.d.ts +20 -21
- package/dist/editor/index.js +26 -36
- package/dist/form/code.d.ts +25 -15
- package/dist/form/code.js +44 -41
- package/dist/form/fieldRegistry.d.ts +17 -13
- package/dist/form/fieldRegistry.js +32 -12
- package/dist/form/full.d.ts +19 -14
- package/dist/form/full.js +26 -28
- package/dist/form/index.d.ts +3 -4
- package/dist/form/index.js +6 -5
- package/dist/form/markdown.d.ts +13 -8
- package/dist/form/markdown.js +22 -23
- package/dist/helpers/proximityConnect.d.ts +3 -2
- package/dist/helpers/proximityConnect.js +2 -5
- package/dist/helpers/workflowEditorHelper.d.ts +14 -5
- package/dist/helpers/workflowEditorHelper.js +28 -25
- package/dist/index.d.ts +28 -24
- package/dist/index.js +27 -50
- package/dist/messages/defaults.d.ts +2 -5
- package/dist/messages/defaults.js +3 -6
- package/dist/messages/index.d.ts +0 -1
- package/dist/messages/index.js +0 -1
- package/dist/mocks/app-forms.d.ts +6 -2
- package/dist/mocks/app-forms.js +11 -4
- package/dist/openapi/v1/openapi.yaml +3 -3
- package/dist/playground/index.d.ts +4 -5
- package/dist/playground/index.js +4 -32
- package/dist/playground/mount.d.ts +25 -0
- package/dist/playground/mount.js +50 -20
- package/dist/registry/{BaseRegistry.d.ts → BaseRegistry.svelte.d.ts} +22 -1
- package/dist/registry/{BaseRegistry.js → BaseRegistry.svelte.js} +37 -1
- package/dist/registry/builtinFormats.d.ts +9 -18
- package/dist/registry/builtinFormats.js +9 -39
- package/dist/registry/builtinNodeTypes.d.ts +53 -0
- package/dist/registry/builtinNodeTypes.js +67 -0
- package/dist/registry/builtinNodes.d.ts +2 -64
- package/dist/registry/builtinNodes.js +7 -103
- package/dist/registry/index.d.ts +3 -4
- package/dist/registry/index.js +4 -6
- package/dist/registry/nodeComponentRegistry.d.ts +182 -15
- package/dist/registry/nodeComponentRegistry.js +235 -17
- package/dist/registry/workflowFormatRegistry.d.ts +14 -9
- package/dist/registry/workflowFormatRegistry.js +24 -8
- package/dist/{schema → schemas}/index.d.ts +2 -2
- package/dist/{schema → schemas}/index.js +2 -2
- package/dist/schemas/v1/workflow.schema.json +3 -3
- package/dist/services/agentSpecExecutionService.d.ts +0 -2
- package/dist/services/agentSpecExecutionService.js +0 -3
- package/dist/services/apiVariableService.d.ts +2 -1
- package/dist/services/apiVariableService.js +16 -47
- package/dist/services/autoSaveService.d.ts +7 -0
- package/dist/services/autoSaveService.js +6 -4
- package/dist/services/categoriesApi.js +3 -6
- package/dist/services/chatService.d.ts +9 -4
- package/dist/services/chatService.js +23 -28
- package/dist/services/draftStorage.d.ts +129 -13
- package/dist/services/draftStorage.js +185 -37
- package/dist/services/dynamicSchemaService.d.ts +2 -1
- package/dist/services/dynamicSchemaService.js +5 -22
- package/dist/services/globalSave.d.ts +13 -12
- package/dist/services/globalSave.js +29 -51
- package/dist/services/historyService.d.ts +9 -3
- package/dist/services/historyService.js +9 -3
- package/dist/services/interruptService.d.ts +15 -9
- package/dist/services/interruptService.js +35 -37
- package/dist/services/nodeExecutionService.d.ts +18 -3
- package/dist/services/nodeExecutionService.js +71 -45
- package/dist/services/playgroundService.d.ts +16 -10
- package/dist/services/playgroundService.js +42 -43
- package/dist/services/portConfigApi.js +3 -6
- package/dist/services/settingsService.d.ts +9 -4
- package/dist/services/settingsService.js +23 -12
- package/dist/services/variableService.d.ts +2 -1
- package/dist/services/variableService.js +2 -2
- package/dist/services/workflowStorage.js +6 -6
- package/dist/stores/apiContext.d.ts +56 -0
- package/dist/stores/apiContext.js +80 -0
- package/dist/stores/categoriesStore.svelte.d.ts +28 -23
- package/dist/stores/categoriesStore.svelte.js +69 -64
- package/dist/stores/getInstance.svelte.d.ts +39 -0
- package/dist/stores/getInstance.svelte.js +65 -0
- package/dist/stores/historyStore.svelte.d.ts +77 -93
- package/dist/stores/historyStore.svelte.js +134 -160
- package/dist/stores/instanceContainer.svelte.d.ts +111 -0
- package/dist/stores/instanceContainer.svelte.js +114 -0
- package/dist/stores/interruptStore.svelte.d.ts +112 -82
- package/dist/stores/interruptStore.svelte.js +253 -226
- package/dist/stores/pipelinePanelStore.svelte.d.ts +27 -3
- package/dist/stores/pipelinePanelStore.svelte.js +61 -14
- package/dist/stores/playgroundStore.svelte.d.ts +169 -222
- package/dist/stores/playgroundStore.svelte.js +513 -580
- package/dist/stores/portCoordinateStore.svelte.d.ts +57 -51
- package/dist/stores/portCoordinateStore.svelte.js +109 -98
- package/dist/stores/settingsStore.svelte.d.ts +4 -1
- package/dist/stores/settingsStore.svelte.js +47 -12
- package/dist/stores/workflowStore.svelte.d.ts +178 -213
- package/dist/stores/workflowStore.svelte.js +449 -501
- package/dist/stories/EdgeDecorator.svelte +5 -2
- package/dist/stories/NodeDecorator.svelte +5 -3
- package/dist/svelte-app.d.ts +60 -10
- package/dist/svelte-app.js +159 -54
- package/dist/types/auth.d.ts +9 -51
- package/dist/types/auth.js +4 -54
- package/dist/types/events.d.ts +6 -3
- package/dist/types/index.d.ts +37 -5
- package/dist/types/index.js +0 -1
- package/dist/types/navbar.d.ts +7 -0
- package/dist/types/playground.d.ts +18 -3
- package/dist/types/settings.d.ts +13 -0
- package/dist/types/settings.js +1 -0
- package/dist/utils/colors.d.ts +47 -21
- package/dist/utils/colors.js +69 -68
- package/dist/utils/connections.d.ts +9 -15
- package/dist/utils/connections.js +13 -32
- package/dist/utils/duration.d.ts +13 -0
- package/dist/utils/duration.js +45 -0
- package/dist/utils/edgeStyling.js +9 -5
- package/dist/utils/fetchWithAuth.d.ts +36 -15
- package/dist/utils/fetchWithAuth.js +53 -23
- package/dist/utils/icons.d.ts +5 -2
- package/dist/utils/icons.js +6 -5
- package/dist/utils/nodeSwap.d.ts +6 -2
- package/dist/utils/nodeSwap.js +62 -126
- package/dist/utils/nodeTypes.d.ts +17 -8
- package/dist/utils/nodeTypes.js +27 -20
- package/dist/utils/performanceUtils.js +7 -0
- package/package.json +7 -5
- package/dist/messages/deprecation.d.ts +0 -20
- package/dist/messages/deprecation.js +0 -33
- package/dist/registry/plugin.d.ts +0 -215
- package/dist/registry/plugin.js +0 -249
- package/dist/services/api.d.ts +0 -129
- package/dist/services/api.js +0 -217
|
@@ -4,18 +4,23 @@
|
|
|
4
4
|
import type { UIAction } from '../../commands/types.js';
|
|
5
5
|
import type { EndpointConfig } from '../../config/endpoints.js';
|
|
6
6
|
import { chatService } from '../../services/chatService.js';
|
|
7
|
-
import {
|
|
7
|
+
import { getInstance } from '../../stores/getInstance.svelte.js';
|
|
8
8
|
import { getBehaviorSettings } from '../../stores/settingsStore.svelte.js';
|
|
9
9
|
import { extractCommands } from '../../chat/responseParser.js';
|
|
10
10
|
import { isMutatingCommand } from '../../chat/commandClassifier.js';
|
|
11
11
|
import { parseCommand } from '../../commands/parser.js';
|
|
12
12
|
import { executeCommand } from '../../commands/index.js';
|
|
13
13
|
import { createStoreCommandContext } from '../../commands/storeIntegration.svelte.js';
|
|
14
|
+
import {
|
|
15
|
+
buildRetryFeedback,
|
|
16
|
+
type BatchOutcome,
|
|
17
|
+
type ParseFailure
|
|
18
|
+
} from '../../chat/batchFeedback.js';
|
|
14
19
|
import CommandPreview from './CommandPreview.svelte';
|
|
15
20
|
import MarkdownDisplay from '../MarkdownDisplay.svelte';
|
|
16
21
|
import { tick } from 'svelte';
|
|
17
22
|
import Icon from '@iconify/svelte';
|
|
18
|
-
import { m
|
|
23
|
+
import { m } from '../../messages/index.js';
|
|
19
24
|
|
|
20
25
|
// =========================================================================
|
|
21
26
|
// Internal Display Message Type
|
|
@@ -36,26 +41,17 @@
|
|
|
36
41
|
nodeTypes: NodeMetadata[];
|
|
37
42
|
workflowId?: string;
|
|
38
43
|
onUIAction?: (action: UIAction) => void;
|
|
39
|
-
/**
|
|
40
|
-
* @deprecated since v1.8 — use `messages.chat.placeholder`. Removed in v2.0.
|
|
41
|
-
*/
|
|
42
|
-
placeholder?: string;
|
|
43
44
|
endpointConfig?: EndpointConfig | null;
|
|
44
45
|
}
|
|
45
46
|
|
|
46
|
-
let { nodeTypes, workflowId, onUIAction,
|
|
47
|
+
let { nodeTypes, workflowId, onUIAction, endpointConfig }: Props = $props();
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
if (placeholder !== undefined) {
|
|
50
|
-
warnDeprecatedProp('AIChatPanel', 'placeholder', 'messages.chat.placeholder');
|
|
51
|
-
}
|
|
49
|
+
const fd = getInstance();
|
|
52
50
|
|
|
53
51
|
// Hoist the chat branch — read in placeholder, header, three welcome states,
|
|
54
52
|
// auto-retry banner, and the send button aria-label.
|
|
55
53
|
const t = $derived(m().chat);
|
|
56
54
|
|
|
57
|
-
const resolvedPlaceholder = $derived(placeholder ?? t.placeholder);
|
|
58
|
-
|
|
59
55
|
// =========================================================================
|
|
60
56
|
// State
|
|
61
57
|
// =========================================================================
|
|
@@ -84,8 +80,8 @@
|
|
|
84
80
|
// =========================================================================
|
|
85
81
|
|
|
86
82
|
$effect(() => {
|
|
87
|
-
|
|
88
|
-
isLoading;
|
|
83
|
+
// Read scroll-relevant state so the effect re-runs when messages change
|
|
84
|
+
const _deps = [displayMessages.length, isLoading];
|
|
89
85
|
tick().then(() => {
|
|
90
86
|
if (messagesElement) {
|
|
91
87
|
messagesElement.scrollTop = messagesElement.scrollHeight;
|
|
@@ -103,7 +99,7 @@
|
|
|
103
99
|
}
|
|
104
100
|
|
|
105
101
|
function getWorkflowState(): unknown {
|
|
106
|
-
const workflow =
|
|
102
|
+
const workflow = fd.workflow.current;
|
|
107
103
|
if (!workflow) return null;
|
|
108
104
|
return {
|
|
109
105
|
nodes: workflow.nodes.map((n) => ({
|
|
@@ -123,7 +119,7 @@
|
|
|
123
119
|
}
|
|
124
120
|
|
|
125
121
|
function getCommandContext() {
|
|
126
|
-
return createStoreCommandContext(nodeTypes, onUIAction);
|
|
122
|
+
return createStoreCommandContext(nodeTypes, onUIAction, fd);
|
|
127
123
|
}
|
|
128
124
|
|
|
129
125
|
// =========================================================================
|
|
@@ -186,28 +182,21 @@
|
|
|
186
182
|
return msg;
|
|
187
183
|
}
|
|
188
184
|
|
|
189
|
-
/**
|
|
185
|
+
/**
|
|
186
|
+
* Execute the parseable commands in a batch one by one with progressive UI
|
|
187
|
+
* feedback.
|
|
188
|
+
*
|
|
189
|
+
* Commands that failed to parse are *isolated* — they stay marked as errors
|
|
190
|
+
* and are skipped, but they no longer abort the whole batch, so the healthy
|
|
191
|
+
* subset still applies (matching the long-standing "applied to a certain
|
|
192
|
+
* extent" expectation). After execution, if anything failed to parse or
|
|
193
|
+
* execute and auto-retry is enabled, specific feedback is sent back so the
|
|
194
|
+
* assistant can resend corrected commands.
|
|
195
|
+
*/
|
|
190
196
|
async function handleApproveCommands(messageIndex: number) {
|
|
191
197
|
const msg = displayMessages[messageIndex];
|
|
192
198
|
if (!msg?.commandPreview) return;
|
|
193
199
|
|
|
194
|
-
// Refuse to run the batch if any command failed to parse. A corrupted batch
|
|
195
|
-
// (e.g. multiline set without """) causes partial execution and can hang the
|
|
196
|
-
// app — rejecting the whole batch is safer than executing the healthy subset.
|
|
197
|
-
const parseErrorCount = msg.commandPreview.filter((c) => c.status === 'error').length;
|
|
198
|
-
if (parseErrorCount > 0) {
|
|
199
|
-
for (const cmd of msg.commandPreview) {
|
|
200
|
-
if (cmd.status === 'pending') {
|
|
201
|
-
cmd.status = 'error';
|
|
202
|
-
cmd.result = 'Batch refused: fix parse errors before executing';
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
appendErrorToHistory(
|
|
206
|
-
`Batch was not executed: ${parseErrorCount} command${parseErrorCount > 1 ? 's have' : ' has'} parse errors. Dismiss this batch and ask the AI to provide corrected commands.`
|
|
207
|
-
);
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
200
|
const context = getCommandContext();
|
|
212
201
|
if (!context) {
|
|
213
202
|
for (const cmd of msg.commandPreview) {
|
|
@@ -220,7 +209,13 @@
|
|
|
220
209
|
return;
|
|
221
210
|
}
|
|
222
211
|
|
|
223
|
-
//
|
|
212
|
+
// Commands that failed to parse (in processResponse) are isolated, not run.
|
|
213
|
+
// Capture their raw text + reason so we can feed specific corrections back.
|
|
214
|
+
const parseErrors: ParseFailure[] = msg.commandPreview
|
|
215
|
+
.filter((c) => c.status === 'error')
|
|
216
|
+
.map((c) => ({ raw: c.raw, error: c.result ?? 'Parse error' }));
|
|
217
|
+
|
|
218
|
+
// Re-parse the pending (parseable) commands, preserving order.
|
|
224
219
|
const pendingItems = msg.commandPreview.filter((c) => c.status === 'pending');
|
|
225
220
|
const parsedCommands: {
|
|
226
221
|
item: CommandPreviewItem;
|
|
@@ -230,23 +225,37 @@
|
|
|
230
225
|
for (const item of pendingItems) {
|
|
231
226
|
const parsed = parseCommand(item.raw);
|
|
232
227
|
if (!parsed.ok) {
|
|
228
|
+
// Re-parse disagreed with processResponse — isolate it like a parse error.
|
|
233
229
|
item.status = 'error';
|
|
234
230
|
item.result = parsed.error;
|
|
235
|
-
|
|
236
|
-
|
|
231
|
+
parseErrors.push({ raw: item.raw, error: parsed.error });
|
|
232
|
+
continue;
|
|
237
233
|
}
|
|
238
234
|
parsedCommands.push({ item, command: parsed.command });
|
|
239
235
|
}
|
|
240
236
|
|
|
241
|
-
// Execute commands one by one inside a single transaction.
|
|
242
|
-
// A 100ms pause between commands lets the canvas visibly update at each step.
|
|
243
237
|
const totalCount = parsedCommands.length;
|
|
238
|
+
|
|
239
|
+
// Nothing parseable to run — feed the parse errors straight back so the
|
|
240
|
+
// assistant can resend a corrected batch.
|
|
241
|
+
if (totalCount === 0) {
|
|
242
|
+
await resolveBatchOutcome({
|
|
243
|
+
completedCount: 0,
|
|
244
|
+
executionError: undefined,
|
|
245
|
+
rolledBack: false,
|
|
246
|
+
parseErrors
|
|
247
|
+
});
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Execute the healthy subset one by one inside a single transaction.
|
|
252
|
+
// A 100ms pause between commands lets the canvas visibly update at each step.
|
|
244
253
|
context.dispatch.startTransaction(
|
|
245
254
|
totalCount === 1 ? 'batch: 1 command' : `batch: ${totalCount} commands`
|
|
246
255
|
);
|
|
247
256
|
|
|
248
257
|
let completedCount = 0;
|
|
249
|
-
let
|
|
258
|
+
let executionError: string | undefined;
|
|
250
259
|
|
|
251
260
|
try {
|
|
252
261
|
for (let i = 0; i < parsedCommands.length; i++) {
|
|
@@ -258,7 +267,7 @@
|
|
|
258
267
|
if (!result.ok) {
|
|
259
268
|
item.status = 'error';
|
|
260
269
|
item.result = result.error;
|
|
261
|
-
|
|
270
|
+
executionError = result.error;
|
|
262
271
|
context.dispatch.cancelTransaction();
|
|
263
272
|
break;
|
|
264
273
|
}
|
|
@@ -283,24 +292,48 @@
|
|
|
283
292
|
return;
|
|
284
293
|
}
|
|
285
294
|
|
|
286
|
-
if (
|
|
287
|
-
|
|
295
|
+
if (executionError !== undefined) {
|
|
296
|
+
// An executed command failed — the whole batch is rolled back (atomic,
|
|
297
|
+
// long-standing behaviour). Retry feedback covers both the execution
|
|
298
|
+
// failure and any parse-skipped commands.
|
|
299
|
+
await resolveBatchOutcome({
|
|
300
|
+
completedCount,
|
|
301
|
+
executionError,
|
|
302
|
+
rolledBack: true,
|
|
303
|
+
parseErrors
|
|
304
|
+
});
|
|
288
305
|
return;
|
|
289
306
|
}
|
|
290
307
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
308
|
+
// Every parseable command applied — commit so the subset persists.
|
|
309
|
+
context.dispatch.commitTransaction();
|
|
310
|
+
|
|
311
|
+
// If some commands couldn't be parsed, ask the assistant to resend them.
|
|
312
|
+
if (parseErrors.length > 0) {
|
|
313
|
+
await resolveBatchOutcome({
|
|
294
314
|
completedCount,
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
);
|
|
299
|
-
|
|
315
|
+
executionError: undefined,
|
|
316
|
+
rolledBack: false,
|
|
317
|
+
parseErrors
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* After a batch finishes with failures (parse and/or execution), either
|
|
324
|
+
* auto-retry with specific feedback so the assistant can self-correct, or —
|
|
325
|
+
* when auto-retry is off/exhausted — surface the same summary in the log.
|
|
326
|
+
*/
|
|
327
|
+
async function resolveBatchOutcome(outcome: BatchOutcome) {
|
|
328
|
+
const feedback = buildRetryFeedback(outcome);
|
|
329
|
+
const canRetry =
|
|
330
|
+
getBehaviorSettings().chatAutoRetry && !!workflowId && autoRetryCount < MAX_AUTO_RETRIES;
|
|
331
|
+
|
|
332
|
+
if (canRetry) {
|
|
333
|
+
autoRetryCount++;
|
|
334
|
+
await sendMessageInternal(feedback, autoRetryCount);
|
|
300
335
|
} else {
|
|
301
|
-
appendErrorToHistory(
|
|
302
|
-
`Command execution failed at command ${completedCount + 1}/${totalCount}: ${batchError}`
|
|
303
|
-
);
|
|
336
|
+
appendErrorToHistory(feedback);
|
|
304
337
|
}
|
|
305
338
|
}
|
|
306
339
|
|
|
@@ -317,36 +350,6 @@
|
|
|
317
350
|
});
|
|
318
351
|
}
|
|
319
352
|
|
|
320
|
-
/** Build a structured error report from a failed batch for the LLM */
|
|
321
|
-
function buildBatchErrorMessage(
|
|
322
|
-
completedCount: number,
|
|
323
|
-
totalCount: number,
|
|
324
|
-
error: string,
|
|
325
|
-
items: CommandPreviewItem[]
|
|
326
|
-
): string {
|
|
327
|
-
const lines: string[] = [
|
|
328
|
-
`Batch execution failed at command ${completedCount + 1}/${totalCount}: ${error}`
|
|
329
|
-
];
|
|
330
|
-
|
|
331
|
-
if (completedCount > 0) {
|
|
332
|
-
lines.push('\nCommands that succeeded (rolled back):');
|
|
333
|
-
for (let i = 0; i < completedCount; i++) {
|
|
334
|
-
lines.push(` ${i + 1}. ${items[i].raw}`);
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
lines.push('\nFailed command:');
|
|
339
|
-
lines.push(` ${items[completedCount]?.raw ?? '(unknown)'}`);
|
|
340
|
-
|
|
341
|
-
const remaining = totalCount - completedCount - 1;
|
|
342
|
-
if (remaining > 0) {
|
|
343
|
-
lines.push(`\n${remaining} command(s) were skipped.`);
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
lines.push('\nPlease provide corrected commands to achieve the same goal.');
|
|
347
|
-
return lines.join('\n');
|
|
348
|
-
}
|
|
349
|
-
|
|
350
353
|
// =========================================================================
|
|
351
354
|
// Message Handling
|
|
352
355
|
// =========================================================================
|
|
@@ -366,7 +369,12 @@
|
|
|
366
369
|
history: history.slice(0, -1) // all except current message
|
|
367
370
|
};
|
|
368
371
|
|
|
369
|
-
const response = await chatService.sendMessage(
|
|
372
|
+
const response = await chatService.sendMessage(
|
|
373
|
+
fd.api.config,
|
|
374
|
+
workflowId,
|
|
375
|
+
request,
|
|
376
|
+
fd.api.authProvider
|
|
377
|
+
);
|
|
370
378
|
const displayMsg = processResponse(response.content);
|
|
371
379
|
displayMessages.push(displayMsg);
|
|
372
380
|
} catch (err) {
|
|
@@ -419,7 +427,8 @@
|
|
|
419
427
|
<span>{t.helpBuild}</span>
|
|
420
428
|
</div>
|
|
421
429
|
{/if}
|
|
422
|
-
|
|
430
|
+
<!-- Append-only chat log without stable IDs — index is the identity -->
|
|
431
|
+
{#each displayMessages as message, msgIndex (msgIndex)}
|
|
423
432
|
{#if message.retryAttempt !== undefined}
|
|
424
433
|
<div
|
|
425
434
|
class="ai-chat-panel__retry-notice"
|
|
@@ -440,7 +449,7 @@
|
|
|
440
449
|
{/if}
|
|
441
450
|
{#if message.readOnlyResults && message.readOnlyResults.length > 0}
|
|
442
451
|
<div class="ai-chat-panel__readonly-results">
|
|
443
|
-
{#each message.readOnlyResults as result}
|
|
452
|
+
{#each message.readOnlyResults as result, i (i)}
|
|
444
453
|
<pre class="ai-chat-panel__readonly-result">{result}</pre>
|
|
445
454
|
{/each}
|
|
446
455
|
</div>
|
|
@@ -475,7 +484,7 @@
|
|
|
475
484
|
bind:value={inputValue}
|
|
476
485
|
onkeydown={handleKeydown}
|
|
477
486
|
class="ai-chat-panel__input"
|
|
478
|
-
placeholder={
|
|
487
|
+
placeholder={t.placeholder}
|
|
479
488
|
rows="1"
|
|
480
489
|
disabled={isLoading}
|
|
481
490
|
></textarea>
|
|
@@ -5,10 +5,6 @@ interface Props {
|
|
|
5
5
|
nodeTypes: NodeMetadata[];
|
|
6
6
|
workflowId?: string;
|
|
7
7
|
onUIAction?: (action: UIAction) => void;
|
|
8
|
-
/**
|
|
9
|
-
* @deprecated since v1.8 — use `messages.chat.placeholder`. Removed in v2.0.
|
|
10
|
-
*/
|
|
11
|
-
placeholder?: string;
|
|
12
8
|
endpointConfig?: EndpointConfig | null;
|
|
13
9
|
}
|
|
14
10
|
declare const AIChatPanel: import("svelte").Component<Props, {}, "">;
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
|
|
33
33
|
<div class="command-preview" role="region" aria-label={t.ariaLabel}>
|
|
34
34
|
<div class="command-preview__list">
|
|
35
|
-
|
|
35
|
+
<!-- Fixed positional batch (raw strings may repeat) — index is the identity -->
|
|
36
|
+
{#each commands as command, i (i)}
|
|
36
37
|
<div class="command-preview__item command-preview__item--{command.status}">
|
|
37
38
|
<span class="command-preview__status">
|
|
38
39
|
{#if command.status === 'pending'}
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
type HelpResultData
|
|
21
21
|
} from '../../commands/index.js';
|
|
22
22
|
import { createStoreCommandContext } from '../../commands/storeIntegration.svelte.js';
|
|
23
|
+
import { getInstance } from '../../stores/getInstance.svelte.js';
|
|
23
24
|
import { updateSettings, getUiSettings } from '../../stores/settingsStore.svelte.js';
|
|
24
25
|
import ConsoleInput from './ConsoleInput.svelte';
|
|
25
26
|
import ConsoleOutput, { type ConsoleEntry } from './ConsoleOutput.svelte';
|
|
@@ -41,13 +42,14 @@
|
|
|
41
42
|
|
|
42
43
|
let { nodeTypes, onUIAction }: Props = $props();
|
|
43
44
|
|
|
45
|
+
const fd = getInstance();
|
|
46
|
+
|
|
44
47
|
let outputEntries: ConsoleEntry[] = $state([]);
|
|
45
|
-
let commandContext: CommandContext | null = $state(null);
|
|
46
48
|
|
|
47
|
-
//
|
|
48
|
-
$
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
// Recreated whenever nodeTypes changes; null while no workflow is loaded
|
|
50
|
+
const commandContext: CommandContext | null = $derived(
|
|
51
|
+
createStoreCommandContext(nodeTypes, onUIAction, fd)
|
|
52
|
+
);
|
|
51
53
|
|
|
52
54
|
/**
|
|
53
55
|
* Attempts to format CommandResult data into a rich display string.
|
|
@@ -6,8 +6,6 @@
|
|
|
6
6
|
-->
|
|
7
7
|
|
|
8
8
|
<script lang="ts">
|
|
9
|
-
import { tick } from 'svelte';
|
|
10
|
-
|
|
11
9
|
export interface Suggestion {
|
|
12
10
|
/** The text to insert into the input */
|
|
13
11
|
value: string;
|
|
@@ -26,9 +24,15 @@
|
|
|
26
24
|
selectedIndex: number;
|
|
27
25
|
/** Called when a suggestion is accepted */
|
|
28
26
|
onAccept: (suggestion: Suggestion) => void;
|
|
27
|
+
/**
|
|
28
|
+
* Listbox element id — supplied by the owning combobox (ConsoleInput) so
|
|
29
|
+
* its aria-controls/aria-activedescendant match, and unique per instance
|
|
30
|
+
* so two FlowDrop editors on one page don't render colliding DOM ids.
|
|
31
|
+
*/
|
|
32
|
+
listboxId: string;
|
|
29
33
|
}
|
|
30
34
|
|
|
31
|
-
let { suggestions, visible, selectedIndex, onAccept }: Props = $props();
|
|
35
|
+
let { suggestions, visible, selectedIndex, onAccept, listboxId }: Props = $props();
|
|
32
36
|
|
|
33
37
|
let listElement: HTMLDivElement | undefined = $state();
|
|
34
38
|
|
|
@@ -43,18 +47,13 @@
|
|
|
43
47
|
</script>
|
|
44
48
|
|
|
45
49
|
{#if visible && suggestions.length > 0}
|
|
46
|
-
<div
|
|
47
|
-
|
|
48
|
-
role="listbox"
|
|
49
|
-
id="console-autocomplete-listbox"
|
|
50
|
-
bind:this={listElement}
|
|
51
|
-
>
|
|
52
|
-
{#each suggestions as suggestion, i}
|
|
50
|
+
<div class="console-autocomplete" role="listbox" id={listboxId} bind:this={listElement}>
|
|
51
|
+
{#each suggestions as suggestion, i (suggestion.value)}
|
|
53
52
|
<div
|
|
54
53
|
class="console-autocomplete__item"
|
|
55
54
|
class:console-autocomplete__item--selected={i === selectedIndex}
|
|
56
55
|
role="option"
|
|
57
|
-
id="
|
|
56
|
+
id="{listboxId}-option-{i}"
|
|
58
57
|
tabindex="-1"
|
|
59
58
|
aria-selected={i === selectedIndex}
|
|
60
59
|
onmousedown={(e: MouseEvent) => {
|
|
@@ -15,6 +15,12 @@ interface Props {
|
|
|
15
15
|
selectedIndex: number;
|
|
16
16
|
/** Called when a suggestion is accepted */
|
|
17
17
|
onAccept: (suggestion: Suggestion) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Listbox element id — supplied by the owning combobox (ConsoleInput) so
|
|
20
|
+
* its aria-controls/aria-activedescendant match, and unique per instance
|
|
21
|
+
* so two FlowDrop editors on one page don't render colliding DOM ids.
|
|
22
|
+
*/
|
|
23
|
+
listboxId: string;
|
|
18
24
|
}
|
|
19
25
|
declare const ConsoleAutocomplete: import("svelte").Component<Props, {}, "">;
|
|
20
26
|
type ConsoleAutocomplete = ReturnType<typeof ConsoleAutocomplete>;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
<script lang="ts">
|
|
9
9
|
import type { NodeMetadata } from '../../types/index.js';
|
|
10
|
-
import {
|
|
10
|
+
import { getInstance } from '../../stores/getInstance.svelte.js';
|
|
11
11
|
import { toShortId, resolveNode } from '../../commands/index.js';
|
|
12
12
|
import ConsoleAutocomplete, { type Suggestion } from './ConsoleAutocomplete.svelte';
|
|
13
13
|
|
|
@@ -26,6 +26,14 @@
|
|
|
26
26
|
|
|
27
27
|
let { open, nodeTypes = [], onSubmit, onBatchSubmit, onClose }: Props = $props();
|
|
28
28
|
|
|
29
|
+
const fd = getInstance();
|
|
30
|
+
|
|
31
|
+
// Unique per component instance so two FlowDrop editors on one page
|
|
32
|
+
// don't render colliding DOM ids; shared with the autocomplete listbox so
|
|
33
|
+
// aria-controls/aria-activedescendant stay consistent.
|
|
34
|
+
const uid = $props.id();
|
|
35
|
+
const listboxId = `${uid}-console-autocomplete-listbox`;
|
|
36
|
+
|
|
29
37
|
let inputValue = $state('');
|
|
30
38
|
let inputElement: HTMLInputElement | undefined = $state();
|
|
31
39
|
|
|
@@ -94,7 +102,7 @@
|
|
|
94
102
|
* Returns suggestions with short IDs and labels.
|
|
95
103
|
*/
|
|
96
104
|
function getWorkflowNodeSuggestions(prefix: string): Suggestion[] {
|
|
97
|
-
const workflow =
|
|
105
|
+
const workflow = fd.workflow.current;
|
|
98
106
|
if (!workflow) return [];
|
|
99
107
|
|
|
100
108
|
const lowerPrefix = prefix.toLowerCase();
|
|
@@ -207,7 +215,7 @@
|
|
|
207
215
|
partial: string,
|
|
208
216
|
filter: 'input' | 'output' | 'all'
|
|
209
217
|
): Suggestion[] {
|
|
210
|
-
const workflow =
|
|
218
|
+
const workflow = fd.workflow.current;
|
|
211
219
|
if (!workflow) return [];
|
|
212
220
|
|
|
213
221
|
const node = resolveNode(nodeId, workflow.nodes);
|
|
@@ -256,7 +264,7 @@
|
|
|
256
264
|
* Get config key suggestions for a resolved node, filtered by prefix.
|
|
257
265
|
*/
|
|
258
266
|
function getConfigKeySuggestions(nodeId: string, partial: string): Suggestion[] {
|
|
259
|
-
const workflow =
|
|
267
|
+
const workflow = fd.workflow.current;
|
|
260
268
|
if (!workflow) return [];
|
|
261
269
|
|
|
262
270
|
const node = resolveNode(nodeId, workflow.nodes);
|
|
@@ -657,6 +665,7 @@
|
|
|
657
665
|
visible={acVisible}
|
|
658
666
|
selectedIndex={acSelectedIndex}
|
|
659
667
|
onAccept={acceptSuggestion}
|
|
668
|
+
{listboxId}
|
|
660
669
|
/>
|
|
661
670
|
<input
|
|
662
671
|
bind:this={inputElement}
|
|
@@ -668,9 +677,9 @@
|
|
|
668
677
|
autocomplete="off"
|
|
669
678
|
role="combobox"
|
|
670
679
|
aria-expanded={acVisible}
|
|
671
|
-
aria-controls=
|
|
680
|
+
aria-controls={listboxId}
|
|
672
681
|
aria-activedescendant={acVisible && acSuggestions.length > 0
|
|
673
|
-
?
|
|
682
|
+
? `${listboxId}-option-${acSelectedIndex}`
|
|
674
683
|
: undefined}
|
|
675
684
|
onkeydown={handleKeydown}
|
|
676
685
|
oninput={handleInput}
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
</script>
|
|
34
34
|
|
|
35
35
|
<div class="console-output" bind:this={outputElement} role="log" aria-live="polite">
|
|
36
|
-
|
|
36
|
+
<!-- Append-only log without stable IDs — index is the identity -->
|
|
37
|
+
{#each entries as entry, i (i)}
|
|
37
38
|
<div class="console-output__entry console-output__entry--{entry.type}">
|
|
38
39
|
{#if entry.type === 'input'}
|
|
39
40
|
<span class="console-output__prefix">></span>
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
<script lang="ts">
|
|
21
21
|
import Icon from '@iconify/svelte';
|
|
22
22
|
import type { FieldSchema } from './types.js';
|
|
23
|
-
import { m
|
|
23
|
+
import { m } from '../../messages/index.js';
|
|
24
24
|
|
|
25
25
|
interface Props {
|
|
26
26
|
/** Field identifier */
|
|
@@ -34,7 +34,8 @@
|
|
|
34
34
|
/** Maximum number of items allowed */
|
|
35
35
|
maxItems?: number;
|
|
36
36
|
/**
|
|
37
|
-
*
|
|
37
|
+
* Per-instance label for the add button (e.g. "Add Header" derived from
|
|
38
|
+
* the item schema title). Falls back to the global `messages.form.array.add`.
|
|
38
39
|
*/
|
|
39
40
|
addLabel?: string;
|
|
40
41
|
/** Whether the field is disabled */
|
|
@@ -54,11 +55,6 @@
|
|
|
54
55
|
onChange
|
|
55
56
|
}: Props = $props();
|
|
56
57
|
|
|
57
|
-
// svelte-ignore state_referenced_locally — deprecation warns once per mount; later prop rebinds aren't relevant
|
|
58
|
-
if (addLabel !== undefined) {
|
|
59
|
-
warnDeprecatedProp('FormArray', 'addLabel', 'messages.form.array.add');
|
|
60
|
-
}
|
|
61
|
-
|
|
62
58
|
// Hoist the array branch — every {#each} iteration would otherwise re-walk
|
|
63
59
|
// `m().form.array.*` for ~6 keys per item. One getter call instead of N×6.
|
|
64
60
|
const t = $derived(m().form.array);
|
|
@@ -390,7 +386,7 @@
|
|
|
390
386
|
onchange={(e) => updateItem(index, e.currentTarget.value)}
|
|
391
387
|
{disabled}
|
|
392
388
|
>
|
|
393
|
-
{#each itemSchema.enum as option}
|
|
389
|
+
{#each itemSchema.enum as option (option)}
|
|
394
390
|
<option value={String(option)}>{String(option)}</option>
|
|
395
391
|
{/each}
|
|
396
392
|
</select>
|
|
@@ -437,7 +433,7 @@
|
|
|
437
433
|
updateObjectProperty(index, propKey, e.currentTarget.value)}
|
|
438
434
|
{disabled}
|
|
439
435
|
>
|
|
440
|
-
{#each propFieldSchema.enum as option}
|
|
436
|
+
{#each propFieldSchema.enum as option (option)}
|
|
441
437
|
<option value={String(option)}>{String(option)}</option>
|
|
442
438
|
{/each}
|
|
443
439
|
</select>
|
|
@@ -11,7 +11,8 @@ interface Props {
|
|
|
11
11
|
/** Maximum number of items allowed */
|
|
12
12
|
maxItems?: number;
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
14
|
+
* Per-instance label for the add button (e.g. "Add Header" derived from
|
|
15
|
+
* the item schema title). Falls back to the global `messages.form.array.add`.
|
|
15
16
|
*/
|
|
16
17
|
addLabel?: string;
|
|
17
18
|
/** Whether the field is disabled */
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
import Icon from '@iconify/svelte';
|
|
26
26
|
import type { AutocompleteConfig, AuthProvider } from '../../types/index.js';
|
|
27
27
|
import type { FieldOption } from './types.js';
|
|
28
|
-
import {
|
|
28
|
+
import { authenticatedFetch } from '../../utils/fetchWithAuth.js';
|
|
29
29
|
import { logger } from '../../utils/logger.js';
|
|
30
30
|
import { m } from '../../messages/index.js';
|
|
31
31
|
|
|
@@ -121,7 +121,8 @@
|
|
|
121
121
|
// Stable fingerprint — any change triggers selection clearing.
|
|
122
122
|
// JSON.stringify gives a canonical string without null-byte ambiguity.
|
|
123
123
|
const depFingerprint = $derived(JSON.stringify(depParamValues));
|
|
124
|
-
//
|
|
124
|
+
// intentional initial snapshot; the effect below tracks subsequent changes
|
|
125
|
+
// svelte-ignore state_referenced_locally
|
|
125
126
|
let prevDepFingerprint = depFingerprint;
|
|
126
127
|
|
|
127
128
|
$effect(() => {
|
|
@@ -135,9 +136,9 @@
|
|
|
135
136
|
});
|
|
136
137
|
|
|
137
138
|
// Generate unique IDs for accessibility
|
|
138
|
-
//
|
|
139
|
-
const listboxId = `${id}-listbox`;
|
|
139
|
+
// id prop never changes
|
|
140
140
|
// svelte-ignore state_referenced_locally
|
|
141
|
+
const listboxId = `${id}-listbox`;
|
|
141
142
|
const getOptionId = (index: number): string => `${id}-option-${index}`;
|
|
142
143
|
|
|
143
144
|
/**
|
|
@@ -244,16 +245,15 @@
|
|
|
244
245
|
|
|
245
246
|
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
246
247
|
try {
|
|
247
|
-
// Build headers with authentication (call getter to get current value)
|
|
248
|
-
const headers = await buildFetchHeaders(getAuthProvider?.());
|
|
249
|
-
|
|
250
248
|
timeoutId = setTimeout(() => controller.abort(), 5000);
|
|
251
249
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
250
|
+
// authenticatedFetch merges auth headers (call getter for current value)
|
|
251
|
+
// and applies the 401/403 lifecycle consistently with the rest of the lib.
|
|
252
|
+
const response = await authenticatedFetch(
|
|
253
|
+
buildUrl(query),
|
|
254
|
+
{ method: 'GET', signal: controller.signal },
|
|
255
|
+
{ authProvider: getAuthProvider?.() }
|
|
256
|
+
);
|
|
257
257
|
|
|
258
258
|
if (!response.ok) {
|
|
259
259
|
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
@@ -639,7 +639,7 @@
|
|
|
639
639
|
class:form-autocomplete--has-value={selectedValues.length > 0}
|
|
640
640
|
>
|
|
641
641
|
<!-- Main input container styled like a textfield/textarea -->
|
|
642
|
-
<!--
|
|
642
|
+
<!-- role="presentation"; keyboard interaction is handled by the <input> inside -->
|
|
643
643
|
<div
|
|
644
644
|
class="form-autocomplete__field"
|
|
645
645
|
class:form-autocomplete__field--focused={isOpen}
|
|
@@ -726,7 +726,7 @@
|
|
|
726
726
|
</div>
|
|
727
727
|
|
|
728
728
|
<!-- Dropdown popover (uses Popover API to render in top layer, bypassing stacking contexts) -->
|
|
729
|
-
<!--
|
|
729
|
+
<!-- role="presentation" container; onmousedown prevents focus loss from input -->
|
|
730
730
|
<div
|
|
731
731
|
bind:this={popoverElement}
|
|
732
732
|
id={listboxId}
|
|
@@ -757,7 +757,8 @@
|
|
|
757
757
|
</li>
|
|
758
758
|
{:else}
|
|
759
759
|
{#each suggestions as option, index (option.value)}
|
|
760
|
-
<!--
|
|
760
|
+
<!-- WAI-ARIA combobox: keyboard nav handled on input, not individual options -->
|
|
761
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
761
762
|
<li
|
|
762
763
|
id={getOptionId(index)}
|
|
763
764
|
class="form-autocomplete__option"
|