diff --git a/api/agentok_api/templates/assistant.j2 b/api/agentok_api/templates/assistant.j2 index 37b92186..853919ce 100644 --- a/api/agentok_api/templates/assistant.j2 +++ b/api/agentok_api/templates/assistant.j2 @@ -1,8 +1,8 @@ # agents.j2 {%- macro generate_assistants(assistant_nodes) %} +# Assistant Agents {%- for node in assistant_nodes %} {%- set name = node.data.name %} - node_{{ node.id }} = {{ node.data.class_type }}( name="{{ name }}", {%- if node.data.system_message %} diff --git a/api/agentok_api/templates/conversable_agent.j2 b/api/agentok_api/templates/conversable_agent.j2 index 312c06ae..681a838c 100644 --- a/api/agentok_api/templates/conversable_agent.j2 +++ b/api/agentok_api/templates/conversable_agent.j2 @@ -1,5 +1,5 @@ # conversable_agents.j2 -{% macro generate_conversable_agents(conversable_nodes) %} +{%- macro generate_conversable_agents(conversable_nodes) -%} import tempfile temp_dir = tempfile.gettempdir() @@ -34,4 +34,4 @@ node_{{ node.id }} = {{ node.data.class_type }}( {%- endif %} ) {% endfor %} -{% endmacro %} \ No newline at end of file +{%- endmacro -%} \ No newline at end of file diff --git a/api/agentok_api/templates/tool.j2 b/api/agentok_api/templates/tool.j2 index 4357aa2d..6e39c0b0 100644 --- a/api/agentok_api/templates/tool.j2 +++ b/api/agentok_api/templates/tool.j2 @@ -1,5 +1,5 @@ # Tools -{%- if tool_dict and tool_dict | length > 0 %} +{% if tool_dict and tool_dict | length > 0 %} {%- for tool in tool_dict.values() %} {%- if tool.code %} diff --git a/frontend/package.json b/frontend/package.json index 5bdcd3b9..07732a8b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -46,6 +46,7 @@ "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "date-fns": "^4.1.0", + "html-to-image": "^1.11.11", "immer": "^10.1.1", "katex": "^0.16.11", "lodash-es": "^4.17.21", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 9a64664d..877eccf9 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -113,6 +113,9 @@ importers: date-fns: specifier: ^4.1.0 version: 4.1.0 + html-to-image: + specifier: ^1.11.11 + version: 1.11.11 immer: specifier: ^10.1.1 version: 10.1.1 @@ -2146,6 +2149,9 @@ packages: html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + html-to-image@1.11.11: + resolution: {integrity: sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA==} + html-url-attributes@3.0.0: resolution: {integrity: sha512-/sXbVCWayk6GDVg3ctOX6nxaVj7So40FcFAnWlWGNAB1LpYKcV5Cd10APjPjW80O7zYW2MsjBV4zZ7IZO5fVow==} @@ -5272,7 +5278,7 @@ snapshots: eslint: 9.6.0 eslint-import-resolver-node: 0.3.9 eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@9.6.0))(eslint@9.6.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@9.6.0))(eslint@9.6.0))(eslint@9.6.0) eslint-plugin-jsx-a11y: 6.9.0(eslint@9.6.0) eslint-plugin-react: 7.35.0(eslint@9.6.0) eslint-plugin-react-hooks: 4.6.2(eslint@9.6.0) @@ -5299,8 +5305,8 @@ snapshots: debug: 4.3.6 enhanced-resolve: 5.17.1 eslint: 9.6.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@9.6.0))(eslint@9.6.0))(eslint@9.6.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@9.6.0))(eslint@9.6.0))(eslint@9.6.0) fast-glob: 3.3.2 get-tsconfig: 4.7.6 is-core-module: 2.15.0 @@ -5311,7 +5317,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@9.6.0))(eslint@9.6.0))(eslint@9.6.0): dependencies: debug: 3.2.7 optionalDependencies: @@ -5322,7 +5328,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@9.6.0))(eslint@9.6.0))(eslint@9.6.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -5332,7 +5338,7 @@ snapshots: doctrine: 2.1.0 eslint: 9.6.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@9.6.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@9.6.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@9.6.0))(eslint@9.6.0))(eslint@9.6.0) hasown: 2.0.2 is-core-module: 2.15.0 is-glob: 4.0.3 @@ -5738,6 +5744,8 @@ snapshots: html-escaper@2.0.2: {} + html-to-image@1.11.11: {} + html-url-attributes@3.0.0: {} husky@9.1.4: {} diff --git a/frontend/src/components/navbar/auth-button.tsx b/frontend/src/components/navbar/auth-button.tsx index 267bd6f5..dcb15177 100644 --- a/frontend/src/components/navbar/auth-button.tsx +++ b/frontend/src/components/navbar/auth-button.tsx @@ -64,7 +64,7 @@ export const AuthButton = () => { {
diff --git a/frontend/src/components/project/project-config.tsx b/frontend/src/components/project/project-config.tsx index 507c9669..11a1040e 100644 --- a/frontend/src/components/project/project-config.tsx +++ b/frontend/src/components/project/project-config.tsx @@ -1,12 +1,60 @@ import { useProject } from '@/hooks'; -import { ScrollArea } from '@radix-ui/react-scroll-area'; +import { ScrollArea } from '../ui/scroll-area'; import { GenericOption } from '../flow/option/option'; +import { Button } from '../ui/button'; +import { Icons } from '../icons'; +import { useReactFlow } from '@xyflow/react'; +import { toast } from '@/hooks/use-toast'; +import { toPng } from 'html-to-image'; export const ProjectConfig = ({ projectId }: { projectId: number }) => { const { project, updateProject } = useProject(projectId); + const instance = useReactFlow(); + const handleChange = (name: string, value: any) => { updateProject({ [name]: value }).catch(console.error); }; + + const handleDownloadImage = () => { + // Get the ReactFlow container element + const flowElement = document.querySelector('.react-flow') as HTMLElement; + if (!flowElement) { + toast({ + title: 'Error', + description: 'Could not find the flow canvas', + variant: 'destructive', + }); + return; + } + + toPng(flowElement, { + backgroundColor: '#ffffff00', // transparent background + quality: 1, + pixelRatio: 2, + }) + .then((dataUrl: string) => { + const link = document.createElement('a'); + link.href = dataUrl; + link.download = `${project?.name || 'flow'}-canvas.png`; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + + toast({ + title: 'Success', + description: 'Canvas image downloaded successfully', + }); + }) + .catch((error: Error) => { + console.error('Failed to download canvas:', error); + toast({ + title: 'Error', + description: 'Failed to download canvas image', + variant: 'destructive', + }); + }); + }; + return (
@@ -27,6 +75,14 @@ export const ProjectConfig = ({ projectId }: { projectId: number }) => { data={{ description: project?.description }} onValueChange={handleChange} /> +
);