@flowdrop/flowdrop 1.7.0 → 1.8.0
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/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 +22 -7
- 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
package/README.md
CHANGED
|
@@ -131,6 +131,16 @@ const app = await mountFlowDropApp(container, {
|
|
|
131
131
|
});
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
+
## Customising messages
|
|
135
|
+
|
|
136
|
+
Every user-facing string flows through a typed `Messages` tree. Pass a callback to override any subset:
|
|
137
|
+
|
|
138
|
+
```svelte
|
|
139
|
+
<FlowDrop messages={() => ({ form: { schema: { save: 'Apply' } } })} />
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Wire the callback to your i18n library (paraglide-js, sveltekit-i18n, etc.) — locale changes propagate automatically. See the [i18n & Custom Messages guide](https://docs.flowdrop.io/guides/i18n) for the full shape and a paraglide-js worked example.
|
|
143
|
+
|
|
134
144
|
## Sub-Module Exports
|
|
135
145
|
|
|
136
146
|
FlowDrop provides tree-shakeable sub-module exports so you can import only what you need:
|
|
@@ -65,6 +65,8 @@
|
|
|
65
65
|
import { logger } from '../utils/logger.js';
|
|
66
66
|
import { validateWorkflowData } from '../utils/validation.js';
|
|
67
67
|
import type { SettingsCategory } from '../types/settings.js';
|
|
68
|
+
import { defaultMessages, mergeMessages, setMessages } from '../messages/index.js';
|
|
69
|
+
import type { MessagesOverride } from '../messages/index.js';
|
|
68
70
|
|
|
69
71
|
/**
|
|
70
72
|
* Configuration props for runtime customization
|
|
@@ -124,6 +126,18 @@
|
|
|
124
126
|
swapStrategies?: SwapStrategy[];
|
|
125
127
|
/** Additional JSON Schema properties to show in the Workflow Settings panel. Values are persisted in workflow.config. */
|
|
126
128
|
workflowSettingsSchema?: ConfigSchema;
|
|
129
|
+
/**
|
|
130
|
+
* Override user-facing strings. Pass either a partial of the `Messages`
|
|
131
|
+
* tree directly, or a callback that returns one. Missing keys fall through
|
|
132
|
+
* to English defaults.
|
|
133
|
+
*
|
|
134
|
+
* For static overrides, a value is fine: `messages={{ common: { save: 'Apply' } }}`.
|
|
135
|
+
* For reactive overrides driven by an i18n library (paraglide, etc.),
|
|
136
|
+
* either form works — Svelte 5's prop reactivity propagates locale changes.
|
|
137
|
+
* The callback form is useful when your translations live behind a
|
|
138
|
+
* function call you'd rather not invoke unless the prop is actually read.
|
|
139
|
+
*/
|
|
140
|
+
messages?: MessagesOverride | (() => MessagesOverride);
|
|
127
141
|
}
|
|
128
142
|
|
|
129
143
|
let {
|
|
@@ -150,12 +164,74 @@
|
|
|
150
164
|
showSettingsSyncButton,
|
|
151
165
|
showSettingsResetButton,
|
|
152
166
|
swapStrategies,
|
|
153
|
-
workflowSettingsSchema
|
|
167
|
+
workflowSettingsSchema,
|
|
168
|
+
messages: messagesOverride
|
|
154
169
|
}: Props = $props();
|
|
155
170
|
|
|
156
171
|
// svelte-ignore state_referenced_locally — feature flags don't change at runtime
|
|
157
172
|
const features = mergeFeatures(propFeatures);
|
|
158
173
|
|
|
174
|
+
// Messages: merge consumer overrides over defaults; expose via context as a
|
|
175
|
+
// getter so consumer-side reactivity (e.g. paraglide-js locale switches)
|
|
176
|
+
// propagates into every child without a subscription. Accepts either a
|
|
177
|
+
// value or a callback — normalize here so the rest of the component sees
|
|
178
|
+
// the merged tree directly.
|
|
179
|
+
let mergedMessages = $derived(
|
|
180
|
+
mergeMessages(
|
|
181
|
+
defaultMessages,
|
|
182
|
+
typeof messagesOverride === 'function' ? messagesOverride() : messagesOverride
|
|
183
|
+
)
|
|
184
|
+
);
|
|
185
|
+
// setContext must run during component init (synchronously, not in $effect)
|
|
186
|
+
// — Svelte enforces that. The context value is a getter that closes over
|
|
187
|
+
// the live $derived, so child components always read the current tree.
|
|
188
|
+
setMessages(() => mergedMessages);
|
|
189
|
+
|
|
190
|
+
// Default navbar primary actions — used when no `navbarActions` prop is supplied.
|
|
191
|
+
// Derived so the labels track locale changes.
|
|
192
|
+
const defaultPrimaryActions = $derived([
|
|
193
|
+
{
|
|
194
|
+
label: mergedMessages.navigation.save,
|
|
195
|
+
href: '#save',
|
|
196
|
+
icon: 'heroicons:document-arrow-down',
|
|
197
|
+
variant: 'primary' as const,
|
|
198
|
+
onclick: (e: Event) => {
|
|
199
|
+
e.preventDefault();
|
|
200
|
+
saveWorkflow();
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
label: mergedMessages.navigation.export,
|
|
205
|
+
href: '#export',
|
|
206
|
+
icon: 'heroicons:arrow-down-tray',
|
|
207
|
+
variant: 'outline' as const,
|
|
208
|
+
onclick: (e: Event) => {
|
|
209
|
+
e.preventDefault();
|
|
210
|
+
exportWorkflow();
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
label: mergedMessages.navigation.import,
|
|
215
|
+
href: '#import',
|
|
216
|
+
icon: 'heroicons:arrow-up-tray',
|
|
217
|
+
variant: 'outline' as const,
|
|
218
|
+
onclick: (e: Event) => {
|
|
219
|
+
e.preventDefault();
|
|
220
|
+
fileInputRef?.click();
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
label: mergedMessages.navigation.workflowSettings,
|
|
225
|
+
href: '#settings',
|
|
226
|
+
icon: 'heroicons:cog-6-tooth',
|
|
227
|
+
variant: 'outline' as const,
|
|
228
|
+
onclick: (e: Event) => {
|
|
229
|
+
e.preventDefault();
|
|
230
|
+
toggleWorkflowSettings();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
]);
|
|
234
|
+
|
|
159
235
|
// Theme system — resolve named theme or custom object, inject CSS tokens from skin
|
|
160
236
|
// Explicit prop wins; falls back to user's persisted theme preference from settings
|
|
161
237
|
let resolvedTheme = $derived(resolveTheme(themeProp ?? getUiSettings().theme));
|
|
@@ -950,50 +1026,7 @@
|
|
|
950
1026
|
{#snippet header()}
|
|
951
1027
|
<Navbar
|
|
952
1028
|
title={breadcrumbTitle}
|
|
953
|
-
primaryActions={navbarActions.length > 0
|
|
954
|
-
? navbarActions
|
|
955
|
-
: [
|
|
956
|
-
{
|
|
957
|
-
label: 'Save',
|
|
958
|
-
href: '#save',
|
|
959
|
-
icon: 'heroicons:document-arrow-down',
|
|
960
|
-
variant: 'primary',
|
|
961
|
-
onclick: (e) => {
|
|
962
|
-
e.preventDefault();
|
|
963
|
-
saveWorkflow();
|
|
964
|
-
}
|
|
965
|
-
},
|
|
966
|
-
{
|
|
967
|
-
label: 'Export',
|
|
968
|
-
href: '#export',
|
|
969
|
-
icon: 'heroicons:arrow-down-tray',
|
|
970
|
-
variant: 'outline',
|
|
971
|
-
onclick: (e) => {
|
|
972
|
-
e.preventDefault();
|
|
973
|
-
exportWorkflow();
|
|
974
|
-
}
|
|
975
|
-
},
|
|
976
|
-
{
|
|
977
|
-
label: 'Import',
|
|
978
|
-
href: '#import',
|
|
979
|
-
icon: 'heroicons:arrow-up-tray',
|
|
980
|
-
variant: 'outline',
|
|
981
|
-
onclick: (e) => {
|
|
982
|
-
e.preventDefault();
|
|
983
|
-
fileInputRef?.click();
|
|
984
|
-
}
|
|
985
|
-
},
|
|
986
|
-
{
|
|
987
|
-
label: 'Workflow Settings',
|
|
988
|
-
href: '#settings',
|
|
989
|
-
icon: 'heroicons:cog-6-tooth',
|
|
990
|
-
variant: 'outline',
|
|
991
|
-
onclick: (e) => {
|
|
992
|
-
e.preventDefault();
|
|
993
|
-
toggleWorkflowSettings();
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
]}
|
|
1029
|
+
primaryActions={navbarActions.length > 0 ? navbarActions : defaultPrimaryActions}
|
|
997
1030
|
showStatus={true}
|
|
998
1031
|
{showSettings}
|
|
999
1032
|
{settingsCategories}
|
|
@@ -1042,7 +1075,7 @@
|
|
|
1042
1075
|
/>
|
|
1043
1076
|
{:else if isWorkflowSettingsOpen}
|
|
1044
1077
|
<ConfigPanel
|
|
1045
|
-
title=
|
|
1078
|
+
title={mergedMessages.navigation.workflowSettingsPanelTitle}
|
|
1046
1079
|
id={getWorkflowStore()?.id}
|
|
1047
1080
|
details={[
|
|
1048
1081
|
{
|
|
@@ -1054,7 +1087,7 @@
|
|
|
1054
1087
|
value: String(getWorkflowStore()?.edges?.length ?? 0)
|
|
1055
1088
|
}
|
|
1056
1089
|
]}
|
|
1057
|
-
configTitle=
|
|
1090
|
+
configTitle={mergedMessages.navigation.workflowSettingsPanelSubtitle}
|
|
1058
1091
|
onClose={() => (isWorkflowSettingsOpen = false)}
|
|
1059
1092
|
>
|
|
1060
1093
|
<ConfigForm
|
|
@@ -1103,7 +1136,8 @@
|
|
|
1103
1136
|
<ConfigPanel
|
|
1104
1137
|
title={currentNode.data.label}
|
|
1105
1138
|
id={currentNode.id}
|
|
1106
|
-
description={currentNode.data.metadata?.description ||
|
|
1139
|
+
description={currentNode.data.metadata?.description ||
|
|
1140
|
+
mergedMessages.navigation.nodeConfigDescription}
|
|
1107
1141
|
details={[
|
|
1108
1142
|
{
|
|
1109
1143
|
label: 'Type',
|
|
@@ -1160,7 +1194,7 @@
|
|
|
1160
1194
|
{/if}
|
|
1161
1195
|
{/snippet}
|
|
1162
1196
|
|
|
1163
|
-
<!-- Bottom Panel: Tabbed Console / AI
|
|
1197
|
+
<!-- Bottom Panel: Tabbed Console / AI Assistant -->
|
|
1164
1198
|
{#snippet bottomPanel()}
|
|
1165
1199
|
<div class="bottom-panel-tabs">
|
|
1166
1200
|
<div class="bottom-panel-tabs__bar">
|
|
@@ -1170,7 +1204,7 @@
|
|
|
1170
1204
|
: ''}"
|
|
1171
1205
|
onclick={() => updateSettings({ ui: { bottomPanelTab: 'console' } })}
|
|
1172
1206
|
>
|
|
1173
|
-
|
|
1207
|
+
{mergedMessages.navigation.bottomPanel.console}
|
|
1174
1208
|
</button>
|
|
1175
1209
|
<button
|
|
1176
1210
|
class="bottom-panel-tabs__tab {getUiSettings().bottomPanelTab === 'chat'
|
|
@@ -1178,7 +1212,7 @@
|
|
|
1178
1212
|
: ''}"
|
|
1179
1213
|
onclick={() => updateSettings({ ui: { bottomPanelTab: 'chat' } })}
|
|
1180
1214
|
>
|
|
1181
|
-
|
|
1215
|
+
{mergedMessages.navigation.bottomPanel.chat}
|
|
1182
1216
|
</button>
|
|
1183
1217
|
</div>
|
|
1184
1218
|
<div class="bottom-panel-tabs__content">
|
|
@@ -1263,15 +1297,19 @@
|
|
|
1263
1297
|
onclick={handleCanvasClick}
|
|
1264
1298
|
onkeydown={(e) => e.key === 'Escape' && closeConfigSidebar()}
|
|
1265
1299
|
role="region"
|
|
1266
|
-
aria-label=
|
|
1300
|
+
aria-label={mergedMessages.layout.workflowCanvas}
|
|
1267
1301
|
>
|
|
1268
1302
|
<!-- Floating sidebar toggle — always visible on the canvas top-left -->
|
|
1269
1303
|
{#if !disableSidebar}
|
|
1270
1304
|
<button
|
|
1271
1305
|
class="flowdrop-sidebar-fab"
|
|
1272
1306
|
onclick={toggleSidebar}
|
|
1273
|
-
aria-label={isSidebarCollapsed
|
|
1274
|
-
|
|
1307
|
+
aria-label={isSidebarCollapsed
|
|
1308
|
+
? mergedMessages.layout.expandSidebar
|
|
1309
|
+
: mergedMessages.layout.collapseSidebar}
|
|
1310
|
+
title={isSidebarCollapsed
|
|
1311
|
+
? mergedMessages.layout.expandSidebar
|
|
1312
|
+
: mergedMessages.layout.collapseSidebar}
|
|
1275
1313
|
>
|
|
1276
1314
|
<Icon icon={isSidebarCollapsed ? 'mdi:menu' : 'mdi:menu-open'} />
|
|
1277
1315
|
</button>
|
|
@@ -5,6 +5,7 @@ import type { AuthProvider } from '../types/auth.js';
|
|
|
5
5
|
import type { FlowDropEventHandlers, FlowDropFeatures } from '../types/events.js';
|
|
6
6
|
import type { FlowDropTheme, FlowDropThemeName } from '../types/theme.js';
|
|
7
7
|
import type { SettingsCategory } from '../types/settings.js';
|
|
8
|
+
import type { MessagesOverride } from '../messages/index.js';
|
|
8
9
|
/**
|
|
9
10
|
* Configuration props for runtime customization
|
|
10
11
|
*/
|
|
@@ -63,6 +64,18 @@ interface Props {
|
|
|
63
64
|
swapStrategies?: SwapStrategy[];
|
|
64
65
|
/** Additional JSON Schema properties to show in the Workflow Settings panel. Values are persisted in workflow.config. */
|
|
65
66
|
workflowSettingsSchema?: ConfigSchema;
|
|
67
|
+
/**
|
|
68
|
+
* Override user-facing strings. Pass either a partial of the `Messages`
|
|
69
|
+
* tree directly, or a callback that returns one. Missing keys fall through
|
|
70
|
+
* to English defaults.
|
|
71
|
+
*
|
|
72
|
+
* For static overrides, a value is fine: `messages={{ common: { save: 'Apply' } }}`.
|
|
73
|
+
* For reactive overrides driven by an i18n library (paraglide, etc.),
|
|
74
|
+
* either form works — Svelte 5's prop reactivity propagates locale changes.
|
|
75
|
+
* The callback form is useful when your translations live behind a
|
|
76
|
+
* function call you'd rather not invoke unless the prop is actually read.
|
|
77
|
+
*/
|
|
78
|
+
messages?: MessagesOverride | (() => MessagesOverride);
|
|
66
79
|
}
|
|
67
80
|
declare const App: import("svelte").Component<Props, {}, "">;
|
|
68
81
|
type App = ReturnType<typeof App>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { ConfigSchema, ConfigValues } from '../types';
|
|
3
3
|
import ConfigForm from './ConfigForm.svelte';
|
|
4
|
+
import { m } from '../messages/index.js';
|
|
4
5
|
|
|
5
6
|
interface Props {
|
|
6
7
|
isOpen: boolean;
|
|
@@ -60,7 +61,7 @@
|
|
|
60
61
|
type="button"
|
|
61
62
|
class="config-modal__close-btn"
|
|
62
63
|
onclick={handleClose}
|
|
63
|
-
aria-label=
|
|
64
|
+
aria-label={m().navigation.closeConfigModal}
|
|
64
65
|
>
|
|
65
66
|
<span aria-hidden="true">×</span>
|
|
66
67
|
</button>
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import Icon from '@iconify/svelte';
|
|
12
12
|
import ReadOnlyDetails from './ReadOnlyDetails.svelte';
|
|
13
13
|
import { getUiSettings } from '../stores/settingsStore.svelte.js';
|
|
14
|
+
import { m } from '../messages/index.js';
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* A single detail item with label and value
|
|
@@ -70,13 +71,13 @@
|
|
|
70
71
|
<button
|
|
71
72
|
class="config-panel__action-btn"
|
|
72
73
|
onclick={onSwap}
|
|
73
|
-
aria-label=
|
|
74
|
+
aria-label={m().layout.swapNode}
|
|
74
75
|
title="Swap node type"
|
|
75
76
|
>
|
|
76
77
|
<Icon icon="heroicons:arrows-right-left" />
|
|
77
78
|
</button>
|
|
78
79
|
{/if}
|
|
79
|
-
<button class="config-panel__close" onclick={onClose} aria-label=
|
|
80
|
+
<button class="config-panel__close" onclick={onClose} aria-label={m().layout.closeConfigPanel}> × </button>
|
|
80
81
|
</div>
|
|
81
82
|
</div>
|
|
82
83
|
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
<script lang="ts">
|
|
8
8
|
import { useSvelteFlow } from '@xyflow/svelte';
|
|
9
9
|
import type { Snippet } from 'svelte';
|
|
10
|
+
import { m } from '../messages/index.js';
|
|
10
11
|
|
|
11
12
|
interface Props {
|
|
12
13
|
ondrop: (nodeTypeData: string, position: { x: number; y: number }) => void;
|
|
@@ -68,7 +69,7 @@
|
|
|
68
69
|
<div
|
|
69
70
|
class="flow-drop-zone"
|
|
70
71
|
role="application"
|
|
71
|
-
aria-label=
|
|
72
|
+
aria-label={m().layout.workflowCanvas}
|
|
72
73
|
ondragover={handleDragOver}
|
|
73
74
|
ondrop={handleDrop}
|
|
74
75
|
>
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
<script lang="ts">
|
|
9
9
|
import Icon from '@iconify/svelte';
|
|
10
10
|
import type { WorkflowNode as WorkflowNodeType } from '../types/index.js';
|
|
11
|
+
import { m } from '../messages/index.js';
|
|
11
12
|
|
|
12
13
|
interface LogEntry {
|
|
13
14
|
timestamp: string;
|
|
@@ -163,7 +164,7 @@
|
|
|
163
164
|
class="logs-sidebar"
|
|
164
165
|
class:logs-sidebar--open={props.isOpen}
|
|
165
166
|
role="dialog"
|
|
166
|
-
aria-label=
|
|
167
|
+
aria-label={m().layout.executionLogs}
|
|
167
168
|
aria-modal="true"
|
|
168
169
|
tabindex="-1"
|
|
169
170
|
onkeydown={handleKeydown}
|
|
@@ -189,7 +190,7 @@
|
|
|
189
190
|
class="logs-sidebar__close-btn"
|
|
190
191
|
onclick={handleClose}
|
|
191
192
|
title="Close logs sidebar (Esc)"
|
|
192
|
-
aria-label=
|
|
193
|
+
aria-label={m().layout.closeLogsSidebar}
|
|
193
194
|
>
|
|
194
195
|
<Icon icon="mdi:close" />
|
|
195
196
|
</button>
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import Logo from './Logo.svelte';
|
|
12
12
|
import SettingsModal from './SettingsModal.svelte';
|
|
13
13
|
import type { SettingsCategory } from '../types/settings.js';
|
|
14
|
+
import { m } from '../messages/index.js';
|
|
14
15
|
|
|
15
16
|
interface NavbarAction {
|
|
16
17
|
label: string;
|
|
@@ -64,6 +65,9 @@
|
|
|
64
65
|
// Settings modal state
|
|
65
66
|
let isSettingsOpen = $state(false);
|
|
66
67
|
|
|
68
|
+
// Hoist the navigation branch — six reads in the template.
|
|
69
|
+
const nav = $derived(m().navigation);
|
|
70
|
+
|
|
67
71
|
// Close dropdown when clicking outside
|
|
68
72
|
function handleClickOutside(event: MouseEvent) {
|
|
69
73
|
const target = event.target as HTMLElement;
|
|
@@ -90,8 +94,8 @@
|
|
|
90
94
|
<Logo />
|
|
91
95
|
</div>
|
|
92
96
|
<div>
|
|
93
|
-
<h1 class="flowdrop-text--logo flowdrop-font--bold">
|
|
94
|
-
<p class="flowdrop-text--tagline flowdrop-text--gray">
|
|
97
|
+
<h1 class="flowdrop-text--logo flowdrop-font--bold">{nav.appName}</h1>
|
|
98
|
+
<p class="flowdrop-text--tagline flowdrop-text--gray">{nav.tagline}</p>
|
|
95
99
|
</div>
|
|
96
100
|
</div>
|
|
97
101
|
</div>
|
|
@@ -104,7 +108,7 @@
|
|
|
104
108
|
<div class="flowdrop-navbar__status-container">
|
|
105
109
|
<div class="flowdrop-navbar__status">
|
|
106
110
|
<div class="flowdrop-navbar__status-indicator"></div>
|
|
107
|
-
<span class="flowdrop-navbar__status-text">
|
|
111
|
+
<span class="flowdrop-navbar__status-text">{nav.connected}</span>
|
|
108
112
|
</div>
|
|
109
113
|
</div>
|
|
110
114
|
{/if}
|
|
@@ -112,7 +116,7 @@
|
|
|
112
116
|
<!-- Title or Breadcrumbs on bottom -->
|
|
113
117
|
{#if breadcrumbs.length > 0}
|
|
114
118
|
<div class="flowdrop-navbar__breadcrumb-container">
|
|
115
|
-
<nav class="flowdrop-navbar__breadcrumb" aria-label=
|
|
119
|
+
<nav class="flowdrop-navbar__breadcrumb" aria-label={nav.breadcrumbAriaLabel}>
|
|
116
120
|
<ol class="flowdrop-navbar__breadcrumb-list">
|
|
117
121
|
{#each breadcrumbs as breadcrumb, index (index)}
|
|
118
122
|
<li class="flowdrop-navbar__breadcrumb-item">
|
|
@@ -241,8 +245,8 @@
|
|
|
241
245
|
<button
|
|
242
246
|
class="flowdrop-navbar__settings-btn"
|
|
243
247
|
onclick={() => (isSettingsOpen = true)}
|
|
244
|
-
title=
|
|
245
|
-
aria-label=
|
|
248
|
+
title={nav.settingsTitle}
|
|
249
|
+
aria-label={nav.settingsAriaLabel}
|
|
246
250
|
>
|
|
247
251
|
<Icon icon="mdi:cog" />
|
|
248
252
|
</button>
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import { getCategoryLabel } from '../stores/categoriesStore.svelte.js';
|
|
14
14
|
import { getUiSettings } from '../stores/settingsStore.svelte.js';
|
|
15
15
|
import { extractConfigDefaults } from '../utils/nodeIds.js';
|
|
16
|
+
import { m } from '../messages/index.js';
|
|
16
17
|
|
|
17
18
|
interface Props {
|
|
18
19
|
nodes: NodeMetadata[];
|
|
@@ -168,7 +169,7 @@
|
|
|
168
169
|
class:flowdrop-sidebar--collapsed={isCollapsed}
|
|
169
170
|
class:flowdrop-sidebar--compact={getUiSettings().compactMode}
|
|
170
171
|
style:width="{isCollapsed ? 0 : getUiSettings().sidebarWidth}px"
|
|
171
|
-
aria-label=
|
|
172
|
+
aria-label={m().layout.componentsSidebar}
|
|
172
173
|
>
|
|
173
174
|
<!-- Search Section — visibility controlled by --fd-sidebar-search-display -->
|
|
174
175
|
<div class="flowdrop-sidebar__search">
|
|
@@ -176,13 +177,13 @@
|
|
|
176
177
|
<div class="flowdrop-join__item flowdrop-flex--1">
|
|
177
178
|
<input
|
|
178
179
|
type="text"
|
|
179
|
-
placeholder=
|
|
180
|
+
placeholder={m().layout.searchComponents}
|
|
180
181
|
class="flowdrop-input flowdrop-join__item flowdrop-w--full"
|
|
181
182
|
bind:value={searchInput}
|
|
182
183
|
oninput={handleSearchChange}
|
|
183
184
|
/>
|
|
184
185
|
</div>
|
|
185
|
-
<button class="flowdrop-btn flowdrop-join__item" aria-label=
|
|
186
|
+
<button class="flowdrop-btn flowdrop-join__item" aria-label={m().layout.searchComponents}>
|
|
186
187
|
<Icon icon="mdi:magnify" class="flowdrop-icon" />
|
|
187
188
|
</button>
|
|
188
189
|
</div>
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
formatExecutionDuration,
|
|
18
18
|
formatLastExecuted
|
|
19
19
|
} from '../utils/nodeStatus.js';
|
|
20
|
+
import { m } from '../messages/index.js';
|
|
20
21
|
|
|
21
22
|
interface Props {
|
|
22
23
|
nodeId?: string;
|
|
@@ -79,6 +80,9 @@
|
|
|
79
80
|
let shouldShow = $derived(
|
|
80
81
|
executionInfo.status !== 'idle' || executionInfo.executionCount > 0 || executionInfo.isExecuting
|
|
81
82
|
);
|
|
83
|
+
|
|
84
|
+
// Hoist the overlay branch — seven reads in the template.
|
|
85
|
+
const overlay = $derived(m().status.overlay);
|
|
82
86
|
</script>
|
|
83
87
|
|
|
84
88
|
{#if shouldShow}
|
|
@@ -98,9 +102,12 @@
|
|
|
98
102
|
"
|
|
99
103
|
onmouseenter={() => (isHovered = true)}
|
|
100
104
|
onmouseleave={() => (isHovered = false)}
|
|
101
|
-
title=
|
|
105
|
+
title={overlay.tooltip({
|
|
106
|
+
status: getStatusLabel(executionInfo.status),
|
|
107
|
+
count: executionInfo.executionCount
|
|
108
|
+
})}
|
|
102
109
|
role="status"
|
|
103
|
-
aria-label=
|
|
110
|
+
aria-label={overlay.ariaLabel({ status: getStatusLabel(executionInfo.status) })}
|
|
104
111
|
>
|
|
105
112
|
<!-- Status Display: [icon] [label] -->
|
|
106
113
|
<div
|
|
@@ -130,18 +137,18 @@
|
|
|
130
137
|
{#if showDetails && isHovered}
|
|
131
138
|
<div class="node-status-overlay__details">
|
|
132
139
|
<div class="node-status-overlay__detail-item">
|
|
133
|
-
<span class="node-status-overlay__detail-label">
|
|
140
|
+
<span class="node-status-overlay__detail-label">{overlay.statusLabel}</span>
|
|
134
141
|
<span class="node-status-overlay__detail-value"
|
|
135
142
|
>{getStatusLabel(executionInfo.status)}</span
|
|
136
143
|
>
|
|
137
144
|
</div>
|
|
138
145
|
<div class="node-status-overlay__detail-item">
|
|
139
|
-
<span class="node-status-overlay__detail-label">
|
|
146
|
+
<span class="node-status-overlay__detail-label">{overlay.executionsLabel}</span>
|
|
140
147
|
<span class="node-status-overlay__detail-value">{executionInfo.executionCount}</span>
|
|
141
148
|
</div>
|
|
142
149
|
{#if executionInfo.lastExecuted}
|
|
143
150
|
<div class="node-status-overlay__detail-item">
|
|
144
|
-
<span class="node-status-overlay__detail-label">
|
|
151
|
+
<span class="node-status-overlay__detail-label">{overlay.lastRunLabel}</span>
|
|
145
152
|
<span class="node-status-overlay__detail-value"
|
|
146
153
|
>{formatLastExecuted(executionInfo.lastExecuted)}</span
|
|
147
154
|
>
|
|
@@ -149,7 +156,7 @@
|
|
|
149
156
|
{/if}
|
|
150
157
|
{#if executionInfo.lastExecutionDuration}
|
|
151
158
|
<div class="node-status-overlay__detail-item">
|
|
152
|
-
<span class="node-status-overlay__detail-label">
|
|
159
|
+
<span class="node-status-overlay__detail-label">{overlay.durationLabel}</span>
|
|
153
160
|
<span class="node-status-overlay__detail-value"
|
|
154
161
|
>{formatExecutionDuration(executionInfo.lastExecutionDuration)}</span
|
|
155
162
|
>
|
|
@@ -157,7 +164,7 @@
|
|
|
157
164
|
{/if}
|
|
158
165
|
{#if executionInfo.lastError}
|
|
159
166
|
<div class="node-status-overlay__detail-item node-status-overlay__detail-item--error">
|
|
160
|
-
<span class="node-status-overlay__detail-label">
|
|
167
|
+
<span class="node-status-overlay__detail-label">{overlay.errorLabel}</span>
|
|
161
168
|
<span class="node-status-overlay__detail-value">{executionInfo.lastError}</span>
|
|
162
169
|
</div>
|
|
163
170
|
{/if}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import Icon from '@iconify/svelte';
|
|
11
11
|
import { getNodeIcon, getCategoryIcon } from '../utils/icons.js';
|
|
12
12
|
import { getCategoryColorToken } from '../utils/colors.js';
|
|
13
|
+
import { m } from '../messages/index.js';
|
|
13
14
|
import { getCategoryLabel } from '../stores/categoriesStore.svelte.js';
|
|
14
15
|
import { getVersionUpgrade } from '../utils/nodeSwap.js';
|
|
15
16
|
|
|
@@ -80,7 +81,7 @@
|
|
|
80
81
|
<div class="swap-picker">
|
|
81
82
|
<!-- Header -->
|
|
82
83
|
<div class="swap-picker__header">
|
|
83
|
-
<button class="swap-picker__back" onclick={onCancel} aria-label=
|
|
84
|
+
<button class="swap-picker__back" onclick={onCancel} aria-label={m().layout.backToConfiguration}>
|
|
84
85
|
<Icon icon="heroicons:arrow-left" />
|
|
85
86
|
</button>
|
|
86
87
|
<h2 class="swap-picker__title">Swap Node</h2>
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import type { Workflow } from '../types/index.js';
|
|
14
14
|
import type { EndpointConfig } from '../config/endpoints.js';
|
|
15
15
|
import { logger } from '../utils/logger.js';
|
|
16
|
+
import { m } from '../messages/index.js';
|
|
16
17
|
|
|
17
18
|
interface Props {
|
|
18
19
|
pipelineId: string;
|
|
@@ -166,9 +167,10 @@
|
|
|
166
167
|
* Get pipeline actions for the parent navbar
|
|
167
168
|
*/
|
|
168
169
|
function getPipelineActions() {
|
|
170
|
+
const sp = m().status.pipeline;
|
|
169
171
|
return [
|
|
170
172
|
{
|
|
171
|
-
label: isLoadingJobStatus ?
|
|
173
|
+
label: isLoadingJobStatus ? sp.refreshing : sp.refresh,
|
|
172
174
|
href: '#refresh',
|
|
173
175
|
icon: isLoadingJobStatus ? 'mdi:loading' : 'mdi:refresh',
|
|
174
176
|
variant: 'outline' as const,
|
|
@@ -178,7 +180,7 @@
|
|
|
178
180
|
}
|
|
179
181
|
},
|
|
180
182
|
{
|
|
181
|
-
label:
|
|
183
|
+
label: sp.viewLogs,
|
|
182
184
|
href: '#logs',
|
|
183
185
|
icon: 'mdi:file-document-outline',
|
|
184
186
|
variant: 'outline' as const,
|
|
@@ -214,29 +216,30 @@
|
|
|
214
216
|
// Send pipeline breadcrumbs to layout when they change
|
|
215
217
|
$effect(() => {
|
|
216
218
|
if (pipelineStatus && pipelineId && workflow) {
|
|
219
|
+
const sp = m().status.pipeline;
|
|
217
220
|
const breadcrumbs = [
|
|
218
221
|
{
|
|
219
|
-
label:
|
|
222
|
+
label: sp.home,
|
|
220
223
|
href: '/',
|
|
221
224
|
icon: 'mdi:home'
|
|
222
225
|
},
|
|
223
226
|
{
|
|
224
|
-
label:
|
|
227
|
+
label: sp.workflows,
|
|
225
228
|
href: '/',
|
|
226
229
|
icon: 'mdi:view-list'
|
|
227
230
|
},
|
|
228
231
|
{
|
|
229
|
-
label: workflow.name ||
|
|
232
|
+
label: workflow.name || sp.workflow,
|
|
230
233
|
href: `/workflow/${workflow.id}/edit`,
|
|
231
234
|
icon: 'mdi:workflow'
|
|
232
235
|
},
|
|
233
236
|
{
|
|
234
|
-
label:
|
|
237
|
+
label: sp.pipelines,
|
|
235
238
|
href: `/workflow/${workflow.id}/pipelines`,
|
|
236
239
|
icon: 'mdi:source-branch'
|
|
237
240
|
},
|
|
238
241
|
{
|
|
239
|
-
label:
|
|
242
|
+
label: sp.pipelineCrumb({ id: pipelineId, status: pipelineStatus }),
|
|
240
243
|
icon: 'mdi:play-circle'
|
|
241
244
|
}
|
|
242
245
|
];
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
-->
|
|
6
6
|
|
|
7
7
|
<script lang="ts">
|
|
8
|
+
import { m } from '../messages/index.js';
|
|
9
|
+
|
|
8
10
|
/**
|
|
9
11
|
* A single detail item with label and value
|
|
10
12
|
*/
|
|
@@ -52,8 +54,8 @@
|
|
|
52
54
|
<button
|
|
53
55
|
class="readonly-details__copy-btn"
|
|
54
56
|
onclick={copyId}
|
|
55
|
-
title=
|
|
56
|
-
aria-label=
|
|
57
|
+
title={m().navigation.copyId}
|
|
58
|
+
aria-label={m().navigation.copyId}
|
|
57
59
|
>
|
|
58
60
|
<svg
|
|
59
61
|
xmlns="http://www.w3.org/2000/svg"
|