@flowdrop/flowdrop 1.3.0 → 1.5.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 +68 -24
- package/dist/adapters/WorkflowAdapter.js +2 -22
- package/dist/adapters/agentspec/autoLayout.d.ts +51 -5
- package/dist/adapters/agentspec/autoLayout.js +120 -23
- package/dist/chat/commandClassifier.d.ts +19 -0
- package/dist/chat/commandClassifier.js +30 -0
- package/dist/chat/index.d.ts +27 -0
- package/dist/chat/index.js +32 -0
- package/dist/chat/responseParser.d.ts +21 -0
- package/dist/chat/responseParser.js +87 -0
- package/dist/commands/batch.d.ts +18 -0
- package/dist/commands/batch.js +56 -0
- package/dist/commands/executor.d.ts +37 -0
- package/dist/commands/executor.js +1044 -0
- package/dist/commands/index.d.ts +14 -0
- package/dist/commands/index.js +17 -0
- package/dist/commands/parser.d.ts +16 -0
- package/dist/commands/parser.js +278 -0
- package/dist/commands/positioner.d.ts +19 -0
- package/dist/commands/positioner.js +33 -0
- package/dist/commands/storeIntegration.svelte.d.ts +16 -0
- package/dist/commands/storeIntegration.svelte.js +67 -0
- package/dist/commands/types.d.ts +343 -0
- package/dist/commands/types.js +45 -0
- package/dist/components/App.svelte +431 -17
- package/dist/components/App.svelte.d.ts +10 -0
- package/dist/components/CanvasBanner.stories.svelte +6 -2
- package/dist/components/CanvasController.svelte +38 -0
- package/dist/components/CanvasController.svelte.d.ts +32 -0
- package/dist/components/ConfigMappingRow.svelte +130 -0
- package/dist/components/ConfigMappingRow.svelte.d.ts +8 -0
- package/dist/components/ConfigPanel.svelte +56 -7
- package/dist/components/ConfigPanel.svelte.d.ts +2 -0
- package/dist/components/FlowDropEdge.svelte +8 -57
- package/dist/components/Logo.svelte +14 -14
- package/dist/components/LogsSidebar.svelte +5 -5
- package/dist/components/Navbar.svelte +58 -10
- package/dist/components/Navbar.svelte.d.ts +7 -0
- package/dist/components/NodeSidebar.svelte +238 -362
- package/dist/components/NodeSwapPicker.svelte +537 -0
- package/dist/components/NodeSwapPicker.svelte.d.ts +16 -0
- package/dist/components/PortMappingRow.svelte +209 -0
- package/dist/components/PortMappingRow.svelte.d.ts +12 -0
- package/dist/components/SwapMappingEditor.svelte +550 -0
- package/dist/components/SwapMappingEditor.svelte.d.ts +12 -0
- package/dist/components/WorkflowEditor.svelte +99 -4
- package/dist/components/WorkflowEditor.svelte.d.ts +8 -0
- package/dist/components/chat/AIChatPanel.svelte +658 -0
- package/dist/components/chat/AIChatPanel.svelte.d.ts +13 -0
- package/dist/components/chat/CommandPreview.svelte +184 -0
- package/dist/components/chat/CommandPreview.svelte.d.ts +9 -0
- package/dist/components/console/CommandConsole.stories.svelte +93 -0
- package/dist/components/console/CommandConsole.stories.svelte.d.ts +27 -0
- package/dist/components/console/CommandConsole.svelte +259 -0
- package/dist/components/console/CommandConsole.svelte.d.ts +11 -0
- package/dist/components/console/ConsoleAutocomplete.svelte +139 -0
- package/dist/components/console/ConsoleAutocomplete.svelte.d.ts +21 -0
- package/dist/components/console/ConsoleInput.svelte +712 -0
- package/dist/components/console/ConsoleInput.svelte.d.ts +16 -0
- package/dist/components/console/ConsoleOutput.svelte +121 -0
- package/dist/components/console/ConsoleOutput.svelte.d.ts +11 -0
- package/dist/components/console/formatters.d.ts +26 -0
- package/dist/components/console/formatters.js +118 -0
- package/dist/components/interrupt/index.d.ts +1 -0
- package/dist/components/interrupt/index.js +1 -0
- package/dist/components/nodes/SimpleNode.stories.svelte +64 -0
- package/dist/components/nodes/SimpleNode.svelte +27 -11
- package/dist/components/nodes/SquareNode.stories.svelte +45 -0
- package/dist/components/nodes/SquareNode.svelte +27 -11
- package/dist/components/nodes/WorkflowNode.stories.svelte +63 -0
- package/dist/config/endpoints.d.ts +8 -0
- package/dist/config/endpoints.js +5 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.js +9 -0
- package/dist/editor/index.d.ts +3 -1
- package/dist/editor/index.js +4 -2
- package/dist/helpers/proximityConnect.js +8 -1
- package/dist/helpers/workflowEditorHelper.d.ts +3 -53
- package/dist/helpers/workflowEditorHelper.js +13 -228
- package/dist/playground/index.d.ts +1 -1
- package/dist/playground/index.js +1 -1
- package/dist/schemas/v1/workflow.schema.json +107 -22
- package/dist/services/chatService.d.ts +65 -0
- package/dist/services/chatService.js +131 -0
- package/dist/services/historyService.d.ts +6 -4
- package/dist/services/historyService.js +21 -6
- package/dist/skins/slate.js +16 -0
- package/dist/stores/interruptStore.svelte.js +6 -1
- package/dist/stores/playgroundStore.svelte.d.ts +1 -1
- package/dist/stores/playgroundStore.svelte.js +11 -2
- package/dist/stores/portCoordinateStore.svelte.d.ts +4 -0
- package/dist/stores/portCoordinateStore.svelte.js +20 -26
- package/dist/stores/workflowStore.svelte.d.ts +31 -2
- package/dist/stores/workflowStore.svelte.js +84 -64
- package/dist/stories/EdgeDecorator.svelte +4 -4
- package/dist/styles/base.css +48 -0
- package/dist/svelte-app.d.ts +7 -1
- package/dist/svelte-app.js +4 -1
- package/dist/types/chat.d.ts +63 -0
- package/dist/types/chat.js +9 -0
- package/dist/types/events.d.ts +28 -2
- package/dist/types/events.js +1 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/settings.d.ts +6 -0
- package/dist/types/settings.js +3 -0
- package/dist/utils/edgeStyling.d.ts +42 -0
- package/dist/utils/edgeStyling.js +176 -0
- package/dist/utils/nodeIds.d.ts +31 -0
- package/dist/utils/nodeIds.js +42 -0
- package/dist/utils/nodeSwap.d.ts +221 -0
- package/dist/utils/nodeSwap.js +686 -0
- package/package.json +6 -1
- package/dist/helpers/nodeLayoutHelper.d.ts +0 -14
- package/dist/helpers/nodeLayoutHelper.js +0 -19
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
CanvasController Component
|
|
3
|
+
Provides viewport control methods (fitView, zoom, pan) via useSvelteFlow().
|
|
4
|
+
Must be rendered inside SvelteFlowProvider context.
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
<script lang="ts">
|
|
8
|
+
import { useSvelteFlow } from "@xyflow/svelte";
|
|
9
|
+
import { getEditorSettings } from "../stores/settingsStore.svelte.js";
|
|
10
|
+
|
|
11
|
+
const { fitView, zoomIn, zoomOut, setZoom, setCenter, setViewport } =
|
|
12
|
+
useSvelteFlow();
|
|
13
|
+
|
|
14
|
+
export function canvasFitView(): void {
|
|
15
|
+
fitView({ padding: 0.2, duration: 300 });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function canvasZoomIn(): void {
|
|
19
|
+
zoomIn({ duration: 300 });
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function canvasZoomOut(): void {
|
|
23
|
+
zoomOut({ duration: 300 });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function canvasZoomTo(level: number): void {
|
|
27
|
+
setZoom(level, { duration: 300 });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function canvasPanTo(x: number, y: number): void {
|
|
31
|
+
setCenter(x, y, { duration: 300 });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function canvasResetView(): void {
|
|
35
|
+
const defaultZoom = getEditorSettings().defaultZoom;
|
|
36
|
+
setViewport({ x: 0, y: 0, zoom: defaultZoom }, { duration: 300 });
|
|
37
|
+
}
|
|
38
|
+
</script>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
2
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
3
|
+
$$bindings?: Bindings;
|
|
4
|
+
} & Exports;
|
|
5
|
+
(internal: unknown, props: Props & {
|
|
6
|
+
$$events?: Events;
|
|
7
|
+
$$slots?: Slots;
|
|
8
|
+
}): Exports & {
|
|
9
|
+
$set?: any;
|
|
10
|
+
$on?: any;
|
|
11
|
+
};
|
|
12
|
+
z_$$bindings?: Bindings;
|
|
13
|
+
}
|
|
14
|
+
declare const CanvasController: $$__sveltets_2_IsomorphicComponent<{
|
|
15
|
+
canvasFitView?: () => void;
|
|
16
|
+
canvasZoomIn?: () => void;
|
|
17
|
+
canvasZoomOut?: () => void;
|
|
18
|
+
canvasZoomTo?: (level: number) => void;
|
|
19
|
+
canvasPanTo?: (x: number, y: number) => void;
|
|
20
|
+
canvasResetView?: () => void;
|
|
21
|
+
}, {
|
|
22
|
+
[evt: string]: CustomEvent<any>;
|
|
23
|
+
}, {}, {
|
|
24
|
+
canvasFitView: () => void;
|
|
25
|
+
canvasZoomIn: () => void;
|
|
26
|
+
canvasZoomOut: () => void;
|
|
27
|
+
canvasZoomTo: (level: number) => void;
|
|
28
|
+
canvasPanTo: (x: number, y: number) => void;
|
|
29
|
+
canvasResetView: () => void;
|
|
30
|
+
}, string>;
|
|
31
|
+
type CanvasController = InstanceType<typeof CanvasController>;
|
|
32
|
+
export default CanvasController;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
ConfigMappingRow Component
|
|
3
|
+
A single row in the config mapping editor showing carry/reset toggle.
|
|
4
|
+
Styled with BEM syntax.
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
<script lang="ts">
|
|
8
|
+
import type { EditableConfigMapping } from "../utils/nodeSwap.js";
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
mapping: EditableConfigMapping;
|
|
12
|
+
onToggle: (key: string) => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const { mapping, onToggle }: Props = $props();
|
|
16
|
+
|
|
17
|
+
function formatValue(value: unknown): string {
|
|
18
|
+
if (value === null || value === undefined) return "—";
|
|
19
|
+
if (typeof value === "string") return value || '""';
|
|
20
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
21
|
+
if (typeof value === "number") return String(value);
|
|
22
|
+
return JSON.stringify(value);
|
|
23
|
+
}
|
|
24
|
+
</script>
|
|
25
|
+
|
|
26
|
+
<div class="config-mapping-row">
|
|
27
|
+
<div class="config-mapping-row__info">
|
|
28
|
+
<span class="config-mapping-row__key">{mapping.title}</span>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
{#if !mapping.isFlat}
|
|
32
|
+
<div class="config-mapping-row__complex">
|
|
33
|
+
Complex value — will use default
|
|
34
|
+
</div>
|
|
35
|
+
{:else}
|
|
36
|
+
<div class="config-mapping-row__values">
|
|
37
|
+
{#if mapping.carryOver}
|
|
38
|
+
<span class="config-mapping-row__value config-mapping-row__value--carried">
|
|
39
|
+
{formatValue(mapping.oldValue)}
|
|
40
|
+
</span>
|
|
41
|
+
{:else}
|
|
42
|
+
<span class="config-mapping-row__value config-mapping-row__value--default">
|
|
43
|
+
{formatValue(mapping.newDefault)}
|
|
44
|
+
</span>
|
|
45
|
+
{/if}
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<button
|
|
49
|
+
class="config-mapping-row__toggle"
|
|
50
|
+
class:config-mapping-row__toggle--carry={mapping.carryOver}
|
|
51
|
+
onclick={() => onToggle(mapping.key)}
|
|
52
|
+
type="button"
|
|
53
|
+
>
|
|
54
|
+
{mapping.carryOver ? "Carry over" : "Use default"}
|
|
55
|
+
</button>
|
|
56
|
+
{/if}
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<style>
|
|
60
|
+
.config-mapping-row {
|
|
61
|
+
display: flex;
|
|
62
|
+
align-items: center;
|
|
63
|
+
gap: var(--fd-space-xs);
|
|
64
|
+
padding: var(--fd-space-xs) 0;
|
|
65
|
+
font-size: var(--fd-text-xs);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.config-mapping-row__info {
|
|
69
|
+
flex: 0 0 auto;
|
|
70
|
+
min-width: 4rem;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.config-mapping-row__key {
|
|
74
|
+
font-weight: 500;
|
|
75
|
+
color: var(--fd-foreground);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.config-mapping-row__complex {
|
|
79
|
+
flex: 1;
|
|
80
|
+
color: var(--fd-muted-foreground);
|
|
81
|
+
font-style: italic;
|
|
82
|
+
font-size: var(--fd-text-xs);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.config-mapping-row__values {
|
|
86
|
+
flex: 1;
|
|
87
|
+
min-width: 0;
|
|
88
|
+
overflow: hidden;
|
|
89
|
+
text-overflow: ellipsis;
|
|
90
|
+
white-space: nowrap;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.config-mapping-row__value {
|
|
94
|
+
font-family: var(--fd-font-mono, monospace);
|
|
95
|
+
font-size: var(--fd-text-xs);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.config-mapping-row__value--carried {
|
|
99
|
+
color: var(--fd-success);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.config-mapping-row__value--default {
|
|
103
|
+
color: var(--fd-muted-foreground);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.config-mapping-row__toggle {
|
|
107
|
+
flex-shrink: 0;
|
|
108
|
+
padding: 0.1875rem 0.5rem;
|
|
109
|
+
border: 1px solid var(--fd-border);
|
|
110
|
+
border-radius: var(--fd-radius-sm);
|
|
111
|
+
background-color: var(--fd-background);
|
|
112
|
+
color: var(--fd-muted-foreground);
|
|
113
|
+
font-size: var(--fd-text-xs);
|
|
114
|
+
font-weight: 500;
|
|
115
|
+
cursor: pointer;
|
|
116
|
+
transition: all var(--fd-transition-fast);
|
|
117
|
+
white-space: nowrap;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.config-mapping-row__toggle:hover {
|
|
121
|
+
border-color: var(--fd-border-strong);
|
|
122
|
+
color: var(--fd-foreground);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.config-mapping-row__toggle--carry {
|
|
126
|
+
background-color: color-mix(in srgb, var(--fd-success) 10%, transparent);
|
|
127
|
+
border-color: color-mix(in srgb, var(--fd-success) 30%, transparent);
|
|
128
|
+
color: var(--fd-success);
|
|
129
|
+
}
|
|
130
|
+
</style>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { EditableConfigMapping } from "../utils/nodeSwap.js";
|
|
2
|
+
interface Props {
|
|
3
|
+
mapping: EditableConfigMapping;
|
|
4
|
+
onToggle: (key: string) => void;
|
|
5
|
+
}
|
|
6
|
+
declare const ConfigMappingRow: import("svelte").Component<Props, {}, "">;
|
|
7
|
+
type ConfigMappingRow = ReturnType<typeof ConfigMappingRow>;
|
|
8
|
+
export default ConfigMappingRow;
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
<script lang="ts">
|
|
10
10
|
import type { Snippet } from "svelte";
|
|
11
|
+
import Icon from "@iconify/svelte";
|
|
11
12
|
import ReadOnlyDetails from "./ReadOnlyDetails.svelte";
|
|
12
13
|
import { getUiSettings } from "../stores/settingsStore.svelte.js";
|
|
13
14
|
|
|
@@ -37,6 +38,8 @@
|
|
|
37
38
|
configTitle?: string;
|
|
38
39
|
/** Callback function when the panel is closed */
|
|
39
40
|
onClose: () => void;
|
|
41
|
+
/** Optional callback to initiate node swap — when provided, shows swap button */
|
|
42
|
+
onSwap?: () => void;
|
|
40
43
|
/** Slot content for the configuration form */
|
|
41
44
|
children?: Snippet;
|
|
42
45
|
}
|
|
@@ -48,6 +51,7 @@
|
|
|
48
51
|
details = [],
|
|
49
52
|
configTitle = "Configuration",
|
|
50
53
|
onClose,
|
|
54
|
+
onSwap,
|
|
51
55
|
children,
|
|
52
56
|
}: Props = $props();
|
|
53
57
|
|
|
@@ -66,13 +70,25 @@
|
|
|
66
70
|
<!-- Header -->
|
|
67
71
|
<div class="config-panel__header">
|
|
68
72
|
<h2 class="config-panel__title">{title}</h2>
|
|
69
|
-
<
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
<div class="config-panel__actions">
|
|
74
|
+
{#if onSwap}
|
|
75
|
+
<button
|
|
76
|
+
class="config-panel__action-btn"
|
|
77
|
+
onclick={onSwap}
|
|
78
|
+
aria-label="Swap node"
|
|
79
|
+
title="Swap node type"
|
|
80
|
+
>
|
|
81
|
+
<Icon icon="heroicons:arrows-right-left" />
|
|
82
|
+
</button>
|
|
83
|
+
{/if}
|
|
84
|
+
<button
|
|
85
|
+
class="config-panel__close"
|
|
86
|
+
onclick={onClose}
|
|
87
|
+
aria-label="Close panel"
|
|
88
|
+
>
|
|
89
|
+
×
|
|
90
|
+
</button>
|
|
91
|
+
</div>
|
|
76
92
|
</div>
|
|
77
93
|
|
|
78
94
|
<!-- Details Section (between header and content) -->
|
|
@@ -118,6 +134,34 @@
|
|
|
118
134
|
color: var(--fd-foreground);
|
|
119
135
|
}
|
|
120
136
|
|
|
137
|
+
.config-panel__actions {
|
|
138
|
+
display: flex;
|
|
139
|
+
align-items: center;
|
|
140
|
+
gap: 0.25rem;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.config-panel__action-btn {
|
|
144
|
+
background: none;
|
|
145
|
+
border: none;
|
|
146
|
+
font-size: 1rem;
|
|
147
|
+
line-height: 1;
|
|
148
|
+
cursor: pointer;
|
|
149
|
+
color: var(--fd-muted-foreground);
|
|
150
|
+
padding: 0.25rem;
|
|
151
|
+
border-radius: var(--fd-radius-sm);
|
|
152
|
+
display: flex;
|
|
153
|
+
align-items: center;
|
|
154
|
+
justify-content: center;
|
|
155
|
+
transition:
|
|
156
|
+
color var(--fd-transition-fast),
|
|
157
|
+
background-color var(--fd-transition-fast);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.config-panel__action-btn:hover {
|
|
161
|
+
color: var(--fd-primary);
|
|
162
|
+
background-color: var(--fd-subtle);
|
|
163
|
+
}
|
|
164
|
+
|
|
121
165
|
.config-panel__close {
|
|
122
166
|
background: none;
|
|
123
167
|
border: none;
|
|
@@ -179,6 +223,11 @@
|
|
|
179
223
|
padding: 0.125rem;
|
|
180
224
|
}
|
|
181
225
|
|
|
226
|
+
.config-panel--compact .config-panel__action-btn {
|
|
227
|
+
font-size: 0.875rem;
|
|
228
|
+
padding: 0.125rem;
|
|
229
|
+
}
|
|
230
|
+
|
|
182
231
|
.config-panel--compact .config-panel__details {
|
|
183
232
|
padding: 0.5rem 0.75rem;
|
|
184
233
|
}
|
|
@@ -24,6 +24,8 @@ interface Props {
|
|
|
24
24
|
configTitle?: string;
|
|
25
25
|
/** Callback function when the panel is closed */
|
|
26
26
|
onClose: () => void;
|
|
27
|
+
/** Optional callback to initiate node swap — when provided, shows swap button */
|
|
28
|
+
onSwap?: () => void;
|
|
27
29
|
/** Slot content for the configuration form */
|
|
28
30
|
children?: Snippet;
|
|
29
31
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
Approach:
|
|
7
7
|
1. Compute the full bezier path (xyflow's getBezierPath)
|
|
8
8
|
2. Parse the SVG path to extract the cubic bezier control points
|
|
9
|
-
3.
|
|
9
|
+
3. Compute exact tangent at endpoint via bezier derivative P'(1) = 3(P3-P2)
|
|
10
10
|
4. Shorten the path along that tangent and draw the arrowhead at the target
|
|
11
11
|
-->
|
|
12
12
|
|
|
@@ -42,32 +42,6 @@
|
|
|
42
42
|
return match ? match[1].trim() : "var(--fd-edge-data, #64748b)";
|
|
43
43
|
});
|
|
44
44
|
|
|
45
|
-
/**
|
|
46
|
-
* Evaluate a cubic bezier at parameter t.
|
|
47
|
-
* P(t) = (1-t)^3 * P0 + 3(1-t)^2 * t * P1 + 3(1-t) * t^2 * P2 + t^3 * P3
|
|
48
|
-
*/
|
|
49
|
-
function bezierAt(
|
|
50
|
-
p0x: number,
|
|
51
|
-
p0y: number,
|
|
52
|
-
p1x: number,
|
|
53
|
-
p1y: number,
|
|
54
|
-
p2x: number,
|
|
55
|
-
p2y: number,
|
|
56
|
-
p3x: number,
|
|
57
|
-
p3y: number,
|
|
58
|
-
t: number,
|
|
59
|
-
): { x: number; y: number } {
|
|
60
|
-
const u = 1 - t;
|
|
61
|
-
const uu = u * u;
|
|
62
|
-
const uuu = uu * u;
|
|
63
|
-
const tt = t * t;
|
|
64
|
-
const ttt = tt * t;
|
|
65
|
-
return {
|
|
66
|
-
x: uuu * p0x + 3 * uu * t * p1x + 3 * u * tt * p2x + ttt * p3x,
|
|
67
|
-
y: uuu * p0y + 3 * uu * t * p1y + 3 * u * tt * p2y + ttt * p3y,
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
45
|
/**
|
|
72
46
|
* Parse the SVG cubic bezier path "M x0,y0 C x1,y1 x2,y2 x3,y3"
|
|
73
47
|
* into the four control points.
|
|
@@ -87,9 +61,6 @@
|
|
|
87
61
|
};
|
|
88
62
|
}
|
|
89
63
|
|
|
90
|
-
// Parameter near the end of the curve for tangent sampling
|
|
91
|
-
const T_SAMPLE = 0.9;
|
|
92
|
-
|
|
93
64
|
let computed = $derived.by(() => {
|
|
94
65
|
// 1. Get the full bezier path from xyflow
|
|
95
66
|
const [fullPath, lx, ly] = getBezierPath({
|
|
@@ -108,39 +79,19 @@
|
|
|
108
79
|
return { path: fullPath, labelX: lx, labelY: ly, angleDeg: 0 };
|
|
109
80
|
}
|
|
110
81
|
|
|
111
|
-
// 3.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
cp.p1x,
|
|
116
|
-
cp.p1y,
|
|
117
|
-
cp.p2x,
|
|
118
|
-
cp.p2y,
|
|
119
|
-
cp.p3x,
|
|
120
|
-
cp.p3y,
|
|
121
|
-
T_SAMPLE,
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
// 4. Tangent direction: from reference point to the target
|
|
125
|
-
const dx = targetX - ref.x;
|
|
126
|
-
const dy = targetY - ref.y;
|
|
82
|
+
// 3. Compute exact tangent at curve endpoint using bezier derivative:
|
|
83
|
+
// P'(1) = 3 * (P3 - P2), giving the true arrival direction
|
|
84
|
+
const dx = cp.p3x - cp.p2x;
|
|
85
|
+
const dy = cp.p3y - cp.p2y;
|
|
127
86
|
const angleDeg = (Math.atan2(dy, dx) * 180) / Math.PI;
|
|
128
87
|
const angleRad = Math.atan2(dy, dx);
|
|
129
88
|
|
|
130
|
-
//
|
|
89
|
+
// 4. Shorten: move the endpoint back along the tangent
|
|
131
90
|
const adjX = targetX - Math.cos(angleRad) * ARROW_LENGTH_PX;
|
|
132
91
|
const adjY = targetY - Math.sin(angleRad) * ARROW_LENGTH_PX;
|
|
133
92
|
|
|
134
|
-
//
|
|
135
|
-
const
|
|
136
|
-
sourceX,
|
|
137
|
-
sourceY,
|
|
138
|
-
targetX: adjX,
|
|
139
|
-
targetY: adjY,
|
|
140
|
-
sourcePosition,
|
|
141
|
-
targetPosition,
|
|
142
|
-
curvature: pathOptions?.curvature,
|
|
143
|
-
});
|
|
93
|
+
// 5. Build shortened path from existing control points — no second getBezierPath() call
|
|
94
|
+
const shortenedPath = `M ${cp.p0x},${cp.p0y} C ${cp.p1x},${cp.p1y} ${cp.p2x},${cp.p2y} ${adjX},${adjY}`;
|
|
144
95
|
|
|
145
96
|
return { path: shortenedPath, labelX: lx, labelY: ly, angleDeg };
|
|
146
97
|
});
|
|
@@ -82,23 +82,23 @@
|
|
|
82
82
|
|
|
83
83
|
<style>
|
|
84
84
|
.flowdrop-logo {
|
|
85
|
-
--logo-bg: #f9f9f9;
|
|
86
|
-
--logo-stroke: #000000;
|
|
87
|
-
--logo-line-fill: #000000;
|
|
88
|
-
--logo-drop: #009cde;
|
|
89
|
-
--logo-circle: #f46351;
|
|
90
|
-
--logo-left: #ccbaf4;
|
|
91
|
-
--logo-right: #ffc423;
|
|
85
|
+
--logo-bg: var(--fd-logo-bg, #f9f9f9);
|
|
86
|
+
--logo-stroke: var(--fd-logo-stroke, #000000);
|
|
87
|
+
--logo-line-fill: var(--fd-logo-line-fill, #000000);
|
|
88
|
+
--logo-drop: var(--fd-logo-drop, #009cde);
|
|
89
|
+
--logo-circle: var(--fd-logo-circle, #f46351);
|
|
90
|
+
--logo-left: var(--fd-logo-left, #ccbaf4);
|
|
91
|
+
--logo-right: var(--fd-logo-right, #ffc423);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
:global([data-theme="dark"]) .flowdrop-logo {
|
|
95
|
-
--logo-bg: none;
|
|
96
|
-
--logo-stroke: #ffffff;
|
|
97
|
-
--logo-line-fill: none;
|
|
98
|
-
--logo-drop: none;
|
|
99
|
-
--logo-circle: none;
|
|
100
|
-
--logo-left: none;
|
|
101
|
-
--logo-right: none;
|
|
95
|
+
--logo-bg: var(--fd-logo-bg, none);
|
|
96
|
+
--logo-stroke: var(--fd-logo-stroke, #ffffff);
|
|
97
|
+
--logo-line-fill: var(--fd-logo-line-fill, none);
|
|
98
|
+
--logo-drop: var(--fd-logo-drop, none);
|
|
99
|
+
--logo-circle: var(--fd-logo-circle, none);
|
|
100
|
+
--logo-left: var(--fd-logo-left, none);
|
|
101
|
+
--logo-right: var(--fd-logo-right, none);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
.flowdrop-logo :global(svg) {
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
/**
|
|
115
115
|
* Filter logs by selected node
|
|
116
116
|
*/
|
|
117
|
-
let filteredLogs = $derived(() => {
|
|
117
|
+
let filteredLogs = $derived.by(() => {
|
|
118
118
|
if (props.selectedNode) {
|
|
119
119
|
return props.logs.filter((log) => log.nodeId === props.selectedNode?.id);
|
|
120
120
|
}
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
* Export logs
|
|
133
133
|
*/
|
|
134
134
|
function exportLogs(): void {
|
|
135
|
-
const logText = filteredLogs
|
|
135
|
+
const logText = filteredLogs
|
|
136
136
|
.map(
|
|
137
137
|
(log) =>
|
|
138
138
|
`[${formatTimestamp(log.timestamp)}] ${log.level.toUpperCase()}: ${log.message}${log.nodeId ? ` (Node: ${log.nodeId})` : ""}`,
|
|
@@ -198,9 +198,9 @@
|
|
|
198
198
|
<!-- Content -->
|
|
199
199
|
<div class="logs-sidebar__content">
|
|
200
200
|
<!-- Logs List -->
|
|
201
|
-
{#if filteredLogs
|
|
201
|
+
{#if filteredLogs.length > 0}
|
|
202
202
|
<div class="logs-sidebar__logs" bind:this={logsContainer}>
|
|
203
|
-
{#each filteredLogs
|
|
203
|
+
{#each filteredLogs as log, index (index)}
|
|
204
204
|
<div
|
|
205
205
|
class="logs-sidebar__log-entry"
|
|
206
206
|
class:logs-sidebar__log-entry--error={log.level === "error"}
|
|
@@ -276,7 +276,7 @@
|
|
|
276
276
|
|
|
277
277
|
{#if props.logs.length > 0}
|
|
278
278
|
<p class="logs-sidebar__info-text">
|
|
279
|
-
{filteredLogs
|
|
279
|
+
{filteredLogs.length} of {props.logs.length} log entries
|
|
280
280
|
</p>
|
|
281
281
|
{/if}
|
|
282
282
|
</div>
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import Icon from "@iconify/svelte";
|
|
11
11
|
import Logo from "./Logo.svelte";
|
|
12
12
|
import SettingsModal from "./SettingsModal.svelte";
|
|
13
|
+
import type { SettingsCategory } from "../types/settings.js";
|
|
13
14
|
|
|
14
15
|
interface NavbarAction {
|
|
15
16
|
label: string;
|
|
@@ -38,6 +39,12 @@
|
|
|
38
39
|
breadcrumbs?: BreadcrumbItem[];
|
|
39
40
|
/** Show settings gear icon */
|
|
40
41
|
showSettings?: boolean;
|
|
42
|
+
/** Which settings tabs to show in the modal */
|
|
43
|
+
settingsCategories?: SettingsCategory[];
|
|
44
|
+
/** Show the "Sync to Cloud" button in the settings modal */
|
|
45
|
+
showSettingsSyncButton?: boolean;
|
|
46
|
+
/** Show the reset buttons in the settings modal */
|
|
47
|
+
showSettingsResetButton?: boolean;
|
|
41
48
|
}
|
|
42
49
|
|
|
43
50
|
let {
|
|
@@ -46,6 +53,9 @@
|
|
|
46
53
|
title,
|
|
47
54
|
breadcrumbs = [],
|
|
48
55
|
showSettings = true,
|
|
56
|
+
settingsCategories,
|
|
57
|
+
showSettingsSyncButton,
|
|
58
|
+
showSettingsResetButton,
|
|
49
59
|
}: Props = $props();
|
|
50
60
|
|
|
51
61
|
// Dropdown state
|
|
@@ -263,7 +273,16 @@
|
|
|
263
273
|
|
|
264
274
|
<!-- Settings Modal -->
|
|
265
275
|
{#if showSettings}
|
|
266
|
-
|
|
276
|
+
{@const settingsModalProps = {
|
|
277
|
+
...(settingsCategories !== undefined && { categories: settingsCategories }),
|
|
278
|
+
...(showSettingsSyncButton !== undefined && {
|
|
279
|
+
showSyncButton: showSettingsSyncButton,
|
|
280
|
+
}),
|
|
281
|
+
...(showSettingsResetButton !== undefined && {
|
|
282
|
+
showResetButton: showSettingsResetButton,
|
|
283
|
+
}),
|
|
284
|
+
}}
|
|
285
|
+
<SettingsModal bind:open={isSettingsOpen} {...settingsModalProps} />
|
|
267
286
|
{/if}
|
|
268
287
|
|
|
269
288
|
<style>
|
|
@@ -739,16 +758,50 @@
|
|
|
739
758
|
}
|
|
740
759
|
|
|
741
760
|
.flowdrop-navbar__start {
|
|
742
|
-
width:
|
|
743
|
-
min-width:
|
|
761
|
+
width: auto;
|
|
762
|
+
min-width: auto;
|
|
763
|
+
flex-shrink: 0;
|
|
744
764
|
}
|
|
745
765
|
|
|
746
|
-
.flowdrop-
|
|
766
|
+
.flowdrop-navbar__center {
|
|
767
|
+
min-width: 0;
|
|
768
|
+
overflow: hidden;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
.flowdrop-navbar__breadcrumb-list {
|
|
772
|
+
overflow: hidden;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/* Show only icons for non-current breadcrumb items */
|
|
776
|
+
.flowdrop-navbar__breadcrumb-link .flowdrop-navbar__breadcrumb-text {
|
|
777
|
+
display: none;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
.flowdrop-navbar__breadcrumb-current .flowdrop-navbar__breadcrumb-text {
|
|
781
|
+
overflow: hidden;
|
|
782
|
+
text-overflow: ellipsis;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/* Force dropdown mode on small screens regardless of theme */
|
|
786
|
+
.flowdrop-navbar__split-actions {
|
|
787
|
+
display: none;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
.flowdrop-navbar__dropdown-mode {
|
|
791
|
+
display: flex;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
.flowdrop-navbar__action-label {
|
|
747
795
|
display: none;
|
|
748
796
|
}
|
|
749
797
|
|
|
798
|
+
.flowdrop-navbar__primary-action {
|
|
799
|
+
padding: 0.5rem;
|
|
800
|
+
border-radius: var(--fd-radius-md) 0 0 var(--fd-radius-md);
|
|
801
|
+
}
|
|
802
|
+
|
|
750
803
|
.flowdrop-text--logo {
|
|
751
|
-
|
|
804
|
+
display: none;
|
|
752
805
|
}
|
|
753
806
|
|
|
754
807
|
.flowdrop-text--tagline {
|
|
@@ -767,11 +820,6 @@
|
|
|
767
820
|
}
|
|
768
821
|
|
|
769
822
|
@media (max-width: 480px) {
|
|
770
|
-
.flowdrop-navbar__start {
|
|
771
|
-
width: 240px;
|
|
772
|
-
min-width: 240px;
|
|
773
|
-
}
|
|
774
|
-
|
|
775
823
|
.flowdrop-navbar__title-text {
|
|
776
824
|
font-size: 0.75rem;
|
|
777
825
|
max-width: 200px;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SettingsCategory } from "../types/settings.js";
|
|
1
2
|
interface NavbarAction {
|
|
2
3
|
label: string;
|
|
3
4
|
href: string;
|
|
@@ -23,6 +24,12 @@ interface Props {
|
|
|
23
24
|
breadcrumbs?: BreadcrumbItem[];
|
|
24
25
|
/** Show settings gear icon */
|
|
25
26
|
showSettings?: boolean;
|
|
27
|
+
/** Which settings tabs to show in the modal */
|
|
28
|
+
settingsCategories?: SettingsCategory[];
|
|
29
|
+
/** Show the "Sync to Cloud" button in the settings modal */
|
|
30
|
+
showSettingsSyncButton?: boolean;
|
|
31
|
+
/** Show the reset buttons in the settings modal */
|
|
32
|
+
showSettingsResetButton?: boolean;
|
|
26
33
|
}
|
|
27
34
|
declare const Navbar: import("svelte").Component<Props, {}, "">;
|
|
28
35
|
type Navbar = ReturnType<typeof Navbar>;
|