From 46c344f38dada5b8f6284dca8c7ca31a07862269 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 18 May 2026 18:39:40 +0200 Subject: [PATCH 01/66] feat: implement flow comparison store and integrate with flow schema and nodes hooks --- package-lock.json | 14 ++++++++++++++ package.json | 1 + .../ce/src/flow/hooks/Flow.compare.hook.ts | 10 ++++++++++ .../ce/src/flow/hooks/Flow.nodes.hook.ts | 4 +++- .../ce/src/flow/hooks/Flow.schema.hook.ts | 19 ++++++++++--------- .../components/nodes/FunctionNodeComponent.ts | 1 + 6 files changed, 39 insertions(+), 10 deletions(-) create mode 100644 src/packages/ce/src/flow/hooks/Flow.compare.hook.ts diff --git a/package-lock.json b/package-lock.json index faeec56f..a410b148 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@code0-tech/triangulum": "^0.22.0", "@codemirror/lang-javascript": "^6.2.5", "@codemirror/lint": "^6.9.5", + "@icons-pack/react-simple-icons": "^13.13.0", "@opentelemetry/api": "^1.9.1", "@opentelemetry/context-zone": "^2.6.1", "@opentelemetry/exporter-logs-otlp-http": "^0.218.0", @@ -877,6 +878,19 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@icons-pack/react-simple-icons": { + "version": "13.13.0", + "resolved": "https://registry.npmjs.org/@icons-pack/react-simple-icons/-/react-simple-icons-13.13.0.tgz", + "integrity": "sha512-B5HhQMIpcSH4z8IZ8HFhD59CboHceKYMpPC9kAwGyKntvPdyJJv26DLu4Z1wAjcCLyrJhf11tMhiQGom9Rxb9g==", + "license": "MIT", + "engines": { + "node": ">=24", + "pnpm": ">=10" + }, + "peerDependencies": { + "react": "^16.13 || ^17 || ^18 || ^19" + } + }, "node_modules/@img/colour": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", diff --git a/package.json b/package.json index c81b86a4..18ed549e 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@code0-tech/triangulum": "^0.22.0", "@codemirror/lang-javascript": "^6.2.5", "@codemirror/lint": "^6.9.5", + "@icons-pack/react-simple-icons": "^13.13.0", "@opentelemetry/api": "^1.9.1", "@opentelemetry/context-zone": "^2.6.1", "@opentelemetry/exporter-logs-otlp-http": "^0.218.0", diff --git a/src/packages/ce/src/flow/hooks/Flow.compare.hook.ts b/src/packages/ce/src/flow/hooks/Flow.compare.hook.ts new file mode 100644 index 00000000..cbf4c085 --- /dev/null +++ b/src/packages/ce/src/flow/hooks/Flow.compare.hook.ts @@ -0,0 +1,10 @@ +import { create } from 'zustand' +import {FlowView} from "@ce/flow/services/Flow.view"; + +export const useFlowCompareStore = create<{ + flow: FlowView | null +}>((setState) => ({ + flow: null, + setFlow: (flow: FlowView) => setState({flow}), + clearFlow: () => setState({flow: null}), +})) \ No newline at end of file diff --git a/src/packages/ce/src/flow/hooks/Flow.nodes.hook.ts b/src/packages/ce/src/flow/hooks/Flow.nodes.hook.ts index a1db92f6..3ed0ab0a 100644 --- a/src/packages/ce/src/flow/hooks/Flow.nodes.hook.ts +++ b/src/packages/ce/src/flow/hooks/Flow.nodes.hook.ts @@ -6,6 +6,7 @@ import {FlowService} from "@edition/flow/services/Flow.service"; import {FunctionService} from "@edition/function/services/Function.service"; import {FunctionNodeComponentProps} from "@edition/function/components/nodes/FunctionNodeComponent"; import {useFlowSchema} from "@edition/flow/hooks/Flow.schema.hook"; +import {useFlowCompareStore} from "@ce/flow/hooks/Flow.compare.hook"; export const useFlowNodes = (flowId: Flow["id"], namespaceId?: Namespace["id"], projectId?: NamespaceProject["id"]): Node[] => { @@ -13,6 +14,7 @@ export const useFlowNodes = (flowId: Flow["id"], namespaceId?: Namespace["id"], const flowStore = useStore(FlowService) const functionService = useService(FunctionService) const functionStore = useStore(FunctionService) + const flowToCompare = useFlowCompareStore(state => state.flow) const [nodes, setNodes] = useState[]>([]) @@ -21,7 +23,7 @@ export const useFlowNodes = (flowId: Flow["id"], namespaceId?: Namespace["id"], [flowId, flowStore.length, flowService] ) - const flowSchema = useFlowSchema(flowId, namespaceId, projectId) + const flowSchema = useFlowSchema(flowToCompare || flowId, namespaceId, projectId) React.useEffect(() => { if (!flow) return diff --git a/src/packages/ce/src/flow/hooks/Flow.schema.hook.ts b/src/packages/ce/src/flow/hooks/Flow.schema.hook.ts index fd84c45b..2bdfa14c 100644 --- a/src/packages/ce/src/flow/hooks/Flow.schema.hook.ts +++ b/src/packages/ce/src/flow/hooks/Flow.schema.hook.ts @@ -6,9 +6,10 @@ import {useSchemaAction} from "@edition/flow/components/FlowWorkerProvider"; import {DatatypeService} from "@edition/datatype/services/Datatype.service"; import {FunctionService} from "@edition/function/services/Function.service"; import {NodeSchema} from "@code0-tech/triangulum"; +import {FlowView} from "@ce/flow/services/Flow.view"; export const useFlowSchema = ( - flowId: Flow['id'], + flow: Flow['id'] | FlowView, namespaceId: Namespace['id'], projectId: NamespaceProject['id'], ): NodeSchema[][] | undefined => { @@ -22,9 +23,9 @@ export const useFlowSchema = ( const [schema, setSchema] = React.useState([]); - const flow = React.useMemo( - () => flowService.getById(flowId, {namespaceId, projectId}), - [flowId, flowService, flowStore] + const flowView = React.useMemo( + () => typeof flow === "string" ? flowService.getById(flow, {namespaceId, projectId}) : flow, + [flow, flowService, flowStore] ) const dataTypes = React.useMemo( @@ -38,19 +39,19 @@ export const useFlowSchema = ( ) React.useEffect(() => { - if (!flow) return + if (!flowView) return if (dataTypes.length <= 0) return if (functions.length <= 0) return const triggerSchema = execute({ - flow, + flow: flowView, dataTypes, functions }) - const schemas = flow.nodes?.nodes?.map(node => { + const schemas = flowView.nodes?.nodes?.map(node => { return execute({ - flow, + flow: flowView, dataTypes, functions, nodeId: node?.id @@ -61,7 +62,7 @@ export const useFlowSchema = ( setSchema(value as NodeSchema[][]) }) - }, [functions.length, dataTypes.length, flowStore, flow?.editedAt, flow?.nodes?.nodes?.length]) + }, [functions.length, dataTypes.length, flowStore, flowView?.editedAt, flowView?.nodes?.nodes?.length]) return schema } diff --git a/src/packages/ce/src/function/components/nodes/FunctionNodeComponent.ts b/src/packages/ce/src/function/components/nodes/FunctionNodeComponent.ts index dc46bfd2..c3dfc95e 100644 --- a/src/packages/ce/src/function/components/nodes/FunctionNodeComponent.ts +++ b/src/packages/ce/src/function/components/nodes/FunctionNodeComponent.ts @@ -7,6 +7,7 @@ export interface FunctionNodeComponentProps extends Record, Com functionId?: FunctionDefinition['id'] flowId: Flow['id'] schema: NodeSchema[] + compareType?: 'added' | 'removed' | 'changed' color: string parentNodeId?: NodeFunction['id'] isParameter?: boolean From 6d2fd0ef03337b68d3dc365a0d26ea546bc4bd8f Mon Sep 17 00:00:00 2001 From: nicosammito Date: Sat, 13 Jun 2026 18:55:04 +0200 Subject: [PATCH 02/66] feat: dep updates --- package-lock.json | 16 ++++++++-------- package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index a410b148..10a8bbdf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@apollo/client": "^4.0.9", "@code0-tech/pictor": "^0.10.3", - "@code0-tech/triangulum": "^0.22.0", + "@code0-tech/triangulum": "^0.23.0", "@codemirror/lang-javascript": "^6.2.5", "@codemirror/lint": "^6.9.5", "@icons-pack/react-simple-icons": "^13.13.0", @@ -460,17 +460,17 @@ } }, "node_modules/@code0-tech/sagittarius-graphql-types": { - "version": "0.0.0-experimental-2585797094-74c645eca45310e3506df6a95c4fab1a2d6abbc7", - "resolved": "https://registry.npmjs.org/@code0-tech/sagittarius-graphql-types/-/sagittarius-graphql-types-0.0.0-experimental-2585797094-74c645eca45310e3506df6a95c4fab1a2d6abbc7.tgz", - "integrity": "sha512-nWg7Jb1bKd49fTb2vGuKk0KPN9mkM+y2EK4z8VVNk21J2xwIb47NazJuP9V8o4WZgCbg4d486IqHDRa5uJweiQ==", + "version": "0.0.0-experimental-2598030203-92acc01bfb8e2f88212c504241726ae16f15a4ac", + "resolved": "https://registry.npmjs.org/@code0-tech/sagittarius-graphql-types/-/sagittarius-graphql-types-0.0.0-experimental-2598030203-92acc01bfb8e2f88212c504241726ae16f15a4ac.tgz", + "integrity": "sha512-yt1X0d9SnNXIig/Yk1e8Ggolee42OytSUhLGjbN7nIyuCns0eryfsffACIPw+igzxtNC5qkqbfm3mKzU9M/Ujg==", "peer": true }, "node_modules/@code0-tech/triangulum": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/@code0-tech/triangulum/-/triangulum-0.22.0.tgz", - "integrity": "sha512-XF1MnG8IA2LvJ0IhA/YBzcH8meXUUpJ5gSwOpABhD8H0Fb4IJPynwm1KmMh977fTAH7j+aD6RpEqdZTU8NUw1w==", + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@code0-tech/triangulum/-/triangulum-0.23.0.tgz", + "integrity": "sha512-GDmfSKoett+olav9HPsaTCUdXR2joCu6FvvmJagCqnLDP4dctNR4dlcUgebLnQeTc0tCBeF0iSGBIzFhoj4bGA==", "peerDependencies": { - "@code0-tech/sagittarius-graphql-types": "0.0.0-experimental-2585797094-74c645eca45310e3506df6a95c4fab1a2d6abbc7", + "@code0-tech/sagittarius-graphql-types": "0.0.0-experimental-2598030203-92acc01bfb8e2f88212c504241726ae16f15a4ac", "@typescript/vfs": "^1.6.4", "lossless-json": "^4.3.0", "typescript": "^5.9.3 || ^6.0.2" diff --git a/package.json b/package.json index 18ed549e..71405fe5 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "dependencies": { "@apollo/client": "^4.0.9", "@code0-tech/pictor": "^0.10.3", - "@code0-tech/triangulum": "^0.22.0", + "@code0-tech/triangulum": "^0.23.0", "@codemirror/lang-javascript": "^6.2.5", "@codemirror/lint": "^6.9.5", "@icons-pack/react-simple-icons": "^13.13.0", From 424595cbb987a0e0f397e2b9f5363083b289ec57 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Sun, 14 Jun 2026 01:12:32 +0200 Subject: [PATCH 03/66] feat: correct imports --- src/packages/ce/src/flow/hooks/Flow.compare.hook.ts | 2 +- src/packages/ce/src/flow/hooks/Flow.nodes.hook.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packages/ce/src/flow/hooks/Flow.compare.hook.ts b/src/packages/ce/src/flow/hooks/Flow.compare.hook.ts index cbf4c085..2a77f300 100644 --- a/src/packages/ce/src/flow/hooks/Flow.compare.hook.ts +++ b/src/packages/ce/src/flow/hooks/Flow.compare.hook.ts @@ -1,5 +1,5 @@ import { create } from 'zustand' -import {FlowView} from "@ce/flow/services/Flow.view"; +import {FlowView} from "@edition/flow/services/Flow.view"; export const useFlowCompareStore = create<{ flow: FlowView | null diff --git a/src/packages/ce/src/flow/hooks/Flow.nodes.hook.ts b/src/packages/ce/src/flow/hooks/Flow.nodes.hook.ts index 3ed0ab0a..359717d0 100644 --- a/src/packages/ce/src/flow/hooks/Flow.nodes.hook.ts +++ b/src/packages/ce/src/flow/hooks/Flow.nodes.hook.ts @@ -6,7 +6,7 @@ import {FlowService} from "@edition/flow/services/Flow.service"; import {FunctionService} from "@edition/function/services/Function.service"; import {FunctionNodeComponentProps} from "@edition/function/components/nodes/FunctionNodeComponent"; import {useFlowSchema} from "@edition/flow/hooks/Flow.schema.hook"; -import {useFlowCompareStore} from "@ce/flow/hooks/Flow.compare.hook"; +import {useFlowCompareStore} from "@edition/flow/hooks/Flow.compare.hook"; export const useFlowNodes = (flowId: Flow["id"], namespaceId?: Namespace["id"], projectId?: NamespaceProject["id"]): Node[] => { From 9ff8af4f6e11c8ad85270e5db5bdac1ad0964c36 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Sun, 14 Jun 2026 01:12:42 +0200 Subject: [PATCH 04/66] feat: updating pictor --- package-lock.json | 10 ++++------ package.json | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 10a8bbdf..bd49f147 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.0", "dependencies": { "@apollo/client": "^4.0.9", - "@code0-tech/pictor": "^0.10.3", + "@code0-tech/pictor": "^0.10.4", "@code0-tech/triangulum": "^0.23.0", "@codemirror/lang-javascript": "^6.2.5", "@codemirror/lint": "^6.9.5", @@ -418,9 +418,9 @@ } }, "node_modules/@code0-tech/pictor": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/@code0-tech/pictor/-/pictor-0.10.3.tgz", - "integrity": "sha512-p6hW6mtbLJ7+KgvyzQ3rVPg5svU8RiVLcf6kt/4+PknQ1A1DQ/k7KJ/6mdTSD0/c2dHrY3dKdoXVcpnl2muOSw==", + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/@code0-tech/pictor/-/pictor-0.10.4.tgz", + "integrity": "sha512-cbFJJ+ijM9d9OOxhxNuiVVghKs5wgc79QCvxIoH0pOIJ/ASCAfOMFh3pIh5gJCQWURddcwkfWhC7Y+wflnozDg==", "peerDependencies": { "@ark-ui/react": "^5.36.2", "@codemirror/autocomplete": "^6.20.0", @@ -445,7 +445,6 @@ "@tabler/icons-react": "3.41.1", "@uiw/codemirror-themes": "^4.25.4", "@uiw/react-codemirror": "^4.25.4", - "@xyflow/react": "^12.10.0", "avvvatars-react": "^0.4.2", "cmdk": "^1.1.1", "js-md5": "^0.8.3", @@ -4236,7 +4235,6 @@ "resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.11.0.tgz", "integrity": "sha512-na4IO33FSs2OS72hASgZDmTYwFAkef7Z74uBUVrong3ARmQQHfnRUVaCFn1kTt5LbS6pK03TbYjCPGLjLFfziA==", "license": "MIT", - "peer": true, "dependencies": { "@xyflow/system": "0.0.77", "classcat": "^5.0.3", diff --git a/package.json b/package.json index 71405fe5..af9f934f 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ }, "dependencies": { "@apollo/client": "^4.0.9", - "@code0-tech/pictor": "^0.10.3", + "@code0-tech/pictor": "^0.10.4", "@code0-tech/triangulum": "^0.23.0", "@codemirror/lang-javascript": "^6.2.5", "@codemirror/lint": "^6.9.5", From f041087e27de91b906bc8097e8555efd78665817 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Sun, 14 Jun 2026 01:12:56 +0200 Subject: [PATCH 05/66] feat: new definition panel --- .../builder/FlowBuilderComponent.tsx | 2 + .../panels/FlowPanelDefinitionComponent.tsx | 137 ++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 src/packages/ce/src/flow/components/panels/FlowPanelDefinitionComponent.tsx diff --git a/src/packages/ce/src/flow/components/builder/FlowBuilderComponent.tsx b/src/packages/ce/src/flow/components/builder/FlowBuilderComponent.tsx index 874057cb..4efe41b0 100644 --- a/src/packages/ce/src/flow/components/builder/FlowBuilderComponent.tsx +++ b/src/packages/ce/src/flow/components/builder/FlowBuilderComponent.tsx @@ -28,6 +28,7 @@ import {FlowPanelLayoutComponent} from "@edition/flow/components/panels/FlowPane import {FlowPanelControlComponent} from "@edition/flow/components/panels/FlowPanelControlComponent"; import {FlowPanelUpdateComponent} from "@edition/flow/components/panels/FlowPanelUpdateComponent"; import {FunctionNodeSquareComponent} from "@edition/function/components/nodes/FunctionNodeSquareComponent"; +import {FlowPanelDefinitionComponent} from "@edition/flow/components/panels/FlowPanelDefinitionComponent"; /** * Dynamically layouts a tree of nodes and their parameter nodes for a flow-based editor. @@ -785,6 +786,7 @@ const InternalFlowBuilder: React.FC = (props) => { + ) : null} diff --git a/src/packages/ce/src/flow/components/panels/FlowPanelDefinitionComponent.tsx b/src/packages/ce/src/flow/components/panels/FlowPanelDefinitionComponent.tsx new file mode 100644 index 00000000..f54956a5 --- /dev/null +++ b/src/packages/ce/src/flow/components/panels/FlowPanelDefinitionComponent.tsx @@ -0,0 +1,137 @@ +import React from "react"; +import {Panel} from "@xyflow/react"; +import {ButtonGroup} from "@code0-tech/pictor/dist/components/button-group/ButtonGroup"; +import { + Button, + Dialog, + DialogContent, + DialogOverlay, + DialogPortal, + DialogTrigger, + Spacing, + Text, useService, useStore +} from "@code0-tech/pictor"; +import {InputWrapper} from "@code0-tech/pictor/dist/components/form/InputWrapper"; +import {IconCheck, IconCopy} from "@tabler/icons-react"; +import {useParams} from "next/navigation"; +import {FlowService} from "@edition/flow/services/Flow.service"; +import {FlowTypeService} from "@edition/flowtype/services/FlowType.service"; +import {ProjectService} from "@edition/project/services/Project.service"; +import {ModuleService} from "@edition/module/services/Module.service"; +import {useCopyToClipboard} from "@uidotdev/usehooks"; +import {Flow, Namespace, NamespaceProject} from "@code0-tech/sagittarius-graphql-types"; + +export const FlowPanelDefinitionComponent: React.FC = () => { + + const params = useParams() + const flowService = useService(FlowService) + const flowStore = useStore(FlowService) + const flowTypeService = useService(FlowTypeService) + const flowTypeStore = useStore(FlowTypeService) + const projectService = useService(ProjectService) + const projectStore = useStore(ProjectService) + const moduleService = useService(ModuleService) + const moduleStore = useStore(ModuleService) + + const [copiedText, copyToClipboard] = useCopyToClipboard(); + const hasCopiedText = Boolean(copiedText); + + const namespaceIndex = params.namespaceId as any as number + const projectIndex = params.projectId as any as number + const flowIndex = params.flowId as any as number + const namespaceId: Namespace['id'] = `gid://sagittarius/Namespace/${namespaceIndex}` + const projectId: NamespaceProject['id'] = `gid://sagittarius/NamespaceProject/${projectIndex}` + const flowId: Flow['id'] = `gid://sagittarius/Flow/${flowIndex}` + + const flow = React.useMemo( + () => flowService.getById(flowId, { + namespaceId, + projectId + }), + [flowId, flowStore, namespaceId, projectId] + ) + + const project = React.useMemo( + () => projectService.getById(projectId, { + namespaceId + }), + [projectId, namespaceId, projectStore] + ) + + const flowType = React.useMemo( + () => flowTypeService.getById(flow?.type?.id, { + namespaceId, + projectId, + runtimeId: project?.primaryRuntime?.id + }), + [flow?.type?.id, namespaceId, projectId, project?.primaryRuntime?.id, flowTypeStore] + ) + + const module = React.useMemo( + () => moduleService.getById(flowType?.runtimeModule?.id, { + namespaceId: namespaceId, + projectId: projectId, + runtimeId: project?.primaryRuntime?.id + }), + [flowType?.runtimeModule?.id, namespaceId, projectId, project?.primaryRuntime?.id, moduleStore] + ) + + let endpoint = `http://${module?.definitions?.nodes?.[0]?.host}:${module?.definitions?.nodes?.[0]?.port}${module?.definitions?.nodes?.[0]?.endpoint}` + .replace("${{project_slug}}", project?.slug ?? "${{project_slug}}") + + flow?.settings?.nodes?.forEach(setting => { + endpoint = endpoint.replace(`\${{${setting?.flowSettingIdentifier}}}`, setting?.value) + }) + + return module?.definitions?.nodes?.[0] && + + + + + + + + + + + setting?.flowSettingIdentifier === "httpMethod")?.value ? ( + + {flow?.settings?.nodes?.find(setting => setting?.flowSettingIdentifier === "httpMethod")?.value} + + ) : undefined} + right={ + + + + }> +
+ + {endpoint} + +
+ +
+
+
+
+
+
+ +} \ No newline at end of file From 2484e29f0a39eebc764a65131537f5f4c66060d7 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Sun, 14 Jun 2026 01:13:17 +0200 Subject: [PATCH 06/66] feat: undefined bug with hashToColor --- src/packages/ce/src/flow/views/FlowExecutionResultView.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/packages/ce/src/flow/views/FlowExecutionResultView.tsx b/src/packages/ce/src/flow/views/FlowExecutionResultView.tsx index b071de74..f1b39221 100644 --- a/src/packages/ce/src/flow/views/FlowExecutionResultView.tsx +++ b/src/packages/ce/src/flow/views/FlowExecutionResultView.tsx @@ -228,7 +228,7 @@ export const FlowExecutionResultView: React.FC = () => { return - + #{id?.match(/ExecutionResult\/(\d+)$/)?.[1]} @@ -298,7 +298,7 @@ export const FlowExecutionResultView: React.FC = () => { minWidth: "16px", minHeight: "16px", }} - color={hashToColor(item?.data?.payload?.id)}/> + color={hashToColor(item?.data?.payload?.id ?? "")}/> { minWidth: "16px", minHeight: "16px", }} - color={hashToColor(item?.data?.payload?.id)}/> + color={hashToColor(item?.data?.payload?.id ?? "")}/> Date: Sun, 14 Jun 2026 01:13:30 +0200 Subject: [PATCH 07/66] feat: UI screen --- .../panels/FlowPanelControlComponent.tsx | 392 ++++++++++-------- 1 file changed, 225 insertions(+), 167 deletions(-) diff --git a/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx b/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx index 3c5a770c..72fc7849 100644 --- a/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx +++ b/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx @@ -1,21 +1,19 @@ import React from "react"; +import {Flow, LiteralValue, NodeFunction, ReferenceValue, SubFlowValue} from "@code0-tech/sagittarius-graphql-types"; import { - Flow, - LiteralValue, - Namespace, - NamespaceProject, - NodeFunction, - ReferenceValue, - SubFlowValue -} from "@code0-tech/sagittarius-graphql-types"; -import { + AuroraBackground, Badge, Button, - Dialog, - DialogContent, - DialogOverlay, - DialogPortal, - DialogTrigger, + Card, + EditorInput, + Flex, + SelectContent, + SelectItem, + SelectItemText, + SelectPortal, + SelectTrigger, + SelectValue, + SelectViewport, Spacing, Text, Tooltip, @@ -32,13 +30,22 @@ import {SuggestionDialogComponent} from "@edition/function/components/suggestion import {useHotkeys} from "react-hotkeys-hook"; import {useSelectedFunctionNode} from "@edition/function/hooks/FunctionNode.selected.hook"; import {useFunctionSuggestions} from "@edition/function/hooks/Function.suggestion.hook"; -import {useParams} from "next/navigation"; -import {FlowTypeService} from "@edition/flowtype/services/FlowType.service"; -import {ProjectService} from "@edition/project/services/Project.service"; -import {ModuleService} from "@edition/module/services/Module.service"; -import {IconCheck, IconCopy} from "@tabler/icons-react"; -import {InputWrapper} from "@code0-tech/pictor/dist/components/form/InputWrapper"; -import {useCopyToClipboard} from "@uidotdev/usehooks"; +import { + IconArrowBigUp, + IconBackspace, + IconChevronDown, + IconLetterA, + IconLetterQ, + IconSend, + IconX +} from "@tabler/icons-react"; +import {HoverCard, HoverCardContent, HoverCardPortal, HoverCardTrigger} from "@radix-ui/react-hover-card"; +import {ChaoticOrbit} from "ldrs/react"; +import {StreamLanguage} from "@codemirror/language"; +import CardSection from "@code0-tech/pictor/dist/components/card/CardSection"; +import {Select} from "@radix-ui/react-select"; +import {SiClaude} from "@icons-pack/react-simple-icons"; +import 'ldrs/react/ChaoticOrbit.css' export interface FlowPanelControlComponentProps { flowId: Flow['id'] @@ -50,71 +57,17 @@ export const FlowPanelControlComponent: React.FC const {flowId} = props //services and stores - const params = useParams() const flowService = useService(FlowService) const flowStore = useStore(FlowService) - const flowTypeService = useService(FlowTypeService) - const flowTypeStore = useStore(FlowTypeService) - const projectService = useService(ProjectService) - const projectStore = useStore(ProjectService) - const moduleService = useService(ModuleService) - const moduleStore = useStore(ModuleService) - - const [copiedText, copyToClipboard] = useCopyToClipboard(); - const hasCopiedText = Boolean(copiedText); + const [, startTransition] = React.useTransition() const [suggestionDialogOpen, setSuggestionDialogOpen] = React.useState(false) const [addNextNodeTooltipOpen, setAddNextNodeTooltipOpen] = React.useState(false) - const namespaceIndex = params.namespaceId as any as number - const projectIndex = params.projectId as any as number - const namespaceId: Namespace['id'] = `gid://sagittarius/Namespace/${namespaceIndex}` - const projectId: NamespaceProject['id'] = `gid://sagittarius/NamespaceProject/${projectIndex}` - //memoized values const selectedNode = useSelectedFunctionNode() const result = useFunctionSuggestions() - const flow = React.useMemo( - () => flowService.getById(flowId, { - namespaceId, - projectId - }), - [flowId, flowStore, namespaceId, projectId] - ) - - const project = React.useMemo( - () => projectService.getById(projectId, { - namespaceId - }), - [projectId, namespaceId, projectStore] - ) - - const flowType = React.useMemo( - () => flowTypeService.getById(flow?.type?.id, { - namespaceId, - projectId, - runtimeId: project?.primaryRuntime?.id - }), - [flow?.type?.id, namespaceId, projectId, project?.primaryRuntime?.id, flowTypeStore] - ) - - const module = React.useMemo( - () => moduleService.getById(flowType?.runtimeModule?.id, { - namespaceId: namespaceId, - projectId: projectId, - runtimeId: project?.primaryRuntime?.id - }), - [flowType?.runtimeModule?.id, namespaceId, projectId, project?.primaryRuntime?.id, moduleStore] - ) - - let endpoint = `http://${module?.definitions?.nodes?.[0]?.host}:${module?.definitions?.nodes?.[0]?.port}${module?.definitions?.nodes?.[0]?.endpoint}` - .replace("${{project_slug}}", project?.slug ?? "${{project_slug}}") - - flow?.settings?.nodes?.forEach(setting => { - endpoint = endpoint.replace(`\${{${setting?.flowSettingIdentifier}}}`, setting?.value) - }) - //callbacks const deleteActiveNode = React.useCallback(() => { if (!selectedNode) return @@ -136,6 +89,8 @@ export const FlowPanelControlComponent: React.FC } }, [flowId, flowService, flowStore, selectedNode]) + const [aiOpen, setAiOpen] = React.useState(false) + useHotkeys('shift+a', (keyboardEvent) => { if (selectedNode && !selectedNode.data.functionId) setSuggestionDialogOpen(true) else setAddNextNodeTooltipOpen(true) @@ -143,101 +98,204 @@ export const FlowPanelControlComponent: React.FC keyboardEvent.preventDefault() }, [selectedNode]) - return + useHotkeys('backspace', (keyboardEvent) => { + if (selectedNode) deleteActiveNode() + else setAddNextNodeTooltipOpen(true) + keyboardEvent.stopPropagation() + keyboardEvent.preventDefault() + }, [selectedNode]) - + useHotkeys('shift+q', (keyboardEvent) => { + setAiOpen(prevState => !prevState) + keyboardEvent.stopPropagation() + keyboardEvent.preventDefault() + }, [selectedNode]) + + return + + + + - - - - + + + + Select a node to delete it + + + + + + + + + + Select a node to add a next node + + + + - - - - Select a node to delete it - - - - - - + + + + + + + Generating... - - - - {!selectedNode || !!selectedNode.data.functionId && - Select a node to add a next node - } - - - {module?.definitions?.nodes?.[0] ? ( - - - - - - - - - setting?.flowSettingIdentifier === "httpMethod")?.value ? ( - - {flow?.settings?.nodes?.find(setting => setting?.flowSettingIdentifier === "httpMethod")?.value} - - ) : undefined} - right={ - - - - }> -
- - {endpoint} - -
- -
-
-
-
- ) : (null as any)} - - - - +
+ + + + + + + + + + + + + + + + + + + + + + ; } From f203374dac2d5e285b30dfc9bd4cbb7a3b4bae26 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Sun, 14 Jun 2026 17:02:52 +0200 Subject: [PATCH 08/66] feat: moving style import to layout --- .../namespace/[namespaceId]/project/[projectId]/flow/layout.tsx | 1 + .../ce/src/flow/components/builder/FlowBuilderComponent.tsx | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/layout.tsx b/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/layout.tsx index 8be44185..b1955ec6 100644 --- a/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/layout.tsx +++ b/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/layout.tsx @@ -1,5 +1,6 @@ "use client" import {FlowLayout} from "@edition/flow/layout/FlowLayout"; +import '@xyflow/react/dist/style.css'; export default FlowLayout diff --git a/src/packages/ce/src/flow/components/builder/FlowBuilderComponent.tsx b/src/packages/ce/src/flow/components/builder/FlowBuilderComponent.tsx index 4efe41b0..758c255b 100644 --- a/src/packages/ce/src/flow/components/builder/FlowBuilderComponent.tsx +++ b/src/packages/ce/src/flow/components/builder/FlowBuilderComponent.tsx @@ -11,7 +11,6 @@ import { ViewportPortal } from "@xyflow/react"; import React from "react"; -import '@xyflow/react/dist/style.css'; import "./FlowBuilderComponent.style.scss" import {FlowBuilderEdgeComponent} from "./FlowBuilderEdgeComponent"; import {Flow, type Namespace, type NamespaceProject} from "@code0-tech/sagittarius-graphql-types"; From 8818f88ce102a572549c5055f3a5caf10e90ace1 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Sun, 14 Jun 2026 17:03:12 +0200 Subject: [PATCH 09/66] feat: new flow overview page with ai chat --- .../project/[projectId]/flow/page.tsx | 67 +----- .../ce/src/flow/pages/FlowOverviewPage.tsx | 208 ++++++++++++++++++ 2 files changed, 210 insertions(+), 65 deletions(-) create mode 100644 src/packages/ce/src/flow/pages/FlowOverviewPage.tsx diff --git a/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/page.tsx b/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/page.tsx index a89644ad..45049222 100644 --- a/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/page.tsx +++ b/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/page.tsx @@ -1,68 +1,5 @@ "use client" -import { - Button, - Col, - Flex, - ScrollArea, - ScrollAreaScrollbar, - ScrollAreaThumb, - ScrollAreaViewport, - Spacing, - Text -} from "@code0-tech/pictor"; -import React from "react"; -import Link from "next/link"; -import {useParams} from "next/navigation"; -import {ResizablePanel} from "@code0-tech/pictor/dist/components/resizable/Resizable"; +import {FlowOverviewPage} from "@edition/flow/pages/FlowOverviewPage"; -export default function Page() { - - const params = useParams() - - const namespaceIndex = params?.namespaceId as any as number - - return - - - - - Create or select a flow - - - Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor - invidunt ut - labore et dolore magna aliquyam erat, sed diam voluptua. - - - - - - - - - Or start from a template - - - - - - - -
- -} \ No newline at end of file +export default FlowOverviewPage \ No newline at end of file diff --git a/src/packages/ce/src/flow/pages/FlowOverviewPage.tsx b/src/packages/ce/src/flow/pages/FlowOverviewPage.tsx new file mode 100644 index 00000000..e901ac7b --- /dev/null +++ b/src/packages/ce/src/flow/pages/FlowOverviewPage.tsx @@ -0,0 +1,208 @@ +import React from "react"; +import {useParams} from "next/navigation"; +import {ResizablePanel} from "@code0-tech/pictor/dist/components/resizable/Resizable"; +import { + AuroraBackground, + Button, + Card, + Col, + EditorInput, + Flex, + Progress, + Row, + SelectContent, + SelectItem, + SelectItemText, + SelectPortal, + SelectTrigger, + SelectValue, + SelectViewport, + Spacing, + Text +} from "@code0-tech/pictor"; +import {Panel} from "@xyflow/react"; +import {StreamLanguage} from "@codemirror/language"; +import CardSection from "@code0-tech/pictor/dist/components/card/CardSection"; +import {Select} from "@radix-ui/react-select"; +import {IconChevronDown, IconSend} from "@tabler/icons-react"; +import {SiClaude, SiDhl, SiDiscord, SiGithub, SiShopify} from "@icons-pack/react-simple-icons"; + +export const FlowOverviewPage: React.FC = () => { + const params = useParams(); + + const namespaceIndex = params?.namespaceId as any as number + + return + + + + Good morning, @root
+ Let's automate something. +
+ + + + + + + + / + + + + + Smart logistics + + + + + + Create a parcel shipment over DHL API on order receivement from shopify. + + + + + + + + + + + / + + + + + Smart devops + + + + + + Create a discord message if a new issue is created in github. + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + Upgrade your license to increase your AI usage limit + + + + + +
+} \ No newline at end of file From 745121a777cbc243900163c094c491d9a51f2da3 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Sun, 14 Jun 2026 17:03:27 +0200 Subject: [PATCH 10/66] feat: design changes to control panel --- .../src/flow/components/panels/FlowPanelControlComponent.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx b/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx index 72fc7849..7d780f5f 100644 --- a/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx +++ b/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx @@ -152,7 +152,7 @@ export const FlowPanelControlComponent: React.FC }} color={"tertiary"}> - Add next node + Add node + @@ -170,7 +170,6 @@ export const FlowPanelControlComponent: React.FC From d880c41172bf50a392f061ba84b07cdb513bacf7 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Sun, 14 Jun 2026 17:03:50 +0200 Subject: [PATCH 12/66] feat: design changes to trigger node --- .../nodes/FunctionNodeTriggerComponent.tsx | 84 +++++++++---------- 1 file changed, 41 insertions(+), 43 deletions(-) diff --git a/src/packages/ce/src/function/components/nodes/FunctionNodeTriggerComponent.tsx b/src/packages/ce/src/function/components/nodes/FunctionNodeTriggerComponent.tsx index a08406bb..bd0eed54 100644 --- a/src/packages/ce/src/function/components/nodes/FunctionNodeTriggerComponent.tsx +++ b/src/packages/ce/src/function/components/nodes/FunctionNodeTriggerComponent.tsx @@ -1,6 +1,6 @@ import React, {CSSProperties, memo} from "react"; import {Handle, Node, NodeProps, Position} from "@xyflow/react"; -import {Badge, Card, Flex, Text, useService, useStore, useStore as usePictorStore} from "@code0-tech/pictor"; +import {Card, Flex, Text, useService, useStore, useStore as usePictorStore} from "@code0-tech/pictor"; import {FunctionNodeComponentProps} from "@edition/function/components/nodes/FunctionNodeComponent"; import {FlowTypeService} from "@edition/flowtype/services/FlowType.service"; import {FlowService} from "@edition/flow/services/Flow.service"; @@ -12,8 +12,6 @@ import {useFlowValidation} from "@edition/flow/hooks/Flow.validation.hook"; import {underlineBySeverity} from "@core/util/inspection"; import {useSelectedFunctionNode} from "@edition/function/hooks/FunctionNode.selected.hook"; import {LiteralBadgeComponent} from "@edition/datatype/components/badges/LiteralBadgeComponent"; -import {ReferenceBadgeComponent} from "@edition/datatype/components/badges/ReferenceBadgeComponent"; -import {NodeBadgeComponent} from "@edition/datatype/components/badges/NodeBadgeComponent"; import {useParams} from "next/navigation"; import {ProjectService} from "@edition/project/services/Project.service"; import {ModuleService} from "@edition/module/services/Module.service"; @@ -151,54 +149,54 @@ export const FunctionNodeTriggerComponent: React.FC - - - - Flow trigger - + {/* + + + + Trigger + + {module?.definitions?.nodes?.[0] && ( - - {endpoint} - + {endpoint} )} - - - - {flow ? displayMessage : flowType?.names?.[0].content ?? FALLBACK_FLOW_TYPE_NAME} - - - { - isReferenced === true ? ( -
- -
- ) : null - } - - + */} + + + + + {flow ? displayMessage : flowType?.names?.[0].content ?? FALLBACK_FLOW_TYPE_NAME} + + + { + isReferenced === true ? ( +
+ +
+ ) : null + } + + +
From 0431f3bdc91da1e793e93feb149030c64caa2f9d Mon Sep 17 00:00:00 2001 From: nicosammito Date: Sun, 14 Jun 2026 17:04:10 +0200 Subject: [PATCH 13/66] feat: correct border radius styling for flow builder --- .../[namespaceId]/project/[projectId]/flow/[flowId]/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/[flowId]/page.tsx b/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/[flowId]/page.tsx index 1d2bde01..c8e49b38 100644 --- a/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/[flowId]/page.tsx +++ b/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/[flowId]/page.tsx @@ -44,7 +44,7 @@ export default function Page() { + style={{...(tab === "execution" ? {borderRadius: "1rem"} : {borderTopLeftRadius: "1rem", borderTopRightRadius: "1rem"})}}> { From 229f02b872dccff164436b98cd7b876b4048079a Mon Sep 17 00:00:00 2001 From: nicosammito Date: Sun, 14 Jun 2026 17:04:20 +0200 Subject: [PATCH 14/66] feat: correct link for flow tab --- src/packages/ce/src/project/views/ProjectTabView.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/packages/ce/src/project/views/ProjectTabView.tsx b/src/packages/ce/src/project/views/ProjectTabView.tsx index 9e616c66..30416b3a 100644 --- a/src/packages/ce/src/project/views/ProjectTabView.tsx +++ b/src/packages/ce/src/project/views/ProjectTabView.tsx @@ -23,11 +23,7 @@ export const ProjectTabView: React.FC = () => { From cd0bf4124d8f61e38884f21b35d3bbcb3eeee381 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Sun, 14 Jun 2026 17:04:35 +0200 Subject: [PATCH 15/66] feat: updating pictor version --- .env.local | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.local b/.env.local index f8c71bbc..8f42ec10 100644 --- a/.env.local +++ b/.env.local @@ -4,7 +4,7 @@ NEXT_PUBLIC_EDITION=ce SAGITTARIUS_GRAPHQL_URL=http://localhost:80/graphql NEXT_PUBLIC_SCULPTOR_VERSION=0.0.0 -NEXT_PUBLIC_PICTOR_VERSION=0.10.3 +NEXT_PUBLIC_PICTOR_VERSION=0.10.4 NEXT_PUBLIC_ALLOWED_REDIRECT_DOMAINS=*.code0.tech,*.codezero.build NEXT_PUBLIC_OTEL_SERVICE_NAME=#"sculptor-client" From 3b200e71db78f31a4dd7d09abc813737fd32335d Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 15 Jun 2026 12:48:09 +0200 Subject: [PATCH 16/66] feat: correct import --- src/packages/ce/src/flow/hooks/Flow.schema.hook.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/ce/src/flow/hooks/Flow.schema.hook.ts b/src/packages/ce/src/flow/hooks/Flow.schema.hook.ts index 2bdfa14c..b9193f28 100644 --- a/src/packages/ce/src/flow/hooks/Flow.schema.hook.ts +++ b/src/packages/ce/src/flow/hooks/Flow.schema.hook.ts @@ -6,7 +6,7 @@ import {useSchemaAction} from "@edition/flow/components/FlowWorkerProvider"; import {DatatypeService} from "@edition/datatype/services/Datatype.service"; import {FunctionService} from "@edition/function/services/Function.service"; import {NodeSchema} from "@code0-tech/triangulum"; -import {FlowView} from "@ce/flow/services/Flow.view"; +import {FlowView} from "@edition/flow/services/Flow.view"; export const useFlowSchema = ( flow: Flow['id'] | FlowView, From 4e6ea31e49d6fbd61c05432c8aaaa193d561bb88 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 15 Jun 2026 12:48:26 +0200 Subject: [PATCH 17/66] feat: introducing static flow templates --- .../ce/src/flow/pages/FlowOverviewPage.tsx | 150 +++++++++--------- 1 file changed, 75 insertions(+), 75 deletions(-) diff --git a/src/packages/ce/src/flow/pages/FlowOverviewPage.tsx b/src/packages/ce/src/flow/pages/FlowOverviewPage.tsx index e901ac7b..1f2156fb 100644 --- a/src/packages/ce/src/flow/pages/FlowOverviewPage.tsx +++ b/src/packages/ce/src/flow/pages/FlowOverviewPage.tsx @@ -1,5 +1,4 @@ import React from "react"; -import {useParams} from "next/navigation"; import {ResizablePanel} from "@code0-tech/pictor/dist/components/resizable/Resizable"; import { AuroraBackground, @@ -9,7 +8,10 @@ import { EditorInput, Flex, Progress, - Row, + ScrollArea, + ScrollAreaScrollbar, + ScrollAreaThumb, + ScrollAreaViewport, SelectContent, SelectItem, SelectItemText, @@ -25,12 +27,31 @@ import {StreamLanguage} from "@codemirror/language"; import CardSection from "@code0-tech/pictor/dist/components/card/CardSection"; import {Select} from "@radix-ui/react-select"; import {IconChevronDown, IconSend} from "@tabler/icons-react"; -import {SiClaude, SiDhl, SiDiscord, SiGithub, SiShopify} from "@icons-pack/react-simple-icons"; +import {SiClaude} from "@icons-pack/react-simple-icons"; +import {icon, IconString} from "@core/util/icons"; + +const flowTemplates = [ + { + icons: [ + "simple:shopify", + "simple:dhl", + ], + description: "Smart logistics", + prompt: "Create a parcel shipment over DHL API on order receivement from shopify." + }, + { + icons: [ + "simple:discord", + "simple:github", + ], + description: "Smart devops", + prompt: "Create a discord message if a new issue is created in github." + }, +] export const FlowOverviewPage: React.FC = () => { - const params = useParams(); - const namespaceIndex = params?.namespaceId as any as number + const [prompt, setPrompt] = React.useState("") return @@ -41,57 +62,60 @@ export const FlowOverviewPage: React.FC = () => { Good morning, @root
Let's automate something.
- - - - - - - - / - - - - - Smart logistics - - - - - - Create a parcel shipment over DHL API on order receivement from shopify. - - - - - - - - - - - / - - - - - Smart devops - - - - - - Create a discord message if a new issue is created in github. - - - - - + {/*@ts-ignore*/} + + + + + + {flowTemplates.map(flowTemplate => { + + const displayIcons = flowTemplate.icons.map(i => icon(i as IconString)) + + return setPrompt(flowTemplate.prompt)}> + + + + {displayIcons.map((DisplayIcon) => ( + /* @ts-ignore*/ + + ))} + + + {flowTemplate.description} + + + + + + {flowTemplate.prompt} + + + + + })} + + + + + + + + setPrompt(value)} wrapperComponent={{ style: { background: "transparent", @@ -109,30 +133,6 @@ export const FlowOverviewPage: React.FC = () => { - - - - - - - - - - Ask - - - - - Agent - - - - - - - - - - - + + + + + + + + + + + + + + - - + + + + + + Upgrade your license to increase your AI usage limit + + + From b7b5aa2aafcc53a8775ea0692c2f31a54f1b9ff9 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 15 Jun 2026 12:48:53 +0200 Subject: [PATCH 19/66] feat: using border instead of box shadow --- .../function/components/nodes/FunctionNodeComponent.style.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/ce/src/function/components/nodes/FunctionNodeComponent.style.scss b/src/packages/ce/src/function/components/nodes/FunctionNodeComponent.style.scss index 40722baf..5b65cd4c 100644 --- a/src/packages/ce/src/function/components/nodes/FunctionNodeComponent.style.scss +++ b/src/packages/ce/src/function/components/nodes/FunctionNodeComponent.style.scss @@ -34,6 +34,6 @@ } &--active { - box-shadow: 0 0 0 1px rgba(variables.$info, .5); + border: 1px solid rgba(variables.$info, .5); } } \ No newline at end of file From cacaead8899fb2083b6bd6852454433a777b4742 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 15 Jun 2026 12:49:17 +0200 Subject: [PATCH 20/66] feat: tooltip --- .../nodes/FunctionNodeDefaultComponent.tsx | 56 ++++++++++++++++++- 1 file changed, 53 insertions(+), 3 deletions(-) diff --git a/src/packages/ce/src/function/components/nodes/FunctionNodeDefaultComponent.tsx b/src/packages/ce/src/function/components/nodes/FunctionNodeDefaultComponent.tsx index 09d9c526..83c133e9 100644 --- a/src/packages/ce/src/function/components/nodes/FunctionNodeDefaultComponent.tsx +++ b/src/packages/ce/src/function/components/nodes/FunctionNodeDefaultComponent.tsx @@ -1,10 +1,10 @@ -import {Handle, Node, NodeProps, Position, useStore} from "@xyflow/react"; +import {Handle, Node, NodeProps, NodeToolbar, Position, useStore} from "@xyflow/react"; import React, {CSSProperties, memo} from "react"; import "./FunctionNodeComponent.style.scss"; import {FunctionNodeComponentProps} from "./FunctionNodeComponent"; -import {Badge, Card, Flex, Text, useService, useStore as usePictorStore} from "@code0-tech/pictor"; +import {Badge, Button, ButtonGroup, Card, Flex, Text, useService, useStore as usePictorStore} from "@code0-tech/pictor"; import {useFlowValidation} from "@edition/flow/hooks/Flow.validation.hook"; -import {IconVariable} from "@tabler/icons-react"; +import {IconBackspace, IconEdit, IconSparkles, IconVariable} from "@tabler/icons-react"; import {FlowService} from "@edition/flow/services/Flow.service"; import {FunctionService} from "@edition/function/services/Function.service"; import {LiteralBadgeComponent} from "@edition/datatype/components/badges/LiteralBadgeComponent"; @@ -147,6 +147,56 @@ export const FunctionNodeDefaultComponent: React.FC + + + + + + + + Date: Mon, 15 Jun 2026 12:49:26 +0200 Subject: [PATCH 21/66] feat: design changes and tooltip --- .../nodes/FunctionNodeTriggerComponent.tsx | 161 ++++++++++++------ 1 file changed, 107 insertions(+), 54 deletions(-) diff --git a/src/packages/ce/src/function/components/nodes/FunctionNodeTriggerComponent.tsx b/src/packages/ce/src/function/components/nodes/FunctionNodeTriggerComponent.tsx index bd0eed54..c80f7097 100644 --- a/src/packages/ce/src/function/components/nodes/FunctionNodeTriggerComponent.tsx +++ b/src/packages/ce/src/function/components/nodes/FunctionNodeTriggerComponent.tsx @@ -1,10 +1,19 @@ import React, {CSSProperties, memo} from "react"; -import {Handle, Node, NodeProps, Position} from "@xyflow/react"; -import {Card, Flex, Text, useService, useStore, useStore as usePictorStore} from "@code0-tech/pictor"; +import {Handle, Node, NodeProps, NodeToolbar, Position} from "@xyflow/react"; +import { + Button, + ButtonGroup, + Card, + Flex, + Text, + useService, + useStore, + useStore as usePictorStore +} from "@code0-tech/pictor"; import {FunctionNodeComponentProps} from "@edition/function/components/nodes/FunctionNodeComponent"; import {FlowTypeService} from "@edition/flowtype/services/FlowType.service"; import {FlowService} from "@edition/flow/services/Flow.service"; -import {IconVariable} from "@tabler/icons-react"; +import {IconEdit, IconSparkles, IconVariable} from "@tabler/icons-react"; import {Namespace, NamespaceProject, NodeFunction} from "@code0-tech/sagittarius-graphql-types"; import {icon, IconString} from "@core/util/icons"; import {FALLBACK_FLOW_TYPE_DISPLAY_MESSAGE, FALLBACK_FLOW_TYPE_NAME} from "@core/util/fallback-translations"; @@ -122,7 +131,7 @@ export const FunctionNodeTriggerComponent: React.FC - @@ -147,57 +156,101 @@ export const FunctionNodeTriggerComponent: React.FC - {/* - - - - Trigger - - - {module?.definitions?.nodes?.[0] && ( - {endpoint} - )} + return ( + + + + + + + + + + +
+ + + + + + { + isReferenced === true ? ( +
+ +
+ ) : null + } + + +
+
+ + {flow ? displayMessage : flowType?.names?.[0].content ?? FALLBACK_FLOW_TYPE_NAME} +
- */} - - - - - {flow ? displayMessage : flowType?.names?.[0].content ?? FALLBACK_FLOW_TYPE_NAME} - - - { - isReferenced === true ? ( -
- -
- ) : null - } - - -
- + ) }) \ No newline at end of file From 35d173b8b1487b3427004ca2ad543d9fbc7624ba Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 15 Jun 2026 12:49:35 +0200 Subject: [PATCH 22/66] feat: support for simple icons --- src/packages/core/src/util/icons.tsx | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/packages/core/src/util/icons.tsx b/src/packages/core/src/util/icons.tsx index 1a6899eb..f79a858c 100644 --- a/src/packages/core/src/util/icons.tsx +++ b/src/packages/core/src/util/icons.tsx @@ -1,11 +1,26 @@ import React from "react" import * as TablerIcons from "@tabler/icons-react" +import * as SimpleIcons from "@icons-pack/react-simple-icons" -export type IconString = `${'tabler'}:${string}` +type TablerIcon = React.FC> +type SimpleIcon = React.FC> +export type IconString = `${'tabler'}:${string}` | `${'simple'}:${string}` -export const icon = (icon?: IconString): React.FC> => { +export const icon = (icon?: IconString): TablerIcon | SimpleIcon => { const fallbackIcon = TablerIcons.IconNote + + if (icon?.startsWith("simple:")) { + const name = icon.replace("simple:", "").trim() + const normalizedName = `Si${name.charAt(0).toUpperCase() + name.slice(1)}` + + const resolvedIcon = normalizedName in SimpleIcons + ? SimpleIcons[normalizedName as keyof typeof SimpleIcons] + : fallbackIcon + + return resolvedIcon as SimpleIcon + } + const normalizedIconName = `Icon${(icon?.replace("tabler:", "") ?? "Note") .trim() .replace(/^icon/i, "") @@ -18,5 +33,5 @@ export const icon = (icon?: IconString): React.FC> + return resolvedIcon as TablerIcon } \ No newline at end of file From 25192eb228e05b289602a9b145cae41104347b3e Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 15 Jun 2026 12:49:42 +0200 Subject: [PATCH 23/66] feat: size prop --- .../components/badges/LiteralBadgeComponent.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/packages/ce/src/datatype/components/badges/LiteralBadgeComponent.tsx b/src/packages/ce/src/datatype/components/badges/LiteralBadgeComponent.tsx index 919319ab..92ecb021 100644 --- a/src/packages/ce/src/datatype/components/badges/LiteralBadgeComponent.tsx +++ b/src/packages/ce/src/datatype/components/badges/LiteralBadgeComponent.tsx @@ -4,14 +4,16 @@ import {Badge, BadgeType, Text, Tooltip, TooltipContent, TooltipPortal, TooltipT import {truncateText} from "@edition/flow/components/folder/FlowFolderComponent"; import {DataTypeJSONInputTreeComponent} from "@edition/datatype/components/inputs/json/DataTypeJSONInputTreeComponent"; import { EditableJSONEntry } from "../inputs/json/DataTypeJSONInputComponent"; +import {Sizes} from "@code0-tech/pictor/dist/utils"; -export interface LiteralBadgeComponentProps extends Omit { +export interface LiteralBadgeComponentProps extends Omit { value: LiteralValue + size?: Sizes } export const LiteralBadgeComponent: React.FC = (props) => { - const {value, ...rest} = props + const {value, size = "sm", ...rest} = props const truncatedValue = React.useMemo(() => { return truncateText(JSON.stringify(value.value), 75) @@ -24,7 +26,7 @@ export const LiteralBadgeComponent: React.FC = (prop !JSON.stringify(value).includes(truncatedValue) ? ( - + {truncatedValue} @@ -39,7 +41,7 @@ export const LiteralBadgeComponent: React.FC = (prop setCollapsedState={function (path: string[], collapsed: boolean): void { }}/> ) : ( - + {JSON.stringify(value.value)} ) @@ -48,7 +50,7 @@ export const LiteralBadgeComponent: React.FC = (prop ) : ( - + {truncatedValue} ) From 5f6d4680857a30a27fc0f06dd147bdb90e6e5070 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 15 Jun 2026 12:50:10 +0200 Subject: [PATCH 24/66] feat: hot key for opening the edit menu and if tab is changing current node is centered --- .../[projectId]/flow/[flowId]/page.tsx | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/[flowId]/page.tsx b/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/[flowId]/page.tsx index c8e49b38..9dcfa315 100644 --- a/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/[flowId]/page.tsx +++ b/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/[flowId]/page.tsx @@ -14,6 +14,8 @@ import { } from "@code0-tech/pictor/dist/components/resizable/Resizable"; import {Layout} from "@code0-tech/pictor/dist/components/layout/Layout"; import {FlowExecutionResultView} from "@edition/flow/views/FlowExecutionResultView"; +import {useHotkeys} from "react-hotkeys-hook"; +import {Node, useReactFlow} from "@xyflow/react"; export default function Page() { @@ -23,6 +25,26 @@ export default function Page() { const flowId: Flow['id'] = `gid://sagittarius/Flow/${flowIndex}` const [tab, setTab] = React.useState(undefined); + const reactFlow = useReactFlow() + + useHotkeys('shift+1', (keyboardEvent) => { + setTab(prevState => prevState === "file" ? undefined : "file") + keyboardEvent.stopPropagation() + keyboardEvent.preventDefault() + }, []) + + React.useEffect(() => { + + const localSelectedNode = reactFlow.getNodes().filter((node) => node.selected)[0] as Node | undefined + if (!localSelectedNode) return + setTimeout(() => { + reactFlow.fitView({ + nodes: [{id: localSelectedNode.id}], + maxZoom: reactFlow.getZoom(), + minZoom: 1, + }); + }, 100) + }, [tab, reactFlow]) return + style={{ + ...(tab === "execution" ? {borderRadius: "1rem"} : { + borderTopLeftRadius: "1rem", + borderTopRightRadius: "1rem" + }) + }}> { @@ -63,7 +90,7 @@ export default function Page() { {tab === "file" && ( <> - From 97f7d3b29c96fadda3d373f001c5b0deaa43ad84 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 15 Jun 2026 14:43:41 +0200 Subject: [PATCH 25/66] feat: removing layout control, because currently we only support 1 layout and replace it with flow name for now --- .../panels/FlowPanelLayoutComponent.tsx | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/src/packages/ce/src/flow/components/panels/FlowPanelLayoutComponent.tsx b/src/packages/ce/src/flow/components/panels/FlowPanelLayoutComponent.tsx index ca162f86..b2317423 100644 --- a/src/packages/ce/src/flow/components/panels/FlowPanelLayoutComponent.tsx +++ b/src/packages/ce/src/flow/components/panels/FlowPanelLayoutComponent.tsx @@ -1,25 +1,35 @@ import React from "react"; -import {IconLayout, IconLayoutDistributeHorizontal, IconLayoutDistributeVertical} from "@tabler/icons-react"; import {Panel} from "@xyflow/react"; -import { - SegmentedControl, - SegmentedControlItem, Text, - Tooltip, - TooltipContent, - TooltipPortal, - TooltipTrigger -} from "@code0-tech/pictor"; +import {Text, useService, useStore} from "@code0-tech/pictor"; +import {useParams} from "next/navigation"; +import {Flow, Namespace, NamespaceProject} from "@code0-tech/sagittarius-graphql-types"; +import {FlowService} from "@edition/flow/services/Flow.service"; -export interface FlowPanelLayoutComponentProps { -} +export const FlowPanelLayoutComponent: React.FC = () => { -export const FlowPanelLayoutComponent: React.FC = (props) => { + const params = useParams() + const flowService = useService(FlowService) + const flowStore = useStore(FlowService) - const {} = props + const namespaceIndex = params.namespaceId as any as number + const projectIndex = params.projectId as any as number + const flowIndex = params.flowId as any as number + const namespaceId: Namespace['id'] = `gid://sagittarius/Namespace/${namespaceIndex}` + const projectId: NamespaceProject['id'] = `gid://sagittarius/NamespaceProject/${projectIndex}` + const flowId: Flow['id'] = `gid://sagittarius/Flow/${flowIndex}` + + const flow = React.useMemo( + () => flowService.getById(flowId, { + namespaceId, + projectId + }), + [flowId, flowStore, namespaceId, projectId] + ) return - + {flow?.name} + {/* @@ -56,6 +66,6 @@ export const FlowPanelLayoutComponent: React.FC = - + */} } \ No newline at end of file From 11cf74886ca4a528ae25a7c2a7e539cebd5cac9d Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 15 Jun 2026 21:37:17 +0200 Subject: [PATCH 26/66] feat: updating triangulum --- package-lock.json | 16 ++++++++-------- package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index bd49f147..a89bae15 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@apollo/client": "^4.0.9", "@code0-tech/pictor": "^0.10.4", - "@code0-tech/triangulum": "^0.23.0", + "@code0-tech/triangulum": "^0.24.0", "@codemirror/lang-javascript": "^6.2.5", "@codemirror/lint": "^6.9.5", "@icons-pack/react-simple-icons": "^13.13.0", @@ -459,17 +459,17 @@ } }, "node_modules/@code0-tech/sagittarius-graphql-types": { - "version": "0.0.0-experimental-2598030203-92acc01bfb8e2f88212c504241726ae16f15a4ac", - "resolved": "https://registry.npmjs.org/@code0-tech/sagittarius-graphql-types/-/sagittarius-graphql-types-0.0.0-experimental-2598030203-92acc01bfb8e2f88212c504241726ae16f15a4ac.tgz", - "integrity": "sha512-yt1X0d9SnNXIig/Yk1e8Ggolee42OytSUhLGjbN7nIyuCns0eryfsffACIPw+igzxtNC5qkqbfm3mKzU9M/Ujg==", + "version": "0.0.0-experimental-2603093384-7294be43b142c66f3b3263d8021d470637a1501d", + "resolved": "https://registry.npmjs.org/@code0-tech/sagittarius-graphql-types/-/sagittarius-graphql-types-0.0.0-experimental-2603093384-7294be43b142c66f3b3263d8021d470637a1501d.tgz", + "integrity": "sha512-/wB4uH812uqMkQQcklO09tayS0q9g4Y6ovp/3egR/84YJnrWNsof0trikE2NcV0ZYyEOlOno3Gp482jyvo7gvg==", "peer": true }, "node_modules/@code0-tech/triangulum": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@code0-tech/triangulum/-/triangulum-0.23.0.tgz", - "integrity": "sha512-GDmfSKoett+olav9HPsaTCUdXR2joCu6FvvmJagCqnLDP4dctNR4dlcUgebLnQeTc0tCBeF0iSGBIzFhoj4bGA==", + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@code0-tech/triangulum/-/triangulum-0.24.0.tgz", + "integrity": "sha512-AIb1lRDQlVxb+4LTy9975TppEs9YeaUclzwGQJjTHYdPs6tfWix7p+Q1TncC+UNU9J2HhMDjBZ1UiMmPua9cew==", "peerDependencies": { - "@code0-tech/sagittarius-graphql-types": "0.0.0-experimental-2598030203-92acc01bfb8e2f88212c504241726ae16f15a4ac", + "@code0-tech/sagittarius-graphql-types": "0.0.0-experimental-2603093384-7294be43b142c66f3b3263d8021d470637a1501d", "@typescript/vfs": "^1.6.4", "lossless-json": "^4.3.0", "typescript": "^5.9.3 || ^6.0.2" diff --git a/package.json b/package.json index af9f934f..27932d68 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "dependencies": { "@apollo/client": "^4.0.9", "@code0-tech/pictor": "^0.10.4", - "@code0-tech/triangulum": "^0.23.0", + "@code0-tech/triangulum": "^0.24.0", "@codemirror/lang-javascript": "^6.2.5", "@codemirror/lint": "^6.9.5", "@icons-pack/react-simple-icons": "^13.13.0", From 31ea699e855b891e58718be9c4b7db2137973398 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Tue, 16 Jun 2026 18:24:31 +0200 Subject: [PATCH 27/66] feat: install necessary deps for subscriptions to work --- package-lock.json | 124 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 3 ++ 2 files changed, 126 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index a89bae15..9e544f81 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,11 +22,14 @@ "@opentelemetry/instrumentation-fetch": "^0.218.0", "@opentelemetry/instrumentation-xml-http-request": "^0.218.0", "@opentelemetry/sdk-trace-web": "^2.6.1", + "@rails/actioncable": "^8.1.300", + "@types/rails__actioncable": "^8.0.3", "@uidotdev/usehooks": "^2.4.1", "@vercel/otel": "^2.1.1", "@xyflow/react": "^12.11.0", "date-fns": "^4.1.0", "graphql": "^16.12.0", + "graphql-ruby-client": "^1.15.1", "graphql-tag": "^2.12.6", "ldrs": "^1.1.9", "lodash": "^4.18.1", @@ -55,6 +58,7 @@ "resolved": "https://registry.npmjs.org/@apollo/client/-/client-4.1.9.tgz", "integrity": "sha512-qfpkQD51tdU/7iAR6aLb4w9o/L7I475DluWHRb61U/3Q0AH29nNOxOBHjBbWDdf16ncPOoQuxne1sEs2NjqBFw==", "license": "MIT", + "peer": true, "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@wry/caches": "^1.0.0", @@ -3355,6 +3359,12 @@ "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", "license": "MIT" }, + "node_modules/@rails/actioncable": { + "version": "8.1.300", + "resolved": "https://registry.npmjs.org/@rails/actioncable/-/actioncable-8.1.300.tgz", + "integrity": "sha512-zOENQsq3NM2jyBY6Z2qtZa3V/R/6OEqA+LGKixQbBMl7kk/J3FXDRcszPe74LsHNgB01jCl/DXu/xA8sHt4I/g==", + "license": "MIT" + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -3503,6 +3513,12 @@ "undici-types": "~7.16.0" } }, + "node_modules/@types/rails__actioncable": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@types/rails__actioncable/-/rails__actioncable-8.0.3.tgz", + "integrity": "sha512-y46MOTYorVQVwlHUyaZYbrh3nIkXsRYNuPna32lb3RngLVBlndNbIPvAUywFfhivftNhYg+vW5sZKWYCVIX2lA==", + "license": "MIT" + }, "node_modules/@types/react": { "version": "19.2.14", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", @@ -7060,6 +7076,23 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -7073,6 +7106,42 @@ "node": ">=10.13.0" } }, + "node_modules/glob/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/globals": { "version": "16.4.0", "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", @@ -7135,6 +7204,26 @@ "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" } }, + "node_modules/graphql-ruby-client": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/graphql-ruby-client/-/graphql-ruby-client-1.15.1.tgz", + "integrity": "sha512-dSn9+zL+4Q7PxLMl8uHpj4wmZbYYer4czer3HKdU3fH25ZUSxs+t06DmjYV/1bFN7UwwLpzot2b8IrW0IyEw7g==", + "license": "LGPL-3.0", + "dependencies": { + "glob": "^13.0.6", + "minimist": "^1.2.0" + }, + "bin": { + "graphql-ruby-client": "cli.js" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@apollo/client": ">=3.3.6", + "graphql": ">=14.3.1" + } + }, "node_modules/graphql-tag": { "version": "2.12.6", "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", @@ -7991,12 +8080,20 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/module-details-from-path": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz", @@ -8389,6 +8486,31 @@ "dev": true, "license": "MIT" }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, "node_modules/perfect-freehand": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/perfect-freehand/-/perfect-freehand-1.2.3.tgz", diff --git a/package.json b/package.json index 27932d68..2e1af7e0 100644 --- a/package.json +++ b/package.json @@ -28,11 +28,14 @@ "@opentelemetry/instrumentation-fetch": "^0.218.0", "@opentelemetry/instrumentation-xml-http-request": "^0.218.0", "@opentelemetry/sdk-trace-web": "^2.6.1", + "@rails/actioncable": "^8.1.300", + "@types/rails__actioncable": "^8.0.3", "@uidotdev/usehooks": "^2.4.1", "@vercel/otel": "^2.1.1", "@xyflow/react": "^12.11.0", "date-fns": "^4.1.0", "graphql": "^16.12.0", + "graphql-ruby-client": "^1.15.1", "graphql-tag": "^2.12.6", "ldrs": "^1.1.9", "lodash": "^4.18.1", From 11f571a0d59775df65355421ea6e098b4defd011 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Tue, 16 Jun 2026 18:24:47 +0200 Subject: [PATCH 28/66] feat: implement subscription routing --- src/app/layout.tsx | 58 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 5b91cbb2..7edbaa40 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -7,7 +7,8 @@ import { HttpLink, InMemoryCache, Observable, - ServerError + ServerError, + split, } from "@apollo/client"; import {ApolloProvider} from "@apollo/client/react"; import React, {Suspense} from "react"; @@ -20,6 +21,8 @@ import {Error} from "@code0-tech/sagittarius-graphql-types"; import {Inter} from 'next/font/google' import {GraphQLFormattedError} from "graphql/error"; import {addIslandErrorNotification} from "@code0-tech/pictor/dist/components/island/Island.hook"; +import {createConsumer} from "@rails/actioncable"; +import ActionCableLink from "graphql-ruby-client/subscriptions/ActionCableLink"; /** * Load the Inter font with Latin subset and swap display strategy @@ -170,21 +173,48 @@ export default function RootLayout({children}: Readonly<{ children: React.ReactN /** * Apollo Client instance with configured links and cache */ - const client = React.useMemo(() => new ApolloClient({ - cache: new InMemoryCache(), - link: ApolloLink.from([errorLink, authMiddleware, responseHandlerLink, new HttpLink({uri: "/graphql"})]), - defaultOptions: { - watchQuery: { - errorPolicy: "all", - }, - query: { - errorPolicy: "all", + const client = React.useMemo(() => { + const cable = createConsumer("/cable") + + const getToken = () => { + try { + const raw = localStorage.getItem("ide_code-zero_session") + return raw ? JSON.parse(raw)?.token : undefined + } catch { + return undefined + } + } + + const hasSubscriptionOperation = ({query: {definitions}}: any) => { + return definitions.some( + ({kind, operation}: any) => kind === "OperationDefinition" && operation === "subscription" + ) + } + + const actionCableLink = new ActionCableLink({ + cable, + connectionParams: () => { + const token = getToken() + return token ? {token: `Session ${token}`} : {} }, - mutate: { - errorPolicy: "all", + }) + + const link = ApolloLink.split( + hasSubscriptionOperation, + actionCableLink, + ApolloLink.from([errorLink, authMiddleware, responseHandlerLink, new HttpLink({uri: "/graphql"})]), + ) + + return new ApolloClient({ + cache: new InMemoryCache(), + link, + defaultOptions: { + watchQuery: {errorPolicy: "all"}, + query: {errorPolicy: "all"}, + mutate: {errorPolicy: "all"}, }, - }, - }), [authMiddleware]) + }) + }, [authMiddleware]) return React.useMemo(() => { return From 9c6724383b050744be2734bfdf4860818cf1b286 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Tue, 16 Jun 2026 21:12:03 +0200 Subject: [PATCH 29/66] feat: generate flow mutation --- .../mutations/AI.generateFlow.mutation.graphql | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/packages/ce/src/ai/services/mutations/AI.generateFlow.mutation.graphql diff --git a/src/packages/ce/src/ai/services/mutations/AI.generateFlow.mutation.graphql b/src/packages/ce/src/ai/services/mutations/AI.generateFlow.mutation.graphql new file mode 100644 index 00000000..563723dd --- /dev/null +++ b/src/packages/ce/src/ai/services/mutations/AI.generateFlow.mutation.graphql @@ -0,0 +1,18 @@ +mutation generateFlow($projectId: NamespaceProjectID!, $modelIdentifier: String!, $prompt: String!, $flowId: FlowID) { + velorumGenerateFlow(input: { + projectId: $projectId + modelIdentifier: $modelIdentifier + prompt: $prompt + flowId: $flowId + }) { + errors { + ...on Error { + errorCode + details { + __typename + } + } + } + executionIdentifier + } +} From 236719907e4f79d8e05d8b25452f422cbf78cac9 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Tue, 16 Jun 2026 21:12:15 +0200 Subject: [PATCH 30/66] feat: generate flow subscription --- .../subscriptions/AI.generateFlow.subscription.graphql | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/packages/ce/src/ai/services/subscriptions/AI.generateFlow.subscription.graphql diff --git a/src/packages/ce/src/ai/services/subscriptions/AI.generateFlow.subscription.graphql b/src/packages/ce/src/ai/services/subscriptions/AI.generateFlow.subscription.graphql new file mode 100644 index 00000000..ec873a2b --- /dev/null +++ b/src/packages/ce/src/ai/services/subscriptions/AI.generateFlow.subscription.graphql @@ -0,0 +1,5 @@ +subscription generateFlow($executionIdentifier: String!) { + velorumGenerateFlow(executionIdentifier: $executionIdentifier) { + flow + } +} From d53fc9705811f015dacbb1734901faa3e32b57b1 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Tue, 16 Jun 2026 21:12:24 +0200 Subject: [PATCH 31/66] feat: model fragment --- .../ce/src/ai/services/fragments/AI.model.fragment.graphql | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/packages/ce/src/ai/services/fragments/AI.model.fragment.graphql diff --git a/src/packages/ce/src/ai/services/fragments/AI.model.fragment.graphql b/src/packages/ce/src/ai/services/fragments/AI.model.fragment.graphql new file mode 100644 index 00000000..2522d14b --- /dev/null +++ b/src/packages/ce/src/ai/services/fragments/AI.model.fragment.graphql @@ -0,0 +1,7 @@ +fragment Model on VelorumModel { + __typename + identifier + name + tokenCost + types +} From bab6ad70bb74f892911a468a72a41334c99ea3ba Mon Sep 17 00:00:00 2001 From: nicosammito Date: Tue, 16 Jun 2026 21:12:36 +0200 Subject: [PATCH 32/66] feat: query all available models --- .../ce/src/ai/services/queries/AI.models.query.graphql | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/packages/ce/src/ai/services/queries/AI.models.query.graphql diff --git a/src/packages/ce/src/ai/services/queries/AI.models.query.graphql b/src/packages/ce/src/ai/services/queries/AI.models.query.graphql new file mode 100644 index 00000000..940175f9 --- /dev/null +++ b/src/packages/ce/src/ai/services/queries/AI.models.query.graphql @@ -0,0 +1,8 @@ +#import '../fragments/AI.model.fragment.graphql' +query models { + velorum { + models { + ...Model + } + } +} From 3f31c300c79f371fb4bd3be9c63de031ffd42929 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Tue, 16 Jun 2026 21:12:50 +0200 Subject: [PATCH 33/66] feat: ai service to query models and execute ai request --- src/packages/ce/src/ai/services/AI.service.ts | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/packages/ce/src/ai/services/AI.service.ts diff --git a/src/packages/ce/src/ai/services/AI.service.ts b/src/packages/ce/src/ai/services/AI.service.ts new file mode 100644 index 00000000..3e81af0c --- /dev/null +++ b/src/packages/ce/src/ai/services/AI.service.ts @@ -0,0 +1,57 @@ +import {ReactiveArrayService, ReactiveArrayStore} from "@code0-tech/pictor"; +import { + Mutation, + Query, + VelorumGenerateFlowInput, + VelorumGenerateFlowPayload, + VelorumModel, +} from "@code0-tech/sagittarius-graphql-types"; +import {GraphqlClient} from "@core/util/graphql-client"; +import velorumModelsQuery from "./queries/AI.models.query.graphql"; +import velorumGenerateFlowMutation from "./mutations/VelorumGenerateFlow.mutation.graphql"; +import {Payload, View} from "@code0-tech/pictor/dist/utils/view"; + +export type Model = VelorumModel & Payload + +export class AIService extends ReactiveArrayService { + + private readonly client: GraphqlClient + private i = 0 + + constructor(client: GraphqlClient, store: ReactiveArrayStore>) { + super(store) + this.client = client + } + + values(): VelorumModel[] { + const models = super.values() + if (models.length > 0) return models + + this.client.query({ + query: velorumModelsQuery, + }).then(result => { + const models = result.data?.velorum?.models ?? [] + models.forEach(model => { + if (model && !this.hasById(model.identifier)) { + this.set(this.i++, new View(model as Model)) + } + }) + }) + + return super.values() + } + + hasById(identifier: VelorumModel["identifier"]): boolean { + return super.values().some(m => m.identifier === identifier) + } + + async generateFlow(payload: VelorumGenerateFlowInput): Promise { + const result = await this.client.mutate({ + mutation: velorumGenerateFlowMutation, + variables: {...payload} + }) + + return result.data?.velorumGenerateFlow ?? undefined + } + +} From 77ee874c448e5d8d9dc6f1811f00dfa1ba09a273 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Tue, 16 Jun 2026 21:13:23 +0200 Subject: [PATCH 34/66] feat: undefinable errors --- .../src/function/components/suggestion/Suggestion.util.tsx | 6 +++--- .../components/suggestion/SuggestionDialogComponent.tsx | 2 +- .../ce/src/module/pages/ModuleConfigurationPage.tsx | 6 +++--- src/packages/ce/src/module/pages/ModulesPage.tsx | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/packages/ce/src/function/components/suggestion/Suggestion.util.tsx b/src/packages/ce/src/function/components/suggestion/Suggestion.util.tsx index d01cd766..358fa08c 100644 --- a/src/packages/ce/src/function/components/suggestion/Suggestion.util.tsx +++ b/src/packages/ce/src/function/components/suggestion/Suggestion.util.tsx @@ -50,9 +50,9 @@ export const useMappedSuggestions = (suggestions: (NodeFunction | SubFlowValue | value: suggestion, icon: functionDefinition?.displayIcon, displayMessage: functionDefinition?.names?.[0].content, - aliases: functionDefinition?.aliases?.[0].content?.split(";"), + aliases: functionDefinition?.aliases?.[0]?.content?.split(";"), definitionSource: module?.identifier, - description: functionDefinition?.descriptions?.[0].content, + description: functionDefinition?.descriptions?.[0]?.content, } } @@ -73,7 +73,7 @@ export const useMappedSuggestions = (suggestions: (NodeFunction | SubFlowValue | return { value: suggestion, icon: suggestion.functionDefinition?.displayIcon, - displayMessage: suggestion.functionDefinition?.names?.[0].content, + displayMessage: suggestion.functionDefinition?.names?.[0]?.content, aliases: suggestion.functionDefinition?.aliases?.[0].content?.split(";"), definitionSource: suggestion.functionDefinition.runtimeModule?.identifier, description: suggestion.functionDefinition?.descriptions?.[0].content, diff --git a/src/packages/ce/src/function/components/suggestion/SuggestionDialogComponent.tsx b/src/packages/ce/src/function/components/suggestion/SuggestionDialogComponent.tsx index 9818652e..86a15b04 100644 --- a/src/packages/ce/src/function/components/suggestion/SuggestionDialogComponent.tsx +++ b/src/packages/ce/src/function/components/suggestion/SuggestionDialogComponent.tsx @@ -114,7 +114,7 @@ export const SuggestionDialogComponent: React.FC const DisplayIcon = icon(suggestion.icon as IconString) return <> - { - {module?.descriptions?.[0].content}
+ {module?.descriptions?.[0]?.content}
This plugin was developed by {" "} @{module?.author} @@ -160,8 +160,8 @@ export const ModuleConfigurationPage: React.FC = () => { if (!moduleConfigurationSchemas[index]) return null return
- diff --git a/src/packages/ce/src/module/pages/ModulesPage.tsx b/src/packages/ce/src/module/pages/ModulesPage.tsx index bcdeafed..8ac6b205 100644 --- a/src/packages/ce/src/module/pages/ModulesPage.tsx +++ b/src/packages/ce/src/module/pages/ModulesPage.tsx @@ -113,7 +113,7 @@ export const ModulesPage: React.FC = () => { - {module.descriptions?.[0].content}
+ {module.descriptions?.[0]?.content}
This plugin was developed by @{module.author} From 5c69f29061aa60465d3a7dc8ee208b8ff6a195b1 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Tue, 16 Jun 2026 21:38:29 +0200 Subject: [PATCH 35/66] feat: imports --- src/packages/ce/src/ai/services/AI.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/ce/src/ai/services/AI.service.ts b/src/packages/ce/src/ai/services/AI.service.ts index 3e81af0c..ac8c1144 100644 --- a/src/packages/ce/src/ai/services/AI.service.ts +++ b/src/packages/ce/src/ai/services/AI.service.ts @@ -7,9 +7,9 @@ import { VelorumModel, } from "@code0-tech/sagittarius-graphql-types"; import {GraphqlClient} from "@core/util/graphql-client"; -import velorumModelsQuery from "./queries/AI.models.query.graphql"; import velorumGenerateFlowMutation from "./mutations/VelorumGenerateFlow.mutation.graphql"; import {Payload, View} from "@code0-tech/pictor/dist/utils/view"; +import velorumModelsQuery from "./queries/AI.models.query.graphql"; export type Model = VelorumModel & Payload From 99ad7e8beda766427a64ee379ae2a59674547841 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Wed, 17 Jun 2026 23:00:35 +0200 Subject: [PATCH 36/66] feat: imports --- src/packages/ce/src/ai/services/AI.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/ce/src/ai/services/AI.service.ts b/src/packages/ce/src/ai/services/AI.service.ts index ac8c1144..095d9312 100644 --- a/src/packages/ce/src/ai/services/AI.service.ts +++ b/src/packages/ce/src/ai/services/AI.service.ts @@ -7,7 +7,7 @@ import { VelorumModel, } from "@code0-tech/sagittarius-graphql-types"; import {GraphqlClient} from "@core/util/graphql-client"; -import velorumGenerateFlowMutation from "./mutations/VelorumGenerateFlow.mutation.graphql"; +import velorumGenerateFlowMutation from "./mutations/AI.generateFlow.mutation.graphql"; import {Payload, View} from "@code0-tech/pictor/dist/utils/view"; import velorumModelsQuery from "./queries/AI.models.query.graphql"; From 4807ee536094b21d119086a82b1c8e41febcd239 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Wed, 17 Jun 2026 23:00:45 +0200 Subject: [PATCH 37/66] feat: AI Chat component --- .../ce/src/ai/components/AIChatComponent.tsx | 220 ++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 src/packages/ce/src/ai/components/AIChatComponent.tsx diff --git a/src/packages/ce/src/ai/components/AIChatComponent.tsx b/src/packages/ce/src/ai/components/AIChatComponent.tsx new file mode 100644 index 00000000..94ac1a65 --- /dev/null +++ b/src/packages/ce/src/ai/components/AIChatComponent.tsx @@ -0,0 +1,220 @@ +import React from "react"; +import { + Button, + ButtonGroup, + Card, + EditorInput, + Flex, + Progress, + SelectContent, + SelectItem, + SelectItemText, + SelectPortal, + SelectTrigger, + SelectValue, + SelectViewport, + Spacing, + Text, + useService, + useStore +} from "@code0-tech/pictor"; +import Link from "next/link"; +import {StreamLanguage} from "@codemirror/language"; +import CardSection from "@code0-tech/pictor/dist/components/card/CardSection"; +import {Select} from "@radix-ui/react-select"; +import {IconChevronDown, IconSend, IconSparkles2Filled} from "@tabler/icons-react"; +import {AIService} from "@edition/ai/services/AI.service"; +import {motion} from "framer-motion"; + +export interface AIChatComponentProps { + onPrompt?: (value: string) => void + prompt?: string +} + +export const AIChatComponent: React.FC = (props) => { + + const {onPrompt, prompt = ""} = props + + const [promptState, setPromptState] = React.useState(prompt) + const aiService = useService(AIService) + const aiStore = useStore(AIService) + + const models = React.useMemo( + () => aiService.values(), + [aiStore] + ) + + const onSend = React.useCallback(() => { + + }, []) + + React.useEffect( + () => setPromptState(prompt), + [prompt, setPromptState] + ) + + return + + + + { + models.length <= 0 && ( + + + + + + + + + + + ) + } + onPrompt?.(value)} + wrapperComponent={{ + style: { + background: "transparent", + boxShadow: "none" + } + }} + placeholder={"Ask AI anything..."} + language={StreamLanguage.define({ + token(stream) { + stream.next() + return null; + } + })}/> + + + + + + + + + + + + + + + + + { + models.length > 0 ? ( + + + Upgrade your license to increase your AI usage limit + + + + ) : ( + + + You currently don't have AI features enabled or don't have any models available + + + ) + } + + + +} \ No newline at end of file From 8848cf762068dbfb1067d8cdc997a0ab05e2745e Mon Sep 17 00:00:00 2001 From: nicosammito Date: Wed, 17 Jun 2026 23:00:59 +0200 Subject: [PATCH 38/66] feat: using ai chat component --- .../ce/src/flow/pages/FlowOverviewPage.tsx | 122 ++---------------- 1 file changed, 14 insertions(+), 108 deletions(-) diff --git a/src/packages/ce/src/flow/pages/FlowOverviewPage.tsx b/src/packages/ce/src/flow/pages/FlowOverviewPage.tsx index 1f2156fb..ce06c030 100644 --- a/src/packages/ce/src/flow/pages/FlowOverviewPage.tsx +++ b/src/packages/ce/src/flow/pages/FlowOverviewPage.tsx @@ -2,33 +2,22 @@ import React from "react"; import {ResizablePanel} from "@code0-tech/pictor/dist/components/resizable/Resizable"; import { AuroraBackground, - Button, Card, Col, - EditorInput, Flex, - Progress, ScrollArea, ScrollAreaScrollbar, ScrollAreaThumb, ScrollAreaViewport, - SelectContent, - SelectItem, - SelectItemText, - SelectPortal, - SelectTrigger, - SelectValue, - SelectViewport, Spacing, - Text + Text, + useService, + useStore } from "@code0-tech/pictor"; import {Panel} from "@xyflow/react"; -import {StreamLanguage} from "@codemirror/language"; -import CardSection from "@code0-tech/pictor/dist/components/card/CardSection"; -import {Select} from "@radix-ui/react-select"; -import {IconChevronDown, IconSend} from "@tabler/icons-react"; -import {SiClaude} from "@icons-pack/react-simple-icons"; import {icon, IconString} from "@core/util/icons"; +import {AIService} from "@edition/ai/services/AI.service"; +import {AIChatComponent} from "@edition/ai/components/AIChatComponent"; const flowTemplates = [ { @@ -53,6 +42,14 @@ export const FlowOverviewPage: React.FC = () => { const [prompt, setPrompt] = React.useState("") + const aiService = useService(AIService) + const aiStore = useStore(AIService) + + const models = React.useMemo( + () => aiService.values(), + [aiStore] + ) + return @@ -111,98 +108,7 @@ export const FlowOverviewPage: React.FC = () => { - - - setPrompt(value)} - wrapperComponent={{ - style: { - background: "transparent", - boxShadow: "none" - } - }} - placeholder={"Ask AI anything..."} - language={StreamLanguage.define({ - token(stream) { - stream.next() - return null; - } - })}/> - - - - - - - - - - - - - - - - - Upgrade your license to increase your AI usage limit - - - - + } \ No newline at end of file From a80ed10bf58697246282b7df77864af2dee9912b Mon Sep 17 00:00:00 2001 From: nicosammito Date: Wed, 17 Jun 2026 23:01:15 +0200 Subject: [PATCH 39/66] feat: using ai chat component --- .../panels/FlowPanelControlComponent.tsx | 123 +----------------- 1 file changed, 5 insertions(+), 118 deletions(-) diff --git a/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx b/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx index ede456d9..90109ad6 100644 --- a/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx +++ b/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx @@ -3,17 +3,7 @@ import {Flow, LiteralValue, NodeFunction, ReferenceValue, SubFlowValue} from "@c import { Badge, Button, - Card, - EditorInput, - Flex, Progress, - SelectContent, - SelectItem, - SelectItemText, - SelectPortal, - SelectTrigger, - SelectValue, - SelectViewport, - Spacing, + Flex, Text, Tooltip, TooltipContent, @@ -29,22 +19,11 @@ import {SuggestionDialogComponent} from "@edition/function/components/suggestion import {useHotkeys} from "react-hotkeys-hook"; import {useSelectedFunctionNode} from "@edition/function/hooks/FunctionNode.selected.hook"; import {useFunctionSuggestions} from "@edition/function/hooks/Function.suggestion.hook"; -import { - IconArrowBigUp, - IconBackspace, - IconChevronDown, - IconLetterA, - IconLetterQ, - IconSend, - IconX -} from "@tabler/icons-react"; +import {IconArrowBigUp, IconBackspace, IconLetterA, IconLetterQ} from "@tabler/icons-react"; import {HoverCard, HoverCardContent, HoverCardPortal, HoverCardTrigger} from "@radix-ui/react-hover-card"; import {ChaoticOrbit} from "ldrs/react"; -import {StreamLanguage} from "@codemirror/language"; -import CardSection from "@code0-tech/pictor/dist/components/card/CardSection"; -import {Select} from "@radix-ui/react-select"; -import {SiClaude} from "@icons-pack/react-simple-icons"; import 'ldrs/react/ChaoticOrbit.css' +import {AIChatComponent} from "@edition/ai/components/AIChatComponent"; export interface FlowPanelControlComponentProps { flowId: Flow['id'] @@ -60,6 +39,7 @@ export const FlowPanelControlComponent: React.FC const flowStore = useStore(FlowService) const [, startTransition] = React.useTransition() + const [prompt, setPrompt] = React.useState("") const [suggestionDialogOpen, setSuggestionDialogOpen] = React.useState(false) const [addNextNodeTooltipOpen, setAddNextNodeTooltipOpen] = React.useState(false) @@ -205,100 +185,7 @@ export const FlowPanelControlComponent: React.FC Generating...
- - - - - - - - - - - - - - - - - - - - - Upgrade your license to increase your AI usage limit - - - - + From d3c70893f3e746a7620d9fffb00d6a8c963fe569 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Wed, 17 Jun 2026 23:01:55 +0200 Subject: [PATCH 40/66] feat: adding ai service to layout --- src/app/(flow)/layout.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/app/(flow)/layout.tsx b/src/app/(flow)/layout.tsx index 62352cbf..2e76f787 100644 --- a/src/app/(flow)/layout.tsx +++ b/src/app/(flow)/layout.tsx @@ -33,6 +33,7 @@ import {RoleView} from "@edition/role/services/Role.view"; import {useUserSession} from "@edition/user/hooks/User.session.hook"; import {Layout} from "@code0-tech/pictor/dist/components/layout/Layout"; import {ModuleService} from "@edition/module/services/Module.service"; +import {AIService, Model} from "@edition/ai/services/AI.service"; export default function FlowLayout({bar, tab, children}: { bar: React.ReactNode, @@ -67,7 +68,7 @@ export default function FlowLayout({bar, tab, children}: { const datatype = usePersistentReactiveArrayService(`dashboard::datatypes::${currentSession?.id}`, (store) => new DatatypeService(graphqlClient, store)) const flowtype = usePersistentReactiveArrayService(`dashboard::flowtypes::${currentSession?.id}`, (store) => new FlowTypeService(graphqlClient, store)) const module = usePersistentReactiveArrayService(`dashboard::modules::${currentSession?.id}`, (store) => new ModuleService(graphqlClient, store)) - + const ai = usePersistentReactiveArrayService(`dashboard::ai::${currentSession?.id}`, (store) => new AIService(graphqlClient, store)) const runtimeId = React.useMemo(() => project[1].getById(projectId, {namespaceId})?.primaryRuntime?.id, [projectId, project[0], namespaceId]) @@ -82,7 +83,7 @@ export default function FlowLayout({bar, tab, children}: { }, [runtimeId, namespaceId, projectId, currentSession, flow, functions, datatype, flowtype]) return + services={[user, organization, member, namespace, runtime, project, role, flow, functions, datatype, flowtype, module, ai]}>
Date: Sat, 20 Jun 2026 15:31:11 +0200 Subject: [PATCH 41/66] feat: adding function support for paramater result view --- src/packages/ce/src/flow/views/FlowExecutionResultView.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/packages/ce/src/flow/views/FlowExecutionResultView.tsx b/src/packages/ce/src/flow/views/FlowExecutionResultView.tsx index f1b39221..12c63e68 100644 --- a/src/packages/ce/src/flow/views/FlowExecutionResultView.tsx +++ b/src/packages/ce/src/flow/views/FlowExecutionResultView.tsx @@ -342,10 +342,11 @@ export const FlowExecutionResultView: React.FC = () => { <> - {item.type === "node" ? "Parameters" : "Input"} + {item.type === "node" || item.type === "function" ? "Parameters" : "Input"} - {item.type === "node" ? item.data.input?.map((input: ExecutionParameterResult, index: number) => { + {item.type === "node" || item.type === "function" ? item.data.input?.map((input: ExecutionParameterResult, index: number) => { + //TODO: for item.type === function this is wrong const parameter: ParameterDefinition = item?.data?.payload?.functionDefinition?.parameterDefinitions?.nodes?.[index] return
From 31fba25924bbebfe2b98ab3e327b636bcdc3fbe0 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Sat, 20 Jun 2026 15:31:21 +0200 Subject: [PATCH 42/66] feat: optional types --- .../ce/src/function/components/suggestion/Suggestion.util.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packages/ce/src/function/components/suggestion/Suggestion.util.tsx b/src/packages/ce/src/function/components/suggestion/Suggestion.util.tsx index 358fa08c..ccc3e50e 100644 --- a/src/packages/ce/src/function/components/suggestion/Suggestion.util.tsx +++ b/src/packages/ce/src/function/components/suggestion/Suggestion.util.tsx @@ -74,9 +74,9 @@ export const useMappedSuggestions = (suggestions: (NodeFunction | SubFlowValue | value: suggestion, icon: suggestion.functionDefinition?.displayIcon, displayMessage: suggestion.functionDefinition?.names?.[0]?.content, - aliases: suggestion.functionDefinition?.aliases?.[0].content?.split(";"), + aliases: (suggestion.functionDefinition?.aliases?.[0]?.content ?? "")?.split(";"), definitionSource: suggestion.functionDefinition.runtimeModule?.identifier, - description: suggestion.functionDefinition?.descriptions?.[0].content, + description: suggestion.functionDefinition?.descriptions?.[0]?.content ?? "", } } From d7bdfd15ed27f98cb7af40e28ace4578ddb80a9e Mon Sep 17 00:00:00 2001 From: nicosammito Date: Sat, 20 Jun 2026 15:31:41 +0200 Subject: [PATCH 43/66] feat: updating triangulum --- package-lock.json | 8 ++++---- package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9e544f81..65be3835 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@apollo/client": "^4.0.9", "@code0-tech/pictor": "^0.10.4", - "@code0-tech/triangulum": "^0.24.0", + "@code0-tech/triangulum": "^0.24.2", "@codemirror/lang-javascript": "^6.2.5", "@codemirror/lint": "^6.9.5", "@icons-pack/react-simple-icons": "^13.13.0", @@ -469,9 +469,9 @@ "peer": true }, "node_modules/@code0-tech/triangulum": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/@code0-tech/triangulum/-/triangulum-0.24.0.tgz", - "integrity": "sha512-AIb1lRDQlVxb+4LTy9975TppEs9YeaUclzwGQJjTHYdPs6tfWix7p+Q1TncC+UNU9J2HhMDjBZ1UiMmPua9cew==", + "version": "0.24.2", + "resolved": "https://registry.npmjs.org/@code0-tech/triangulum/-/triangulum-0.24.2.tgz", + "integrity": "sha512-yNAhVRuBnTBBgnshHCIidgsZn1ogWztBe5rvhKWpBHGCNnDzC1peStkOuOdoZ7IJcglDJkXnqr9HRLc6XvkAzA==", "peerDependencies": { "@code0-tech/sagittarius-graphql-types": "0.0.0-experimental-2603093384-7294be43b142c66f3b3263d8021d470637a1501d", "@typescript/vfs": "^1.6.4", diff --git a/package.json b/package.json index 2e1af7e0..41bc2113 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "dependencies": { "@apollo/client": "^4.0.9", "@code0-tech/pictor": "^0.10.4", - "@code0-tech/triangulum": "^0.24.0", + "@code0-tech/triangulum": "^0.24.2", "@codemirror/lang-javascript": "^6.2.5", "@codemirror/lint": "^6.9.5", "@icons-pack/react-simple-icons": "^13.13.0", From 50c2ddcbbe77e832f31280f29dd2c9556e426144 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Sat, 20 Jun 2026 17:00:51 +0200 Subject: [PATCH 44/66] feat: updating triangulum --- package-lock.json | 16 ++++++++-------- package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 65be3835..a19facf1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "dependencies": { "@apollo/client": "^4.0.9", "@code0-tech/pictor": "^0.10.4", - "@code0-tech/triangulum": "^0.24.2", + "@code0-tech/triangulum": "^0.25.0", "@codemirror/lang-javascript": "^6.2.5", "@codemirror/lint": "^6.9.5", "@icons-pack/react-simple-icons": "^13.13.0", @@ -463,17 +463,17 @@ } }, "node_modules/@code0-tech/sagittarius-graphql-types": { - "version": "0.0.0-experimental-2603093384-7294be43b142c66f3b3263d8021d470637a1501d", - "resolved": "https://registry.npmjs.org/@code0-tech/sagittarius-graphql-types/-/sagittarius-graphql-types-0.0.0-experimental-2603093384-7294be43b142c66f3b3263d8021d470637a1501d.tgz", - "integrity": "sha512-/wB4uH812uqMkQQcklO09tayS0q9g4Y6ovp/3egR/84YJnrWNsof0trikE2NcV0ZYyEOlOno3Gp482jyvo7gvg==", + "version": "0.0.0-experimental-2616186698-14d97e4ac998067751f9cbd9f8fc05c6fed628fe", + "resolved": "https://registry.npmjs.org/@code0-tech/sagittarius-graphql-types/-/sagittarius-graphql-types-0.0.0-experimental-2616186698-14d97e4ac998067751f9cbd9f8fc05c6fed628fe.tgz", + "integrity": "sha512-8TqdH9pFs1HQjxqGtxgFslnKdvlVBaKooKrWvpPK4LGdYwu8n1MZFSqXu6sl4+OvrPaTEQxqhGdScjut+da9DA==", "peer": true }, "node_modules/@code0-tech/triangulum": { - "version": "0.24.2", - "resolved": "https://registry.npmjs.org/@code0-tech/triangulum/-/triangulum-0.24.2.tgz", - "integrity": "sha512-yNAhVRuBnTBBgnshHCIidgsZn1ogWztBe5rvhKWpBHGCNnDzC1peStkOuOdoZ7IJcglDJkXnqr9HRLc6XvkAzA==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@code0-tech/triangulum/-/triangulum-0.25.0.tgz", + "integrity": "sha512-Mm1Ed49EUuSlOYe6jeRdjepG6oSHj+vcKuYIWfGKPLUM1eW884aD6hWqqCXxCerMbinCFuH3q1MOH/CTSiYouw==", "peerDependencies": { - "@code0-tech/sagittarius-graphql-types": "0.0.0-experimental-2603093384-7294be43b142c66f3b3263d8021d470637a1501d", + "@code0-tech/sagittarius-graphql-types": "0.0.0-experimental-2616186698-14d97e4ac998067751f9cbd9f8fc05c6fed628fe", "@typescript/vfs": "^1.6.4", "lossless-json": "^4.3.0", "typescript": "^5.9.3 || ^6.0.2" diff --git a/package.json b/package.json index 41bc2113..2884ce6e 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "dependencies": { "@apollo/client": "^4.0.9", "@code0-tech/pictor": "^0.10.4", - "@code0-tech/triangulum": "^0.24.2", + "@code0-tech/triangulum": "^0.25.0", "@codemirror/lang-javascript": "^6.2.5", "@codemirror/lint": "^6.9.5", "@icons-pack/react-simple-icons": "^13.13.0", From 0110e374c494a5ca7376d5d5558f43f056a351ca Mon Sep 17 00:00:00 2001 From: nicosammito Date: Sun, 21 Jun 2026 15:39:32 +0200 Subject: [PATCH 45/66] feat: ai interface changes because of sagittarius changes --- src/packages/ce/src/ai/services/AI.service.ts | 21 +++-- .../fragments/AI.model.fragment.graphql | 2 +- .../AI.generateFlow.mutation.graphql | 2 +- .../services/queries/AI.models.query.graphql | 2 +- .../AI.generateFlow.subscription.graphql | 77 ++++++++++++++++++- 5 files changed, 88 insertions(+), 16 deletions(-) diff --git a/src/packages/ce/src/ai/services/AI.service.ts b/src/packages/ce/src/ai/services/AI.service.ts index 095d9312..aadc7b0b 100644 --- a/src/packages/ce/src/ai/services/AI.service.ts +++ b/src/packages/ce/src/ai/services/AI.service.ts @@ -1,17 +1,16 @@ import {ReactiveArrayService, ReactiveArrayStore} from "@code0-tech/pictor"; import { + AiGenerateFlowInput, AiGenerateFlowPayload, + AiModel, Mutation, - Query, - VelorumGenerateFlowInput, - VelorumGenerateFlowPayload, - VelorumModel, + Query } from "@code0-tech/sagittarius-graphql-types"; import {GraphqlClient} from "@core/util/graphql-client"; import velorumGenerateFlowMutation from "./mutations/AI.generateFlow.mutation.graphql"; import {Payload, View} from "@code0-tech/pictor/dist/utils/view"; import velorumModelsQuery from "./queries/AI.models.query.graphql"; -export type Model = VelorumModel & Payload +export type Model = AiModel & Payload export class AIService extends ReactiveArrayService { @@ -23,14 +22,14 @@ export class AIService extends ReactiveArrayService { this.client = client } - values(): VelorumModel[] { + values(): Model[] { const models = super.values() if (models.length > 0) return models this.client.query({ query: velorumModelsQuery, }).then(result => { - const models = result.data?.velorum?.models ?? [] + const models = result.data?.ai?.models ?? [] models.forEach(model => { if (model && !this.hasById(model.identifier)) { this.set(this.i++, new View(model as Model)) @@ -41,17 +40,17 @@ export class AIService extends ReactiveArrayService { return super.values() } - hasById(identifier: VelorumModel["identifier"]): boolean { + hasById(identifier: Model["identifier"]): boolean { return super.values().some(m => m.identifier === identifier) } - async generateFlow(payload: VelorumGenerateFlowInput): Promise { - const result = await this.client.mutate({ + async generateFlow(payload: AiGenerateFlowInput): Promise { + const result = await this.client.mutate({ mutation: velorumGenerateFlowMutation, variables: {...payload} }) - return result.data?.velorumGenerateFlow ?? undefined + return result.data?.aiGenerateFlow ?? undefined } } diff --git a/src/packages/ce/src/ai/services/fragments/AI.model.fragment.graphql b/src/packages/ce/src/ai/services/fragments/AI.model.fragment.graphql index 2522d14b..55d538d6 100644 --- a/src/packages/ce/src/ai/services/fragments/AI.model.fragment.graphql +++ b/src/packages/ce/src/ai/services/fragments/AI.model.fragment.graphql @@ -1,4 +1,4 @@ -fragment Model on VelorumModel { +fragment Model on AiModel { __typename identifier name diff --git a/src/packages/ce/src/ai/services/mutations/AI.generateFlow.mutation.graphql b/src/packages/ce/src/ai/services/mutations/AI.generateFlow.mutation.graphql index 563723dd..bd1b2695 100644 --- a/src/packages/ce/src/ai/services/mutations/AI.generateFlow.mutation.graphql +++ b/src/packages/ce/src/ai/services/mutations/AI.generateFlow.mutation.graphql @@ -1,5 +1,5 @@ mutation generateFlow($projectId: NamespaceProjectID!, $modelIdentifier: String!, $prompt: String!, $flowId: FlowID) { - velorumGenerateFlow(input: { + aiGenerateFlow(input: { projectId: $projectId modelIdentifier: $modelIdentifier prompt: $prompt diff --git a/src/packages/ce/src/ai/services/queries/AI.models.query.graphql b/src/packages/ce/src/ai/services/queries/AI.models.query.graphql index 940175f9..8c222b8e 100644 --- a/src/packages/ce/src/ai/services/queries/AI.models.query.graphql +++ b/src/packages/ce/src/ai/services/queries/AI.models.query.graphql @@ -1,6 +1,6 @@ #import '../fragments/AI.model.fragment.graphql' query models { - velorum { + ai { models { ...Model } diff --git a/src/packages/ce/src/ai/services/subscriptions/AI.generateFlow.subscription.graphql b/src/packages/ce/src/ai/services/subscriptions/AI.generateFlow.subscription.graphql index ec873a2b..ed8f7e03 100644 --- a/src/packages/ce/src/ai/services/subscriptions/AI.generateFlow.subscription.graphql +++ b/src/packages/ce/src/ai/services/subscriptions/AI.generateFlow.subscription.graphql @@ -1,5 +1,78 @@ subscription generateFlow($executionIdentifier: String!) { - velorumGenerateFlow(executionIdentifier: $executionIdentifier) { - flow + aiGenerateFlow(executionIdentifier: $executionIdentifier) { + flow { + __typename + name + startingNodeId + settings { + __typename + id + value + cast + flowSettingIdentifier + } + type { + __typename + id + identifier + } + nodes { + __typename + id + nextNodeId + parameters { + __typename + id + cast + value { + __typename + ... on AiGenerationLiteralValue { + __typename + value + } + ... on AiGenerationReferenceValue { + __typename + id + nodeFunctionId + inputIndex + inputTypeIdentifier + parameterIndex + referencePath { + __typename + path + arrayIndex + } + } + ... on AiGenerationSubFlowValue { + __typename + startingNodeId + signature + settings { + __typename + identifier + defaultValue + hidden + optional + } + functionDefinition { + __typename + id + identifier + } + } + } + parameterDefinition { + __typename + id + identifier + } + } + functionDefinition { + __typename + id + identifier + } + } + } } } From 6ef4047533fdff441e483106f2bb38d5f2182053 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 22 Jun 2026 00:11:08 +0200 Subject: [PATCH 46/66] feat: ai interface --- .env.local | 1 + next.config.ts | 7 +- .../ce/src/ai/components/AIChatComponent.tsx | 207 ++++++++++++++---- src/packages/ce/src/ai/util/AI.flow.mapper.ts | 97 ++++++++ .../views/ApplicationBreadcrumbView.tsx | 3 - .../inputs/DataTypeInputControlsComponent.tsx | 1 - .../number/DataTypeNumberInputComponent.tsx | 1 - .../builder/FlowBuilderComponent.tsx | 2 +- .../panels/FlowPanelControlComponent.tsx | 49 +++-- .../ce/src/flow/pages/FlowOverviewPage.tsx | 46 +++- .../ce/src/flow/services/Flow.service.ts | 34 ++- .../fragments/Flow.basic.fragment.graphql | 3 + .../flow/services/queries/Flow.query.graphql | 27 +++ 13 files changed, 401 insertions(+), 77 deletions(-) create mode 100644 src/packages/ce/src/ai/util/AI.flow.mapper.ts create mode 100644 src/packages/ce/src/flow/services/queries/Flow.query.graphql diff --git a/.env.local b/.env.local index 8f42ec10..01beed63 100644 --- a/.env.local +++ b/.env.local @@ -2,6 +2,7 @@ EDITION=ce NEXT_PUBLIC_EDITION=ce SAGITTARIUS_GRAPHQL_URL=http://localhost:80/graphql +SAGITTARIUS_CABLE_URL=http://localhost:80/cable NEXT_PUBLIC_SCULPTOR_VERSION=0.0.0 NEXT_PUBLIC_PICTOR_VERSION=0.10.4 diff --git a/next.config.ts b/next.config.ts index 39f4b383..8e539e00 100644 --- a/next.config.ts +++ b/next.config.ts @@ -3,6 +3,7 @@ import path from "node:path"; const EDITION = process.env.EDITION ?? "ce"; const SAGITTARIUS_GRAPHQL_URL = process.env.SAGITTARIUS_GRAPHQL_URL ?? 'http://localhost:3010/graphql'; +const SAGITTARIUS_CABLE_URL = process.env.SAGITTARIUS_CABLE_URL ?? 'http://localhost:3010/cable'; const cspHeader = ` default-src 'self'; @@ -15,7 +16,7 @@ const cspHeader = ` form-action 'self'; frame-ancestors 'none'; worker-src 'self' blob: data: *; - connect-src 'self' ${SAGITTARIUS_GRAPHQL_URL} ${process.env.NEXT_PUBLIC_OTEL_LOGS_ENDPOINT} ${process.env.NEXT_PUBLIC_OTEL_TRACES_ENDPOINT}; + connect-src 'self' ${SAGITTARIUS_GRAPHQL_URL} ${SAGITTARIUS_CABLE_URL.replace("http", "ws")} ${process.env.NEXT_PUBLIC_OTEL_LOGS_ENDPOINT} ${process.env.NEXT_PUBLIC_OTEL_TRACES_ENDPOINT}; ` const nextConfig: NextConfig = { @@ -55,6 +56,10 @@ const nextConfig: NextConfig = { { source: '/graphql', destination: SAGITTARIUS_GRAPHQL_URL // Proxy to Backend + }, + { + source: '/cable', + destination: SAGITTARIUS_CABLE_URL // Proxy to Backend } ]; } diff --git a/src/packages/ce/src/ai/components/AIChatComponent.tsx b/src/packages/ce/src/ai/components/AIChatComponent.tsx index 94ac1a65..7e623eae 100644 --- a/src/packages/ce/src/ai/components/AIChatComponent.tsx +++ b/src/packages/ce/src/ai/components/AIChatComponent.tsx @@ -22,32 +22,104 @@ import Link from "next/link"; import {StreamLanguage} from "@codemirror/language"; import CardSection from "@code0-tech/pictor/dist/components/card/CardSection"; import {Select} from "@radix-ui/react-select"; -import {IconChevronDown, IconSend, IconSparkles2Filled} from "@tabler/icons-react"; +import {IconChevronDown, IconPlayerStop, IconSend, IconSparkles2Filled} from "@tabler/icons-react"; import {AIService} from "@edition/ai/services/AI.service"; import {motion} from "framer-motion"; +import {Flow, NamespaceProject, Subscription} from "@code0-tech/sagittarius-graphql-types"; +import {useSubscription} from "@apollo/client/react"; +import generateFlowSubscription from "@edition/ai/services/subscriptions/AI.generateFlow.subscription.graphql" + +const GENERATING_VARIANTS = [ + "Generating...", + "Thinking...", + "Analyzing your prompt...", + "Composing flow...", + "Crafting nodes...", + "Wiring it up...", + "Almost there..." +] export interface AIChatComponentProps { + projectId: NamespaceProject['id'] + flowId?: Flow['id'] onPrompt?: (value: string) => void prompt?: string + onData?: (data: any) => void + } export const AIChatComponent: React.FC = (props) => { - const {onPrompt, prompt = ""} = props + const {projectId, flowId, onPrompt, prompt = "", onData} = props - const [promptState, setPromptState] = React.useState(prompt) const aiService = useService(AIService) const aiStore = useStore(AIService) + const [promptState, setPromptState] = React.useState(prompt) + const [model, setModel] = React.useState(undefined) + const [executionIdentifier, setExecutionIdentifier] = React.useState(null) + const [isAIActive, setIsAIActive] = React.useState(false) + const [aiVariantIndex, setAiVariantIndex] = React.useState(0) + const [aiErrorMessage, setAiErrorMessage] = React.useState(null) + const models = React.useMemo( () => aiService.values(), [aiStore] ) + + const {data} = useSubscription(generateFlowSubscription, { + variables: {executionIdentifier: executionIdentifier}, + skip: !executionIdentifier, + onData: (data) => { + setIsAIActive(true) + if (data.data.data?.aiGenerateFlow?.flow) { + onData?.(data.data.data?.aiGenerateFlow) + setPromptState("") + } else if (data.data.data?.aiGenerateFlow?.flow === null) { + setExecutionIdentifier(null) + setAiErrorMessage("Generation failed. Try another model.") + } + }, + onComplete: () => setIsAIActive(false), + onError: () => { + setIsAIActive(false) + setAiErrorMessage("Generation failed. Try another model.") + }, + }) + + const aiLoading = React.useMemo( + () => ((!data || Object.keys(data).length === 0) && executionIdentifier && isAIActive), + [executionIdentifier, data, isAIActive] + ) + const onSend = React.useCallback(() => { + aiService.generateFlow({ + prompt: promptState, + projectId: projectId!, + modelIdentifier: model!, + flowId: flowId, + }).then(payload => { + if ((payload?.errors?.length ?? 0) <= 0) { + setExecutionIdentifier(payload?.executionIdentifier ?? null) + } + }) + }, [aiService, model, promptState]) + React.useEffect(() => { + const id = setInterval( + () => setAiVariantIndex(i => (i + 1) % GENERATING_VARIANTS.length), + 4000 + ) + return () => clearInterval(id) }, []) + React.useEffect(() => { + setModel(models.length > 0 + ? models.reduce((max, obj) => (obj?.tokenCost ?? 1) > (max?.tokenCost ?? 1) ? obj : max)?.identifier ?? undefined + : undefined) + }, [models]) + React.useEffect( () => setPromptState(prompt), [prompt, setPromptState] @@ -100,22 +172,60 @@ export const AIChatComponent: React.FC = (props) => { ) } - onPrompt?.(value)} - wrapperComponent={{ - style: { - background: "transparent", - boxShadow: "none" - } - }} - placeholder={"Ask AI anything..."} - language={StreamLanguage.define({ - token(stream) { - stream.next() - return null; - } - })}/> + { + aiLoading ? ( +
+ + + {GENERATING_VARIANTS[aiVariantIndex]} + + +
+ ) : ( + onPrompt?.(value)} + wrapperComponent={{ + style: { + background: "transparent", + boxShadow: "none" + } + }} + placeholder={"Ask AI anything..."} + language={StreamLanguage.define({ + token(stream) { + stream.next() + return null; + } + })}/> + ) + } @@ -141,11 +251,7 @@ export const AIChatComponent: React.FC = (props) => { - + {!aiLoading && aiErrorMessage ? ( + + {aiErrorMessage} + + ) : null} + {aiLoading ? ( + + ) : ( + + )} @@ -196,24 +313,24 @@ export const AIChatComponent: React.FC = (props) => { mass: 0.8 }} > - - { - models.length > 0 ? ( - - - Upgrade your license to increase your AI usage limit - - - - ) : ( - - - You currently don't have AI features enabled or don't have any models available - - - ) - } + + { + models.length > 0 ? ( + + + Upgrade your license to increase your AI usage limit + + + + ) : ( + + + You currently don't have AI features enabled or don't have any models available + + + ) + } diff --git a/src/packages/ce/src/ai/util/AI.flow.mapper.ts b/src/packages/ce/src/ai/util/AI.flow.mapper.ts new file mode 100644 index 00000000..f26b2b33 --- /dev/null +++ b/src/packages/ce/src/ai/util/AI.flow.mapper.ts @@ -0,0 +1,97 @@ +import { + AiGenerationFlow, + AiGenerationLiteralValue, + AiGenerationNodeValue, + AiGenerationReferenceValue, + AiGenerationSubFlowValue, + FlowInput, + FlowSettingInput, + NodeFunctionInput, + NodeParameterInput, + NodeParameterValueInput, +} from "@code0-tech/sagittarius-graphql-types" + +const FALLBACK_FLOW_NAME = "Untitled flow" + +const mapValue = (value?: AiGenerationNodeValue | null): NodeParameterValueInput => { + switch (value?.__typename) { + case "AiGenerationLiteralValue": + return {literalValue: (value as AiGenerationLiteralValue).value ?? null} + case "AiGenerationReferenceValue": { + const v = value as AiGenerationReferenceValue + return { + referenceValue: { + ...(v.nodeFunctionId ? {nodeFunctionId: v.nodeFunctionId} : {}), + ...(v.parameterIndex != null ? {parameterIndex: v.parameterIndex} : {}), + ...(v.inputIndex != null ? {inputIndex: v.inputIndex} : {}), + ...(v.inputTypeIdentifier ? {inputTypeIdentifier: v.inputTypeIdentifier} : {}), + referencePath: v.referencePath?.map(p => ({ + path: p.path, + arrayIndex: p.arrayIndex, + })) ?? [], + }, + } + } + case "AiGenerationSubFlowValue": { + const v = value as AiGenerationSubFlowValue + return { + subFlowValue: { + ...(v.startingNodeId + ? {startingNodeId: v.startingNodeId} + : v.functionDefinition?.identifier + ? {functionIdentifier: v.functionDefinition.identifier} + : {}), + signature: v.signature ?? "", + settings: v.settings?.map(s => ({ + defaultValue: s.defaultValue, + hidden: s.hidden, + identifier: s.identifier!, + optional: s.optional, + })), + }, + } + } + default: + return {literalValue: null} + } +} + +export const ensureUniqueFlowName = (name: string, existingNames: string[]): string => { + if (!existingNames.includes(name)) return name + let i = 2 + while (existingNames.includes(`${name} v${i}`)) i++ + return `${name} v${i}` +} + +export const mapAiGenerationFlowToFlowInput = ( + aiFlow: AiGenerationFlow, + options?: { existingNames?: string[] } +): FlowInput | undefined => { + if (!aiFlow.type?.id) return undefined + + const baseName = aiFlow.name?.trim() ? aiFlow.name.trim() : FALLBACK_FLOW_NAME + const name = ensureUniqueFlowName(baseName, options?.existingNames ?? []) + + const settings: FlowSettingInput[] = (aiFlow.settings ?? []).map(s => ({ + cast: s?.cast ?? undefined, + value: s?.value, + })) + + const nodes: NodeFunctionInput[] = (aiFlow.nodes ?? []).map(node => ({ + id: node?.id!, + nextNodeId: node?.nextNodeId ?? undefined, + functionDefinitionId: node?.functionDefinition?.id!, + parameters: (node?.parameters ?? []).map(p => ({ + cast: p?.cast ?? undefined, + value: mapValue(p?.value), + })), + })) + + return { + name, + type: aiFlow.type.id, + startingNodeId: aiFlow.startingNodeId ?? undefined, + settings, + nodes, + } +} diff --git a/src/packages/ce/src/application/views/ApplicationBreadcrumbView.tsx b/src/packages/ce/src/application/views/ApplicationBreadcrumbView.tsx index c2d10091..950b02c5 100644 --- a/src/packages/ce/src/application/views/ApplicationBreadcrumbView.tsx +++ b/src/packages/ce/src/application/views/ApplicationBreadcrumbView.tsx @@ -12,9 +12,6 @@ export const ApplicationBreadcrumbView: React.FC = () => { const namespaceIndex = params.namespaceId as string | undefined const projectIndex = params.projectId as string | undefined - - console.log(path, path.split("/"), path.split("/").filter(value => value != "").slice(0, path.split("/").length)) - return { (namespaceIndex || projectIndex) ? ( diff --git a/src/packages/ce/src/datatype/components/inputs/DataTypeInputControlsComponent.tsx b/src/packages/ce/src/datatype/components/inputs/DataTypeInputControlsComponent.tsx index 2d20024a..f66422ed 100644 --- a/src/packages/ce/src/datatype/components/inputs/DataTypeInputControlsComponent.tsx +++ b/src/packages/ce/src/datatype/components/inputs/DataTypeInputControlsComponent.tsx @@ -73,7 +73,6 @@ export const DataTypeInputControlsComponent: React.FC onSelect?.(suggest)}> diff --git a/src/packages/ce/src/datatype/components/inputs/number/DataTypeNumberInputComponent.tsx b/src/packages/ce/src/datatype/components/inputs/number/DataTypeNumberInputComponent.tsx index 265b5fc4..7c1d5293 100644 --- a/src/packages/ce/src/datatype/components/inputs/number/DataTypeNumberInputComponent.tsx +++ b/src/packages/ce/src/datatype/components/inputs/number/DataTypeNumberInputComponent.tsx @@ -24,7 +24,6 @@ export const DataTypeNumberInputComponent: React.FC { if (typeof value === "string") { - console.log(value) formValidation?.setValue?.(value ? {__typename: "LiteralValue", value: !Number.isNaN(Number(value)) ? Number(value) : value} : undefined) onChange?.(value ? {__typename: "LiteralValue", value: !Number.isNaN(Number(value)) ? Number(value) : value} : undefined) } else { diff --git a/src/packages/ce/src/flow/components/builder/FlowBuilderComponent.tsx b/src/packages/ce/src/flow/components/builder/FlowBuilderComponent.tsx index 758c255b..5de345e2 100644 --- a/src/packages/ce/src/flow/components/builder/FlowBuilderComponent.tsx +++ b/src/packages/ce/src/flow/components/builder/FlowBuilderComponent.tsx @@ -783,7 +783,7 @@ const InternalFlowBuilder: React.FC = (props) => { - + diff --git a/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx b/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx index 90109ad6..8be5802d 100644 --- a/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx +++ b/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx @@ -1,5 +1,13 @@ import React from "react"; -import {Flow, LiteralValue, NodeFunction, ReferenceValue, SubFlowValue} from "@code0-tech/sagittarius-graphql-types"; +import { + AiGenerateFlowSubscriptionPayload, + Flow, + LiteralValue, Namespace, + NamespaceProject, + NodeFunction, + ReferenceValue, + SubFlowValue +} from "@code0-tech/sagittarius-graphql-types"; import { Badge, Button, @@ -24,15 +32,19 @@ import {HoverCard, HoverCardContent, HoverCardPortal, HoverCardTrigger} from "@r import {ChaoticOrbit} from "ldrs/react"; import 'ldrs/react/ChaoticOrbit.css' import {AIChatComponent} from "@edition/ai/components/AIChatComponent"; +import {mapAiGenerationFlowToFlowInput} from "@edition/ai/util/AI.flow.mapper"; +import {addIslandSuccessNotification} from "@code0-tech/pictor/dist/components/island/Island.hook"; export interface FlowPanelControlComponentProps { + namespaceId: Namespace['id'] + projectId: NamespaceProject['id'] flowId: Flow['id'] } export const FlowPanelControlComponent: React.FC = (props) => { //props - const {flowId} = props + const {namespaceId, projectId, flowId} = props //services and stores const flowService = useService(FlowService) @@ -56,6 +68,27 @@ export const FlowPanelControlComponent: React.FC }) }, [selectedNode, flowService, flowStore]) + const onAIData = React.useCallback((payload: AiGenerateFlowSubscriptionPayload) => { + const aiFlow = payload?.flow + if (!aiFlow) return + + const existingNames = (flowService.values({namespaceId, projectId}) ?? []) + .map(f => f.name) + .filter((n): n is string => !!n) + + const flowInput = mapAiGenerationFlowToFlowInput(aiFlow, {existingNames}) + if (!flowInput) return + + flowService.flowUpdate({ + flowId: flowId!, + flowInput: flowInput, + }).then(result => { + if ((result?.errors?.length ?? 0) <= 0) { + addIslandSuccessNotification({message: "Created flow"}) + } + }) + }, [flowStore, namespaceId, projectId]) + const addNodeToFlow = React.useCallback((suggestion: NodeFunction | ReferenceValue | LiteralValue | SubFlowValue) => { if (flowId && suggestion.__typename === "NodeFunction" && selectedNode?.id.includes("NodeFunction")) { startTransition(async () => { @@ -175,17 +208,7 @@ export const FlowPanelControlComponent: React.FC - - - - Generating... - - - + diff --git a/src/packages/ce/src/flow/pages/FlowOverviewPage.tsx b/src/packages/ce/src/flow/pages/FlowOverviewPage.tsx index ce06c030..e1e6f51c 100644 --- a/src/packages/ce/src/flow/pages/FlowOverviewPage.tsx +++ b/src/packages/ce/src/flow/pages/FlowOverviewPage.tsx @@ -16,8 +16,16 @@ import { } from "@code0-tech/pictor"; import {Panel} from "@xyflow/react"; import {icon, IconString} from "@core/util/icons"; -import {AIService} from "@edition/ai/services/AI.service"; import {AIChatComponent} from "@edition/ai/components/AIChatComponent"; +import {useParams} from "next/navigation"; +import { + AiGenerateFlowSubscriptionPayload, + Namespace, + NamespaceProject +} from "@code0-tech/sagittarius-graphql-types"; +import {FlowService} from "@edition/flow/services/Flow.service"; +import {mapAiGenerationFlowToFlowInput} from "@edition/ai/util/AI.flow.mapper"; +import {addIslandSuccessNotification} from "@code0-tech/pictor/dist/components/island/Island.hook"; const flowTemplates = [ { @@ -40,15 +48,37 @@ const flowTemplates = [ export const FlowOverviewPage: React.FC = () => { + const params = useParams() const [prompt, setPrompt] = React.useState("") - const aiService = useService(AIService) - const aiStore = useStore(AIService) + const namespaceIndex = params.namespaceId as any as number + const projectIndex = params.projectId as any as number + const namespaceId: Namespace['id'] = `gid://sagittarius/Namespace/${namespaceIndex}` + const projectId: NamespaceProject['id'] = `gid://sagittarius/NamespaceProject/${projectIndex}` + + const flowService = useService(FlowService) + const flowStore = useStore(FlowService) + + const onAIData = React.useCallback((payload: AiGenerateFlowSubscriptionPayload) => { + const aiFlow = payload?.flow + if (!aiFlow) return + + const existingNames = (flowService.values({namespaceId, projectId}) ?? []) + .map(f => f.name) + .filter((n): n is string => !!n) + + const flowInput = mapAiGenerationFlowToFlowInput(aiFlow, {existingNames}) + if (!flowInput) return - const models = React.useMemo( - () => aiService.values(), - [aiStore] - ) + flowService.flowCreate({ + flow: flowInput, + projectId: projectId, + }).then(result => { + if ((result?.errors?.length ?? 0) <= 0) { + addIslandSuccessNotification({message: "Created flow"}) + } + }) + }, [flowService, flowStore, namespaceId, projectId]) return @@ -108,7 +138,7 @@ export const FlowOverviewPage: React.FC = () => { - + } \ No newline at end of file diff --git a/src/packages/ce/src/flow/services/Flow.service.ts b/src/packages/ce/src/flow/services/Flow.service.ts index a30d22cb..b2c41c64 100644 --- a/src/packages/ce/src/flow/services/Flow.service.ts +++ b/src/packages/ce/src/flow/services/Flow.service.ts @@ -24,6 +24,7 @@ import { } from "@code0-tech/sagittarius-graphql-types"; import {GraphqlClient} from "@core/util/graphql-client"; import flowsQuery from "@edition/flow/services/queries/Flows.query.graphql"; +import flowQuery from "@edition/flow/services/queries/Flow.query.graphql"; import flowCreateMutation from "@edition/flow/services/mutations/Flow.create.mutation.graphql"; import flowDeleteMutation from "@edition/flow/services/mutations/Flow.delete.mutation.graphql"; import flowUpdateMutation from "@edition/flow/services/mutations/Flow.update.mutation.graphql"; @@ -408,10 +409,35 @@ export class FlowService extends ReactiveArrayService({ + query: flowQuery, + variables: { + namespaceId: mutationFlow.project.namespace.id, + projectId: mutationFlow?.project.id, + flowId: mutationFlow.id, + + firstNode: 50, + afterNode: null, + + firstNodeParameter: 50, + afterNodeParameter: null, + + firstSetting: 50, + afterSetting: null, + + firstNodeResult: 50, + afterNodeResult: null + } + }).then(res => { + const flow = res.data?.namespace?.project?.flow + this.add(new View(flow!)) + }) + } else { + mutationFlow.nodes = {nodes: []} + this.add(new View(mutationFlow)) } } diff --git a/src/packages/ce/src/flow/services/fragments/Flow.basic.fragment.graphql b/src/packages/ce/src/flow/services/fragments/Flow.basic.fragment.graphql index 04859612..3a295a35 100644 --- a/src/packages/ce/src/flow/services/fragments/Flow.basic.fragment.graphql +++ b/src/packages/ce/src/flow/services/fragments/Flow.basic.fragment.graphql @@ -9,6 +9,9 @@ fragment BasicFlow on Flow { project { __typename id + namespace { + id + } } settings { __typename diff --git a/src/packages/ce/src/flow/services/queries/Flow.query.graphql b/src/packages/ce/src/flow/services/queries/Flow.query.graphql new file mode 100644 index 00000000..835b680e --- /dev/null +++ b/src/packages/ce/src/flow/services/queries/Flow.query.graphql @@ -0,0 +1,27 @@ +#import "../fragments/Flow.fragment.graphql" + +query Flows ( + $namespaceId: NamespaceID! + $projectId: NamespaceProjectID! + $flowId: FlowID! + + $firstNode: Int!, + $afterNode: String, + + $firstNodeParameter: Int!, + $afterNodeParameter: String, + + $firstSetting: Int!, + $afterSetting: String, + + $firstNodeResult: Int!, + $afterNodeResult: String +) { + namespace(id: $namespaceId) { + project(id: $projectId) { + flow (id: $flowId) { + ...Flow + } + } + } +} \ No newline at end of file From 5a6ad40a51fed7a5da8b4718bd77c549b060ab7d Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 22 Jun 2026 02:29:39 +0200 Subject: [PATCH 47/66] feat: remove empty space --- src/packages/ce/src/ai/components/AIChatComponent.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/packages/ce/src/ai/components/AIChatComponent.tsx b/src/packages/ce/src/ai/components/AIChatComponent.tsx index 7e623eae..392ff239 100644 --- a/src/packages/ce/src/ai/components/AIChatComponent.tsx +++ b/src/packages/ce/src/ai/components/AIChatComponent.tsx @@ -67,7 +67,6 @@ export const AIChatComponent: React.FC = (props) => { [aiStore] ) - const {data} = useSubscription(generateFlowSubscription, { variables: {executionIdentifier: executionIdentifier}, skip: !executionIdentifier, From 28e19ae121dbae7b1ffb450fbb0410d025c38f9d Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 22 Jun 2026 02:29:53 +0200 Subject: [PATCH 48/66] feat: adding namespace link over project to flow --- .../ce/src/flow/services/fragments/Flow.basic.fragment.graphql | 1 + 1 file changed, 1 insertion(+) diff --git a/src/packages/ce/src/flow/services/fragments/Flow.basic.fragment.graphql b/src/packages/ce/src/flow/services/fragments/Flow.basic.fragment.graphql index 3a295a35..157b9154 100644 --- a/src/packages/ce/src/flow/services/fragments/Flow.basic.fragment.graphql +++ b/src/packages/ce/src/flow/services/fragments/Flow.basic.fragment.graphql @@ -10,6 +10,7 @@ fragment BasicFlow on Flow { __typename id namespace { + __typename id } } From 4612581b2cda54532485077641be5b5fb84c35de Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 22 Jun 2026 02:30:08 +0200 Subject: [PATCH 49/66] feat: interface design for flow compare store --- src/packages/ce/src/flow/hooks/Flow.compare.hook.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/packages/ce/src/flow/hooks/Flow.compare.hook.ts b/src/packages/ce/src/flow/hooks/Flow.compare.hook.ts index 2a77f300..c34b4f40 100644 --- a/src/packages/ce/src/flow/hooks/Flow.compare.hook.ts +++ b/src/packages/ce/src/flow/hooks/Flow.compare.hook.ts @@ -1,9 +1,13 @@ import { create } from 'zustand' import {FlowView} from "@edition/flow/services/Flow.view"; -export const useFlowCompareStore = create<{ +interface FlowCompareState { flow: FlowView | null -}>((setState) => ({ + setFlow: (flow: FlowView) => void + clearFlow: () => void +} + +export const useFlowCompareStore = create((setState) => ({ flow: null, setFlow: (flow: FlowView) => setState({flow}), clearFlow: () => setState({flow: null}), From a382c8182d2a5b6271ffadc0fb38f91aac891ac6 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 22 Jun 2026 02:30:42 +0200 Subject: [PATCH 50/66] feat: new correct deps for nodes and edge calculation --- src/packages/ce/src/flow/hooks/Flow.edges.hook.ts | 9 +++++++-- src/packages/ce/src/flow/hooks/Flow.nodes.hook.ts | 6 +++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/packages/ce/src/flow/hooks/Flow.edges.hook.ts b/src/packages/ce/src/flow/hooks/Flow.edges.hook.ts index e8de2d03..deac3544 100644 --- a/src/packages/ce/src/flow/hooks/Flow.edges.hook.ts +++ b/src/packages/ce/src/flow/hooks/Flow.edges.hook.ts @@ -6,6 +6,7 @@ import {FlowService} from "@edition/flow/services/Flow.service"; import {FunctionService} from "@edition/function/services/Function.service"; import {FALLBACK_FUNCTION_PARAMETER_NAME} from "@core/util/fallback-translations"; import {FlowBuilderEdgeDataProps} from "@edition/flow/components/builder/FlowBuilderEdgeComponent"; +import {useFlowCompareStore} from "@edition/flow/hooks/Flow.compare.hook"; // @ts-ignore export const useEdges = (flowId: Flow['id'], namespaceId?: Namespace['id'], projectId?: NamespaceProject['id']): Edge[] => { @@ -14,8 +15,12 @@ export const useEdges = (flowId: Flow['id'], namespaceId?: Namespace['id'], proj const flowStore = useStore(FlowService) const functionService = useService(FunctionService); const functionStore = useStore(FunctionService) + const flowToCompare = useFlowCompareStore(state => state.flow) - const flow = React.useMemo(() => flowService.getById(flowId, {namespaceId, projectId}), [flowId, flowStore]) + const flow = React.useMemo( + () => flowService.getById(flowId, {namespaceId, projectId}), + [flowId, namespaceId, projectId, flowStore, flowService] + ) return React.useMemo(() => { if (!flow) return [] @@ -154,5 +159,5 @@ export const useEdges = (flowId: Flow['id'], namespaceId?: Namespace['id'], proj } return edges - }, [flow?.editedAt, functionStore.length]); + }, [flowStore, flow?.editedAt, flow, flowToCompare, functionStore.length]); }; \ No newline at end of file diff --git a/src/packages/ce/src/flow/hooks/Flow.nodes.hook.ts b/src/packages/ce/src/flow/hooks/Flow.nodes.hook.ts index 359717d0..be9912b0 100644 --- a/src/packages/ce/src/flow/hooks/Flow.nodes.hook.ts +++ b/src/packages/ce/src/flow/hooks/Flow.nodes.hook.ts @@ -20,10 +20,10 @@ export const useFlowNodes = (flowId: Flow["id"], namespaceId?: Namespace["id"], const flow = React.useMemo( () => flowService.getById(flowId, {namespaceId, projectId}), - [flowId, flowStore.length, flowService] + [flowId, namespaceId, projectId, flowStore, flowService] ) - const flowSchema = useFlowSchema(flowToCompare || flowId, namespaceId, projectId) + const flowSchema = useFlowSchema(flowId, namespaceId, projectId) React.useEffect(() => { if (!flow) return @@ -144,7 +144,7 @@ export const useFlowNodes = (flowId: Flow["id"], namespaceId?: Namespace["id"], const start = flowService.getNodeById(flow.id, flow.startingNodeId); if (start) traverse(start); } - }, [flowStore, flow?.editedAt, functionStore.length, flowSchema]); + }, [flowStore, flow?.editedAt, flow, flowToCompare, functionStore.length, flowSchema]); return nodes }; \ No newline at end of file From 8dbf00acd4d379afcaeb6c36893ddbf3fb20591d Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 22 Jun 2026 02:30:58 +0200 Subject: [PATCH 51/66] feat: namespace link over project on flow --- .../ce/src/flow/services/fragments/Flow.fragment.graphql | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/packages/ce/src/flow/services/fragments/Flow.fragment.graphql b/src/packages/ce/src/flow/services/fragments/Flow.fragment.graphql index db441771..d0fbbf2e 100644 --- a/src/packages/ce/src/flow/services/fragments/Flow.fragment.graphql +++ b/src/packages/ce/src/flow/services/fragments/Flow.fragment.graphql @@ -82,6 +82,10 @@ fragment Flow on Flow { project { __typename id + namespace { + __typename + id + } } settings (first: $firstSetting, after: $afterSetting) { __typename From 07a523d2e58489271de78699fc222e7984a286a5 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 22 Jun 2026 02:31:17 +0200 Subject: [PATCH 52/66] feat: adding re-fetch possibility on flow update --- .../ce/src/flow/services/Flow.service.ts | 41 ++++++++++++++++--- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/src/packages/ce/src/flow/services/Flow.service.ts b/src/packages/ce/src/flow/services/Flow.service.ts index b2c41c64..d430c2a7 100644 --- a/src/packages/ce/src/flow/services/Flow.service.ts +++ b/src/packages/ce/src/flow/services/Flow.service.ts @@ -128,8 +128,10 @@ export class FlowService extends ReactiveArrayService { + async flowUpdate(payload: NamespacesProjectsFlowsUpdateInput, force?: boolean): Promise { const flow = this.getById(payload.flowId) @@ -477,9 +479,38 @@ export class FlowService extends ReactiveArrayService f.id === payload.flowId) - flow.updatedAt = result.data.namespacesProjectsFlowsUpdate.flow.updatedAt - flow.editedAt = undefined - this.set(flowIndex, new View(flow)) + + if (force) { + this.client.query({ + query: flowQuery, + variables: { + namespaceId: flow?.project?.namespace?.id, + projectId: flow?.project?.id, + flowId: flow?.id, + + firstNode: 50, + afterNode: null, + + firstNodeParameter: 50, + afterNodeParameter: null, + + firstSetting: 50, + afterSetting: null, + + firstNodeResult: 50, + afterNodeResult: null + } + }).then(res => { + const lflow = res.data?.namespace?.project?.flow! as FlowView + lflow.updatedAt = Date.now().toString() + lflow.editedAt = undefined + this.set(flowIndex, new View(lflow!)) + }) + } else { + flow.updatedAt = Date.now().toString() + flow.editedAt = undefined + this.set(flowIndex, new View(flow)) + } } return result.data?.namespacesProjectsFlowsUpdate ?? undefined From aa41ff31850f72d5208422ffed6eff92e1d86710 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 22 Jun 2026 02:31:28 +0200 Subject: [PATCH 53/66] feat: correct ids for FlowPanelControlComponent --- .../ce/src/flow/components/builder/FlowBuilderComponent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/ce/src/flow/components/builder/FlowBuilderComponent.tsx b/src/packages/ce/src/flow/components/builder/FlowBuilderComponent.tsx index 5de345e2..b45498f2 100644 --- a/src/packages/ce/src/flow/components/builder/FlowBuilderComponent.tsx +++ b/src/packages/ce/src/flow/components/builder/FlowBuilderComponent.tsx @@ -783,7 +783,7 @@ const InternalFlowBuilder: React.FC = (props) => { - + From 40a4bc2e6af2a92e5077ff0762e65d3b64aa7c14 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 22 Jun 2026 02:31:37 +0200 Subject: [PATCH 54/66] feat: adding ai support --- .../panels/FlowPanelControlComponent.tsx | 62 ++++++++++++++----- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx b/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx index 8be5802d..16ca76f0 100644 --- a/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx +++ b/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx @@ -34,6 +34,8 @@ import 'ldrs/react/ChaoticOrbit.css' import {AIChatComponent} from "@edition/ai/components/AIChatComponent"; import {mapAiGenerationFlowToFlowInput} from "@edition/ai/util/AI.flow.mapper"; import {addIslandSuccessNotification} from "@code0-tech/pictor/dist/components/island/Island.hook"; +import {useFlowCompareStore} from "@edition/flow/hooks/Flow.compare.hook"; +import {FlowView} from "@edition/flow/services/Flow.view"; export interface FlowPanelControlComponentProps { namespaceId: Namespace['id'] @@ -49,6 +51,9 @@ export const FlowPanelControlComponent: React.FC //services and stores const flowService = useService(FlowService) const flowStore = useStore(FlowService) + const compareFlow = useFlowCompareStore(state => state.flow) + const setCompareFlow = useFlowCompareStore(state => state.setFlow) + const clearCompareFlow = useFlowCompareStore(state => state.clearFlow) const [, startTransition] = React.useTransition() const [prompt, setPrompt] = React.useState("") @@ -72,22 +77,46 @@ export const FlowPanelControlComponent: React.FC const aiFlow = payload?.flow if (!aiFlow) return + const currentFlow = flowService.getById(flowId, {namespaceId, projectId}) + if (!currentFlow) return + + const currentFlowName = currentFlow.name ?? undefined const existingNames = (flowService.values({namespaceId, projectId}) ?? []) .map(f => f.name) - .filter((n): n is string => !!n) + .filter((n): n is string => !!n && n !== currentFlowName) const flowInput = mapAiGenerationFlowToFlowInput(aiFlow, {existingNames}) if (!flowInput) return + const oldFlowSnapshot: FlowView = JSON.parse(JSON.stringify(currentFlow)) + + flowService.flowUpdate({ + flowId: flowId!, + flowInput: flowInput, + }, true).then(result => { + if ((result?.errors?.length ?? 0) <= 0) { + setCompareFlow(oldFlowSnapshot) + addIslandSuccessNotification({message: "Updated flow"}) + } + }) + }, [flowService, flowStore, namespaceId, projectId, flowId, setCompareFlow]) + + const onAcceptAIChanges = React.useCallback(() => { + clearCompareFlow() + }, [clearCompareFlow]) + + const onDiscardAIChanges = React.useCallback(() => { + if (!compareFlow) return + const flowInput = flowService.getPayload(compareFlow) flowService.flowUpdate({ flowId: flowId!, flowInput: flowInput, - }).then(result => { + }, true).then(result => { if ((result?.errors?.length ?? 0) <= 0) { - addIslandSuccessNotification({message: "Created flow"}) + clearCompareFlow() } }) - }, [flowStore, namespaceId, projectId]) + }, [compareFlow, flowService, flowId, clearCompareFlow]) const addNodeToFlow = React.useCallback((suggestion: NodeFunction | ReferenceValue | LiteralValue | SubFlowValue) => { if (flowId && suggestion.__typename === "NodeFunction" && selectedNode?.id.includes("NodeFunction")) { @@ -198,17 +227,20 @@ export const FlowPanelControlComponent: React.FC - - - - - - - + {compareFlow && ( + + + + + + + )} + From 22de95b9c0c7306fe26b348d14573bbef944d959 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 22 Jun 2026 02:31:45 +0200 Subject: [PATCH 55/66] feat: correct ids --- .../project/[projectId]/flow/[flowId]/page.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/[flowId]/page.tsx b/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/[flowId]/page.tsx index 9dcfa315..074806e8 100644 --- a/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/[flowId]/page.tsx +++ b/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/[flowId]/page.tsx @@ -4,7 +4,7 @@ import {Button, Flex,} from "@code0-tech/pictor"; import React from "react"; import {IconFile, IconPlayerPlay} from "@tabler/icons-react"; import {useParams} from "next/navigation"; -import {Flow} from "@code0-tech/sagittarius-graphql-types"; +import {Flow, Namespace, NamespaceProject} from "@code0-tech/sagittarius-graphql-types"; import {FlowBuilderComponent} from "@edition/flow/components/builder/FlowBuilderComponent"; import {FunctionFilesComponent} from "@edition/function/components/files/FunctionFilesComponent"; import { @@ -21,6 +21,10 @@ export default function Page() { const params = useParams() + const namespaceIndex = params.namespaceId as any as number + const namespaceId: Namespace['id'] = `gid://sagittarius/Namespace/${namespaceIndex}` + const projectIndex = params.projectId as any as number + const projectId: NamespaceProject['id'] = `gid://sagittarius/NamespaceProject/${projectIndex}` const flowIndex = params.flowId as any as number const flowId: Flow['id'] = `gid://sagittarius/Flow/${flowIndex}` @@ -72,7 +76,7 @@ export default function Page() { borderTopRightRadius: "1rem" }) }}> - + { tab === "execution" && ( From b26d01c42ab9b9c3703aef0432d4652674f5d845 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 22 Jun 2026 14:11:55 +0200 Subject: [PATCH 56/66] feat: switching back to use memo --- .../ce/src/flow/hooks/Flow.nodes.hook.ts | 56 +++++++++---------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/src/packages/ce/src/flow/hooks/Flow.nodes.hook.ts b/src/packages/ce/src/flow/hooks/Flow.nodes.hook.ts index be9912b0..58ef3098 100644 --- a/src/packages/ce/src/flow/hooks/Flow.nodes.hook.ts +++ b/src/packages/ce/src/flow/hooks/Flow.nodes.hook.ts @@ -1,6 +1,6 @@ import {Node} from "@xyflow/react"; import type {Flow, Namespace, NamespaceProject, NodeFunction} from "@code0-tech/sagittarius-graphql-types"; -import React, {useState} from "react"; +import React from "react"; import {hashToColor, useService, useStore} from "@code0-tech/pictor"; import {FlowService} from "@edition/flow/services/Flow.service"; import {FunctionService} from "@edition/function/services/Function.service"; @@ -16,8 +16,6 @@ export const useFlowNodes = (flowId: Flow["id"], namespaceId?: Namespace["id"], const functionStore = useStore(FunctionService) const flowToCompare = useFlowCompareStore(state => state.flow) - const [nodes, setNodes] = useState[]>([]) - const flow = React.useMemo( () => flowService.getById(flowId, {namespaceId, projectId}), [flowId, namespaceId, projectId, flowStore, flowService] @@ -25,18 +23,18 @@ export const useFlowNodes = (flowId: Flow["id"], namespaceId?: Namespace["id"], const flowSchema = useFlowSchema(flowId, namespaceId, projectId) - React.useEffect(() => { - if (!flow) return - if (functionStore.length <= 0) return - if ((flowSchema?.length ?? 0) < (flow.nodes?.nodes?.length ?? 1)) return; + return React.useMemo[]>(() => { + if (!flow) return [] + if (functionStore.length <= 0) return [] - const visited = new Set(); + const nodes: Node[] = [] + const visited = new Set() let groupCounter = 0; let globalIndex = 0; // Trigger node (ID == flow.id -> edge compatible) - setNodes(() => [{ + nodes.push({ id: `${flow.id}`, type: "trigger", position: {x: 0, y: 0}, @@ -47,7 +45,7 @@ export const useFlowNodes = (flowId: Flow["id"], namespaceId?: Namespace["id"], color: hashToColor(flowId!), schema: flowSchema?.filter(nodeSchema => nodeSchema?.some(schema => !schema.nodeId))?.flat()! }, - }]) + }) const traverse = ( node: NodeFunction, @@ -58,12 +56,12 @@ export const useFlowNodes = (flowId: Flow["id"], namespaceId?: Namespace["id"], if (!node?.id) return const functionDefinition = functionService.getById(node.functionDefinition?.id) - const nodeId = node.id; + const nodeId = node.id if (!visited.has(nodeId)) { - visited.add(nodeId); + visited.add(nodeId) - setNodes(prevState => [...prevState, { + nodes.push({ id: nodeId, type: functionDefinition && "design" in functionDefinition ? functionDefinition?.design as string : "default", position: {x: 0, y: 0}, @@ -77,15 +75,15 @@ export const useFlowNodes = (flowId: Flow["id"], namespaceId?: Namespace["id"], color: hashToColor(nodeId), schema: flowSchema?.filter(nodeSchema => nodeSchema?.some(schema => schema.nodeId === node.id))?.flat()! }, - }]) + }) } node.parameters?.nodes?.forEach((param) => { - const value = param?.value; - if (!value || value.__typename !== "SubFlowValue") return; + const value = param?.value + if (!value || value.__typename !== "SubFlowValue") return if (value.functionDefinition?.id) { - setNodes(prevState => [...prevState, { + nodes.push({ id: `${nodeId}-${param.id}`, type: functionDefinition && "design" in functionDefinition ? functionDefinition?.design as string : "square", position: {x: 0, y: 0}, @@ -102,16 +100,16 @@ export const useFlowNodes = (flowId: Flow["id"], namespaceId?: Namespace["id"], color: hashToColor(value?.startingNodeId ?? value?.functionDefinition?.id ?? ""), schema: [] }, - }]) + }) return } - const groupId = `${nodeId}-group-${groupCounter++}`; + const groupId = `${nodeId}-group-${groupCounter++}` if (!visited.has(groupId)) { - visited.add(groupId); + visited.add(groupId) - setNodes(prevState => [...prevState, { + nodes.push({ id: groupId, type: "group", position: {x: 0, y: 0}, @@ -125,14 +123,12 @@ export const useFlowNodes = (flowId: Flow["id"], namespaceId?: Namespace["id"], color: hashToColor(value?.startingNodeId ?? ""), schema: [] }, - }]) + }) } - const child = flowService.getNodeById(flowId, value.startingNodeId); - if (child) traverse(child, groupId); - - - }); + const child = flowService.getNodeById(flowId, value.startingNodeId) + if (child) traverse(child, groupId) + }) if (node.nextNodeId) { const next = flowService.getNodeById(flow.id, node.nextNodeId); @@ -144,7 +140,7 @@ export const useFlowNodes = (flowId: Flow["id"], namespaceId?: Namespace["id"], const start = flowService.getNodeById(flow.id, flow.startingNodeId); if (start) traverse(start); } - }, [flowStore, flow?.editedAt, flow, flowToCompare, functionStore.length, flowSchema]); - return nodes -}; \ No newline at end of file + return nodes + }, [flowStore, flow?.editedAt, flow, flowToCompare, functionStore.length, flowSchema, flowId]) +} \ No newline at end of file From 2e1b615b2814e101ca2d27bffbad35f1c9b5da5a Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 22 Jun 2026 14:12:12 +0200 Subject: [PATCH 57/66] feat: correct cancellation --- src/packages/ce/src/flow/hooks/Flow.schema.hook.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/packages/ce/src/flow/hooks/Flow.schema.hook.ts b/src/packages/ce/src/flow/hooks/Flow.schema.hook.ts index b9193f28..0b56c160 100644 --- a/src/packages/ce/src/flow/hooks/Flow.schema.hook.ts +++ b/src/packages/ce/src/flow/hooks/Flow.schema.hook.ts @@ -43,6 +43,8 @@ export const useFlowSchema = ( if (dataTypes.length <= 0) return if (functions.length <= 0) return + let cancelled = false + const triggerSchema = execute({ flow: flowView, dataTypes, @@ -59,9 +61,13 @@ export const useFlowSchema = ( }) Promise.all([triggerSchema!, ...schemas!]).then((value) => { + if (cancelled) return setSchema(value as NodeSchema[][]) }) + return () => { + cancelled = true + } }, [functions.length, dataTypes.length, flowStore, flowView?.editedAt, flowView?.nodes?.nodes?.length]) return schema From 48e8422cb150b4c7a9ca33a20c3daf6094eb04ae Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 22 Jun 2026 14:12:38 +0200 Subject: [PATCH 58/66] feat: network only request to make sure the curren flow is received --- src/packages/ce/src/flow/services/Flow.service.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/packages/ce/src/flow/services/Flow.service.ts b/src/packages/ce/src/flow/services/Flow.service.ts index d430c2a7..1cb68294 100644 --- a/src/packages/ce/src/flow/services/Flow.service.ts +++ b/src/packages/ce/src/flow/services/Flow.service.ts @@ -483,6 +483,7 @@ export class FlowService extends ReactiveArrayService({ query: flowQuery, + fetchPolicy: "network-only", variables: { namespaceId: flow?.project?.namespace?.id, projectId: flow?.project?.id, From 52cccea15bd5688c0918b8271aca9b785e45ae96 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 22 Jun 2026 14:30:36 +0200 Subject: [PATCH 59/66] feat: routing prompt change back to parent component --- src/packages/ce/src/ai/components/AIChatComponent.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/packages/ce/src/ai/components/AIChatComponent.tsx b/src/packages/ce/src/ai/components/AIChatComponent.tsx index 392ff239..f8e88498 100644 --- a/src/packages/ce/src/ai/components/AIChatComponent.tsx +++ b/src/packages/ce/src/ai/components/AIChatComponent.tsx @@ -209,7 +209,10 @@ export const AIChatComponent: React.FC = (props) => { ) : ( onPrompt?.(value)} + onChange={(value) => { + setPromptState(value) + onPrompt?.(value) + }} wrapperComponent={{ style: { background: "transparent", From 0163fc984636a100db07aec22f79a11682324dad Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 22 Jun 2026 16:06:01 +0200 Subject: [PATCH 60/66] feat: removing unnecessary callback --- src/packages/ce/src/ai/components/AIChatComponent.tsx | 6 ++---- .../flow/components/panels/FlowPanelControlComponent.tsx | 8 ++++---- src/packages/ce/src/flow/pages/FlowOverviewPage.tsx | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/packages/ce/src/ai/components/AIChatComponent.tsx b/src/packages/ce/src/ai/components/AIChatComponent.tsx index f8e88498..8137d827 100644 --- a/src/packages/ce/src/ai/components/AIChatComponent.tsx +++ b/src/packages/ce/src/ai/components/AIChatComponent.tsx @@ -42,7 +42,6 @@ const GENERATING_VARIANTS = [ export interface AIChatComponentProps { projectId: NamespaceProject['id'] flowId?: Flow['id'] - onPrompt?: (value: string) => void prompt?: string onData?: (data: any) => void @@ -50,7 +49,7 @@ export interface AIChatComponentProps { export const AIChatComponent: React.FC = (props) => { - const {projectId, flowId, onPrompt, prompt = "", onData} = props + const {projectId, flowId, prompt = "", onData} = props const aiService = useService(AIService) const aiStore = useStore(AIService) @@ -208,10 +207,9 @@ export const AIChatComponent: React.FC = (props) => {
) : ( { setPromptState(value) - onPrompt?.(value) }} wrapperComponent={{ style: { diff --git a/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx b/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx index 16ca76f0..2f06a3df 100644 --- a/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx +++ b/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx @@ -2,7 +2,8 @@ import React from "react"; import { AiGenerateFlowSubscriptionPayload, Flow, - LiteralValue, Namespace, + LiteralValue, + Namespace, NamespaceProject, NodeFunction, ReferenceValue, @@ -29,7 +30,6 @@ import {useSelectedFunctionNode} from "@edition/function/hooks/FunctionNode.sele import {useFunctionSuggestions} from "@edition/function/hooks/Function.suggestion.hook"; import {IconArrowBigUp, IconBackspace, IconLetterA, IconLetterQ} from "@tabler/icons-react"; import {HoverCard, HoverCardContent, HoverCardPortal, HoverCardTrigger} from "@radix-ui/react-hover-card"; -import {ChaoticOrbit} from "ldrs/react"; import 'ldrs/react/ChaoticOrbit.css' import {AIChatComponent} from "@edition/ai/components/AIChatComponent"; import {mapAiGenerationFlowToFlowInput} from "@edition/ai/util/AI.flow.mapper"; @@ -56,7 +56,6 @@ export const FlowPanelControlComponent: React.FC const clearCompareFlow = useFlowCompareStore(state => state.clearFlow) const [, startTransition] = React.useTransition() - const [prompt, setPrompt] = React.useState("") const [suggestionDialogOpen, setSuggestionDialogOpen] = React.useState(false) const [addNextNodeTooltipOpen, setAddNextNodeTooltipOpen] = React.useState(false) @@ -239,7 +238,8 @@ export const FlowPanelControlComponent: React.FC )} - diff --git a/src/packages/ce/src/flow/pages/FlowOverviewPage.tsx b/src/packages/ce/src/flow/pages/FlowOverviewPage.tsx index e1e6f51c..08dc34a5 100644 --- a/src/packages/ce/src/flow/pages/FlowOverviewPage.tsx +++ b/src/packages/ce/src/flow/pages/FlowOverviewPage.tsx @@ -138,7 +138,7 @@ export const FlowOverviewPage: React.FC = () => { - + } \ No newline at end of file From 43c7c0aba08ad1564bf5207cbf61f32a5fa49146 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 22 Jun 2026 16:06:48 +0200 Subject: [PATCH 61/66] feat: adding key for memorization --- src/packages/ce/src/ai/components/AIChatComponent.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/packages/ce/src/ai/components/AIChatComponent.tsx b/src/packages/ce/src/ai/components/AIChatComponent.tsx index 8137d827..cc6c1f68 100644 --- a/src/packages/ce/src/ai/components/AIChatComponent.tsx +++ b/src/packages/ce/src/ai/components/AIChatComponent.tsx @@ -207,6 +207,7 @@ export const AIChatComponent: React.FC = (props) => {
) : ( { setPromptState(value) From 5253b057d8a7ec2ec3e3c0080409690234cfb1df Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 22 Jun 2026 16:08:40 +0200 Subject: [PATCH 62/66] feat: adding shortcut for execution tab --- .../project/[projectId]/flow/[flowId]/page.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/[flowId]/page.tsx b/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/[flowId]/page.tsx index 074806e8..0b05d6fd 100644 --- a/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/[flowId]/page.tsx +++ b/src/app/(flow)/namespace/[namespaceId]/project/[projectId]/flow/[flowId]/page.tsx @@ -37,6 +37,12 @@ export default function Page() { keyboardEvent.preventDefault() }, []) + useHotkeys('shift+2', (keyboardEvent) => { + setTab(prevState => prevState === "execution" ? undefined : "execution") + keyboardEvent.stopPropagation() + keyboardEvent.preventDefault() + }, []) + React.useEffect(() => { const localSelectedNode = reactFlow.getNodes().filter((node) => node.selected)[0] as Node | undefined From 0d76729a794107865e204f85c1258ea7f55b358b Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 22 Jun 2026 16:11:38 +0200 Subject: [PATCH 63/66] feat: renaming, alias imports and outsourcing --- .../fragments/AI.flow.fragment.graphql | 74 ++++++++++++++++++ .../fragments/AI.model.fragment.graphql | 2 +- .../services/queries/AI.models.query.graphql | 4 +- .../AI.generateFlow.subscription.graphql | 75 +------------------ 4 files changed, 80 insertions(+), 75 deletions(-) create mode 100644 src/packages/ce/src/ai/services/fragments/AI.flow.fragment.graphql diff --git a/src/packages/ce/src/ai/services/fragments/AI.flow.fragment.graphql b/src/packages/ce/src/ai/services/fragments/AI.flow.fragment.graphql new file mode 100644 index 00000000..436501bd --- /dev/null +++ b/src/packages/ce/src/ai/services/fragments/AI.flow.fragment.graphql @@ -0,0 +1,74 @@ +fragment AIFlow on AiGenerationFlow { + __typename + name + startingNodeId + settings { + __typename + id + value + cast + flowSettingIdentifier + } + type { + __typename + id + identifier + } + nodes { + __typename + id + nextNodeId + parameters { + __typename + id + cast + value { + __typename + ... on AiGenerationLiteralValue { + __typename + value + } + ... on AiGenerationReferenceValue { + __typename + id + nodeFunctionId + inputIndex + inputTypeIdentifier + parameterIndex + referencePath { + __typename + path + arrayIndex + } + } + ... on AiGenerationSubFlowValue { + __typename + startingNodeId + signature + settings { + __typename + identifier + defaultValue + hidden + optional + } + functionDefinition { + __typename + id + identifier + } + } + } + parameterDefinition { + __typename + id + identifier + } + } + functionDefinition { + __typename + id + identifier + } + } +} \ No newline at end of file diff --git a/src/packages/ce/src/ai/services/fragments/AI.model.fragment.graphql b/src/packages/ce/src/ai/services/fragments/AI.model.fragment.graphql index 55d538d6..f890e93e 100644 --- a/src/packages/ce/src/ai/services/fragments/AI.model.fragment.graphql +++ b/src/packages/ce/src/ai/services/fragments/AI.model.fragment.graphql @@ -1,4 +1,4 @@ -fragment Model on AiModel { +fragment AIModel on AiModel { __typename identifier name diff --git a/src/packages/ce/src/ai/services/queries/AI.models.query.graphql b/src/packages/ce/src/ai/services/queries/AI.models.query.graphql index 8c222b8e..820ce54c 100644 --- a/src/packages/ce/src/ai/services/queries/AI.models.query.graphql +++ b/src/packages/ce/src/ai/services/queries/AI.models.query.graphql @@ -1,8 +1,8 @@ -#import '../fragments/AI.model.fragment.graphql' +#import '@edition/ai/services/fragments/AI.model.fragment.graphql' query models { ai { models { - ...Model + ...AIModel } } } diff --git a/src/packages/ce/src/ai/services/subscriptions/AI.generateFlow.subscription.graphql b/src/packages/ce/src/ai/services/subscriptions/AI.generateFlow.subscription.graphql index ed8f7e03..a719d196 100644 --- a/src/packages/ce/src/ai/services/subscriptions/AI.generateFlow.subscription.graphql +++ b/src/packages/ce/src/ai/services/subscriptions/AI.generateFlow.subscription.graphql @@ -1,78 +1,9 @@ +#import '@edition/ai/services/fragments/AI.flow.fragment.graphql' + subscription generateFlow($executionIdentifier: String!) { aiGenerateFlow(executionIdentifier: $executionIdentifier) { flow { - __typename - name - startingNodeId - settings { - __typename - id - value - cast - flowSettingIdentifier - } - type { - __typename - id - identifier - } - nodes { - __typename - id - nextNodeId - parameters { - __typename - id - cast - value { - __typename - ... on AiGenerationLiteralValue { - __typename - value - } - ... on AiGenerationReferenceValue { - __typename - id - nodeFunctionId - inputIndex - inputTypeIdentifier - parameterIndex - referencePath { - __typename - path - arrayIndex - } - } - ... on AiGenerationSubFlowValue { - __typename - startingNodeId - signature - settings { - __typename - identifier - defaultValue - hidden - optional - } - functionDefinition { - __typename - id - identifier - } - } - } - parameterDefinition { - __typename - id - identifier - } - } - functionDefinition { - __typename - id - identifier - } - } + ...AIFlow } } } From 6ba506a50f7cc4332ffbaa8dae9457688f69ed68 Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 22 Jun 2026 16:13:07 +0200 Subject: [PATCH 64/66] feat: alias import --- src/packages/ce/src/flow/services/queries/Flow.query.graphql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/packages/ce/src/flow/services/queries/Flow.query.graphql b/src/packages/ce/src/flow/services/queries/Flow.query.graphql index 835b680e..a1187ccc 100644 --- a/src/packages/ce/src/flow/services/queries/Flow.query.graphql +++ b/src/packages/ce/src/flow/services/queries/Flow.query.graphql @@ -1,4 +1,4 @@ -#import "../fragments/Flow.fragment.graphql" +#import "@edition/flow/services/fragments/Flow.fragment.graphql" query Flows ( $namespaceId: NamespaceID! From d9d2d14f8c8a8214956e0761354b689889e557ed Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 22 Jun 2026 16:16:37 +0200 Subject: [PATCH 65/66] feat: rollback to only id --- .../ce/src/flow/hooks/Flow.schema.hook.ts | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/packages/ce/src/flow/hooks/Flow.schema.hook.ts b/src/packages/ce/src/flow/hooks/Flow.schema.hook.ts index 0b56c160..59c8b394 100644 --- a/src/packages/ce/src/flow/hooks/Flow.schema.hook.ts +++ b/src/packages/ce/src/flow/hooks/Flow.schema.hook.ts @@ -6,10 +6,9 @@ import {useSchemaAction} from "@edition/flow/components/FlowWorkerProvider"; import {DatatypeService} from "@edition/datatype/services/Datatype.service"; import {FunctionService} from "@edition/function/services/Function.service"; import {NodeSchema} from "@code0-tech/triangulum"; -import {FlowView} from "@edition/flow/services/Flow.view"; export const useFlowSchema = ( - flow: Flow['id'] | FlowView, + flowId: Flow['id'], namespaceId: Namespace['id'], projectId: NamespaceProject['id'], ): NodeSchema[][] | undefined => { @@ -23,9 +22,9 @@ export const useFlowSchema = ( const [schema, setSchema] = React.useState([]); - const flowView = React.useMemo( - () => typeof flow === "string" ? flowService.getById(flow, {namespaceId, projectId}) : flow, - [flow, flowService, flowStore] + const flow = React.useMemo( + () => flowService.getById(flowId, {namespaceId, projectId}), + [flowId, flowService, flowStore] ) const dataTypes = React.useMemo( @@ -39,21 +38,21 @@ export const useFlowSchema = ( ) React.useEffect(() => { - if (!flowView) return + if (!flow) return if (dataTypes.length <= 0) return if (functions.length <= 0) return let cancelled = false const triggerSchema = execute({ - flow: flowView, + flow, dataTypes, functions }) - const schemas = flowView.nodes?.nodes?.map(node => { + const schemas = flow.nodes?.nodes?.map(node => { return execute({ - flow: flowView, + flow, dataTypes, functions, nodeId: node?.id @@ -68,7 +67,7 @@ export const useFlowSchema = ( return () => { cancelled = true } - }, [functions.length, dataTypes.length, flowStore, flowView?.editedAt, flowView?.nodes?.nodes?.length]) + }, [functions.length, dataTypes.length, flowStore, flow?.editedAt, flow?.nodes?.nodes?.length]) return schema } From f08c179003383c70e9473a95404eb333b563ff8b Mon Sep 17 00:00:00 2001 From: nicosammito Date: Mon, 22 Jun 2026 18:04:33 +0200 Subject: [PATCH 66/66] feat: error handling --- .../ce/src/ai/components/AIChatComponent.tsx | 12 +++++++++--- .../components/panels/FlowPanelControlComponent.tsx | 6 +++--- src/packages/ce/src/flow/pages/FlowOverviewPage.tsx | 4 ++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/packages/ce/src/ai/components/AIChatComponent.tsx b/src/packages/ce/src/ai/components/AIChatComponent.tsx index cc6c1f68..2bf01c8c 100644 --- a/src/packages/ce/src/ai/components/AIChatComponent.tsx +++ b/src/packages/ce/src/ai/components/AIChatComponent.tsx @@ -43,7 +43,7 @@ export interface AIChatComponentProps { projectId: NamespaceProject['id'] flowId?: Flow['id'] prompt?: string - onData?: (data: any) => void + onData?: (data: any) => string | void } @@ -72,8 +72,13 @@ export const AIChatComponent: React.FC = (props) => { onData: (data) => { setIsAIActive(true) if (data.data.data?.aiGenerateFlow?.flow) { - onData?.(data.data.data?.aiGenerateFlow) - setPromptState("") + const result = onData?.(data.data.data?.aiGenerateFlow) + if (typeof result === "string") { + setAiErrorMessage(result) + } else { + setPromptState("") + } + setExecutionIdentifier(null) } else if (data.data.data?.aiGenerateFlow?.flow === null) { setExecutionIdentifier(null) setAiErrorMessage("Generation failed. Try another model.") @@ -83,6 +88,7 @@ export const AIChatComponent: React.FC = (props) => { onError: () => { setIsAIActive(false) setAiErrorMessage("Generation failed. Try another model.") + setExecutionIdentifier(null) }, }) diff --git a/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx b/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx index 2f06a3df..cdf28ec5 100644 --- a/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx +++ b/src/packages/ce/src/flow/components/panels/FlowPanelControlComponent.tsx @@ -74,10 +74,10 @@ export const FlowPanelControlComponent: React.FC const onAIData = React.useCallback((payload: AiGenerateFlowSubscriptionPayload) => { const aiFlow = payload?.flow - if (!aiFlow) return + if (!aiFlow) return "No flow returned. Try again." const currentFlow = flowService.getById(flowId, {namespaceId, projectId}) - if (!currentFlow) return + if (!currentFlow) return "Flow not found. Try again." const currentFlowName = currentFlow.name ?? undefined const existingNames = (flowService.values({namespaceId, projectId}) ?? []) @@ -85,7 +85,7 @@ export const FlowPanelControlComponent: React.FC .filter((n): n is string => !!n && n !== currentFlowName) const flowInput = mapAiGenerationFlowToFlowInput(aiFlow, {existingNames}) - if (!flowInput) return + if (!flowInput) return "Invalid flow type. Try another model." const oldFlowSnapshot: FlowView = JSON.parse(JSON.stringify(currentFlow)) diff --git a/src/packages/ce/src/flow/pages/FlowOverviewPage.tsx b/src/packages/ce/src/flow/pages/FlowOverviewPage.tsx index 08dc34a5..355930a7 100644 --- a/src/packages/ce/src/flow/pages/FlowOverviewPage.tsx +++ b/src/packages/ce/src/flow/pages/FlowOverviewPage.tsx @@ -61,14 +61,14 @@ export const FlowOverviewPage: React.FC = () => { const onAIData = React.useCallback((payload: AiGenerateFlowSubscriptionPayload) => { const aiFlow = payload?.flow - if (!aiFlow) return + if (!aiFlow) return "No flow returned. Try again." const existingNames = (flowService.values({namespaceId, projectId}) ?? []) .map(f => f.name) .filter((n): n is string => !!n) const flowInput = mapAiGenerationFlowToFlowInput(aiFlow, {existingNames}) - if (!flowInput) return + if (!flowInput) return "Invalid flow type. Try another model." flowService.flowCreate({ flow: flowInput,