diff --git a/core/src/configurable-nodes/configurable-node-utils.ts b/core/src/configurable-nodes/configurable-node-utils.ts
index 19122d1e5..707847d66 100644
--- a/core/src/configurable-nodes/configurable-node-utils.ts
+++ b/core/src/configurable-nodes/configurable-node-utils.ts
@@ -36,9 +36,9 @@ export function extractInputsFromValue(
let val: ConfigurableValue = _val as any;
if (!isConfigurableValue(val)) {
- console.warn(
- `Value ${key} isn't a valid ConfigurableValue, converting to dynamic`
- );
+ // console.warn(
+ // `Value ${key} isn't a valid ConfigurableValue, converting to dynamic`
+ // );
val = configurableValue("dynamic", `{{${key}}}`);
}
diff --git a/core/src/configurable-nodes/configurable-nodes.ts b/core/src/configurable-nodes/configurable-nodes.ts
index 0468c6191..90700b8ee 100644
--- a/core/src/configurable-nodes/configurable-nodes.ts
+++ b/core/src/configurable-nodes/configurable-nodes.ts
@@ -489,9 +489,9 @@ export function processConfigurableNode(node: CodeNode, secrets: Record 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
- if (t[2]) _.ops.pop();
- _.trys.pop(); continue;
- }
- op = body.call(thisArg, _);
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
- }
-};
-Object.defineProperty(exports, "__esModule", { value: true });
-var loader_1 = require("@flyde/loader");
-var path_1 = require("path");
-function main() {
- return __awaiter(this, void 0, void 0, function () {
- var result;
- return __generator(this, function (_a) {
- switch (_a.label) {
- case 0:
- console.log("š Running your first Flyde flow!");
- return [4 /*yield*/, (0, loader_1.runFlow)(path_1.default.join(__dirname, "hello-world.flyde"), {})];
- case 1:
- result = _a.sent();
- console.log("Result:", result);
- console.log("ā Flow executed successfully!");
- return [2 /*return*/];
- }
- });
- });
-}
-main().catch(console.error);
diff --git a/create-flyde-app/templates/default/package.json b/create-flyde-app/templates/default/package.json
index a8c0c7c68..bf7f29d66 100644
--- a/create-flyde-app/templates/default/package.json
+++ b/create-flyde-app/templates/default/package.json
@@ -2,9 +2,9 @@
"name": "my-flyde-project",
"version": "1.0.0",
"description": "A Flyde visual programming project",
- "main": "index.js",
+ "main": "index.ts",
"scripts": {
- "start": "node index.js",
+ "start": "tsx index.ts",
"dev": "tsx watch index.ts"
},
"dependencies": {
diff --git a/main.code-workspace b/main.code-workspace
index aa5ef310b..6dd4d20d1 100644
--- a/main.code-workspace
+++ b/main.code-workspace
@@ -18,6 +18,9 @@
{
"path": "vscode"
},
+ {
+ "path": "create-flyde-app"
+ }
],
"settings": {
"search.useGlobalIgnoreFiles": true,
diff --git a/nodes/src/ThirdParty/index.ts b/nodes/src/ThirdParty/browser.ts
similarity index 94%
rename from nodes/src/ThirdParty/index.ts
rename to nodes/src/ThirdParty/browser.ts
index 442055a64..46eb0aed6 100644
--- a/nodes/src/ThirdParty/index.ts
+++ b/nodes/src/ThirdParty/browser.ts
@@ -1,4 +1,3 @@
-export { CodeNode } from "@flyde/core";
export { Supabase } from "./supabase.flyde";
export { OpenAI } from "./openai.flyde";
export { OpenAIResponsesAPI } from "./openai-responses.flyde";
diff --git a/nodes/src/ThirdParty/server.ts b/nodes/src/ThirdParty/server.ts
index 29f2175c2..5f17b1c6b 100644
--- a/nodes/src/ThirdParty/server.ts
+++ b/nodes/src/ThirdParty/server.ts
@@ -1,2 +1,3 @@
-export * from "./";
-export { PostgreSQL } from "./postgres.flyde";
\ No newline at end of file
+export * from "./browser";
+export { PostgreSQL } from "./postgres.flyde";
+export { GoogleSheets } from "./googlesheets.flyde";
\ No newline at end of file
diff --git a/nodes/src/all-browser.ts b/nodes/src/all-browser.ts
index 6009fddb7..09e59f821 100644
--- a/nodes/src/all-browser.ts
+++ b/nodes/src/all-browser.ts
@@ -10,4 +10,4 @@ export * from "./State";
export * from "./Timing";
export * from "./Values/index";
export * from "./Note/Note.flyde";
-export * from "./ThirdParty";
+export * from "./ThirdParty/browser";
diff --git a/nodes/src/nodes-library-data.ts b/nodes/src/nodes-library-data.ts
index f6a4f5049..e0eb79a50 100644
--- a/nodes/src/nodes-library-data.ts
+++ b/nodes/src/nodes-library-data.ts
@@ -29,7 +29,7 @@ import {
codeNodeToImportableEditorNode,
NodeLibraryData,
} from "@flyde/core";
-import { Airtable, Anthropic, DiscordMessage, Firecrawl, GoogleSheets, LLMCondition, Notion, Resend, ScrapingBee, SendGrid, Slack, Supabase, Tavily } from "./ThirdParty";
+import { Airtable, Anthropic, DiscordMessage, Firecrawl, GoogleSheets, LLMCondition, Notion, Resend, ScrapingBee, SendGrid, Slack, Supabase, Tavily } from "./ThirdParty/browser";
import { OpenAIResponsesAPI } from "./ThirdParty/openai-responses.flyde";
const nodesSource: CodeNodeSource = {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0756460a8..9caac238b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -599,7 +599,7 @@ importers:
version: 5.62.0(eslint@8.57.1)(typescript@5.8.3)
'@vitejs/plugin-react':
specifier: ^4.2.0
- version: 4.6.0(vite@5.4.19(@types/node@20.19.4)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.1))
+ version: 4.6.0(vite@5.4.19(@types/node@20.19.4)(lightningcss@1.30.1)(sass-embedded@1.89.2)(sass@1.89.2)(terser@5.43.1))
'@vscode/sqlite3':
specifier: 5.1.8-vscode
version: 5.1.8-vscode
@@ -618,6 +618,9 @@ importers:
mocha:
specifier: ^10.0.0
version: 10.8.2
+ sass-embedded:
+ specifier: ^1.89.2
+ version: 1.89.2
sinon:
specifier: ^15.0.4
version: 15.2.0
@@ -626,10 +629,10 @@ importers:
version: 5.8.3
vite:
specifier: ^5.0.0
- version: 5.4.19(@types/node@20.19.4)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.1)
+ version: 5.4.19(@types/node@20.19.4)(lightningcss@1.30.1)(sass-embedded@1.89.2)(sass@1.89.2)(terser@5.43.1)
vite-plugin-checker:
specifier: ^0.9.3
- version: 0.9.3(eslint@8.57.1)(optionator@0.9.4)(typescript@5.8.3)(vite@5.4.19(@types/node@20.19.4)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.1))
+ version: 0.9.3(eslint@8.57.1)(optionator@0.9.4)(typescript@5.8.3)(vite@5.4.19(@types/node@20.19.4)(lightningcss@1.30.1)(sass-embedded@1.89.2)(sass@1.89.2)(terser@5.43.1))
website:
dependencies:
@@ -890,6 +893,9 @@ packages:
resolution: {integrity: sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==}
engines: {node: '>=6.9.0'}
+ '@bufbuild/protobuf@2.6.1':
+ resolution: {integrity: sha512-DaG6XlyKpz08bmHY5SGX2gfIllaqtDJ/KwVoxsmP22COOLYwDBe7yD3DZGwXem/Xq7QOc9cuR7R3MpAv5CFfDw==}
+
'@cspotcode/source-map-support@0.8.1':
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
engines: {node: '>=12'}
@@ -3644,6 +3650,9 @@ packages:
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
hasBin: true
+ buffer-builder@0.2.0:
+ resolution: {integrity: sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg==}
+
buffer-crc32@0.2.13:
resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==}
@@ -3863,6 +3872,9 @@ packages:
colorette@2.0.20:
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
+ colorjs.io@0.5.2:
+ resolution: {integrity: sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==}
+
combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
@@ -6903,6 +6915,107 @@ packages:
safer-buffer@2.1.2:
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+ sass-embedded-android-arm64@1.89.2:
+ resolution: {integrity: sha512-+pq7a7AUpItNyPu61sRlP6G2A8pSPpyazASb+8AK2pVlFayCSPAEgpwpCE9A2/Xj86xJZeMizzKUHxM2CBCUxA==}
+ engines: {node: '>=14.0.0'}
+ cpu: [arm64]
+ os: [android]
+
+ sass-embedded-android-arm@1.89.2:
+ resolution: {integrity: sha512-oHAPTboBHRZlDBhyRB6dvDKh4KvFs+DZibDHXbkSI6dBZxMTT+Yb2ivocHnctVGucKTLQeT7+OM5DjWHyynL/A==}
+ engines: {node: '>=14.0.0'}
+ cpu: [arm]
+ os: [android]
+
+ sass-embedded-android-riscv64@1.89.2:
+ resolution: {integrity: sha512-HfJJWp/S6XSYvlGAqNdakeEMPOdhBkj2s2lN6SHnON54rahKem+z9pUbCriUJfM65Z90lakdGuOfidY61R9TYg==}
+ engines: {node: '>=14.0.0'}
+ cpu: [riscv64]
+ os: [android]
+
+ sass-embedded-android-x64@1.89.2:
+ resolution: {integrity: sha512-BGPzq53VH5z5HN8de6jfMqJjnRe1E6sfnCWFd4pK+CAiuM7iw5Fx6BQZu3ikfI1l2GY0y6pRXzsVLdp/j4EKEA==}
+ engines: {node: '>=14.0.0'}
+ cpu: [x64]
+ os: [android]
+
+ sass-embedded-darwin-arm64@1.89.2:
+ resolution: {integrity: sha512-UCm3RL/tzMpG7DsubARsvGUNXC5pgfQvP+RRFJo9XPIi6elopY5B6H4m9dRYDpHA+scjVthdiDwkPYr9+S/KGw==}
+ engines: {node: '>=14.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ sass-embedded-darwin-x64@1.89.2:
+ resolution: {integrity: sha512-D9WxtDY5VYtMApXRuhQK9VkPHB8R79NIIR6xxVlN2MIdEid/TZWi1MHNweieETXhWGrKhRKglwnHxxyKdJYMnA==}
+ engines: {node: '>=14.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ sass-embedded-linux-arm64@1.89.2:
+ resolution: {integrity: sha512-2N4WW5LLsbtrWUJ7iTpjvhajGIbmDR18ZzYRywHdMLpfdPApuHPMDF5CYzHbS+LLx2UAx7CFKBnj5LLjY6eFgQ==}
+ engines: {node: '>=14.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ sass-embedded-linux-arm@1.89.2:
+ resolution: {integrity: sha512-leP0t5U4r95dc90o8TCWfxNXwMAsQhpWxTkdtySDpngoqtTy3miMd7EYNYd1znI0FN1CBaUvbdCMbnbPwygDlA==}
+ engines: {node: '>=14.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ sass-embedded-linux-musl-arm64@1.89.2:
+ resolution: {integrity: sha512-nTyuaBX6U1A/cG7WJh0pKD1gY8hbg1m2SnzsyoFG+exQ0lBX/lwTLHq3nyhF+0atv7YYhYKbmfz+sjPP8CZ9lw==}
+ engines: {node: '>=14.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ sass-embedded-linux-musl-arm@1.89.2:
+ resolution: {integrity: sha512-Z6gG2FiVEEdxYHRi2sS5VIYBmp17351bWtOCUZ/thBM66+e70yiN6Eyqjz80DjL8haRUegNQgy9ZJqsLAAmr9g==}
+ engines: {node: '>=14.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ sass-embedded-linux-musl-riscv64@1.89.2:
+ resolution: {integrity: sha512-N6oul+qALO0SwGY8JW7H/Vs0oZIMrRMBM4GqX3AjM/6y8JsJRxkAwnfd0fDyK+aICMFarDqQonQNIx99gdTZqw==}
+ engines: {node: '>=14.0.0'}
+ cpu: [riscv64]
+ os: [linux]
+
+ sass-embedded-linux-musl-x64@1.89.2:
+ resolution: {integrity: sha512-K+FmWcdj/uyP8GiG9foxOCPfb5OAZG0uSVq80DKgVSC0U44AdGjvAvVZkrgFEcZ6cCqlNC2JfYmslB5iqdL7tg==}
+ engines: {node: '>=14.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ sass-embedded-linux-riscv64@1.89.2:
+ resolution: {integrity: sha512-g9nTbnD/3yhOaskeqeBQETbtfDQWRgsjHok6bn7DdAuwBsyrR3JlSFyqKc46pn9Xxd9SQQZU8AzM4IR+sY0A0w==}
+ engines: {node: '>=14.0.0'}
+ cpu: [riscv64]
+ os: [linux]
+
+ sass-embedded-linux-x64@1.89.2:
+ resolution: {integrity: sha512-Ax7dKvzncyQzIl4r7012KCMBvJzOz4uwSNoyoM5IV6y5I1f5hEwI25+U4WfuTqdkv42taCMgpjZbh9ERr6JVMQ==}
+ engines: {node: '>=14.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ sass-embedded-win32-arm64@1.89.2:
+ resolution: {integrity: sha512-j96iJni50ZUsfD6tRxDQE2QSYQ2WrfHxeiyAXf41Kw0V4w5KYR/Sf6rCZQLMTUOHnD16qTMVpQi20LQSqf4WGg==}
+ engines: {node: '>=14.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ sass-embedded-win32-x64@1.89.2:
+ resolution: {integrity: sha512-cS2j5ljdkQsb4PaORiClaVYynE9OAPZG/XjbOMxpQmjRIf7UroY4PEIH+Waf+y47PfXFX9SyxhYuw2NIKGbEng==}
+ engines: {node: '>=14.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ sass-embedded@1.89.2:
+ resolution: {integrity: sha512-Ack2K8rc57kCFcYlf3HXpZEJFNUX8xd8DILldksREmYXQkRHI879yy8q4mRDJgrojkySMZqmmmW1NxrFxMsYaA==}
+ engines: {node: '>=16.0.0'}
+ hasBin: true
+
sass@1.89.2:
resolution: {integrity: sha512-xCmtksBKd/jdJ9Bt9p7nPKiuqrlBMBuuGkQlkhZjjQk3Ty48lv93k5Dq6OPkKt4XwxDJ7tvlfrTa1MPA9bf+QA==}
engines: {node: '>=14.0.0'}
@@ -7265,6 +7378,14 @@ packages:
symbol-tree@3.2.4:
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+ sync-child-process@1.0.2:
+ resolution: {integrity: sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==}
+ engines: {node: '>=16.0.0'}
+
+ sync-message-port@1.1.3:
+ resolution: {integrity: sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==}
+ engines: {node: '>=16.0.0'}
+
tailwind-merge@2.6.0:
resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==}
@@ -7657,6 +7778,9 @@ packages:
v8-compile-cache-lib@3.0.1:
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
+ varint@6.0.0:
+ resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==}
+
vary@1.1.2:
resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
engines: {node: '>= 0.8'}
@@ -8215,6 +8339,8 @@ snapshots:
'@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.27.1
+ '@bufbuild/protobuf@2.6.1': {}
+
'@cspotcode/source-map-support@0.8.1':
dependencies:
'@jridgewell/trace-mapping': 0.3.9
@@ -10392,7 +10518,7 @@ snapshots:
'@unrs/resolver-binding-win32-x64-msvc@1.10.1':
optional: true
- '@vitejs/plugin-react@4.6.0(vite@5.4.19(@types/node@20.19.4)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.1))':
+ '@vitejs/plugin-react@4.6.0(vite@5.4.19(@types/node@20.19.4)(lightningcss@1.30.1)(sass-embedded@1.89.2)(sass@1.89.2)(terser@5.43.1))':
dependencies:
'@babel/core': 7.28.0
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.0)
@@ -10400,7 +10526,7 @@ snapshots:
'@rolldown/pluginutils': 1.0.0-beta.19
'@types/babel__core': 7.20.5
react-refresh: 0.17.0
- vite: 5.4.19(@types/node@20.19.4)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.1)
+ vite: 5.4.19(@types/node@20.19.4)(lightningcss@1.30.1)(sass-embedded@1.89.2)(sass@1.89.2)(terser@5.43.1)
transitivePeerDependencies:
- supports-color
@@ -10918,6 +11044,8 @@ snapshots:
node-releases: 2.0.19
update-browserslist-db: 1.1.3(browserslist@4.25.1)
+ buffer-builder@0.2.0: {}
+
buffer-crc32@0.2.13: {}
buffer-equal-constant-time@1.0.1: {}
@@ -11157,6 +11285,8 @@ snapshots:
colorette@2.0.20: {}
+ colorjs.io@0.5.2: {}
+
combined-stream@1.0.8:
dependencies:
delayed-stream: 1.0.0
@@ -14980,6 +15110,82 @@ snapshots:
safer-buffer@2.1.2: {}
+ sass-embedded-android-arm64@1.89.2:
+ optional: true
+
+ sass-embedded-android-arm@1.89.2:
+ optional: true
+
+ sass-embedded-android-riscv64@1.89.2:
+ optional: true
+
+ sass-embedded-android-x64@1.89.2:
+ optional: true
+
+ sass-embedded-darwin-arm64@1.89.2:
+ optional: true
+
+ sass-embedded-darwin-x64@1.89.2:
+ optional: true
+
+ sass-embedded-linux-arm64@1.89.2:
+ optional: true
+
+ sass-embedded-linux-arm@1.89.2:
+ optional: true
+
+ sass-embedded-linux-musl-arm64@1.89.2:
+ optional: true
+
+ sass-embedded-linux-musl-arm@1.89.2:
+ optional: true
+
+ sass-embedded-linux-musl-riscv64@1.89.2:
+ optional: true
+
+ sass-embedded-linux-musl-x64@1.89.2:
+ optional: true
+
+ sass-embedded-linux-riscv64@1.89.2:
+ optional: true
+
+ sass-embedded-linux-x64@1.89.2:
+ optional: true
+
+ sass-embedded-win32-arm64@1.89.2:
+ optional: true
+
+ sass-embedded-win32-x64@1.89.2:
+ optional: true
+
+ sass-embedded@1.89.2:
+ dependencies:
+ '@bufbuild/protobuf': 2.6.1
+ buffer-builder: 0.2.0
+ colorjs.io: 0.5.2
+ immutable: 5.1.3
+ rxjs: 7.8.2
+ supports-color: 8.1.1
+ sync-child-process: 1.0.2
+ varint: 6.0.0
+ optionalDependencies:
+ sass-embedded-android-arm: 1.89.2
+ sass-embedded-android-arm64: 1.89.2
+ sass-embedded-android-riscv64: 1.89.2
+ sass-embedded-android-x64: 1.89.2
+ sass-embedded-darwin-arm64: 1.89.2
+ sass-embedded-darwin-x64: 1.89.2
+ sass-embedded-linux-arm: 1.89.2
+ sass-embedded-linux-arm64: 1.89.2
+ sass-embedded-linux-musl-arm: 1.89.2
+ sass-embedded-linux-musl-arm64: 1.89.2
+ sass-embedded-linux-musl-riscv64: 1.89.2
+ sass-embedded-linux-musl-x64: 1.89.2
+ sass-embedded-linux-riscv64: 1.89.2
+ sass-embedded-linux-x64: 1.89.2
+ sass-embedded-win32-arm64: 1.89.2
+ sass-embedded-win32-x64: 1.89.2
+
sass@1.89.2:
dependencies:
chokidar: 4.0.3
@@ -15452,6 +15658,12 @@ snapshots:
symbol-tree@3.2.4: {}
+ sync-child-process@1.0.2:
+ dependencies:
+ sync-message-port: 1.1.3
+
+ sync-message-port@1.1.3: {}
+
tailwind-merge@2.6.0: {}
tailwindcss-animate@1.0.7(tailwindcss@3.3.2(ts-node@10.9.2(@types/node@20.19.4)(typescript@5.8.3))):
@@ -15932,6 +16144,8 @@ snapshots:
v8-compile-cache-lib@3.0.1: {}
+ varint@6.0.0: {}
+
vary@1.1.2: {}
verror@1.10.0:
@@ -15955,7 +16169,7 @@ snapshots:
'@types/unist': 3.0.3
vfile-message: 4.0.2
- vite-plugin-checker@0.9.3(eslint@8.57.1)(optionator@0.9.4)(typescript@5.8.3)(vite@5.4.19(@types/node@20.19.4)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.1)):
+ vite-plugin-checker@0.9.3(eslint@8.57.1)(optionator@0.9.4)(typescript@5.8.3)(vite@5.4.19(@types/node@20.19.4)(lightningcss@1.30.1)(sass-embedded@1.89.2)(sass@1.89.2)(terser@5.43.1)):
dependencies:
'@babel/code-frame': 7.27.1
chokidar: 4.0.3
@@ -15965,14 +16179,14 @@ snapshots:
strip-ansi: 7.1.0
tiny-invariant: 1.3.3
tinyglobby: 0.2.14
- vite: 5.4.19(@types/node@20.19.4)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.1)
+ vite: 5.4.19(@types/node@20.19.4)(lightningcss@1.30.1)(sass-embedded@1.89.2)(sass@1.89.2)(terser@5.43.1)
vscode-uri: 3.1.0
optionalDependencies:
eslint: 8.57.1
optionator: 0.9.4
typescript: 5.8.3
- vite@5.4.19(@types/node@20.19.4)(lightningcss@1.30.1)(sass@1.89.2)(terser@5.43.1):
+ vite@5.4.19(@types/node@20.19.4)(lightningcss@1.30.1)(sass-embedded@1.89.2)(sass@1.89.2)(terser@5.43.1):
dependencies:
esbuild: 0.21.5
postcss: 8.5.6
@@ -15982,6 +16196,7 @@ snapshots:
fsevents: 2.3.3
lightningcss: 1.30.1
sass: 1.89.2
+ sass-embedded: 1.89.2
terser: 5.43.1
vscode-uri@3.1.0: {}
diff --git a/vscode/demo.gif b/vscode/demo.gif
deleted file mode 100644
index 2e1edd595..000000000
Binary files a/vscode/demo.gif and /dev/null differ
diff --git a/vscode/icon.png b/vscode/icon.png
index 444187fee..9ef0dfdc5 100644
Binary files a/vscode/icon.png and b/vscode/icon.png differ
diff --git a/vscode/src/createNodeFinderWithOverrides.ts b/vscode/src/createNodeFinderWithOverrides.ts
index 5f51b3eb3..d4aac141c 100644
--- a/vscode/src/createNodeFinderWithOverrides.ts
+++ b/vscode/src/createNodeFinderWithOverrides.ts
@@ -3,13 +3,14 @@ import * as fs from "fs";
import { createServerReferencedNodeFinder } from "@flyde/loader/dist/server";
import { ReferencedNodeFinder } from "@flyde/loader/dist/resolver/ReferencedNodeFinder";
import { NodeInstance } from "@flyde/core";
+import { getBaseNodesLibraryData } from "@flyde/nodes/dist/nodes-library-data";
/**
- * Creates a node finder that checks for .flyde-nodes.json overrides before
+ * Creates a node finder that checks for flyde-nodes.json overrides before
* falling back to the default server-based node resolution.
*
* This allows users to define custom nodes from external runtimes
- * by placing a .flyde-nodes.json file in the same directory as their flow file.
+ * by placing a flyde-nodes.json file in the same directory as their flow file.
*
* @param flowPath - The full path to the current flow file
* @returns A ReferencedNodeFinder function that includes override support
@@ -18,8 +19,8 @@ export function createNodeFinderWithOverrides(flowPath: string): ReferencedNodeF
const baseNodeFinder = createServerReferencedNodeFinder(flowPath);
return (instance: NodeInstance) => {
- // Check if we have a .flyde-nodes.json override in the same directory
- const overridePath = path.join(path.dirname(flowPath), '.flyde-nodes.json');
+ // Check if we have a flyde-nodes.json override in the same directory
+ const overridePath = path.join(path.dirname(flowPath), 'flyde-nodes.json');
if (fs.existsSync(overridePath) && instance.source?.type === 'custom') {
try {
@@ -29,11 +30,22 @@ export function createNodeFinderWithOverrides(flowPath: string): ReferencedNodeF
// Look for node definition in the nodes section
if (overrideData.nodes && overrideData.nodes[instance.nodeId]) {
const nodeDefinition = overrideData.nodes[instance.nodeId];
+
+ // Check if this is a shorthand reference to @flyde/nodes
+ if (nodeDefinition === "@flyde/nodes") {
+ const baseLibraryData = getBaseNodesLibraryData();
+ const allBaseNodes = baseLibraryData.groups.flatMap((group: any) => group.nodes);
+ const baseNode = allBaseNodes.find((node: any) => node.id === instance.nodeId);
+ if (baseNode) {
+ return baseNode.editorNode;
+ }
+ }
+
// Return the editorNode part, which is what resolveEditorNode expects
return nodeDefinition.editorNode;
}
} catch (err) {
- console.error("Error reading .flyde-nodes.json for node resolution:", err);
+ console.error("Error reading flyde-nodes.json for node resolution:", err);
}
}
diff --git a/vscode/src/embedded-server/services/scan-importable-nodes.ts b/vscode/src/embedded-server/services/scan-importable-nodes.ts
index 814dcd0be..5505877ce 100644
--- a/vscode/src/embedded-server/services/scan-importable-nodes.ts
+++ b/vscode/src/embedded-server/services/scan-importable-nodes.ts
@@ -101,7 +101,7 @@ export async function scanImportableNodes(
const importableNode = visualNodeToImportableEditorNode(flow.node, {
type: "file",
- data: file.fullPath,
+ data: relativePath,
});
acc[relativePath].push(importableNode);
diff --git a/vscode/src/flydeEditor.ts b/vscode/src/flydeEditor.ts
index 84247717c..c0ab2496a 100644
--- a/vscode/src/flydeEditor.ts
+++ b/vscode/src/flydeEditor.ts
@@ -480,8 +480,8 @@ export class FlydeEditorEditorProvider
? firstWorkspace.uri.fsPath
: path.dirname(fullDocumentPath);
- // Check for .flyde-nodes.json override file
- const overridePath = path.join(path.dirname(fullDocumentPath), '.flyde-nodes.json');
+ // Check for flyde-nodes.json override file
+ const overridePath = path.join(path.dirname(fullDocumentPath), 'flyde-nodes.json');
if (fs.existsSync(overridePath)) {
try {
const overrideContent = fs.readFileSync(overridePath, 'utf8');
@@ -489,6 +489,10 @@ export class FlydeEditorEditorProvider
// Transform the new structure to the expected format
if (overrideData.nodes && overrideData.groups) {
+ // Get base library data for resolving @flyde/nodes shorthand references
+ const baseLibraryData = getBaseNodesLibraryData();
+ const allBaseNodes = baseLibraryData.groups.flatMap((group: any) => group.nodes);
+
const transformedGroups = overrideData.groups.map((group: any) => ({
title: group.title,
nodes: group.nodeIds.map((nodeId: string) => {
@@ -497,7 +501,16 @@ export class FlydeEditorEditorProvider
throw new Error(`Node definition not found for ${nodeId}`);
}
- // Return the node definition as ImportableEditorNode - now includes editorNode
+ // Check if this is a shorthand reference to @flyde/nodes
+ if (nodeDefinition === "@flyde/nodes") {
+ const baseNode = allBaseNodes.find((node: any) => node.id === nodeId);
+ if (!baseNode) {
+ throw new Error(`Node ${nodeId} not found in @flyde/nodes`);
+ }
+ return baseNode;
+ }
+
+ // Return the full node definition as ImportableEditorNode - now includes editorNode
return nodeDefinition;
})
}));
@@ -512,7 +525,7 @@ export class FlydeEditorEditorProvider
break;
}
} catch (err) {
- console.error("Error reading .flyde-nodes.json:", err);
+ console.error("Error reading flyde-nodes.json:", err);
// Fall through to default behavior if override file is invalid
}
}
diff --git a/vscode/src/test/main.test.ts b/vscode/src/test/main.test.ts
index b7ed73aca..b36e19bc0 100644
--- a/vscode/src/test/main.test.ts
+++ b/vscode/src/test/main.test.ts
@@ -16,6 +16,9 @@ import {
getDebuggerEvents,
buildTestFilePath,
buildTempFilePath,
+ addNodeFromMenu,
+ waitForInstanceCount,
+ findMenuItemByText,
} from "./pageObjects";
import { createTempTestWorkspace } from "./testFileUtils";
import { webviewTestingCommand } from "./testUtils";
@@ -54,6 +57,24 @@ suite("Extension Test Suite", () => {
assert(menuItems.length >= 30, `Expected at least 30 menu items. Found ${menuItems.length}`);
}).retries(3);
+ test("Adds nodes from menu to canvas", async () => {
+ const testFile = buildTestFilePath("HelloWorld.flyde");
+ await openFlydeFile(testFile);
+ await waitForFlowEditor();
+
+ const initialInstances = await getInstances();
+ const initialCount = initialInstances.length;
+
+ await addNodeFromMenu("Value");
+ await waitForInstanceCount(initialCount + 1);
+
+ const finalInstances = await getInstances();
+ const valueExists = finalInstances.some(instance =>
+ instance.textContent?.includes("Value")
+ );
+ assert(valueExists, "Expected to find added 'Value' node in canvas");
+ }).retries(3);
+
test("Test flow functionality works", async () => {
const testFile = buildTestFilePath("HelloWorld.flyde");
await openFlydeFile(testFile);
@@ -69,7 +90,7 @@ suite("Extension Test Suite", () => {
}, 4000);
}).retries(3);
- test("Loads custom nodes from .flyde-nodes.json override", async () => {
+ test("Loads custom nodes from flyde-nodes.json override", async () => {
// Open the flow file from the custom-nodes-override directory
const testFile = path.join(tmpDir, "custom-nodes-override", "CustomNodesFlow.flyde");
await openFlydeFile(testFile);
@@ -98,6 +119,17 @@ suite("Extension Test Suite", () => {
assert(customBobExists, "Expected 'Custom Bob' node to appear in the menu");
assert(customAliceExists, "Expected 'Custom Alice' node to appear in the menu");
+ // Check if @flyde/nodes shorthand nodes appear in the menu with proper display names
+ const getAttributeExists = menuItems.some(item =>
+ item.textContent?.includes("Get Property")
+ );
+ const httpMenuExists = menuItems.some(item =>
+ item.textContent?.includes("HTTP Request")
+ );
+
+ assert(getAttributeExists, "Expected 'Get Property' node (GetAttribute from @flyde/nodes) to appear in the menu");
+ assert(httpMenuExists, "Expected 'HTTP Request' node (Http from @flyde/nodes) to appear in the menu");
+
// Click on Alice to add it to the canvas
await webviewTestingCommand("clickByText", { text: "Custom Alice" });
@@ -114,6 +146,52 @@ suite("Extension Test Suite", () => {
);
assert(aliceExists, "Expected to find 'Custom Alice' instance in the canvas after adding it");
+ // Test adding @flyde/nodes shorthand nodes to the canvas
+ // First, add GetAttribute node
+ await clickAddNodesButton();
+ await webviewTestingCommand("clickByText", { text: "Get Property" });
+
+ // Wait for the node to be added
+ await eventually(async () => {
+ const instancesWithGetAttr = await getInstances();
+ assert(instancesWithGetAttr.length === 3, `Expected 3 instances after adding GetAttribute, but got ${instancesWithGetAttr.length}`);
+ }, 2000);
+
+ // Verify GetAttribute shows proper display name - check that it's not just showing the ID
+ const instancesWithGetAttr = await getInstances();
+ const getAttrExists = instancesWithGetAttr.some(instance => {
+ const text = instance.textContent || "";
+ // Should contain "Get" and not be just "GetAttribute" (proving it resolved from @flyde/nodes)
+ return text.includes("Get") && !text.trim().endsWith("GetAttribute");
+ });
+ assert(getAttrExists, "Expected to find GetAttribute instance with resolved display name (not just 'GetAttribute' ID) in the canvas");
+
+ // Add HTTP node
+ await clickAddNodesButton();
+ await webviewTestingCommand("clickByText", { text: "HTTP Request" });
+
+ // Wait for the node to be added
+ await eventually(async () => {
+ const instancesWithHttp = await getInstances();
+ assert(instancesWithHttp.length === 4, `Expected 4 instances after adding HTTP, but got ${instancesWithHttp.length}`);
+ }, 2000);
+
+ // Verify HTTP shows proper display name - check that it's not just showing the ID
+ const instancesWithHttp = await getInstances();
+ const httpExists = instancesWithHttp.some(instance => {
+ const text = instance.textContent || "";
+ // Should contain "HTTP" and not be just "Http" (proving it resolved from @flyde/nodes)
+ return text.includes("HTTP") && !text.trim().endsWith("Http");
+ });
+
+ // Debug: log all instance text to see what we actually got
+ if (!httpExists) {
+ const allTexts = instancesWithHttp.map(i => i.textContent || "").filter(t => t.trim());
+ console.log("All instance texts:", allTexts);
+ }
+
+ assert(httpExists, "Expected to find HTTP instance with resolved display name (not just 'Http' ID) in the canvas");
+
}).retries(3);
@@ -139,6 +217,49 @@ suite("Extension Test Suite", () => {
});
});
+ suite("Nested Flows", () => {
+ test("handles adding nested flows and properly loads flows after adding them", async () => {
+ const testFile = path.join(tmpDir, "nested-flows", "ParentFlow.flyde");
+ await openFlydeFile(testFile);
+ await waitForFlowEditor();
+
+ // Should load parent flow with existing nested child flow
+ const initialInstances = await getInstances();
+ assert(initialInstances.length === 1, `Expected 1 initial instance. Got ${initialInstances.length}`);
+
+ // Add another ChildFlow from menu using new utilities
+ await addNodeFromMenu("ChildFlow");
+ await waitForInstanceCount(2);
+
+ // Verify we now have 2 instances
+ const instancesAfterAdd = await getInstances();
+ assert(instancesAfterAdd.length === 2, `Expected 2 instances after adding nested flow. Got ${instancesAfterAdd.length}`);
+
+ await delay(3000);
+
+ // Save the file to trigger the path resolution bug
+ await vscode.commands.executeCommand("workbench.action.files.save");
+ await delay(1000);
+
+ // Close and reopen to test for path duplication/corruption
+ await vscode.commands.executeCommand("workbench.action.closeActiveEditor");
+ await delay(500);
+ await openFlydeFile(testFile);
+ await waitForFlowEditor();
+
+
+ await waitForInstanceCount(2);
+ const reloadedInstances = await getInstances();
+ // Both should be ChildFlow instances
+ const childFlowCount = reloadedInstances.filter(instance =>
+ instance.textContent?.includes("ChildFlow")
+ ).length;
+
+ assert(childFlowCount === 2, `Expected 2 ChildFlow instances after reload, but got ${childFlowCount}. This suggests the fix for absolute paths didn't work correctly.`);
+
+ }).retries(3);
+ });
+
suite("Note node", () => {
test("renders note node", async () => {
const testFile = buildTestFilePath("NoteFixture.flyde");
diff --git a/vscode/src/test/pageObjects.ts b/vscode/src/test/pageObjects.ts
index 936cf41f7..24729d9fe 100644
--- a/vscode/src/test/pageObjects.ts
+++ b/vscode/src/test/pageObjects.ts
@@ -44,17 +44,47 @@ export async function getMenuItems() {
return await webviewTestingCommand("$$", { selector: "[cmdk-item]" });
}
+
export async function runFlow() {
await webviewTestingCommand("click", { selector: "button.run-btn" });
-
+
await eventually(async () => {
const dialogs = await webviewTestingCommand("$$", { selector: "[role=dialog]" });
assert(dialogs.length === 1, "Test dialog not found");
});
-
+
await webviewTestingCommand("clickByText", { text: "Run", tagName: "button" });
}
export async function getDebuggerEvents() {
return await webviewTestingCommand("getDebuggerEvents", {});
+}
+
+// Menu utilities
+export async function findMenuItemByText(text: string) {
+ const menuItems = await getMenuItems();
+ return menuItems.find(item =>
+ item.textContent?.includes(text)
+ );
+}
+
+export async function addNodeFromMenu(nodeText: string) {
+ await clickAddNodesButton();
+
+ const menuItem = await findMenuItemByText(nodeText);
+ if (!menuItem) {
+ const allMenuItems = await getMenuItems();
+ const allTexts = allMenuItems.map(item => item.textContent);
+ throw new Error(`Node "${nodeText}" not found in menu. Available: ${allTexts.join(', ')}`);
+ }
+
+ await webviewTestingCommand("clickByText", { text: nodeText });
+}
+
+export async function waitForInstanceCount(expectedCount: number, timeout = 2000) {
+ await eventually(async () => {
+ const instances = await getInstances();
+ assert(instances.length === expectedCount,
+ `Expected ${expectedCount} instances, but got ${instances.length}`);
+ }, timeout);
}
\ No newline at end of file
diff --git a/vscode/src/test/testUtils.ts b/vscode/src/test/testUtils.ts
index f8e19d56f..cff37806a 100644
--- a/vscode/src/test/testUtils.ts
+++ b/vscode/src/test/testUtils.ts
@@ -15,6 +15,7 @@ export type WebViewTestingCommands = {
};
click: { params: { selector: string }; response: void };
clickByText: { params: { text: string; tagName?: string }; response: void };
+ type: { params: { selector: string; text: string }; response: void };
hasText: { params: { selector: string; text: string }; response: boolean };
getDebuggerEvents: {
params: {};
diff --git a/vscode/src/webview/index.tsx b/vscode/src/webview/index.tsx
index efe50555f..9bcad4d90 100644
--- a/vscode/src/webview/index.tsx
+++ b/vscode/src/webview/index.tsx
@@ -75,6 +75,42 @@ window.addEventListener("message", (event) => {
}
break;
}
+ case "type": {
+ const { selector, text } = data.params;
+ const element = document.querySelector(selector) as HTMLInputElement;
+ if (element) {
+ element.focus();
+ // Clear existing value first
+ element.value = '';
+
+ // Simulate typing each character
+ for (const char of text) {
+ element.value += char;
+ // Dispatch events that React and cmdk expect
+ element.dispatchEvent(new InputEvent('input', {
+ bubbles: true,
+ cancelable: true,
+ data: char,
+ inputType: 'insertText'
+ }));
+ element.dispatchEvent(new KeyboardEvent('keydown', {
+ key: char,
+ bubbles: true
+ }));
+ element.dispatchEvent(new KeyboardEvent('keyup', {
+ key: char,
+ bubbles: true
+ }));
+ }
+
+ // Final change event
+ element.dispatchEvent(new Event('change', { bubbles: true }));
+ response = {};
+ } else {
+ error = `Element not found: ${selector}`;
+ }
+ break;
+ }
case "getDebuggerEvents": {
response = (window as any).__testCapturedDebuggerEvents || [];
// Clear events after retrieval to avoid accumulation
diff --git a/vscode/test-fixtures/custom-nodes-override/bob.flyde.ts b/vscode/test-fixtures/custom-nodes-override/bob.flyde.ts
new file mode 100644
index 000000000..89d7f7f12
--- /dev/null
+++ b/vscode/test-fixtures/custom-nodes-override/bob.flyde.ts
@@ -0,0 +1,11 @@
+import { CodeNode } from "@flyde/core";
+
+export default {
+ id: 'Bob2',
+ inputs: {},
+ outputs: {},
+ run: async (inputs, context) => {
+ console.log('Bob2');
+
+ },
+} satisfies CodeNode;
\ No newline at end of file
diff --git a/vscode/test-fixtures/custom-nodes-override/flyde-nodes.json b/vscode/test-fixtures/custom-nodes-override/flyde-nodes.json
new file mode 100644
index 000000000..d54a31702
--- /dev/null
+++ b/vscode/test-fixtures/custom-nodes-override/flyde-nodes.json
@@ -0,0 +1,68 @@
+{
+ "nodes": {
+ "CustomBob": {
+ "id": "CustomBob",
+ "displayName": "Custom Bob",
+ "description": "A custom Bob node for testing",
+ "icon": "fa-user",
+ "type": "code",
+ "source": {
+ "type": "custom",
+ "data": "custom://CustomBob"
+ },
+ "editorNode": {
+ "id": "CustomBob",
+ "displayName": "Custom Bob",
+ "description": "A custom Bob node for testing",
+ "inputs": {
+ "value": {
+ "description": "Input value"
+ }
+ },
+ "outputs": {
+ "result": {
+ "description": "Output result"
+ }
+ }
+ }
+ },
+ "CustomAlice": {
+ "id": "CustomAlice",
+ "displayName": "Custom Alice",
+ "description": "A custom Alice node for testing",
+ "icon": "fa-user-friends",
+ "type": "code",
+ "source": {
+ "type": "custom",
+ "data": "custom://CustomAlice"
+ },
+ "editorNode": {
+ "id": "CustomAlice",
+ "displayName": "Custom Alice",
+ "description": "A custom Alice node for testing",
+ "inputs": {
+ "message": {
+ "description": "Message input"
+ }
+ },
+ "outputs": {
+ "greeting": {
+ "description": "Greeting output"
+ }
+ }
+ }
+ },
+ "GetAttribute": "@flyde/nodes",
+ "Http": "@flyde/nodes"
+ },
+ "groups": [
+ {
+ "title": "Custom Runtime Nodes",
+ "nodeIds": ["CustomBob", "CustomAlice"]
+ },
+ {
+ "title": "Standard Library Shortcuts",
+ "nodeIds": ["GetAttribute", "Http"]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/vscode/test-fixtures/nested-flows/ChildFlow.flyde b/vscode/test-fixtures/nested-flows/ChildFlow.flyde
new file mode 100644
index 000000000..9730532e6
--- /dev/null
+++ b/vscode/test-fixtures/nested-flows/ChildFlow.flyde
@@ -0,0 +1,44 @@
+imports: {}
+node:
+ id: ChildFlow
+ inputs:
+ n:
+ mode: required
+ type: number
+ outputs:
+ r:
+ type: number
+ instances:
+ - id: add-one
+ nodeId: Add
+ inputConfig: {}
+ config:
+ b: 1
+ pos:
+ x: 300
+ y: 200
+ type: code
+ source:
+ type: package
+ data: "@flyde/nodes"
+ connections:
+ - from:
+ insId: __this
+ pinId: n
+ to:
+ insId: add-one
+ pinId: a
+ - from:
+ insId: add-one
+ pinId: r
+ to:
+ insId: __this
+ pinId: r
+ inputsPosition:
+ n:
+ x: 100
+ y: 200
+ outputsPosition:
+ r:
+ x: 500
+ y: 200
\ No newline at end of file
diff --git a/vscode/test-fixtures/nested-flows/ParentFlow.flyde b/vscode/test-fixtures/nested-flows/ParentFlow.flyde
new file mode 100644
index 000000000..e15005ba7
--- /dev/null
+++ b/vscode/test-fixtures/nested-flows/ParentFlow.flyde
@@ -0,0 +1,43 @@
+imports: {}
+node:
+ instances:
+ - pos:
+ x: 300
+ y: 200
+ id: nested-instance
+ inputConfig: {}
+ nodeId: ChildFlow
+ config: {}
+ type: visual
+ source:
+ type: file
+ data: ./ChildFlow.flyde
+ connections:
+ - from:
+ insId: __this
+ pinId: n
+ to:
+ insId: nested-instance
+ pinId: n
+ - from:
+ insId: nested-instance
+ pinId: r
+ to:
+ insId: __this
+ pinId: r
+ id: ParentFlow
+ inputs:
+ n:
+ mode: required
+ type: number
+ outputs:
+ r:
+ type: number
+ inputsPosition:
+ n:
+ x: 100
+ y: 200
+ outputsPosition:
+ r:
+ x: 500
+ y: 200
diff --git a/website/components/EmbeddedFlyde.tsx b/website/components/EmbeddedFlyde.tsx
index 23d520a5f..2b4f9c362 100644
--- a/website/components/EmbeddedFlyde.tsx
+++ b/website/components/EmbeddedFlyde.tsx
@@ -122,11 +122,11 @@ export const EmbeddedFlyde = (props: EmbeddedFlydeProps) => {
let parsedValue = value;
if (typeof value === 'string' && value.includes('subject:') && value.includes('content:')) {
console.log("Raw InlineValue output:", value);
- // The InlineValue outputs a template string, let's try to parse it as an object
- const cleaned = value.replace(/^\s*{\s*/, '{').replace(/\s*}\s*$/, '}').replace(/(\w+):\s*"([^"]*?)"/g, '"$1": "$2"');
- console.log("Attempting to parse cleaned string:", cleaned);
- parsedValue = JSON.parse(cleaned);
-
+ // The InlineValue outputs a template string, let's try to parse it as an object
+ const cleaned = value.replace(/^\s*{\s*/, '{').replace(/\s*}\s*$/, '}').replace(/(\w+):\s*"([^"]*?)"/g, '"$1": "$2"');
+ console.log("Attempting to parse cleaned string:", cleaned);
+ parsedValue = JSON.parse(cleaned);
+
}
console.log("Setting execution results for key:", key, "with parsed value:", parsedValue);
@@ -155,7 +155,7 @@ export const EmbeddedFlyde = (props: EmbeddedFlydeProps) => {
// Show results panel after successful run
setShowResults(true);
-
+
// Re-center the board after showing results
setTimeout(() => {
if (flowEditorRef.current && flowEditorRef.current.centerViewPort) {
@@ -232,27 +232,16 @@ export const EmbeddedFlyde = (props: EmbeddedFlydeProps) => {
// Generate example code based on active example
const getExampleCode = () => {
- const exampleName = activeExample === 'blog-generator' ? 'BlogGenerator' : 'Chatbot';
- const inputType = activeExample === 'blog-generator' ? '{topic: string}' : '{message: string}';
- const outputType = activeExample === 'blog-generator' ? '{blogPost: any}' : '{response: string}';
- const inputExample = activeExample === 'blog-generator' ? "'AI in 2024'" : "'Hello, how are you?'";
-
- return `import { loadFlow } from '@flyde/loader';
-
-// Load the visual flow as a TypeScript function
-const ${activeExample.replace('-', '')}Flow = loadFlow<${inputType}, ${outputType}>('./Flow.flyde');
-
-// Execute with input
-async function run${exampleName}(input: string) {
- const result = await ${activeExample.replace('-', '')}Flow({
- ${activeExample === 'blog-generator' ? 'topic' : 'message'}: input
- });
- return result.${activeExample === 'blog-generator' ? 'blogPost' : 'response'};
-}
+ const inputExample = activeExample === 'blog-generator' ? "'AI in 2025'" : "'Hello, how are you?'";
+
+ return `import { runFlow } from '@flyde/loader';
-// Usage example
-const result = await run${exampleName}(${inputExample});
-console.log(result);`;
+const topic = ${inputExample};
+
+// Execute the flow directly
+const result = await runFlow('Flow.flyde', {topic});
+
+console.log(result.${activeExample === 'blog-generator' ? 'blogPost' : 'response'});`;
};
@@ -334,7 +323,7 @@ console.log(result);`;
onToggleResults={() => {
const newShowResults = !showResults;
setShowResults(newShowResults);
-
+
// Re-center the board after toggling results
// Use a longer delay when opening the panel
const delay = newShowResults ? 500 : 300;
diff --git a/website/content/docs/4-integrate-flows.mdx b/website/content/docs/4-integrate-flows.mdx
index a7b6da37e..8cf5591e7 100644
--- a/website/content/docs/4-integrate-flows.mdx
+++ b/website/content/docs/4-integrate-flows.mdx
@@ -17,23 +17,25 @@ For example, given a .flyde flow that converts Celsius to Fahrenheit:
You can load and run it from your code as following:
```ts
-import { loadFlow } from "@flyde/loader";
-
-const execute = await loadFlow("./celsius-to-fahrenheit.flyde");
-
-const inputs = { celsius: 0 }; // "celcius" is a main input in the flow, therefore it must be provided when executing the flow
-const { result } = execute(inputs); // execute returns a "result" promise, along with a cleanup function that can be used to cancel the execution.
+import { runFlow } from "@flyde/loader";
+import path from "path";
-const { fahrenheit } = await result; // each output in the flow is a property on the result object
+// Execute the flow directly with inputs
+const result = await runFlow(path.join(__dirname, "celsius-to-fahrenheit.flyde"), {
+ celsius: 0 // "celsius" is a main input in the flow
+});
console.log(result.fahrenheit); // 32
```
-The `execute` function returns an object with a `result` property - a promise that resolves to the result of the flow.
+The `runFlow` function directly returns the result of the flow execution.
-You may also listen to outputs before the flow completes by passing an "onOutputs" callback to the second argument of `execute`:
+For more advanced use cases, you can use the `loadFlow` function to get more control over execution:
```ts
+import { loadFlow } from "@flyde/loader";
+
+const execute = await loadFlow("./celsius-to-fahrenheit.flyde");
const { result } = execute(inputs, {
onOutputs: (key, value) => {
console.log(`output with key ${key} emitted value ${value}`);
diff --git a/website/pages/index.tsx b/website/pages/index.tsx
index 39354ac0c..843272736 100644
--- a/website/pages/index.tsx
+++ b/website/pages/index.tsx
@@ -41,11 +41,13 @@ export default function Home() {
- Visual AI Flows.In Your Codebase
+ Stop building AI logic outside your codebase
-
- An enterprise-ready, holistic solution for prototyping, integrating, evaluating and iterating on AI-heavy backend logic, directly in your codebase.
-
+
+
+ Like n8n, but for TypeScript. Build AI agents visually, import your functions, deploy with your app. Works with VS Code, Cursor, Windsurf. Plays nicely with Claude Code, Gemini CLI, and more.
+
+
@@ -63,14 +65,15 @@ export default function Home() {
This flow demonstrates LLM integration across multiple providers (OpenAI & Anthropic) with structured output generation, content processing, and response formatting.
- {/* CTA after demo */}
-
+ {/* CTA after demo - More prominent */}
+
- Get started
+ Quick Start
+
Get Flyde running in your project in under 5 minutes
{/* Subtle Example Picker Below
@@ -85,10 +88,6 @@ export default function Home() {
-
-
Subscribe to be notified when Flyde 1.0.0 launches
-
-
Why Flyde
- The only visual flow builder that runs in-codebase, with full access to your runtime code
+ The only visual flow builder that runs in-codebase, with full access to your runtime code. Works seamlessly with AI coding tools like Cursor and Windsurf.
-
+
+
+
+
+
+
AI Coding Enhanced
+
Works seamlessly with Cursor, Windsurf, and other AI coding tools. Augments rather than replaces your development workflow.
@@ -22,7 +35,7 @@ export default function QuickStart() {
Get Flyde
- Flyde combines a visual editor that lives in VS Code/Cursor/Windsurf with the @flyde/loader npm package that runs your flows.
+ Flyde combines a visual editor that lives in your IDE with the @flyde/loader npm package that runs your flows.
@@ -49,8 +62,8 @@ export default function QuickStart() {
ā Creates project with working flow
-
ā Installs VS Code extension
-
ā Opens VS Code with your first flow
+
ā Installs editor extension
+
ā Opens your editor with your first flow
@@ -60,13 +73,38 @@ export default function QuickStart() {