diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..8b42382f --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +public-hoist-pattern[]=vite diff --git a/package.json b/package.json index 0b8a7ed3..2be56bb9 100644 --- a/package.json +++ b/package.json @@ -16,5 +16,8 @@ "lint": "npm -w packages/studio run lint", "lint:fix": "npm -w packages/studio run lint:fix", "type-check": "tsc --noEmit" + }, + "devDependencies": { + "vite": "8.0.14" } } diff --git a/packages/studio/src/canvas/RpaNodeAdapter.ts b/packages/studio/src/canvas/RpaNodeAdapter.ts index a3d6b432..e594c946 100644 --- a/packages/studio/src/canvas/RpaNodeAdapter.ts +++ b/packages/studio/src/canvas/RpaNodeAdapter.ts @@ -4,7 +4,7 @@ */ import type { Node, Edge } from '@reactflow/core'; -import type { RpaNode, RpaEdge } from '@rpaforge/domain-model'; +import type { RpaNode, RpaEdge } from '../types/domain-model'; export function rpaNodeToReactFlowNode(rpaNode: RpaNode): Node { return { diff --git a/packages/studio/src/components/Common/MermaidPreview.tsx b/packages/studio/src/components/Common/MermaidPreview.tsx index 7a59eade..b5d29f40 100644 --- a/packages/studio/src/components/Common/MermaidPreview.tsx +++ b/packages/studio/src/components/Common/MermaidPreview.tsx @@ -1,14 +1,14 @@ import { useState, useCallback, useRef, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { FaTimes, FaDownload, FaCopy, FaCode, FaImage } from 'react-icons/fa'; -import type { RpaNode, RpaEdge } from '@rpaforge/domain-model'; +import type { Node, Edge } from '@reactflow/core'; import { useForcedColors, useResolvedTheme } from '../../hooks/useTheme'; interface MermaidPreviewProps { isOpen: boolean; onClose: () => void; - nodes: RpaNode[]; - edges: RpaEdge[]; + nodes: Node[]; + edges: Edge[]; title?: string; } @@ -20,7 +20,7 @@ function sanitizeLabel(label: string): string { return label.replace(/"/g, "'").replace(/\n/g, ' ').replace(/[()]/g, ''); } -function getNodeLabel(node: RpaNode): string { +function getNodeLabel(node: Node): string { const blockData = node.data?.blockData as { type?: string; condition?: string; itemVariable?: string; activityId?: string; variableName?: string } | undefined; if (!blockData) { return String(node.data?.label || 'Node'); diff --git a/packages/studio/src/hooks/useFileOperations.ts b/packages/studio/src/hooks/useFileOperations.ts index 375d2f3b..0d48a8fa 100644 --- a/packages/studio/src/hooks/useFileOperations.ts +++ b/packages/studio/src/hooks/useFileOperations.ts @@ -400,7 +400,7 @@ export const useFileOperations = (): UseFileOperationsResult => { return false; } - const loaded = loadProcess(diagram.metadata, diagram.nodes, diagram.edges); + const loaded = loadProcess(diagram.metadata, diagram.nodes as ProcessNode[], diagram.edges); if (!loaded) { setLastError('Failed to load diagram: exactly one Start node is required.'); return false; diff --git a/packages/studio/src/hooks/useProcess.ts b/packages/studio/src/hooks/useProcess.ts index 2c08cfe4..8cadfe09 100644 --- a/packages/studio/src/hooks/useProcess.ts +++ b/packages/studio/src/hooks/useProcess.ts @@ -1,5 +1,6 @@ import { useCallback } from 'react'; -import type { RpaNode, RpaEdge } from '@rpaforge/domain-model'; +import type { RpaNode, RpaEdge } from '../types/domain-model'; +import type { Edge } from '@reactflow/core'; import { useBlockStore, type ProcessNodeData, type ProcessNode, normalizeNode, createStartBlockNode } from '../stores/blockStore'; import { useHistoryStore } from '../stores/historyStore'; import { useSelectionStore } from '../stores/selectionStore'; diff --git a/packages/studio/src/types/domain-model.ts b/packages/studio/src/types/domain-model.ts new file mode 100644 index 00000000..b5b65a1a --- /dev/null +++ b/packages/studio/src/types/domain-model.ts @@ -0,0 +1,20 @@ +/** + * Re-export domain model types for studio + * Bridge to @rpaforge/domain-model package during build + */ + +export type RpaNode = { + id: string; + type?: string; + data: D; + position?: { x: number; y: number }; + width?: number | null; + height?: number | null; +}; + +export type RpaEdge = { + id: string; + source: string; + target: string; + handle?: string | null; +}; diff --git a/packages/studio/src/utils/fileUtils.test.ts b/packages/studio/src/utils/fileUtils.test.ts index f038a4dc..552b81c9 100644 --- a/packages/studio/src/utils/fileUtils.test.ts +++ b/packages/studio/src/utils/fileUtils.test.ts @@ -33,11 +33,7 @@ describe('fileUtils diagram round-trip', () => { id: 'edge-1', source: 'start-1', target: 'node-2', - sourceHandle: 'true', - targetHandle: 'input', - type: 'custom', - data: { type: 'true', animated: false }, - style: { stroke: '#22C55E', strokeWidth: 2, strokeDasharray: '5,5' }, + handle: 'true', }, ], { @@ -53,11 +49,9 @@ describe('fileUtils diagram round-trip', () => { expect(result.success).toBe(true); expect(result.diagram?.edges[0]).toMatchObject({ id: 'edge-1', - sourceHandle: 'true', - targetHandle: 'input', - type: 'custom', - data: { type: 'true', animated: false }, - style: { stroke: '#22C55E', strokeWidth: 2, strokeDasharray: '5,5' }, + source: 'start-1', + target: 'node-2', + handle: 'true', }); }); diff --git a/packages/studio/src/utils/fileUtils.ts b/packages/studio/src/utils/fileUtils.ts index 5d010be9..49d6e417 100644 --- a/packages/studio/src/utils/fileUtils.ts +++ b/packages/studio/src/utils/fileUtils.ts @@ -1,4 +1,4 @@ -import type { RpaNode, RpaEdge } from '@rpaforge/domain-model'; +import type { RpaNode, RpaEdge } from '../types/domain-model'; import type { ProcessNodeData, ProcessMetadata } from '../stores/processStore'; import type { DiagramDocument, ProjectConfig } from '../stores/diagramStore'; import type { ProcessVariable } from '../stores/variableStore'; diff --git a/packages/studio/src/utils/mermaidGenerator.ts b/packages/studio/src/utils/mermaidGenerator.ts index 4e4c13e4..8041e768 100644 --- a/packages/studio/src/utils/mermaidGenerator.ts +++ b/packages/studio/src/utils/mermaidGenerator.ts @@ -1,6 +1,8 @@ -import type { RpaNode, RpaEdge } from '@rpaforge/domain-model'; +import type { RpaNode, RpaEdge } from '../types/domain-model'; import type { BlockData, IfBlockData, WhileBlockData, ForEachBlockData, SwitchBlockData } from '../types/blocks'; +type NodeData = { blockData?: BlockData; label?: string }; + interface MermaidNode { id: string; label: string; @@ -16,7 +18,7 @@ function sanitizeLabel(label: string): string { return label.replace(/"/g, "'").replace(/\n/g, ' ').replace(/[()]/g, ''); } -function getNodeLabel(node: RpaNode): string { +function getNodeLabel(node: RpaNode): string { const blockData = node.data?.blockData as BlockData; if (!blockData) { return node.data?.label || node.id || 'Node'; @@ -118,7 +120,7 @@ function buildGraph(edges: RpaEdge[]): Map { return graph; } -export function diagramToMermaid(nodes: RpaNode[], edges: RpaEdge[]): string { +export function diagramToMermaid(nodes: RpaNode[], edges: RpaEdge[]): string { if (nodes.length === 0) { return 'flowchart TD\n empty(No nodes in diagram)'; } diff --git a/packages/studio/src/utils/templateLoader.ts b/packages/studio/src/utils/templateLoader.ts index c9d70b5e..1c60ad45 100644 --- a/packages/studio/src/utils/templateLoader.ts +++ b/packages/studio/src/utils/templateLoader.ts @@ -1,4 +1,4 @@ -import type { RpaNode, RpaEdge } from '@rpaforge/domain-model'; +import type { Node, Edge } from '@reactflow/core'; import type { ProcessMetadata, ProcessNodeData } from '../stores/processStore'; import type { DiagramType } from '../stores/diagramStore'; import type { BlockData, BlockType } from '../types/blocks'; diff --git a/packages/studio/src/vite-env.d.ts b/packages/studio/src/vite-env.d.ts new file mode 100644 index 00000000..42f5276e --- /dev/null +++ b/packages/studio/src/vite-env.d.ts @@ -0,0 +1,8 @@ +/// + +declare const __APP_VERSION__: string; + +declare module '*.css' { + const content: Record; + export default content; +} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index fca589d6..ce4937cf 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -5,3 +5,4 @@ onlyBuiltDependencies: - electron - esbuild - "@parcel/watcher" + - electron-winstaller