@process.co/element-dev-server 0.0.1-test-3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,108 @@
1
+ # Process.co Element Dev Server
2
+
3
+ A CLI tool for developing Process.co elements with an interactive development server.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm install
9
+ pnpm build
10
+ ```
11
+
12
+ ## Usage
13
+
14
+ The CLI accepts an optional path argument to specify which directory to scan for Process.co element modules:
15
+
16
+ ```bash
17
+ # Use current directory
18
+ process-element
19
+
20
+ # Use a specific path
21
+ process-element /path/to/your/elements
22
+
23
+ # Use relative path
24
+ process-element ../my-elements
25
+ ```
26
+
27
+ ## Features
28
+
29
+ - **Interactive UI**: Uses Ink to provide a beautiful terminal interface
30
+ - **Path Validation**: Automatically validates that the provided path exists and is a directory
31
+ - **Element Discovery**: Uses the @process.co/elements library to discover and load element modules
32
+ - **Dev Server Launch**: Launches a Vite development server for the selected element
33
+ - **Smart Directory Detection**: Automatically finds the appropriate dev directory for each element
34
+
35
+ ## How It Works
36
+
37
+ The CLI uses the `@process.co/elements` library to:
38
+
39
+ 1. **Scan for Elements**: Looks for `.mjs` or `.mts` files in the specified directory
40
+ 2. **Load Modules**: Imports each element module using the elements library
41
+ 3. **Display Information**: Shows element names and types in an interactive list
42
+ 4. **Launch Dev Server**: Starts a Vite server in the appropriate directory
43
+
44
+ ## Development
45
+
46
+ ```bash
47
+ # Build the project
48
+ pnpm build
49
+
50
+ # Run in development mode
51
+ pnpm cli
52
+
53
+ # Watch for changes
54
+ pnpm dev
55
+ ```
56
+
57
+ ## Requirements
58
+
59
+ - Node.js 18+
60
+ - A directory containing Process.co element modules (`.mjs` or `.mts` files)
61
+ - Each element should have proper exports with `name` and `type` properties
62
+ - Elements should have a `dev/` directory with a `vite.config.js` file (optional)
63
+
64
+ ## Element Module Format
65
+
66
+ Elements should be exported as modules with the following structure:
67
+
68
+ ```javascript
69
+ // example-element.mjs
70
+ export default {
71
+ name: 'Example Element',
72
+ type: 'app', // or 'action', 'signal', 'credential'
73
+ description: 'An example element',
74
+ // ... other properties
75
+ };
76
+ ```
77
+
78
+ ## Error Handling
79
+
80
+ The CLI provides clear error messages for common issues:
81
+
82
+ - **Path doesn't exist**: Shows the full path that was not found
83
+ - **Path is not a directory**: Validates that the path points to a directory
84
+ - **No elements found**: Provides guidance when no valid element modules are found
85
+ - **Module loading errors**: Shows specific errors when element modules fail to load
86
+
87
+ ## Examples
88
+
89
+ ```bash
90
+ # From a project root with element modules
91
+ process-element
92
+
93
+ # From anywhere, pointing to a specific project
94
+ process-element ~/projects/my-process-app
95
+
96
+ # Using relative paths
97
+ process-element ./elements
98
+ ```
99
+
100
+ ## Element Discovery
101
+
102
+ The CLI will automatically discover elements that:
103
+
104
+ - Are `.mjs` or `.mts` files in the specified directory
105
+ - Have proper exports with `name` and `type` properties
106
+ - Can be successfully imported by the `@process.co/elements` library
107
+
108
+ Elements are displayed with their name and type for easy identification.
@@ -0,0 +1 @@
1
+ import{useState as y,useEffect as F,useRef as ie}from"react";import{Box as m,Text as e}from"ink";import W from"ink-select-input";import E from"fs";import u from"path";import{createServer as se}from"vite";import{fileURLToPath as le}from"url";import v from"@process.co/element-dev-support/dist/index.js";import{Fragment as ce,jsx as o,jsxs as r}from"react/jsx-runtime";var he=({rootDir:g})=>{let[P,V]=y([]),[T,z]=y(null),[k,q]=y([]),[_,Q]=y(null),[Y,Z]=y([]),[ee,b]=y(null),[K,te]=y(null),[x,c]=y("loading"),[R,H]=y("elements"),[C,G]=y([]),w=ie(G);w.current=G;let[U,ne]=y(null),[ue,re]=y(null),oe=async(a,t,i)=>{try{let l=E.statSync(a).isDirectory()?a:u.dirname(a),s=i==="action"?"actions":"sources",n=u.join(l,s);if(!E.existsSync(n))return null;let f=(await v.importFolderModulesOfType(s,i,l,["common"])).find(p=>p.key===t);if(!f)return null;let j=E.readdirSync(n).filter(p=>{let h=u.join(n,p);return E.statSync(h).isDirectory()}),S=null,B=null;for(let p of j){let h=u.join(n,p),J=[`${p}.mjs`,`${p}.mts`,`${p}.js`,`${p}.ts`,"index.mjs","index.mts","index.js","index.ts"];for(let L of J){let d=u.join(h,L);if(E.existsSync(d))try{let{module:N}=await v.importFromPath(d);if(N.key===t){S=d,B=h;break}}catch{continue}}if(S)break}if(!S)return null;let A=u.join(l,"ui");return f&&f.ui&&(A=u.join(l,"ui",f.ui)),{modulePath:S,uiDir:A}}catch{return null}};F(()=>{(async()=>{try{try{let s=await v.autoDetectElement(g);if(s!=="pipedream"){let n=await v.loadElementPointers(g,s);V([{label:`${n.name} (${n.elementType})`,value:n.name,info:n,path:g}]),c("ready");return}}catch{}let t=E.readdirSync(g,{withFileTypes:!0}),i=[];for(let s of t){let n=u.join(g,s.name);if(s.isDirectory())try{E.readdirSync(n).some(f=>f.endsWith(".mjs")||f.endsWith(".mts")||f.endsWith(".js")||f.endsWith(".ts")||f.endsWith(".tsx")||f.endsWith(".jsx"))&&i.push(n)}catch{}}if(i.length===0){c("no-elements");return}let l=[];for(let s of i)try{let n=await v.loadElementPointers(s,"auto");l.push({label:`${n.name} (${n.elementType})`,value:n.name,info:n,path:s})}catch{}if(l.length>0){V(l),c("ready");return}else c("no-elements")}catch{c("error")}})()},[g]),F(()=>{U&&x==="launching"&&c("dev-server-running")},[U,x]),F(()=>{},[x]);let M=async(a,t,i)=>{if(!a)return;let l=await oe(a.path,t.value,t.type);if(!l){c("error");return}let s=E.statSync(a.path).isDirectory()?a.path:u.dirname(a.path),n=u.join(s,"dev"),D=E.existsSync(n)?n:s,f=le(import.meta.url),j=u.dirname(f),S=u.resolve(j,"../.process/vite.config.cjs"),B=u.join(D,"vite.config.js"),A=u.join(D,"vite.config.ts"),p=S;E.existsSync(B)?p=B:E.existsSync(A)&&(p=A);let h=await v.loadElementPointers(a.path,"auto"),J=h.actions?.find(d=>d.key===t.value)||h.signals?.find(d=>d.key===t.value);process.env.VITE_ELEMENT_PATH=a.path,process.env.VITE_ELEMENT_TYPE=t.type,process.env.VITE_ELEMENT_NAME=a.info.name,process.env.VITE_ACTION_SIGNAL_KEY=t.value,process.env.VITE_PROPERTY_KEY=i?.propertyKey||"",process.env.VITE_PROPERTY_TYPE=i?.type||"",process.env.VITE_PROPERTY_UI_PATH=i?.uiPath||"",process.env.VITE_MODULE_PATH=l.modulePath,process.env.VITE_UI_DIRECTORY=l.uiDir;let L=1;try{let d=await se({configFile:p,root:u.resolve(j,"../.process"),optimizeDeps:{include:["react","react-dom"],exclude:[a.path]},define:{"import.meta.env.VITE_ELEMENT_PATH":JSON.stringify(a.path),"import.meta.env.VITE_ELEMENT_TYPE":JSON.stringify(t.type),"import.meta.env.VITE_ELEMENT_NAME":JSON.stringify(a.info.name),"import.meta.env.VITE_ACTION_SIGNAL_KEY":JSON.stringify(t.value),"import.meta.env.VITE_PROPERTY_KEY":JSON.stringify(i?.propertyKey||null),"import.meta.env.VITE_PROPERTY_TYPE":JSON.stringify(i?.type||null),"import.meta.env.VITE_PROPERTY_UI_PATH":JSON.stringify(i?.uiPath||null),"import.meta.env.VITE_MODULE_PATH":JSON.stringify(l.modulePath),"import.meta.env.VITE_UI_DIRECTORY":JSON.stringify(l.uiDir),"import.meta.env.VITE_ELEMENT_MODULE":JSON.stringify(h),"import.meta.env.VITE_CURRENT_ACTION_SIGNAL":JSON.stringify(J),"import.meta.env.VITE_SELECTED_PROPERTY":JSON.stringify(i)},logLevel:"info",customLogger:{info(I){w.current(O=>[...O,I].slice(-L))},warn(I){w.current(O=>[...O,`[warn] ${I}`].slice(-L))},error(I){w.current(O=>[...O,`[error] ${I}`].slice(-L))},clearScreen(){},hasWarned:!1,warnOnce(I){},hasErrorLogged:()=>!1}});await d.listen();let N=`http://localhost:${d.config.server?.port||5173}`;ne(N),re(`Dev server running at ${N}`);let X=()=>{d.close()};process.on("SIGINT",X),process.on("SIGTERM",X),d.watcher.on("change",I=>{}),c("dev-server-running"),process.stdin.resume()}catch{c("error")}},$=async a=>{if(R==="elements"){let t=P.find(i=>i.value===a.value);if(!t)return;z(t),c("loading-actions-signals");try{let i=await v.loadElementPointers(t.path,"auto"),l=[];i.actions&&Array.isArray(i.actions)&&i.actions.forEach((s,n)=>{l.push({label:`Action: ${s.name||s.key}`,value:s.key,type:"action",path:t.path,description:s.description})}),i.signals&&Array.isArray(i.signals)&&i.signals.forEach((s,n)=>{l.push({label:`Signal: ${s.name||s.key}`,value:s.key,type:"signal",path:t.path,description:s.description})}),l.length===0&&l.push({label:"Default Component",value:"default",type:"action",path:t.path,description:"Default component from element"}),q(l),H("actions-signals"),c("ready")}catch{c("error")}}else if(R==="actions-signals"){let t=k.find(i=>i.value===a.value);if(!t||!T)return;Q(t),c("loading-properties");try{let i=await v.loadElementPointers(T.path,"auto"),l=i.actions?.find(n=>n.key===t.value)||i.signals?.find(n=>n.key===t.value);if(l&&l.ui){b(t.value),c("launching"),M(T,t,null).catch(n=>{c("error")});return}if(!l||!l.props){b(t.value),c("launching");try{await M(T,t,null)}catch{c("error")}return}let s=[];if(l.props.forEach(n=>{n.ui&&s.push({label:`\u{1F3A8} ${n.key} (${n.ui})`,value:`ui-${n.key}`,propertyKey:n.key,type:"ui-variant",uiPath:n.ui,description:n.description,propertyData:n})}),s.length===0){b(t.value),c("launching"),await M(T,t,null);return}Z(s),H("properties"),c("ready")}catch{c("error")}}else{let t=Y.find(i=>i.value===a.value);if(!t||!_||!T)return;te(t),b(t.value),c("launching"),await M(T,_,t)}};return x==="loading"?r(m,{flexDirection:"column",marginTop:2,children:[r(e,{children:["\u{1F50D} Loading elements from: ",o(e,{color:"cyan",children:g})]}),o(e,{children:"Loading..."})]}):x==="no-elements"?r(m,{flexDirection:"column",marginTop:2,children:[r(e,{color:"red",children:["\u274C No valid elements found in: ",o(e,{color:"cyan",children:g})]}),o(e,{color:"yellow",children:"Make sure the directory contains valid element modules."}),o(e,{color:"yellow",children:"Supported formats: Pipedream, n8n, Doflo, Process.co"}),o(e,{color:"yellow",children:"Supported file types: .js, .ts, .mjs, .mts, .jsx, .tsx"})]}):x==="error"?r(m,{flexDirection:"column",marginTop:2,children:[r(e,{color:"red",children:["\u274C Error loading elements from: ",o(e,{color:"cyan",children:g})]}),o(e,{color:"yellow",children:"Check that the directory contains valid element modules."})]}):x==="loading-actions-signals"?r(m,{flexDirection:"column",marginTop:2,children:[r(e,{children:["\u{1F50D} Loading actions and signals for '",o(e,{color:"cyan",children:T?.info.name}),"'..."]}),o(e,{children:"Loading..."})]}):x==="loading-properties"?r(m,{flexDirection:"column",marginTop:2,children:[r(e,{children:["\u{1F50D} Loading properties for '",o(e,{color:"cyan",children:_?.label}),"'..."]}),o(e,{children:"Loading..."})]}):x==="launching"?r(m,{flexDirection:"column",marginTop:2,children:[r(e,{color:"green",children:["\u{1F680} Launching dev server for '",o(e,{color:"cyan",children:ee}),"'..."]}),r(e,{children:["Directory: ",o(e,{color:"cyan",children:g})]})]}):x==="dev-server-running"?r(ce,{children:[r(m,{flexDirection:"column",flexShrink:1,borderStyle:"round",margin:2,marginBottom:1,paddingX:2,paddingY:1,borderColor:"#8759F2",children:[r(e,{children:["Server Running: ",o(e,{color:"#01D4E7",underline:!0,children:U})]}),r(e,{children:["Element: ",o(e,{color:"#01D4E7",children:T?.info.name})]}),r(e,{children:["Action/Signal: ",o(e,{color:"#01D4E7",children:_?.label})]}),K?r(e,{children:["Property UI: ",o(e,{color:"#01D4E7",children:K.label})]}):r(e,{children:["UI Mode: ",o(e,{color:"#01D4E7",children:"Action/Signal Level"})]}),o(e,{color:"yellow",children:"Press Ctrl+C to stop the server"})]}),r(m,{marginTop:1,marginLeft:4,flexDirection:"column",children:[o(e,{color:"gray",children:"Server Status:"}),C.length>0?C.map((a,t)=>o(e,{color:"gray",children:a},t)):o(e,{color:"gray",children:"Waiting for activity..."})]})]}):R==="properties"?r(m,{flexDirection:"column",children:[r(e,{children:["\u{1F4C1} Element: ",o(e,{color:"cyan",children:T?.info.name})]}),r(e,{children:["\u{1F4CB} Action/Signal: ",o(e,{color:"#01D4E7",children:_?.label})]}),r(e,{children:["Found ",Y.length," property(ies):"]}),o(W,{items:Y,onSelect:$})]}):R==="actions-signals"?r(m,{flexDirection:"column",marginTop:2,marginBottom:2,children:[r(e,{children:["\u{1F4C1} Element: ",o(e,{color:"#01D4E7",children:T?.info.name})]}),r(m,{flexDirection:"column",marginTop:1,children:[r(e,{children:["Found ",k.length," action(s)/signal(s):"]}),o(W,{items:k,onSelect:$})]})]}):r(m,{flexDirection:"column",marginTop:2,marginBottom:2,children:[r(m,{flexDirection:"column",children:[r(e,{children:["\u{1F4C1} Directory: ",o(e,{color:"#01D4E7",children:g})]}),r(m,{flexDirection:"column",marginTop:1,children:[r(e,{children:["Found ",P.length," element(s):"]}),o(W,{items:P,onSelect:$})]})]}),o(ae,{logs:C})]})},ae=({logs:g})=>g.length?r(m,{flexDirection:"column",marginTop:1,children:[o(e,{color:"gray",children:"Output:"}),g.map((P,V)=>o(e,{color:"gray",children:P},V))]}):null;export{he as a};
@@ -0,0 +1 @@
1
+ import{Box as t,Text as o}from"ink";import r from"path";import{fileURLToPath as f}from"url";import{readFileSync as n}from"fs";import{Fragment as h,jsx as _,jsxs as e}from"react/jsx-runtime";var x=f(import.meta.url),a=r.dirname(x),c=r.resolve(r.join(a,"../package.json")),i=JSON.parse(n(c,"utf8"));function d(l){return new Array(42-l.length).fill(" ").join("")}function B(){return _(h,{children:e(t,{flexDirection:"column",display:"flex",alignSelf:"flex-start",marginLeft:3,marginTop:1,marginBottom:1,children:[e(t,{flexGrow:1,children:[_(o,{color:"whiteBright",bold:!0,children:" _ __ _ __ ___ ___ ___ ___ ___"}),_(o,{color:"#dffff",children:" ___ ___ "})]}),e(t,{flexGrow:1,children:[_(o,{color:"whiteBright",bold:!0,children:" | '_ \\| '__/ _ \\ / __/ _ \\/ __/ __|"}),_(o,{color:"#dffff",children:" / __/ _ \\"})]}),e(t,{flexGrow:1,children:[_(o,{color:"whiteBright",bold:!0,children:" | |_) | | | (_) | (_| __/\\__ \\__ \\"}),_(o,{color:"#dffff",children:"| (_| (_) | "})]}),e(t,{flexGrow:1,children:[_(o,{color:"whiteBright",bold:!0,children:" | .__/|_| \\___/ \\___\\___||___/___"}),_(o,{color:"#FF3C82",children:"(_)"}),_(o,{color:"#dffff",children:"___\\___/"})]}),e(t,{flexGrow:1,children:[e(o,{color:"whiteBright",bold:!0,children:[" |_|",d(i.version)]}),e(o,{color:"whiteBright",bold:!0,children:["v",i.version]})]}),_(t,{flexGrow:1,paddingTop:1,children:_(o,{color:"whiteBright",bold:!0,children:"a CLI to aid in the building of process.co elements"})})]})})}export{d as a,B as b};
package/dist/cli.js ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+ import{b as t}from"./chunk-QPYNK3X7.js";import{a as r}from"./chunk-NT4V5NUJ.js";import{render as p}from"ink";import c from"meow";import m from"path";import i from"fs";import{Fragment as f,jsx as o,jsxs as d}from"react/jsx-runtime";process.stdout.write("\x1Bc");var s=c(`
3
+ Usage
4
+ $ process-element [path]
5
+
6
+ Options
7
+ --help Show help
8
+ --version Show version
9
+
10
+ Examples
11
+ $ process-element # Use current directory
12
+ $ process-element /path/to/elements # Use specified path
13
+ $ process-element ./elements # Use relative path
14
+
15
+ Description
16
+ Scans the specified directory for Process.co element modules (.mjs/.mts files)
17
+ and provides an interactive interface to launch development servers.
18
+ `,{importMeta:import.meta}),e=s.input[0]?m.resolve(s.input[0]):process.cwd();i.existsSync(e)||(console.error(`\u274C Error: Path '${e}' does not exist.`),process.exit(1));var a=i.statSync(e);a.isDirectory()||(console.error(`\u274C Error: '${e}' is not a directory.`),process.exit(1));var l=({rootDir:n})=>d(f,{children:[o(t,{}),o(r,{rootDir:n})]});p(o(l,{rootDir:e}));
package/dist/logo.js ADDED
@@ -0,0 +1 @@
1
+ import{a,b}from"./chunk-QPYNK3X7.js";export{b as default,a as lengthAsSpace};
package/dist/ui.js ADDED
@@ -0,0 +1 @@
1
+ import{a}from"./chunk-NT4V5NUJ.js";export{a as DevUI};
@@ -0,0 +1 @@
1
+ var o="0.0.0-development",v={version:o};export{v as default,o as version};
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@process.co/element-dev-server",
3
+ "version": "0.0.1-test-3",
4
+ "description": "Helper Library for developing elements for Process.co",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": "./dist/index.js"
10
+ },
11
+ "bin": {
12
+ "proc-dev": "tsx ./dist/cli.js"
13
+ },
14
+ "files": [
15
+ "dist/*",
16
+ "src/*",
17
+ "bundle/*"
18
+ ],
19
+ "publishConfig": {
20
+ "access": "public",
21
+ "registry": "https://registry.npmjs.org",
22
+ "scope": "@process.co"
23
+ },
24
+ "release": {
25
+ "branches": [
26
+ "main"
27
+ ]
28
+ },
29
+ "scripts": {
30
+ "build": "rm -rf ./dist && echo '{\"version\":'$(jq '.version' ./package.json)'}' > ./src/version.json && tsup && chmod +x ./dist/cli.js",
31
+ "dev": "tsx src/cli.tsx",
32
+ "cli": "tsx dist/cli.js",
33
+ "test": "jest"
34
+ },
35
+ "keywords": [
36
+ "process",
37
+ "element",
38
+ "cli",
39
+ "validation",
40
+ "compatibility"
41
+ ],
42
+ "author": {
43
+ "name": "Process.co Team",
44
+ "email": "developers@process.co"
45
+ },
46
+ "license": "ISC",
47
+ "dependencies": {
48
+ "@process.co/element-dev-support": "github:process-co/npm-element-dev-support#main",
49
+ "@vitejs/plugin-react": "^4.3.4",
50
+ "axios": "^1.8.4",
51
+ "ink": "^6.0.1",
52
+ "ink-select-input": "^6.2.0",
53
+ "io-ts": "^2.2.22",
54
+ "meow": "^13.2.0",
55
+ "querystring": "^0.2.1",
56
+ "react": "^19.1.0",
57
+ "react-dom": "^19.1.0",
58
+ "tsup": "^8.5.0",
59
+ "tsx": "^4.19.1",
60
+ "type-fest": "^4.21.0",
61
+ "vite": "^7.0.2"
62
+ },
63
+ "devDependencies": {
64
+ "@babel/core": "^7.26.9",
65
+ "@babel/preset-env": "^7.26.9",
66
+ "@babel/preset-typescript": "^7.26.0",
67
+ "@repo/config-jest": "workspace:^",
68
+ "@repo/config-typescript": "workspace:^",
69
+ "@types/jest": "^29.5.14",
70
+ "@types/node": "22.13.17",
71
+ "@types/react": "19.1.0",
72
+ "@vercel/ncc": "^0.38.1",
73
+ "babel-jest": "^29.7.0",
74
+ "jest": "^29.7.0",
75
+ "ts-jest": "^29.2.6",
76
+ "typescript": "^5.7.3"
77
+ },
78
+ "registry": "https://registry.npmjs.org"
79
+ }
package/src/cli.tsx ADDED
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env node
2
+
3
+ process.stdout.write('\x1Bc'); // Clear the terminal
4
+
5
+ import { render } from 'ink';
6
+ import meow from 'meow';
7
+ import React from 'react';
8
+ import path from 'path';
9
+ import fs from 'fs';
10
+ import { DevUI } from './ui.js';
11
+ import Logo from './logo.js';
12
+
13
+ const cli = meow(`
14
+ Usage
15
+ $ process-element [path]
16
+
17
+ Options
18
+ --help Show help
19
+ --version Show version
20
+
21
+ Examples
22
+ $ process-element # Use current directory
23
+ $ process-element /path/to/elements # Use specified path
24
+ $ process-element ./elements # Use relative path
25
+
26
+ Description
27
+ Scans the specified directory for Process.co element modules (.mjs/.mts files)
28
+ and provides an interactive interface to launch development servers.
29
+ `, {
30
+ importMeta: import.meta,
31
+ });
32
+
33
+ // Get the path from arguments or use current directory
34
+ const targetPath = cli.input[0] ? path.resolve(cli.input[0]) : process.cwd();
35
+
36
+ // Validate that the path exists
37
+ if (!fs.existsSync(targetPath)) {
38
+ console.error(`❌ Error: Path '${targetPath}' does not exist.`);
39
+ process.exit(1);
40
+ }
41
+
42
+ // Check if it's a directory
43
+ const stats = fs.statSync(targetPath);
44
+ if (!stats.isDirectory()) {
45
+ console.error(`❌ Error: '${targetPath}' is not a directory.`);
46
+ process.exit(1);
47
+ }
48
+
49
+ const App = ({ rootDir }: { rootDir: string }) => (
50
+ <>
51
+ <Logo />
52
+ <DevUI rootDir={rootDir} />
53
+ </>
54
+ );
55
+
56
+ render(<App rootDir={targetPath} />);
package/src/logo.tsx ADDED
@@ -0,0 +1,31 @@
1
+ import React from "react";
2
+ import { Box, Text } from "ink";
3
+ import path from "path";
4
+ import { fileURLToPath } from 'url';
5
+ import { readFileSync } from 'fs';
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
9
+ const packageJsonPath = path.resolve(path.join(__dirname, "../package.json"));
10
+ const pack = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as { version: string };
11
+
12
+
13
+ export function lengthAsSpace(version:string){
14
+ return new Array(42 - version.length).fill(" ").join("")
15
+ }
16
+
17
+ export default function (){
18
+
19
+ // const version = pack.version
20
+
21
+ return (<>
22
+ <Box flexDirection="column" display="flex" alignSelf="flex-start" marginLeft={3} marginTop={1} marginBottom={1} >
23
+ <Box flexGrow={1} ><Text color="whiteBright" bold={true} > _ __ _ __ ___ ___ ___ ___ ___</Text><Text color="#dffff"> ___ ___ </Text></Box>
24
+ <Box flexGrow={1}><Text color="whiteBright" bold={true} > | '_ \| '__/ _ \ / __/ _ \/ __/ __|</Text><Text color="#dffff"> / __/ _ \</Text></Box>
25
+ <Box flexGrow={1}><Text color="whiteBright" bold={true}> | |_) | | | (_) | (_| __/\__ \__ \</Text><Text color="#dffff">| (_| (_) | </Text></Box>
26
+ <Box flexGrow={1}><Text color="whiteBright" bold={true}> | .__/|_| \___/ \___\___||___/___</Text><Text color="#FF3C82">(_)</Text><Text color="#dffff">___\___/</Text></Box>
27
+ <Box flexGrow={1}><Text color="whiteBright" bold={true}> |_|{lengthAsSpace(pack.version)}</Text><Text color="whiteBright" bold={true} >v{pack.version}</Text></Box>
28
+ <Box flexGrow={1} paddingTop={1} ><Text color="whiteBright" bold={true} >a CLI to aid in the building of process.co elements</Text></Box>
29
+ </Box>
30
+ </>)
31
+ }
package/src/ui.tsx ADDED
@@ -0,0 +1,651 @@
1
+ // src/DevUI.js
2
+ import React, { useState, useEffect, useRef } from 'react';
3
+ import { Box, Text } from 'ink';
4
+ import SelectInput from 'ink-select-input';
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import { createServer } from 'vite';
8
+ import { fileURLToPath } from 'url';
9
+ import dev_support from '@process.co/element-dev-support/dist/index.js';
10
+
11
+ interface ElementItem {
12
+ label: string;
13
+ value: string;
14
+ info: dev_support.IProcessDefinitionUIInfo;
15
+ path: string;
16
+ }
17
+
18
+ interface ActionSignalItem {
19
+ label: string;
20
+ value: string;
21
+ type: 'action' | 'signal';
22
+ path: string;
23
+ description?: string;
24
+ }
25
+
26
+ interface PropertyItem {
27
+ label: string;
28
+ value: string;
29
+ propertyKey: string;
30
+ type: 'standard' | 'ui-variant';
31
+ uiPath?: string;
32
+ description?: string;
33
+ propertyData?: any;
34
+ }
35
+
36
+ export const DevUI = ({ rootDir }: { rootDir: string }) => {
37
+ const [elements, setElements] = useState<ElementItem[]>([]);
38
+ const [selectedElement, setSelectedElement] = useState<ElementItem | null>(null);
39
+ const [actionsSignals, setActionsSignals] = useState<ActionSignalItem[]>([]);
40
+ const [selectedActionSignal, setSelectedActionSignal] = useState<ActionSignalItem | null>(null);
41
+ const [properties, setProperties] = useState<PropertyItem[]>([]);
42
+ const [selected, setSelected] = useState<string | null>(null);
43
+ const [selectedProperty, setSelectedProperty] = useState<PropertyItem | null>(null);
44
+ const [status, setStatus] = useState<'loading' | 'ready' | 'no-elements' | 'error' | 'loading-actions-signals' | 'loading-properties' | 'launching' | 'dev-server-running'>('loading');
45
+ const [step, setStep] = useState<'elements' | 'actions-signals' | 'properties'>('elements');
46
+ const [viteLogs, setViteLogs] = useState<string[]>([]);
47
+ const viteLogsRef = useRef(setViteLogs);
48
+ viteLogsRef.current = setViteLogs;
49
+ const [viteUrl, setViteUrl] = useState<string | null>(null);
50
+ // Add a serverMessage state for display messages
51
+ const [serverMessage, setServerMessage] = useState<string | null>(null);
52
+
53
+ // Function to get the actual module path for an action/signal
54
+ const getModulePath = async (elementPath: string, actionSignalKey: string, type: 'action' | 'signal'): Promise<{ modulePath: string; uiDir: string } | null> => {
55
+ try {
56
+ // Get the base directory of the element
57
+ const elementDir = fs.statSync(elementPath).isDirectory()
58
+ ? elementPath
59
+ : path.dirname(elementPath);
60
+
61
+ // Determine the folder to look in based on type
62
+ const folderName = type === 'action' ? 'actions' : 'sources';
63
+ const folderPath = path.join(elementDir, folderName);
64
+
65
+ // Check if the folder exists
66
+ if (!fs.existsSync(folderPath)) {
67
+ return null;
68
+ }
69
+
70
+ // Get all modules of the specific type to find the one with matching key
71
+ const modules = await dev_support.importFolderModulesOfType(folderName, type, elementDir, ['common']);
72
+
73
+ const targetModule = modules.find(module => module.key === actionSignalKey);
74
+
75
+ if (!targetModule) {
76
+ return null;
77
+ }
78
+
79
+ // The importFolderModulesOfType function already found the module, so we need to get its path
80
+ // We can do this by looking at the module's structure or by scanning the folder
81
+ const subfolders = fs.readdirSync(folderPath).filter(item => {
82
+ const itemPath = path.join(folderPath, item);
83
+ return fs.statSync(itemPath).isDirectory();
84
+ });
85
+
86
+ let modulePath = null;
87
+ let moduleDir = null;
88
+
89
+ // Look through each subfolder to find the one that contains our module
90
+ for (const subfolder of subfolders) {
91
+ const subfolderPath = path.join(folderPath, subfolder);
92
+
93
+ // Look for the module file in this subfolder
94
+ const possibleFiles = [
95
+ `${subfolder}.mjs`,
96
+ `${subfolder}.mts`,
97
+ `${subfolder}.js`,
98
+ `${subfolder}.ts`,
99
+ 'index.mjs',
100
+ 'index.mts',
101
+ 'index.js',
102
+ 'index.ts'
103
+ ];
104
+
105
+ for (const file of possibleFiles) {
106
+ const filePath = path.join(subfolderPath, file);
107
+ if (fs.existsSync(filePath)) {
108
+ // Import this file to check if it's the right module
109
+ try {
110
+ const { module } = await dev_support.importFromPath(filePath);
111
+
112
+ // Check if this module has the right key
113
+ if (module.key === actionSignalKey) {
114
+ modulePath = filePath;
115
+ moduleDir = subfolderPath;
116
+ break;
117
+ }
118
+ } catch (importError) {
119
+ // Continue to next file if import fails
120
+ continue;
121
+ }
122
+ }
123
+ }
124
+
125
+ if (modulePath) break;
126
+ }
127
+
128
+ if (!modulePath) {
129
+ return null;
130
+ }
131
+
132
+ // Determine the UI directory
133
+ let uiDir = path.join(elementDir, 'ui');
134
+ if (targetModule && targetModule.ui) {
135
+ uiDir = path.join(elementDir, 'ui', targetModule.ui);
136
+ }
137
+
138
+ return { modulePath, uiDir };
139
+ } catch (error) {
140
+ return null;
141
+ }
142
+ };
143
+
144
+ useEffect(() => {
145
+ const loadElements = async () => {
146
+ try {
147
+ // First, check if the target directory itself is a valid element
148
+ try {
149
+ const elementType = await dev_support.autoDetectElement(rootDir);
150
+ if (elementType !== 'pipedream') { // If it's a valid element type
151
+ const elementInfo = await dev_support.loadElementPointers(rootDir, elementType);
152
+ setElements([{
153
+ label: `${elementInfo.name} (${elementInfo.elementType})`,
154
+ value: elementInfo.name,
155
+ info: elementInfo,
156
+ path: rootDir
157
+ }]);
158
+ setStatus('ready');
159
+ return;
160
+ }
161
+ } catch (error) {
162
+ // Target directory is not a valid element, continue to scan subdirectories
163
+ }
164
+
165
+ // Scan for potential element directories/files
166
+ const entries = fs.readdirSync(rootDir, { withFileTypes: true });
167
+ const potentialElements: string[] = [];
168
+
169
+ // Look for directories only (elements are directories, not individual files)
170
+ for (const entry of entries) {
171
+ const entryPath = path.join(rootDir, entry.name);
172
+
173
+ if (entry.isDirectory()) {
174
+ // Check if directory contains element files
175
+ try {
176
+ const subEntries = fs.readdirSync(entryPath);
177
+ if (subEntries.some(file =>
178
+ file.endsWith('.mjs') ||
179
+ file.endsWith('.mts') ||
180
+ file.endsWith('.js') ||
181
+ file.endsWith('.ts') ||
182
+ file.endsWith('.tsx') ||
183
+ file.endsWith('.jsx')
184
+ )) {
185
+ potentialElements.push(entryPath);
186
+ }
187
+ } catch (error) {
188
+ // Skip directories we can't read
189
+ }
190
+ }
191
+ // Skip individual files - elements are always directories
192
+ }
193
+
194
+ if (potentialElements.length === 0) {
195
+ setStatus('no-elements');
196
+ return;
197
+ }
198
+
199
+ // Try to load each potential element
200
+ const loadedElements: ElementItem[] = [];
201
+
202
+ for (const elementPath of potentialElements) {
203
+ try {
204
+ const elementInfo = await dev_support.loadElementPointers(elementPath, 'auto');
205
+ loadedElements.push({
206
+ label: `${elementInfo.name} (${elementInfo.elementType})`,
207
+ value: elementInfo.name,
208
+ info: elementInfo,
209
+ path: elementPath
210
+ });
211
+ } catch (error) {
212
+ // Skip elements that can't be loaded
213
+ }
214
+ }
215
+
216
+ if (loadedElements.length > 0) {
217
+ setElements(loadedElements);
218
+ setStatus('ready');
219
+ return;
220
+ } else {
221
+ setStatus('no-elements');
222
+ }
223
+ } catch (error) {
224
+ setStatus('error');
225
+ }
226
+ };
227
+
228
+ loadElements();
229
+ }, [rootDir]);
230
+
231
+ // React to viteUrl changes to show the banner
232
+ useEffect(() => {
233
+ if (viteUrl && status === 'launching') {
234
+ setStatus('dev-server-running');
235
+ }
236
+ }, [viteUrl, status]);
237
+
238
+ // Debug: log all status changes
239
+ useEffect(() => {
240
+ // console.log(`Status changed to: ${status}`);
241
+ }, [status]);
242
+
243
+ const launchDevServer = async (element: ElementItem, actionSignal: ActionSignalItem, property: PropertyItem | null) => {
244
+ if (!element) return;
245
+
246
+ // Get the actual module path and UI directory for the action/signal
247
+ const moduleInfo = await getModulePath(element.path, actionSignal.value, actionSignal.type);
248
+
249
+ if (!moduleInfo) {
250
+ setStatus('error');
251
+ return;
252
+ }
253
+
254
+ // Try to find a dev directory or use the element's directory
255
+ const elementDir = fs.statSync(element.path).isDirectory()
256
+ ? element.path
257
+ : path.dirname(element.path);
258
+
259
+ const devDir = path.join(elementDir, 'dev');
260
+
261
+ // Check if dev directory exists, otherwise use the element directory
262
+ const workingDir = fs.existsSync(devDir) ? devDir : elementDir;
263
+
264
+ // Use the internal Vite config from .process directory
265
+ const __filename = fileURLToPath(import.meta.url);
266
+ const __dirname = path.dirname(__filename);
267
+ const internalViteConfig = path.resolve(__dirname, '../.process/vite.config.cjs');
268
+
269
+ // Check if user has their own vite config, otherwise use internal one
270
+ const userViteConfig = path.join(workingDir, 'vite.config.js');
271
+ const userViteConfigTs = path.join(workingDir, 'vite.config.ts');
272
+
273
+ let configToUse = internalViteConfig;
274
+ if (fs.existsSync(userViteConfig)) {
275
+ configToUse = userViteConfig;
276
+ } else if (fs.existsSync(userViteConfigTs)) {
277
+ configToUse = userViteConfigTs;
278
+ }
279
+
280
+ // Load the complete element data from compatibility module
281
+ const elementModule = await dev_support.loadElementPointers(element.path, 'auto');
282
+ const currentActionSignal = (elementModule.actions as any[])?.find(action => action.key === actionSignal.value) ||
283
+ (elementModule.signals as any[])?.find(signal => signal.key === actionSignal.value);
284
+
285
+ // Set process.env variables for Vite config
286
+ process.env.VITE_ELEMENT_PATH = element.path;
287
+ process.env.VITE_ELEMENT_TYPE = actionSignal.type;
288
+ process.env.VITE_ELEMENT_NAME = element.info.name;
289
+ process.env.VITE_ACTION_SIGNAL_KEY = actionSignal.value;
290
+ process.env.VITE_PROPERTY_KEY = property?.propertyKey || '';
291
+ process.env.VITE_PROPERTY_TYPE = property?.type || '';
292
+ process.env.VITE_PROPERTY_UI_PATH = property?.uiPath || '';
293
+ process.env.VITE_MODULE_PATH = moduleInfo.modulePath;
294
+ process.env.VITE_UI_DIRECTORY = moduleInfo.uiDir;
295
+
296
+
297
+ const viteLogLength = 1;
298
+ // Create Vite server programmatically
299
+ try {
300
+ const vite = await createServer({
301
+ configFile: configToUse,
302
+ root: path.resolve(__dirname, '../.process'), // Use .process directory as root
303
+ optimizeDeps: {
304
+ include: ['react', 'react-dom'],
305
+ exclude: [element.path] // Exclude the external element from optimization
306
+ },
307
+ define: {
308
+ // Pass complete element data directly to Vite
309
+ 'import.meta.env.VITE_ELEMENT_PATH': JSON.stringify(element.path),
310
+ 'import.meta.env.VITE_ELEMENT_TYPE': JSON.stringify(actionSignal.type),
311
+ 'import.meta.env.VITE_ELEMENT_NAME': JSON.stringify(element.info.name),
312
+ 'import.meta.env.VITE_ACTION_SIGNAL_KEY': JSON.stringify(actionSignal.value),
313
+ 'import.meta.env.VITE_PROPERTY_KEY': JSON.stringify(property?.propertyKey || null),
314
+ 'import.meta.env.VITE_PROPERTY_TYPE': JSON.stringify(property?.type || null),
315
+ 'import.meta.env.VITE_PROPERTY_UI_PATH': JSON.stringify(property?.uiPath || null),
316
+ // Add the actual module paths
317
+ 'import.meta.env.VITE_MODULE_PATH': JSON.stringify(moduleInfo.modulePath),
318
+ 'import.meta.env.VITE_UI_DIRECTORY': JSON.stringify(moduleInfo.uiDir),
319
+ // Pass the complete element data from compatibility module
320
+ 'import.meta.env.VITE_ELEMENT_MODULE': JSON.stringify(elementModule),
321
+ 'import.meta.env.VITE_CURRENT_ACTION_SIGNAL': JSON.stringify(currentActionSignal),
322
+ 'import.meta.env.VITE_SELECTED_PROPERTY': JSON.stringify(property)
323
+ },
324
+ logLevel: 'info',
325
+ customLogger: {
326
+ info(msg) { viteLogsRef.current(logs => [...logs, msg].slice(-viteLogLength)); },
327
+ warn(msg) { viteLogsRef.current(logs => [...logs, `[warn] ${msg}`].slice(-viteLogLength)); },
328
+ error(msg) { viteLogsRef.current(logs => [...logs, `[error] ${msg}`].slice(-viteLogLength)); },
329
+ clearScreen() { },
330
+ hasWarned: false,
331
+ warnOnce(msg) { },
332
+ hasErrorLogged: () => false,
333
+ }
334
+ });
335
+
336
+ await vite.listen();
337
+ const serverUrl = `http://localhost:${vite.config.server?.port || 5173}`;
338
+ setViteUrl(serverUrl); // <-- set the state
339
+ setServerMessage(`Dev server running at ${serverUrl}`);
340
+
341
+ // Keep the server running and handle cleanup
342
+ const cleanup = () => {
343
+ vite.close();
344
+ };
345
+
346
+ // Handle process termination
347
+ process.on('SIGINT', cleanup);
348
+ process.on('SIGTERM', cleanup);
349
+
350
+ // Handle file changes
351
+ vite.watcher.on('change', (file: string) => {
352
+ // console.log(`File changed: ${file}`);
353
+ });
354
+
355
+ // Keep the process alive
356
+ setStatus('dev-server-running');
357
+
358
+ // Don't exit - keep the server running
359
+ process.stdin.resume();
360
+
361
+ } catch (error: any) {
362
+ setStatus('error');
363
+ }
364
+ };
365
+
366
+ const handleSelect = async (item: { value: string }) => {
367
+ if (step === 'elements') {
368
+ // First step: select an element
369
+ const element = elements.find(el => el.value === item.value);
370
+ if (!element) return;
371
+
372
+ setSelectedElement(element);
373
+ setStatus('loading-actions-signals');
374
+
375
+ try {
376
+ // Load the element to get its actions and signals
377
+ const elementModule = await dev_support.loadElementPointers(element.path, 'auto');
378
+
379
+ const items: ActionSignalItem[] = [];
380
+
381
+ // Add actions
382
+ if (elementModule.actions && Array.isArray(elementModule.actions)) {
383
+ elementModule.actions.forEach((action, index) => {
384
+ items.push({
385
+ label: `Action: ${action.name || action.key}`,
386
+ value: action.key,
387
+ type: 'action',
388
+ path: element.path,
389
+ description: action.description
390
+ });
391
+ });
392
+ }
393
+
394
+ // Add signals
395
+ if (elementModule.signals && Array.isArray(elementModule.signals)) {
396
+ elementModule.signals.forEach((signal, index) => {
397
+ items.push({
398
+ label: `Signal: ${signal.name || signal.key}`,
399
+ value: signal.key,
400
+ type: 'signal',
401
+ path: element.path,
402
+ description: signal.description
403
+ });
404
+ });
405
+ }
406
+
407
+ if (items.length === 0) {
408
+ // If no actions/signals found, create a default item
409
+ items.push({
410
+ label: 'Default Component',
411
+ value: 'default',
412
+ type: 'action',
413
+ path: element.path,
414
+ description: 'Default component from element'
415
+ });
416
+ }
417
+
418
+ setActionsSignals(items);
419
+ setStep('actions-signals');
420
+ setStatus('ready');
421
+ } catch (error) {
422
+ setStatus('error');
423
+ }
424
+ } else if (step === 'actions-signals') {
425
+ // Second step: select an action/signal
426
+ const selectedActionSignal = actionsSignals.find(i => i.value === item.value);
427
+ if (!selectedActionSignal || !selectedElement) return;
428
+
429
+ setSelectedActionSignal(selectedActionSignal);
430
+ setStatus('loading-properties');
431
+
432
+ try {
433
+ // Load the element to get its properties
434
+ const elementModule = await dev_support.loadElementPointers(selectedElement.path, 'auto');
435
+
436
+ const currentActionSignal = (elementModule.actions as any[])?.find(action => action.key === selectedActionSignal.value) ||
437
+ (elementModule.signals as any[])?.find(signal => signal.key === selectedActionSignal.value);
438
+
439
+ // Check if the action/signal has its own UI
440
+ if (currentActionSignal && currentActionSignal.ui) {
441
+ setSelected(selectedActionSignal.value);
442
+ setStatus('launching');
443
+ // Launch server without awaiting - let it run in background
444
+ launchDevServer(selectedElement, selectedActionSignal, null).catch(error => {
445
+ setStatus('error');
446
+ });
447
+ return;
448
+ }
449
+
450
+ // No UI found, check for properties with UI
451
+ if (!currentActionSignal || !currentActionSignal.props) {
452
+ setSelected(selectedActionSignal.value);
453
+ setStatus('launching');
454
+ try {
455
+ await launchDevServer(selectedElement, selectedActionSignal, null);
456
+ } catch (error) {
457
+ setStatus('error');
458
+ }
459
+ return;
460
+ }
461
+
462
+ const propertyItems: PropertyItem[] = [];
463
+
464
+ // Add properties that have UI components
465
+ // props is an array, not an object
466
+ currentActionSignal.props.forEach((prop: any) => {
467
+ if (prop.ui) {
468
+ // Property has a UI variant
469
+ propertyItems.push({
470
+ label: `🎨 ${prop.key} (${prop.ui})`,
471
+ value: `ui-${prop.key}`,
472
+ propertyKey: prop.key,
473
+ type: 'ui-variant',
474
+ uiPath: prop.ui,
475
+ description: prop.description,
476
+ propertyData: prop
477
+ });
478
+ }
479
+ });
480
+
481
+ if (propertyItems.length === 0) {
482
+ setSelected(selectedActionSignal.value);
483
+ setStatus('launching');
484
+ await launchDevServer(selectedElement, selectedActionSignal, null);
485
+ return;
486
+ }
487
+
488
+ setProperties(propertyItems);
489
+ setStep('properties');
490
+ setStatus('ready');
491
+ } catch (error) {
492
+ setStatus('error');
493
+ }
494
+ } else {
495
+ // Third step: select a property (or launch directly)
496
+ const selectedProperty = properties.find(i => i.value === item.value);
497
+ if (!selectedProperty || !selectedActionSignal || !selectedElement) return;
498
+
499
+ setSelectedProperty(selectedProperty);
500
+ setSelected(selectedProperty.value);
501
+ setStatus('launching');
502
+ await launchDevServer(selectedElement, selectedActionSignal, selectedProperty);
503
+ }
504
+ };
505
+
506
+ if (status === 'loading') {
507
+ return (
508
+ <Box flexDirection="column" marginTop={2}>
509
+ <Text>🔍 Loading elements from: <Text color="cyan">{rootDir}</Text></Text>
510
+ <Text>Loading...</Text>
511
+ </Box>
512
+ );
513
+ }
514
+
515
+ if (status === 'no-elements') {
516
+ return (
517
+ <Box flexDirection="column" marginTop={2}>
518
+ <Text color="red">❌ No valid elements found in: <Text color="cyan">{rootDir}</Text></Text>
519
+ <Text color="yellow">Make sure the directory contains valid element modules.</Text>
520
+ <Text color="yellow">Supported formats: Pipedream, n8n, Doflo, Process.co</Text>
521
+ <Text color="yellow">Supported file types: .js, .ts, .mjs, .mts, .jsx, .tsx</Text>
522
+ </Box>
523
+ );
524
+ }
525
+
526
+ if (status === 'error') {
527
+ return (
528
+ <Box flexDirection="column" marginTop={2}>
529
+ <Text color="red">❌ Error loading elements from: <Text color="cyan">{rootDir}</Text></Text>
530
+ <Text color="yellow">Check that the directory contains valid element modules.</Text>
531
+ </Box>
532
+ );
533
+ }
534
+
535
+ if (status === 'loading-actions-signals') {
536
+ return (
537
+ <Box flexDirection="column" marginTop={2}>
538
+ <Text>🔍 Loading actions and signals for '<Text color="cyan">{selectedElement?.info.name}</Text>'...</Text>
539
+ <Text>Loading...</Text>
540
+ </Box>
541
+ );
542
+ }
543
+
544
+ if (status === 'loading-properties') {
545
+ return (
546
+ <Box flexDirection="column" marginTop={2}>
547
+ <Text>🔍 Loading properties for '<Text color="cyan">{selectedActionSignal?.label}</Text>'...</Text>
548
+ <Text>Loading...</Text>
549
+ </Box>
550
+ );
551
+ }
552
+
553
+ if (status === 'launching') {
554
+ return (
555
+ <Box flexDirection="column" marginTop={2}>
556
+ <Text color="green">🚀 Launching dev server for '<Text color="cyan">{selected}</Text>'...</Text>
557
+ <Text>Directory: <Text color="cyan">{rootDir}</Text></Text>
558
+ </Box>
559
+ );
560
+ }
561
+
562
+ if (status === 'dev-server-running') {
563
+ return (
564
+ <>
565
+ <Box flexDirection="column" flexShrink={1} borderStyle={"round"} margin={2} marginBottom={1} paddingX={2} paddingY={1} borderColor="#8759F2" >
566
+ {/* <ViteBanner url={viteUrl} /> */}
567
+ {/* <Text color="green">✅ Dev server is running!</Text> */}
568
+ {/* {serverMessage && <Text>{serverMessage}</Text>} */}
569
+ <Text>Server Running: <Text color="#01D4E7" underline>{viteUrl}</Text></Text>
570
+ <Text>Element: <Text color="#01D4E7">{selectedElement?.info.name}</Text></Text>
571
+ <Text>Action/Signal: <Text color="#01D4E7">{selectedActionSignal?.label}</Text></Text>
572
+ {selectedProperty ? (
573
+ <Text>Property UI: <Text color="#01D4E7">{selectedProperty.label}</Text></Text>
574
+ ) : (
575
+ <Text>UI Mode: <Text color="#01D4E7">Action/Signal Level</Text></Text>
576
+ )}
577
+ <Text color="yellow">Press Ctrl+C to stop the server</Text>
578
+ </Box>
579
+ <Box marginTop={1} marginLeft={4} flexDirection="column">
580
+ <Text color="gray">Server Status:</Text>
581
+ {viteLogs.length > 0 ? (
582
+ viteLogs.map((line, i) => (
583
+ <Text key={i} color="gray">{line}</Text>
584
+ ))
585
+ ) : (
586
+ <Text color="gray">Waiting for activity...</Text>
587
+ )}
588
+ </Box></>
589
+
590
+ );
591
+ }
592
+
593
+ if (step === 'properties') {
594
+ return (
595
+ <Box flexDirection="column">
596
+ <Text>📁 Element: <Text color="cyan">{selectedElement?.info.name}</Text></Text>
597
+ <Text>📋 Action/Signal: <Text color="#01D4E7">{selectedActionSignal?.label}</Text></Text>
598
+ <Text>Found {properties.length} property(ies):</Text>
599
+ <SelectInput items={properties} onSelect={handleSelect} />
600
+ </Box>
601
+ );
602
+ }
603
+
604
+ if (step === 'actions-signals') {
605
+ return (
606
+ <Box flexDirection="column" marginTop={2} marginBottom={2}>
607
+ {/* <Text>Status: {step}</Text> */}
608
+ <Text>📁 Element: <Text color="#01D4E7">{selectedElement?.info.name}</Text></Text>
609
+ <Box flexDirection="column" marginTop={1}>
610
+ <Text>Found {actionsSignals.length} action(s)/signal(s):</Text>
611
+ <SelectInput items={actionsSignals} onSelect={handleSelect} />
612
+ </Box>
613
+ </Box>
614
+ );
615
+ }
616
+
617
+ return (
618
+ <Box flexDirection="column" marginTop={2} marginBottom={2}>
619
+ {/* <ViteBanner url={viteUrl} /> */}
620
+ <Box flexDirection="column">
621
+ <Text>📁 Directory: <Text color="#01D4E7">{rootDir}</Text></Text>
622
+ <Box flexDirection="column" marginTop={1}>
623
+ <Text>Found {elements.length} element(s):</Text>
624
+ <SelectInput items={elements} onSelect={handleSelect} /></Box>
625
+ </Box>
626
+ <ViteLogPanel logs={viteLogs} />
627
+ </Box>
628
+ );
629
+ };
630
+
631
+ // Vite log panel
632
+ const ViteLogPanel = ({ logs }: { logs: string[] }) => {
633
+ if (!logs.length) return null;
634
+ return (
635
+ <Box flexDirection="column" marginTop={1}>
636
+ <Text color="gray">Output:</Text>
637
+ {logs.map((line, i) => (
638
+ <Text key={i} color="gray">{line}</Text>
639
+ ))}
640
+ </Box>
641
+ );
642
+ };
643
+
644
+ // Vite banner component
645
+ const ViteBanner = ({ url }: { url: string | null }) =>
646
+ url ? (
647
+ <Box marginBottom={1} justifyContent="center" borderStyle="double" borderColor="#8759F2" marginX={10}>
648
+ <Text color="green">🚀 Process Element Server running at: </Text>
649
+ <Text color="cyan" underline>{url}</Text>
650
+ </Box>
651
+ ) : null;
@@ -0,0 +1 @@
1
+ {"version":"0.0.0-development"}