@process.co/element-dev-server 0.0.1-test-5 → 0.0.2

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.
@@ -0,0 +1,18 @@
1
+ body {
2
+ background-color: #f0f0f0;
3
+ }
4
+
5
+ .eds-container {
6
+ background-color: #f0f0f0;
7
+ padding: 20px;
8
+ border-radius: 10px;
9
+ box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.1);
10
+ }
11
+
12
+ .eds-container h2 {
13
+ color: #333;
14
+ font-size: 24px;
15
+ font-weight: 600;
16
+ margin-bottom: 20px;
17
+ text-align: center;
18
+ }
@@ -1,7 +1,12 @@
1
1
  import React, { useState, useEffect } from 'react';
2
2
  import { createRoot } from 'react-dom/client';
3
+ import { ErrorBoundary } from 'react-error-boundary';
4
+ import { DevProvider, DevToolbar } from '@process.co/ui/dev';
5
+ import { Button } from '@process.co/ui';
3
6
 
4
-
7
+ // Import the CSS from @process.co/ui so it's available for all components
8
+ import '@process.co/ui/styles';
9
+ import './eds.css';
5
10
 
6
11
  // This will be dynamically loaded based on the selected element
7
12
  interface ElementComponent {
@@ -9,6 +14,9 @@ interface ElementComponent {
9
14
  [key: string]: any;
10
15
  }
11
16
 
17
+ // Storage key for persisting component value
18
+ const VALUE_STORAGE_KEY = 'process-dev:component-value';
19
+
12
20
  function DevServer() {
13
21
  const [ElementComponent, setElementComponent] = useState<any>(null);
14
22
  const [loading, setLoading] = useState(true);
@@ -16,26 +24,66 @@ function DevServer() {
16
24
 
17
25
 
18
26
  const [readonly, setReadonly] = useState(false);
19
- const [value, setValue] = useState<any>({});
27
+
28
+ // Persist value to localStorage for stable IDs across reloads
29
+ const [value, setValue] = useState<any>(() => {
30
+ try {
31
+ const stored = localStorage.getItem(VALUE_STORAGE_KEY);
32
+ if (stored) {
33
+ return JSON.parse(stored);
34
+ }
35
+ } catch (e) {
36
+ console.warn('Failed to load value from localStorage:', e);
37
+ }
38
+ return {};
39
+ });
40
+
41
+ // Save value to localStorage when it changes
42
+ useEffect(() => {
43
+ try {
44
+ localStorage.setItem(VALUE_STORAGE_KEY, JSON.stringify(value));
45
+ } catch (e) {
46
+ console.warn('Failed to save value to localStorage:', e);
47
+ }
48
+ }, [value]);
20
49
 
21
50
  const handleReadonly = () => {
22
51
  setReadonly(!readonly);
23
52
  }
24
53
 
54
+ interface ImportMetaEnv {
55
+ // readonly VITE_ELEMENT?: string;
56
+ readonly VITE_ELEMENT_PATH?: string;
57
+ readonly VITE_ELEMENT_TYPE?: string;
58
+ readonly VITE_ELEMENT_NAME?: string;
59
+ readonly VITE_ACTION_SIGNAL_KEY?: string;
60
+ readonly VITE_PROPERTY_KEY?: string;
61
+ readonly VITE_PROPERTY_TYPE?: string;
62
+ readonly VITE_PROPERTY_UI_PATH?: string;
63
+ readonly VITE_MODULE_PATH?: string;
64
+ readonly VITE_UI_DIRECTORY?: string;
65
+ readonly VITE_ELEMENT_MODULE?: { actions: any[], signals: any[] };
66
+ readonly VITE_CURRENT_ACTION_SIGNAL?: { ui: string };
67
+ readonly VITE_SELECTED_PROPERTY?: { propertyKey: string, type: string, uiPath: string, propertyData: any };
68
+ }
69
+
70
+
25
71
  useEffect(() => {
26
72
  const loadElement = async () => {
27
73
  try {
28
74
 
75
+ const env = (import.meta as any).env as ImportMetaEnv;
76
+
29
77
  // Get element path and type from environment variables (passed by CLI)
30
- const elementPath = import.meta.env.VITE_ELEMENT_PATH;
31
- const elementType = import.meta.env.VITE_ELEMENT_TYPE || 'action';
32
- const elementName = import.meta.env.VITE_ELEMENT_NAME;
33
- const actionSignalKey = import.meta.env.VITE_ACTION_SIGNAL_KEY;
34
- const propertyKey = import.meta.env.VITE_PROPERTY_KEY;
35
- const propertyType = import.meta.env.VITE_PROPERTY_TYPE;
36
- const propertyUIPath = import.meta.env.VITE_PROPERTY_UI_PATH;
37
- const modulePath = import.meta.env.VITE_MODULE_PATH;
38
- const uiDirectory = import.meta.env.VITE_UI_DIRECTORY;
78
+ const elementPath = env.VITE_ELEMENT_PATH;
79
+ const elementType = env.VITE_ELEMENT_TYPE || 'action';
80
+ const elementName = env.VITE_ELEMENT_NAME;
81
+ const actionSignalKey = env.VITE_ACTION_SIGNAL_KEY;
82
+ const propertyKey = env.VITE_PROPERTY_KEY;
83
+ const propertyType = env.VITE_PROPERTY_TYPE;
84
+ const propertyUIPath = env.VITE_PROPERTY_UI_PATH;
85
+ const modulePath = env.VITE_MODULE_PATH;
86
+ const uiDirectory = env.VITE_UI_DIRECTORY;
39
87
 
40
88
  // console.log('Element data:', {
41
89
  // elementPath,
@@ -67,9 +115,9 @@ function DevServer() {
67
115
  // debugger;
68
116
 
69
117
  // Use the complete element data from the compatibility module passed via environment variables
70
- const elementModule = import.meta.env.VITE_ELEMENT_MODULE || {};
71
- const currentActionSignal = import.meta.env.VITE_CURRENT_ACTION_SIGNAL || {};
72
- const selectedProperty = import.meta.env.VITE_SELECTED_PROPERTY || {};
118
+ const elementModule = env.VITE_ELEMENT_MODULE || {} as { actions: any[], signals: any[] };
119
+ const currentActionSignal = env.VITE_CURRENT_ACTION_SIGNAL || {} as { ui: string };
120
+ const selectedProperty = env.VITE_SELECTED_PROPERTY || {} as { propertyKey: string, type: string, uiPath: string, propertyData: any };
73
121
 
74
122
  // console.log('Element module from compatibility:', elementModule);
75
123
  // console.log('Current action/signal:', currentActionSignal);
@@ -226,52 +274,210 @@ function DevServer() {
226
274
  <h2>Element Development Server</h2>
227
275
 
228
276
  <h3>Rendered Component</h3>
229
- <div style={{ marginBottom: '10px' }}>
230
- <button
277
+ <div style={{ marginBottom: '10px', display: 'flex', gap: '8px' }}>
278
+ <Button variant="outline"
279
+ size="sm"
231
280
  onClick={handleReadonly}
232
281
  >
233
282
  {readonly ? 'Make Editable' : 'Make Read Only'}
234
- </button>
235
- </div>
236
- <div style={{ border: '2px solid #007acc', padding: '20px', borderRadius: '8px', backgroundColor: '#f8f9fa' }}>
237
- {typeof ElementComponent === 'function' ? (
238
- <ElementComponent
239
- value={value}
240
- onChange={(newValue: any) => setValue(newValue)}
241
- onBlur={() => console.log('Component onBlur')}
242
- readonly={readonly}
243
- />
244
- ) : React.isValidElement(ElementComponent) ? (
245
- ElementComponent
246
- ) : (
247
- <div style={{ color: '#666', fontStyle: 'italic' }}>
248
- Component is not a valid React component. Type: {typeof ElementComponent}
249
- <br />
250
- <small>This might be the element metadata instead of the UI component.</small>
251
- </div>
252
- )}
253
- </div>
254
- <div style={{ marginBottom: '10px' }}>
255
- <h3>Value</h3>
256
- <pre style={{ backgroundColor: '#f5f5f5', padding: '10px', overflow: 'auto' }}>
257
- {JSON.stringify(value, null, 2)}
258
- </pre>
259
- </div>
260
- <div style={{ border: '1px solid #ccc', padding: '20px', marginTop: '20px', marginBottom: '20px', backgroundColor: '#f8f9fa' }}>
261
- <h3>Loaded Component Information</h3>
262
- <p><strong>Element:</strong> {import.meta.env.VITE_ELEMENT_NAME}</p>
263
- <p><strong>Type:</strong> {import.meta.env.VITE_ELEMENT_TYPE}</p>
264
- <p><strong>Action/Signal:</strong> {import.meta.env.VITE_ACTION_SIGNAL_KEY}</p>
265
- <p><strong>Module Path:</strong> {import.meta.env.VITE_ELEMENT_PATH}</p>
266
- <p><strong>UI Directory:</strong> {import.meta.env.VITE_UI_DIRECTORY || 'Not available'}</p>
267
-
268
- <h3>Component Details</h3>
269
- <pre style={{ backgroundColor: '#f5f5f5', padding: '10px', overflow: 'auto' }}>
270
- {JSON.stringify(ElementComponent, null, 2)}
271
- </pre>
272
-
283
+ </Button>
284
+ {/* <Button
285
+ onClick={() => {
286
+ if (confirm('Clear component value? This will reset all data and IDs.')) {
287
+ setValue({});
288
+ localStorage.removeItem(VALUE_STORAGE_KEY);
289
+ }
290
+ }}
291
+ style={{ backgroundColor: '#fee2e2', color: '#991b1b' }}
292
+ >
293
+ Clear Value
294
+ </Button> */}
273
295
  </div>
274
- </div>
296
+ <DevProvider storageKey="process-dev" >
297
+ <div style={{ border: '2px solid #007acc', padding: '20px', borderRadius: '8px', backgroundColor: '#f8f9fa' }}>
298
+
299
+ <ErrorBoundary fallbackRender={({ error, resetErrorBoundary }) => (
300
+
301
+
302
+ <div style={{
303
+ border: '2px solid #dc2626',
304
+ borderRadius: '8px',
305
+ padding: '20px',
306
+ margin: '10px 0',
307
+ backgroundColor: '#fef2f2',
308
+ fontFamily: 'Monaco, Consolas, "Courier New", monospace',
309
+ fontSize: '14px'
310
+ }}>
311
+ <div style={{ display: 'flex', alignItems: 'center', marginBottom: '16px' }}>
312
+ <span style={{
313
+ fontSize: '20px',
314
+ marginRight: '8px'
315
+ }}>💥</span>
316
+ <h3 style={{
317
+ margin: 0,
318
+ color: '#dc2626',
319
+ fontSize: '18px',
320
+ fontWeight: 'bold'
321
+ }}>
322
+ Component Error
323
+ </h3>
324
+ </div>
325
+
326
+ <div style={{
327
+ backgroundColor: '#1f2937',
328
+ color: '#f9fafb',
329
+ padding: '16px',
330
+ borderRadius: '4px',
331
+ marginBottom: '16px',
332
+ overflow: 'auto',
333
+ maxHeight: '300px'
334
+ }}>
335
+ <div style={{ color: '#fca5a5', fontWeight: 'bold', marginBottom: '8px' }}>
336
+ {error.name}: {error.message}
337
+ </div>
338
+ {error.stack && (
339
+ <pre style={{
340
+ margin: 0,
341
+ whiteSpace: 'pre-wrap',
342
+ fontSize: '12px',
343
+ lineHeight: '1.4'
344
+ }}>
345
+ {error.stack}
346
+ </pre>
347
+ )}
348
+ </div>
349
+
350
+ <div style={{ marginBottom: '16px' }}>
351
+ <h4 style={{ margin: '0 0 8px 0', color: '#374151', fontSize: '14px' }}>
352
+ Error Details:
353
+ </h4>
354
+ <div style={{
355
+ backgroundColor: '#f9fafb',
356
+ padding: '12px',
357
+ borderRadius: '4px',
358
+ fontSize: '12px'
359
+ }}>
360
+ <div><strong>Error Type:</strong> {error.name}</div>
361
+ <div><strong>Message:</strong> {error.message}</div>
362
+ {error.cause && <div><strong>Cause:</strong> {String(error.cause)}</div>}
363
+ <div><strong>Timestamp:</strong> {new Date().toISOString()}</div>
364
+ </div>
365
+ </div>
366
+
367
+ <div style={{
368
+ display: 'flex',
369
+ gap: '12px',
370
+ alignItems: 'center'
371
+ }}>
372
+ <button
373
+ onClick={resetErrorBoundary}
374
+ style={{
375
+ backgroundColor: '#dc2626',
376
+ color: 'white',
377
+ border: 'none',
378
+ padding: '8px 16px',
379
+ borderRadius: '4px',
380
+ cursor: 'pointer',
381
+ fontSize: '14px',
382
+ fontWeight: '500'
383
+ }}
384
+ onMouseOver={(e) => (e.target as HTMLElement).style.backgroundColor = '#b91c1c'}
385
+ onMouseOut={(e) => (e.target as HTMLElement).style.backgroundColor = '#dc2626'}
386
+ >
387
+ 🔄 Try Again
388
+ </button>
389
+
390
+ <button
391
+ onClick={() => {
392
+ console.error('Component Error Details:', {
393
+ error,
394
+ stack: error.stack,
395
+ timestamp: new Date().toISOString(),
396
+ userAgent: navigator.userAgent,
397
+ url: window.location.href
398
+ });
399
+ alert('Error details logged to console');
400
+ }}
401
+ style={{
402
+ backgroundColor: '#6b7280',
403
+ color: 'white',
404
+ border: 'none',
405
+ padding: '8px 16px',
406
+ borderRadius: '4px',
407
+ cursor: 'pointer',
408
+ fontSize: '14px',
409
+ fontWeight: '500'
410
+ }}
411
+ onMouseOver={(e) => (e.target as HTMLElement).style.backgroundColor = '#4b5563'}
412
+ onMouseOut={(e) => (e.target as HTMLElement).style.backgroundColor = '#6b7280'}
413
+ >
414
+ 📋 Log to Console
415
+ </button>
416
+ </div>
417
+
418
+ <div style={{
419
+ marginTop: '16px',
420
+ padding: '12px',
421
+ backgroundColor: '#eff6ff',
422
+ borderRadius: '4px',
423
+ fontSize: '12px',
424
+ color: '#1e40af'
425
+ }}>
426
+ <strong>💡 Debug Tips:</strong>
427
+ <ul style={{ margin: '8px 0 0 0', paddingLeft: '20px' }}>
428
+ <li>Check the console for detailed error information</li>
429
+ <li>Verify all required props are being passed to the component</li>
430
+ <li>Check for undefined variables or missing imports</li>
431
+ <li>Use browser dev tools to inspect the component state</li>
432
+ </ul>
433
+ </div>
434
+ </div>
435
+ )}>
436
+ {typeof ElementComponent === 'function' ? (
437
+ <ElementComponent
438
+ fieldName="devField"
439
+ fieldId="dev-field-1"
440
+ value={value}
441
+ onChange={(newValue: any) => setValue(newValue)}
442
+ onBlur={() => console.log('Component onBlur')}
443
+ readonly={readonly}
444
+ />
445
+ ) : (
446
+ <div style={{ color: '#666', fontStyle: 'italic' }}>
447
+ Component is not a valid React component. Type: {typeof ElementComponent}
448
+ <br />
449
+ <small>This might be the element metadata instead of the UI component.</small>
450
+ </div>
451
+ )}
452
+ </ErrorBoundary>
453
+ </div>
454
+ {/* <div style={{ marginBottom: '10px' }}>
455
+ <h3>Value</h3>
456
+ <pre style={{ backgroundColor: '#f5f5f5', padding: '10px', overflow: 'auto' }}>
457
+ {JSON.stringify(value, null, 2)}
458
+ </pre>
459
+ </div> */}
460
+ <div style={{ marginTop: '10px' }}>
461
+ <DevToolbar />
462
+ </div>
463
+
464
+ <div style={{ border: '1px solid #ccc', padding: '20px', marginTop: '20px', marginBottom: '20px', backgroundColor: '#f8f9fa' }}>
465
+ <h3>Loaded Component Information</h3>
466
+ <p><strong>Element:</strong> {import.meta.env.VITE_ELEMENT_NAME}</p>
467
+ <p><strong>Type:</strong> {import.meta.env.VITE_ELEMENT_TYPE}</p>
468
+ <p><strong>Action/Signal:</strong> {import.meta.env.VITE_ACTION_SIGNAL_KEY}</p>
469
+ <p><strong>Module Path:</strong> {import.meta.env.VITE_ELEMENT_PATH}</p>
470
+ <p><strong>UI Directory:</strong> {import.meta.env.VITE_UI_DIRECTORY || 'Not available'}</p>
471
+
472
+ <h3>Component Details</h3>
473
+ <pre style={{ backgroundColor: '#f5f5f5', padding: '10px', overflow: 'auto' }}>
474
+ {JSON.stringify(ElementComponent, null, 2)}
475
+ </pre>
476
+
477
+ </div>
478
+ </DevProvider>
479
+
480
+ </div >
275
481
  );
276
482
  }
277
483
 
@@ -11,6 +11,9 @@ interface ImportMetaEnv {
11
11
  readonly VITE_PROPERTY_UI_PATH?: string;
12
12
  readonly VITE_MODULE_PATH?: string;
13
13
  readonly VITE_UI_DIRECTORY?: string;
14
+ readonly VITE_ELEMENT_MODULE?: string;
15
+ readonly VITE_CURRENT_ACTION_SIGNAL?: string;
16
+ readonly VITE_SELECTED_PROPERTY?: string;
14
17
  }
15
18
 
16
19
  interface ImportMeta {
@@ -4,6 +4,66 @@ const { defineConfig } = require('vite');
4
4
  const react = require('@vitejs/plugin-react');
5
5
  const path = require('path');
6
6
  const fs = require('fs');
7
+
8
+ // Find where element-dev-server is installed
9
+ // This could be in the user's node_modules or in a monorepo
10
+ const elementDevServerRoot = path.resolve(__dirname, '..');
11
+
12
+ // Debug mode - set PROC_DEV_DEBUG=true to see detailed logging
13
+ const DEBUG = process.env.PROC_DEV_DEBUG === 'true';
14
+
15
+ // Find the local @process.co/ui source for development (monorepo override)
16
+ const localUISource = path.resolve(elementDevServerRoot, '..', 'ui', 'src');
17
+ const localUIPackageRoot = path.resolve(elementDevServerRoot, '..', 'ui');
18
+ const localUIBuiltCSS = path.resolve(localUIPackageRoot, 'css', 'ui.css');
19
+ const hasLocalUISource = fs.existsSync(localUISource);
20
+ if (DEBUG) {
21
+ if (hasLocalUISource) {
22
+ console.log('🎨 Using local @process.co/ui source from monorepo');
23
+ console.log(' Source:', localUISource);
24
+ console.log(' CSS:', localUIBuiltCSS);
25
+ console.log(' CSS exists?', fs.existsSync(localUIBuiltCSS));
26
+ } else {
27
+ console.log('📦 Using published @process.co/ui from npm');
28
+ }
29
+ }
30
+
31
+ // Find the local @process.co/utilities source for development (monorepo override)
32
+ const localUtilitiesSource = path.resolve(elementDevServerRoot, '..', 'utilities', 'src');
33
+ const hasLocalUtilitiesSource = fs.existsSync(localUtilitiesSource);
34
+ if (DEBUG) {
35
+ if (hasLocalUtilitiesSource) {
36
+ console.log('🔧 Using local @process.co/utilities source from monorepo');
37
+ console.log(' Source:', localUtilitiesSource);
38
+ } else {
39
+ console.log('📦 Using published @process.co/utilities from npm');
40
+ }
41
+ }
42
+
43
+ // Function to find node_modules that contains our dependencies
44
+ function findNodeModulesWithDeps() {
45
+ const possiblePaths = [
46
+ // In the element-dev-server package itself
47
+ path.join(elementDevServerRoot, 'node_modules'),
48
+ // One level up (for pnpm hoisting in user's project)
49
+ path.join(elementDevServerRoot, '..', '..'),
50
+ // Two levels up (for npm/yarn in user's project)
51
+ path.join(elementDevServerRoot, '..', '..', '..'),
52
+ ];
53
+
54
+ for (const basePath of possiblePaths) {
55
+ const testPath = path.join(basePath, '@radix-ui', 'react-slot');
56
+ if (fs.existsSync(testPath)) {
57
+ return basePath;
58
+ }
59
+ }
60
+
61
+ // Default to element-dev-server's node_modules
62
+ return path.join(elementDevServerRoot, 'node_modules');
63
+ }
64
+
65
+ const depsNodeModules = findNodeModulesWithDeps();
66
+ if (DEBUG) console.log('🔍 Resolving UI dependencies from:', depsNodeModules);
7
67
 
8
68
  // Access environment variables passed from CLI
9
69
  const elementPath = process.env.VITE_ELEMENT_PATH;
@@ -28,11 +88,93 @@ const propertyUIPath = process.env.VITE_PROPERTY_UI_PATH;
28
88
  // console.log('Vite config index.html path:', path.resolve(__dirname, 'index.html'));
29
89
  // console.log('Vite config index.tsx path:', path.resolve(__dirname, 'index.tsx'));
30
90
 
31
- module.exports = defineConfig({
32
- plugins: [
33
- react(),
34
- {
35
- name: 'element-virtual-modules',
91
+ // Build plugins array
92
+ const plugins = [react()];
93
+
94
+ // Add CSS resolver if in monorepo
95
+ if (hasLocalUISource) {
96
+ if (DEBUG) console.log('🔧 Adding ui-css-resolver plugin');
97
+ plugins.push({
98
+ name: 'ui-css-resolver',
99
+ enforce: 'pre',
100
+ resolveId(source, importer) {
101
+ if (DEBUG) console.log('🔍 ui-css-resolver checking:', source);
102
+ if (source === '@process.co/ui/styles') {
103
+ if (DEBUG) console.log('✅ Resolving @process.co/ui/styles to:', localUIBuiltCSS);
104
+ return localUIBuiltCSS;
105
+ }
106
+ return null;
107
+ }
108
+ });
109
+ if (DEBUG) console.log('✅ ui-css-resolver plugin added, total plugins:', plugins.length);
110
+ }
111
+
112
+ // Add UI package alias resolver if in monorepo
113
+ if (hasLocalUISource) {
114
+ plugins.push({
115
+ name: 'ui-package-alias-resolver',
116
+ enforce: 'pre',
117
+ resolveId(source, importer) {
118
+ // Only process @/ imports
119
+ if (!source.startsWith('@/')) return null;
120
+
121
+ // Only process if importer is from the UI source
122
+ if (!importer) return null;
123
+
124
+ // Convert importer to absolute path if it's relative
125
+ let absoluteImporter = importer;
126
+ if (!path.isAbsolute(importer)) {
127
+ // If it's a relative path like "../../ui/src/...", resolve it from the .process directory
128
+ // __dirname is the .process directory where this config file lives
129
+ absoluteImporter = path.resolve(__dirname, importer);
130
+ }
131
+
132
+ const normalizedImporter = path.normalize(absoluteImporter);
133
+ const normalizedUISource = path.normalize(localUISource);
134
+
135
+ // Check if the importer is from the UI source
136
+ if (!normalizedImporter.includes(normalizedUISource)) return null;
137
+
138
+ const relativePath = source.substring(2); // Remove '@/'
139
+ const resolvedPath = path.join(localUISource, relativePath);
140
+
141
+ // Try common extensions
142
+ const extensions = ['.ts', '.tsx', '.js', '.jsx'];
143
+ for (const ext of extensions) {
144
+ const testPath = resolvedPath + ext;
145
+ if (fs.existsSync(testPath)) {
146
+ const stats = fs.statSync(testPath);
147
+ if (stats.isFile()) {
148
+ return testPath;
149
+ }
150
+ }
151
+ }
152
+
153
+ // Try without extension (might already have one)
154
+ if (fs.existsSync(resolvedPath)) {
155
+ const stats = fs.statSync(resolvedPath);
156
+ if (stats.isFile()) {
157
+ return resolvedPath;
158
+ }
159
+
160
+ // It's a directory, try index files
161
+ if (stats.isDirectory()) {
162
+ for (const indexFile of ['index.ts', 'index.tsx', 'index.js', 'index.jsx']) {
163
+ const indexPath = path.join(resolvedPath, indexFile);
164
+ if (fs.existsSync(indexPath)) {
165
+ return indexPath;
166
+ }
167
+ }
168
+ }
169
+ }
170
+
171
+ return null;
172
+ }
173
+ });
174
+ }
175
+
176
+ plugins.push({
177
+ name: 'element-virtual-modules',
36
178
  resolveId(source) {
37
179
  // Main element module
38
180
  if (source === '/element-main') {
@@ -77,9 +219,10 @@ module.exports = defineConfig({
77
219
  }
78
220
  return null;
79
221
  }
80
- },
81
- {
82
- name: 'external-element-loader',
222
+ });
223
+
224
+ plugins.push({
225
+ name: 'external-element-loader',
83
226
 
84
227
  configureServer(server) {
85
228
  // Set up file watching for the element directory
@@ -113,6 +256,99 @@ module.exports = defineConfig({
113
256
  }
114
257
  });
115
258
  }
259
+
260
+ // Also watch the local @process.co/ui source for hot reloading
261
+ if (hasLocalUISource) {
262
+ if (DEBUG) console.log(`🔍 Setting up file watching for @process.co/ui source: ${localUISource}`);
263
+ server.watcher.add(localUISource);
264
+
265
+ // Watch CSS source files and trigger rebuild
266
+ const cssSourceFiles = [
267
+ path.join(localUISource, 'styles'),
268
+ path.join(localUISource, 'themes')
269
+ ];
270
+ cssSourceFiles.forEach(dir => {
271
+ if (fs.existsSync(dir)) {
272
+ server.watcher.add(dir);
273
+ }
274
+ });
275
+
276
+ let cssRebuildTimeout = null;
277
+
278
+ server.watcher.on('change', (file) => {
279
+ if (file.startsWith(localUISource)) {
280
+ if (DEBUG) console.log(`🎨 @process.co/ui source file changed: ${file}`);
281
+
282
+ // Check if it's a CSS-related file
283
+ const isCSSFile = file.includes('/styles/') || file.includes('/themes/') || file.endsWith('.css');
284
+
285
+ if (isCSSFile) {
286
+ console.log(`🎨 CSS source changed, rebuilding...`);
287
+
288
+ // Debounce CSS rebuilds
289
+ clearTimeout(cssRebuildTimeout);
290
+ cssRebuildTimeout = setTimeout(async () => {
291
+ try {
292
+ const { exec } = require('child_process');
293
+ const { promisify } = require('util');
294
+ const execAsync = promisify(exec);
295
+
296
+ if (DEBUG) console.log('📦 Rebuilding CSS...');
297
+ await execAsync('pnpm run generate:css', { cwd: localUIPackageRoot });
298
+ console.log('✅ CSS rebuilt');
299
+
300
+ // Invalidate the CSS module and trigger full reload
301
+ const cssModule = server.moduleGraph.getModuleById(localUIBuiltCSS);
302
+ if (cssModule) {
303
+ server.moduleGraph.invalidateModule(cssModule);
304
+ }
305
+
306
+ server.ws.send({
307
+ type: 'full-reload',
308
+ path: '*'
309
+ });
310
+ } catch (error) {
311
+ console.error('❌ Failed to rebuild CSS:', error.message);
312
+ }
313
+ }, 500);
314
+ } else {
315
+ // For non-CSS files, just trigger HMR
316
+ const module = server.moduleGraph.getModuleById(file);
317
+ if (module) {
318
+ server.moduleGraph.invalidateModule(module);
319
+ }
320
+
321
+ server.ws.send({
322
+ type: 'full-reload',
323
+ path: '*'
324
+ });
325
+ }
326
+ }
327
+ });
328
+ }
329
+
330
+ // Also watch the local @process.co/utilities source for hot reloading
331
+ if (hasLocalUtilitiesSource) {
332
+ if (DEBUG) console.log(`🔍 Setting up file watching for @process.co/utilities source: ${localUtilitiesSource}`);
333
+ server.watcher.add(localUtilitiesSource);
334
+
335
+ server.watcher.on('change', (file) => {
336
+ if (file.startsWith(localUtilitiesSource)) {
337
+ if (DEBUG) console.log(`🔧 @process.co/utilities source file changed: ${file}`);
338
+
339
+ // Trigger HMR
340
+ const module = server.moduleGraph.getModuleById(file);
341
+ if (module) {
342
+ server.moduleGraph.invalidateModule(module);
343
+ }
344
+
345
+ server.ws.send({
346
+ type: 'full-reload',
347
+ path: '*'
348
+ });
349
+ }
350
+ });
351
+ }
116
352
  },
117
353
 
118
354
  resolveId(source, importer) {
@@ -230,16 +466,28 @@ module.exports = defineConfig({
230
466
 
231
467
  return ctx.modules;
232
468
  }
233
- }
234
- ],
469
+ });
470
+
471
+ module.exports = defineConfig({
472
+ plugins,
235
473
  // root is set programmatically in the CLI
236
474
  server: {
237
475
  port: 5173,
238
476
  open: true,
239
477
  host: true,
240
478
  fs: {
241
- // Allow serving files from outside the root
242
- allow: ['..']
479
+ // Allow serving files from outside the root, including our deps node_modules
480
+ allow: [
481
+ '..',
482
+ elementDevServerRoot,
483
+ depsNodeModules,
484
+ // Allow the user's element directory
485
+ ...(elementPath ? [elementPath, path.dirname(elementPath)] : []),
486
+ // Allow the local @process.co/ui source for hot reloading
487
+ ...(hasLocalUISource ? [localUISource, path.dirname(localUISource)] : []),
488
+ // Allow the local @process.co/utilities source for hot reloading
489
+ ...(hasLocalUtilitiesSource ? [localUtilitiesSource, path.dirname(localUtilitiesSource)] : [])
490
+ ]
243
491
  },
244
492
  watch: {
245
493
  // Watch external directories for changes
@@ -257,6 +505,19 @@ module.exports = defineConfig({
257
505
  resolve: {
258
506
  alias: {
259
507
  '@': path.resolve(process.cwd(), 'src'),
508
+ // Override @process.co/ui with local monorepo source in dev mode
509
+ // ORDER MATTERS: More specific paths must come BEFORE more general ones!
510
+ ...(hasLocalUISource && {
511
+ // Put subpath exports FIRST so they match before the general @process.co/ui
512
+ '@process.co/ui/styles': localUIBuiltCSS,
513
+ '@process.co/ui/fields': path.join(localUISource, 'components', 'fields'),
514
+ '@process.co/ui/dev': path.join(localUISource, 'components', 'dev'),
515
+ '@process.co/ui': localUISource
516
+ }),
517
+ // Override @process.co/utilities with local monorepo source in dev mode
518
+ ...(hasLocalUtilitiesSource && {
519
+ '@process.co/utilities': localUtilitiesSource
520
+ }),
260
521
  // Dynamically map element paths based on the elementPath from CLI
261
522
  ...(elementPath && {
262
523
  // Map the element directory to a virtual path that Vite can resolve
@@ -267,10 +528,27 @@ module.exports = defineConfig({
267
528
  'ui'
268
529
  )
269
530
  })
270
- }
531
+ },
532
+ // Resolve modules from the element-dev-server's node_modules
533
+ // This allows the dev server to provide all dependencies
534
+ preserveSymlinks: false,
271
535
  },
536
+ // Tell Vite where to find dependencies - use the discovered node_modules
537
+ cacheDir: path.join(depsNodeModules, '.vite'),
272
538
  optimizeDeps: {
273
- include: ['react', 'react-dom']
539
+ include: ['react', 'react-dom', 'clsx', 'tailwind-merge', 'zustand', 'uuid', '@monaco-editor/react', '@fortawesome/react-fontawesome', '@fortawesome/pro-regular-svg-icons', '@fortawesome/pro-solid-svg-icons', '@fortawesome/pro-duotone-svg-icons', '@fortawesome/pro-light-svg-icons', '@radix-ui/react-slot', '@radix-ui/react-dialog', '@radix-ui/react-dropdown-menu', '@radix-ui/react-separator', '@radix-ui/react-tooltip', '@radix-ui/react-popover', '@radix-ui/react-accordion', '@radix-ui/react-tabs', '@radix-ui/react-toggle', '@radix-ui/react-toggle-group', '@radix-ui/react-progress', '@radix-ui/react-radio-group', '@radix-ui/react-scroll-area', '@radix-ui/react-select', '@radix-ui/react-slider', '@radix-ui/react-switch'],
540
+ // Exclude local UI source, utilities source, and external elements from optimization for hot reloading
541
+ exclude: [
542
+ ...(hasLocalUISource ? ['@process.co/ui'] : []),
543
+ ...(hasLocalUtilitiesSource ? ['@process.co/utilities'] : []),
544
+ ...(elementPath ? [elementPath] : [])
545
+ ],
546
+ // Force Vite to look in element-dev-server's node_modules for these deps
547
+ esbuildOptions: {
548
+ resolveExtensions: ['.js', '.jsx', '.ts', '.tsx', '.mjs'],
549
+ // Use the discovered node_modules path
550
+ nodePaths: [depsNodeModules]
551
+ }
274
552
  },
275
553
  // Environment variables will be passed programmatically
276
554
  });
package/README.md CHANGED
@@ -1,108 +1,228 @@
1
- # Process.co Element Dev Server
1
+ # @process.co/element-dev-server
2
2
 
3
- A CLI tool for developing Process.co elements with an interactive development server.
3
+ A development harness for previewing and testing Process.co element UI components without needing to set up a full development environment.
4
4
 
5
- ## Installation
5
+ ## ✨ Features
6
+
7
+ - **Zero Configuration**: Just install and run - no need to install UI dependencies separately
8
+ - **Auto-Discovery**: Automatically finds and serves all required UI library dependencies
9
+ - **FontAwesome Pro Included**: All FontAwesome Pro icon sets bundled (if you have access)
10
+ - **Hot Reload**: Instant updates as you modify your element UI components
11
+ - **Interactive Selection**: Visual interface to select elements, actions, and properties to test
12
+
13
+ ## 📦 Installation
6
14
 
7
15
  ```bash
8
- pnpm install
9
- pnpm build
16
+ # In your element project
17
+ pnpm add -D @process.co/element-dev-server
10
18
  ```
11
19
 
12
- ## Usage
20
+ That's it! No need to install:
21
+ - ❌ `@process.co/ui`
22
+ - ❌ `@radix-ui/*` components
23
+ - ❌ `@fortawesome/*` packages
24
+ - ❌ `zustand`, `@monaco-editor/react`, etc.
25
+
26
+ All dependencies are automatically provided by the dev server! 🎉
27
+
28
+ ## 🚀 Usage
13
29
 
14
- The CLI accepts an optional path argument to specify which directory to scan for Process.co element modules:
30
+ ### Basic Usage
15
31
 
16
32
  ```bash
17
- # Use current directory
18
- process-element
33
+ npx proc-dev
34
+ ```
35
+
36
+ This will:
37
+ 1. Scan your project for elements
38
+ 2. Show an interactive menu to select an element
39
+ 3. Let you choose an action/signal and property to preview
40
+ 4. Launch a Vite dev server at `http://localhost:5173`
19
41
 
20
- # Use a specific path
21
- process-element /path/to/your/elements
42
+ ### What You'll See
22
43
 
23
- # Use relative path
24
- process-element ../my-elements
44
+ ```
45
+ ╭─────────────────────────────────────────╮
46
+ │ Process.co Element Development Server │
47
+ ╰─────────────────────────────────────────╯
48
+
49
+ Select an element to develop:
50
+ > process_internal
51
+ my-custom-element
52
+ another-element
53
+
54
+ Select an action/signal:
55
+ > Action: Switch
56
+ Action: Transform
57
+ Signal: DataReceived
58
+
59
+ Select a property to preview:
60
+ > cases (ui.switch)
61
+ defaultValue (text)
25
62
  ```
26
63
 
27
- ## Features
64
+ The dev server will then launch with your selected UI component loaded and ready to test!
28
65
 
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
66
+ ## 🔍 How It Works
34
67
 
35
- ## How It Works
68
+ ### Automatic Dependency Resolution
36
69
 
37
- The CLI uses the `@process.co/elements` library to:
70
+ The dev server uses a smart resolution strategy to find UI dependencies:
38
71
 
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
72
+ 1. **Searches multiple locations**:
73
+ - In the element-dev-server package itself
74
+ - One level up (for pnpm hoisting)
75
+ - Two levels up (for npm/yarn nesting)
43
76
 
44
- ## Development
77
+ 2. **Provides all dependencies** including:
78
+ - ✅ `@process.co/ui` - Complete UI library
79
+ - ✅ All `@radix-ui/*` components (18+ packages)
80
+ - ✅ FontAwesome Pro icons (regular, solid, light, duotone)
81
+ - ✅ `@monaco-editor/react` - Code editor
82
+ - ✅ `zustand` - State management
83
+ - ✅ Common utilities (bundled: `clsx`, `tailwind-merge`, `class-variance-authority`)
45
84
 
46
- ```bash
47
- # Build the project
48
- pnpm build
85
+ 3. **Logs the resolution**:
86
+ ```
87
+ 🔍 Resolving UI dependencies from: /path/to/node_modules
88
+ ```
49
89
 
50
- # Run in development mode
51
- pnpm cli
90
+ ### Vite Configuration
52
91
 
53
- # Watch for changes
54
- pnpm dev
92
+ The dev server automatically configures Vite to:
93
+ - Allow serving files from outside the project root
94
+ - Resolve modules from the discovered dependency location
95
+ - Pre-bundle and optimize all UI dependencies
96
+ - Watch your element files for changes and hot-reload
97
+
98
+ ## 📁 Project Structure Expected
99
+
100
+ Your element project should have this structure:
101
+
102
+ ```
103
+ your-element-project/
104
+ ├── package.json
105
+ ├── node_modules/
106
+ │ └── @process.co/element-dev-server/ # Installed here
107
+ └── elements/
108
+ └── your-element/
109
+ ├── index.ts (or .mts)
110
+ ├── actions/
111
+ │ └── someAction/
112
+ │ ├── index.ts
113
+ │ └── ui/
114
+ │ └── someProperty.tsx # Your UI component
115
+ └── signals/
116
+ └── someSignal/
117
+ ├── index.ts
118
+ └── ui/
119
+ └── someProperty.tsx
55
120
  ```
56
121
 
57
- ## Requirements
122
+ ## 🎨 Developing UI Components
123
+
124
+ ### Example UI Component
125
+
126
+ ```typescript
127
+ // elements/your-element/actions/transform/ui/config.tsx
128
+ import { Button, Input } from '@process.co/ui';
129
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
130
+ import { faWand } from '@fortawesome/pro-solid-svg-icons';
131
+
132
+ export default function ConfigUI({ value, onChange, readonly }) {
133
+ return (
134
+ <div className="space-y-4">
135
+ <Input
136
+ value={value.name}
137
+ onChange={(e) => onChange({ ...value, name: e.target.value })}
138
+ disabled={readonly}
139
+ placeholder="Enter name..."
140
+ />
141
+ <Button onClick={() => console.log('Transform!')}>
142
+ <FontAwesomeIcon icon={faWand} className="mr-2" />
143
+ Transform
144
+ </Button>
145
+ </div>
146
+ );
147
+ }
148
+ ```
58
149
 
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)
150
+ All imports work automatically - no additional setup required!
63
151
 
64
- ## Element Module Format
152
+ ## 🛠️ Troubleshooting
65
153
 
66
- Elements should be exported as modules with the following structure:
154
+ ### Dependencies Not Found
67
155
 
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
- };
156
+ If you see errors like:
157
+ ```
158
+ ERROR: Could not resolve "@radix-ui/react-slot"
76
159
  ```
77
160
 
78
- ## Error Handling
161
+ **Solution**: Check the console output for the dependency resolution path:
162
+ ```
163
+ 🔍 Resolving UI dependencies from: /path/to/node_modules
164
+ ```
79
165
 
80
- The CLI provides clear error messages for common issues:
166
+ Verify that path contains the expected packages. If not, try:
167
+ ```bash
168
+ rm -rf node_modules
169
+ pnpm install
170
+ ```
81
171
 
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
172
+ ### Vite Port Already in Use
86
173
 
87
- ## Examples
174
+ The dev server uses port `5173` by default. If it's in use, you'll see an error.
88
175
 
89
- ```bash
90
- # From a project root with element modules
91
- process-element
176
+ **Solution**: Stop any other Vite servers or modify the port in `.process/vite.config.cjs`
177
+
178
+ ### FontAwesome Icons Not Working
179
+
180
+ If you don't have FontAwesome Pro access, some icons won't be available.
181
+
182
+ **Solution**: The dev server gracefully handles missing FontAwesome packages. If you need specific icon sets, ensure you have access to FontAwesome Pro and they're installed in the monorepo.
183
+
184
+ ## 📝 Package Manager Compatibility
92
185
 
93
- # From anywhere, pointing to a specific project
94
- process-element ~/projects/my-process-app
186
+ ### pnpm (Recommended)
187
+ Works perfectly with pnpm's hoisting strategy.
95
188
 
96
- # Using relative paths
97
- process-element ./elements
189
+ ### npm
190
+ Works with npm's nested node_modules.
191
+
192
+ ### yarn ✅
193
+ Works with yarn's hoisting.
194
+
195
+ ## 🔐 FontAwesome Pro
196
+
197
+ If you have FontAwesome Pro access configured in your `.npmrc`:
198
+
199
+ ```ini
200
+ @fortawesome:registry=https://npm.fontawesome.com/
201
+ //npm.fontawesome.com/:_authToken=YOUR-TOKEN
98
202
  ```
99
203
 
100
- ## Element Discovery
204
+ The dev server automatically includes these icon sets:
205
+ - `@fortawesome/pro-regular-svg-icons`
206
+ - `@fortawesome/pro-solid-svg-icons`
207
+ - `@fortawesome/pro-light-svg-icons`
208
+ - `@fortawesome/pro-duotone-svg-icons`
209
+
210
+ ## 📚 Additional Documentation
211
+
212
+ - [Dependency Resolution](./DEPENDENCY-RESOLUTION.md) - Deep dive into how dependencies are discovered and served
213
+ - [UI Library Peer Dependencies](../ui/PEER_DEPENDENCIES.md) - Complete list of available UI components
214
+
215
+ ## 🤝 Contributing
216
+
217
+ When publishing to npm, this package should include:
218
+ - All Radix UI components
219
+ - FontAwesome React wrapper
220
+ - Monaco Editor
221
+ - Zustand
222
+ - The `.process/` directory with Vite config
101
223
 
102
- The CLI will automatically discover elements that:
224
+ The `tsup` build only bundles the CLI code - dependencies are included via package.json.
103
225
 
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
226
+ ## 📄 License
107
227
 
108
- Elements are displayed with their name and type for easy identification.
228
+ ISC
@@ -0,0 +1 @@
1
+ import{useState as y,useEffect as H,useRef as ce}from"react";import{Box as m,Text as e}from"ink";import W from"ink-select-input";import T from"fs";import u from"path";import{createServer as ue}from"vite";import{fileURLToPath as me}from"url";import S from"@process.co/element-dev-support/dist/index.js";import{Fragment as fe,jsx as r,jsxs as o}from"react/jsx-runtime";var Pe=({rootDir:g})=>{let[L,w]=y([]),[E,Q]=y(null),[C,Z]=y([]),[A,ee]=y(null),[j,te]=y([]),[ne,U]=y(null),[K,oe]=y(null),[x,c]=y("loading"),[V,G]=y("elements"),[B,X]=y([]),R=ce(X);R.current=X;let[$,re]=y(null),[pe,ie]=y(null),[se,le]=y(!1),ae=async(a,t,i)=>{try{let l=T.statSync(a).isDirectory()?a:u.dirname(a),s=i==="action"?"actions":"sources",n=u.join(l,s);if(!T.existsSync(n))return null;let p=(await S.importFolderModulesOfType(s,i,l,["common"])).find(d=>d.key===t);if(!p)return null;let P=T.readdirSync(n).filter(d=>{let I=u.join(n,d);return T.statSync(I).isDirectory()}),_=null,M=null;for(let d of P){let I=u.join(n,d),J=[`${d}.mjs`,`${d}.mts`,`${d}.js`,`${d}.ts`,"index.mjs","index.mts","index.js","index.ts"];for(let F of J){let h=u.join(I,F);if(T.existsSync(h))try{let{module:D}=await S.importFromPath(h);if(D.key===t){_=h,M=I;break}}catch{continue}}if(_)break}if(!_)return null;let N=u.join(l,"ui");return p&&p.ui&&(N=u.join(l,"ui",p.ui)),{modulePath:_,uiDir:N}}catch{return null}};H(()=>{(async()=>{try{try{let s=await S.autoDetectElement(g);if(s!=="pipedream"){let n=await S.loadElementPointers(g,s);w([{label:`${n.name} (${n.elementType})`,value:n.name,info:n,path:g}]),c("ready");return}}catch{}let t=T.readdirSync(g,{withFileTypes:!0}),i=[];for(let s of t){let n=u.join(g,s.name);if(s.isDirectory())try{T.readdirSync(n).some(p=>p.endsWith(".mjs")||p.endsWith(".mts")||p.endsWith(".js")||p.endsWith(".ts")||p.endsWith(".tsx")||p.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 S.loadElementPointers(s,"auto");l.push({label:`${n.name} (${n.elementType})`,value:n.name,info:n,path:s})}catch{}if(l.length>0){w(l),c("ready");return}else c("no-elements")}catch{c("error")}})()},[g]),H(()=>{$&&x==="launching"&&c("dev-server-running")},[$,x]),H(()=>{},[x]);let k=async(a,t,i)=>{if(!a)return;let l=await ae(a.path,t.value,t.type);if(!l){c("error");return}let s=T.statSync(a.path).isDirectory()?a.path:u.dirname(a.path),n=u.join(s,"dev"),O=T.existsSync(n)?n:s,p=me(import.meta.url),P=u.dirname(p),_=u.resolve(P,"../.process/vite.config.cjs"),M=u.join(O,"vite.config.js"),N=u.join(O,"vite.config.ts"),d=_;T.existsSync(M)?d=M:T.existsSync(N)&&(d=N);let I=await S.loadElementPointers(a.path,"auto"),J=I.actions?.find(f=>f.key===t.value)||I.signals?.find(f=>f.key===t.value),F=[u.resolve(process.cwd(),"process-co","ui","src"),u.resolve(P,"..","..","ui","src"),u.resolve(P,"..","ui","src")];console.log("\u{1F50D} Checking for local UI source..."),console.log(" process.cwd():",process.cwd()),console.log(" __dirname:",P);let h=!1;for(let f of F)if(console.log(" Checking path:",f,"exists:",T.existsSync(f)),T.existsSync(f)){h=!0,console.log(" \u2705 Found local UI source at:",f);break}console.log(" hasLocalUISource:",h),le(h),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,process.env.VITE_HAS_LOCAL_UI_SOURCE=h?"true":"false";let D=1;try{let f=await ue({configFile:d,root:u.resolve(P,"../.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(I),"import.meta.env.VITE_CURRENT_ACTION_SIGNAL":JSON.stringify(J),"import.meta.env.VITE_SELECTED_PROPERTY":JSON.stringify(i),"import.meta.env.VITE_HAS_LOCAL_UI_SOURCE":JSON.stringify(h)},logLevel:"info",customLogger:{info(v){R.current(b=>[...b,v].slice(-D))},warn(v){console.warn(`[warn] ${v}`),R.current(b=>[...b,`[warn] ${v}`].slice(-D))},error(v){console.error(`[error] ${v}`),R.current(b=>[...b,`[error] ${v}`].slice(-D))},clearScreen(){},hasWarned:!1,warnOnce(v){},hasErrorLogged:()=>!1}});await f.listen();let z=`http://localhost:${f.config.server?.port||5173}`;re(z),ie(`Dev server running at ${z}`);let q=()=>{f.close()};process.on("SIGINT",q),process.on("SIGTERM",q),f.watcher.on("change",v=>{}),c("dev-server-running"),process.stdin.resume()}catch{c("error")}},Y=async a=>{if(V==="elements"){let t=L.find(i=>i.value===a.value);if(!t)return;Q(t),c("loading-actions-signals");try{let i=await S.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"}),Z(l),G("actions-signals"),c("ready")}catch{c("error")}}else if(V==="actions-signals"){let t=C.find(i=>i.value===a.value);if(!t||!E)return;ee(t),c("loading-properties");try{let i=await S.loadElementPointers(E.path,"auto"),l=i.actions?.find(n=>n.key===t.value)||i.signals?.find(n=>n.key===t.value);if(l&&l.ui){U(t.value),c("launching"),k(E,t,null).catch(n=>{c("error")});return}if(!l||!l.props){U(t.value),c("launching");try{await k(E,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){U(t.value),c("launching"),await k(E,t,null);return}te(s),G("properties"),c("ready")}catch{c("error")}}else{let t=j.find(i=>i.value===a.value);if(!t||!A||!E)return;oe(t),U(t.value),c("launching"),await k(E,A,t)}};return x==="loading"?o(m,{flexDirection:"column",marginTop:2,children:[o(e,{children:["\u{1F50D} Loading elements from: ",r(e,{color:"cyan",children:g})]}),r(e,{children:"Loading..."})]}):x==="no-elements"?o(m,{flexDirection:"column",marginTop:2,children:[o(e,{color:"red",children:["\u274C No valid elements found in: ",r(e,{color:"cyan",children:g})]}),r(e,{color:"yellow",children:"Make sure the directory contains valid element modules."}),r(e,{color:"yellow",children:"Supported formats: Pipedream, n8n, Doflo, Process.co"}),r(e,{color:"yellow",children:"Supported file types: .js, .ts, .mjs, .mts, .jsx, .tsx"})]}):x==="error"?o(m,{flexDirection:"column",marginTop:2,children:[o(e,{color:"red",children:["\u274C Error loading elements from: ",r(e,{color:"cyan",children:g})]}),r(e,{color:"yellow",children:"Check that the directory contains valid element modules."})]}):x==="loading-actions-signals"?o(m,{flexDirection:"column",marginTop:2,children:[o(e,{children:["\u{1F50D} Loading actions and signals for '",r(e,{color:"cyan",children:E?.info.name}),"'..."]}),r(e,{children:"Loading..."})]}):x==="loading-properties"?o(m,{flexDirection:"column",marginTop:2,children:[o(e,{children:["\u{1F50D} Loading properties for '",r(e,{color:"cyan",children:A?.label}),"'..."]}),r(e,{children:"Loading..."})]}):x==="launching"?o(m,{flexDirection:"column",marginTop:2,children:[o(e,{color:"green",children:["\u{1F680} Launching dev server for '",r(e,{color:"cyan",children:ne}),"'..."]}),o(e,{children:["Directory: ",r(e,{color:"cyan",children:g})]})]}):x==="dev-server-running"?o(fe,{children:[o(m,{flexDirection:"column",flexShrink:1,borderStyle:"round",margin:2,marginBottom:1,paddingX:2,paddingY:1,borderColor:"#8759F2",children:[o(e,{children:["Server Running: ",r(e,{color:"#01D4E7",underline:!0,children:$})]}),o(e,{children:["Element: ",r(e,{color:"#01D4E7",children:E?.info.name})]}),o(e,{children:["Action/Signal: ",r(e,{color:"#01D4E7",children:A?.label})]}),K?o(e,{children:["Property UI: ",r(e,{color:"#01D4E7",children:K.label})]}):o(e,{children:["UI Mode: ",r(e,{color:"#01D4E7",children:"Action/Signal Level"})]}),se&&o(e,{children:["UI Source: ",r(e,{color:"#8759F2",children:"Local Monorepo (Hot Reload Enabled)"})]}),r(e,{color:"yellow",children:"Press Ctrl+C to stop the server"})]}),o(m,{marginTop:1,marginLeft:4,flexDirection:"column",children:[r(e,{color:"gray",children:"Server Status:"}),B.length>0?B.map((a,t)=>r(e,{color:"gray",children:a},t)):r(e,{color:"gray",children:"Waiting for activity..."})]})]}):V==="properties"?o(m,{flexDirection:"column",children:[o(e,{children:["\u{1F4C1} Element: ",r(e,{color:"cyan",children:E?.info.name})]}),o(e,{children:["\u{1F4CB} Action/Signal: ",r(e,{color:"#01D4E7",children:A?.label})]}),o(e,{children:["Found ",j.length," property(ies):"]}),r(W,{items:j,onSelect:Y})]}):V==="actions-signals"?o(m,{flexDirection:"column",marginTop:2,marginBottom:2,children:[o(e,{children:["\u{1F4C1} Element: ",r(e,{color:"#01D4E7",children:E?.info.name})]}),o(m,{flexDirection:"column",marginTop:1,children:[o(e,{children:["Found ",C.length," action(s)/signal(s):"]}),r(W,{items:C,onSelect:Y})]})]}):o(m,{flexDirection:"column",marginTop:2,marginBottom:2,children:[o(m,{flexDirection:"column",children:[o(e,{children:["\u{1F4C1} Directory: ",r(e,{color:"#01D4E7",children:g})]}),o(m,{flexDirection:"column",marginTop:1,children:[o(e,{children:["Found ",L.length," element(s):"]}),r(W,{items:L,onSelect:Y})]})]}),r(ge,{logs:B})]})},ge=({logs:g})=>g.length?o(m,{flexDirection:"column",marginTop:1,children:[r(e,{color:"gray",children:"Output:"}),g.map((L,w)=>r(e,{color:"gray",children:L},w))]}):null;export{Pe as a};
@@ -0,0 +1 @@
1
+ import{useState as d,useEffect as H,useRef as ce}from"react";import{Box as m,Text as e}from"ink";import W from"ink-select-input";import y from"fs";import u from"path";import{createServer as ue}from"vite";import{fileURLToPath as me}from"url";import S from"@process.co/element-dev-support/dist/index.js";import{Fragment as ge,jsx as o,jsxs as r}from"react/jsx-runtime";var Pe=({rootDir:f})=>{let[L,V]=d([]),[T,Q]=d(null),[k,Z]=d([]),[A,ee]=d(null),[B,te]=d([]),[ne,R]=d(null),[K,re]=d(null),[x,c]=d("loading"),[w,G]=d("elements"),[C,X]=d([]),U=ce(X);U.current=X;let[$,oe]=d(null),[pe,ie]=d(null),[se,le]=d(!1),ae=async(a,t,i)=>{try{let l=y.statSync(a).isDirectory()?a:u.dirname(a),s=i==="action"?"actions":"sources",n=u.join(l,s);if(!y.existsSync(n))return null;let g=(await S.importFolderModulesOfType(s,i,l,["common"])).find(p=>p.key===t);if(!g)return null;let P=y.readdirSync(n).filter(p=>{let h=u.join(n,p);return y.statSync(h).isDirectory()}),_=null,j=null;for(let p of P){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 F of J){let I=u.join(h,F);if(y.existsSync(I))try{let{module:D}=await S.importFromPath(I);if(D.key===t){_=I,j=h;break}}catch{continue}}if(_)break}if(!_)return null;let N=u.join(l,"ui");return g&&g.ui&&(N=u.join(l,"ui",g.ui)),{modulePath:_,uiDir:N}}catch{return null}};H(()=>{(async()=>{try{try{let s=await S.autoDetectElement(f);if(s!=="pipedream"){let n=await S.loadElementPointers(f,s);V([{label:`${n.name} (${n.elementType})`,value:n.name,info:n,path:f}]),c("ready");return}}catch{}let t=y.readdirSync(f,{withFileTypes:!0}),i=[];for(let s of t){let n=u.join(f,s.name);if(s.isDirectory())try{y.readdirSync(n).some(g=>g.endsWith(".mjs")||g.endsWith(".mts")||g.endsWith(".js")||g.endsWith(".ts")||g.endsWith(".tsx")||g.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 S.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")}})()},[f]),H(()=>{$&&x==="launching"&&c("dev-server-running")},[$,x]),H(()=>{},[x]);let M=async(a,t,i)=>{if(!a)return;let l=await ae(a.path,t.value,t.type);if(!l){c("error");return}let s=y.statSync(a.path).isDirectory()?a.path:u.dirname(a.path),n=u.join(s,"dev"),O=y.existsSync(n)?n:s,g=me(import.meta.url),P=u.dirname(g),_=u.resolve(P,"../.process/vite.config.cjs"),j=u.join(O,"vite.config.js"),N=u.join(O,"vite.config.ts"),p=_;y.existsSync(j)?p=j:y.existsSync(N)&&(p=N);let h=await S.loadElementPointers(a.path,"auto"),J=h.actions?.find(E=>E.key===t.value)||h.signals?.find(E=>E.key===t.value),F=[u.resolve(process.cwd(),"process-co","ui","src"),u.resolve(P,"..","..","ui","src"),u.resolve(P,"..","ui","src")],I=!1;for(let E of F)if(y.existsSync(E)){I=!0;break}le(I),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,process.env.VITE_HAS_LOCAL_UI_SOURCE=I?"true":"false";let D=1;try{let E=await ue({configFile:p,root:u.resolve(P,"../.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),"import.meta.env.VITE_HAS_LOCAL_UI_SOURCE":JSON.stringify(I)},logLevel:"info",customLogger:{info(v){U.current(b=>[...b,v].slice(-D))},warn(v){console.warn(`[warn] ${v}`),U.current(b=>[...b,`[warn] ${v}`].slice(-D))},error(v){console.error(`[error] ${v}`),U.current(b=>[...b,`[error] ${v}`].slice(-D))},clearScreen(){},hasWarned:!1,warnOnce(v){},hasErrorLogged:()=>!1}});await E.listen();let z=`http://localhost:${E.config.server?.port||5173}`;oe(z),ie(`Dev server running at ${z}`);let q=()=>{E.close()};process.on("SIGINT",q),process.on("SIGTERM",q),E.watcher.on("change",v=>{}),c("dev-server-running"),process.stdin.resume()}catch{c("error")}},Y=async a=>{if(w==="elements"){let t=L.find(i=>i.value===a.value);if(!t)return;Q(t),c("loading-actions-signals");try{let i=await S.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"}),Z(l),G("actions-signals"),c("ready")}catch{c("error")}}else if(w==="actions-signals"){let t=k.find(i=>i.value===a.value);if(!t||!T)return;ee(t),c("loading-properties");try{let i=await S.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){R(t.value),c("launching"),M(T,t,null).catch(n=>{c("error")});return}if(!l||!l.props){R(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){R(t.value),c("launching"),await M(T,t,null);return}te(s),G("properties"),c("ready")}catch{c("error")}}else{let t=B.find(i=>i.value===a.value);if(!t||!A||!T)return;re(t),R(t.value),c("launching"),await M(T,A,t)}};return x==="loading"?r(m,{flexDirection:"column",marginTop:2,children:[r(e,{children:["\u{1F50D} Loading elements from: ",o(e,{color:"cyan",children:f})]}),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:f})]}),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:f})]}),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:A?.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:ne}),"'..."]}),r(e,{children:["Directory: ",o(e,{color:"cyan",children:f})]})]}):x==="dev-server-running"?r(ge,{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:$})]}),r(e,{children:["Element: ",o(e,{color:"#01D4E7",children:T?.info.name})]}),r(e,{children:["Action/Signal: ",o(e,{color:"#01D4E7",children:A?.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"})]}),se&&r(e,{children:["UI Source: ",o(e,{color:"#8759F2",children:"Local Monorepo (Hot Reload Enabled)"})]}),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..."})]})]}):w==="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:A?.label})]}),r(e,{children:["Found ",B.length," property(ies):"]}),o(W,{items:B,onSelect:Y})]}):w==="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:Y})]})]}):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:f})]}),r(m,{flexDirection:"column",marginTop:1,children:[r(e,{children:["Found ",L.length," element(s):"]}),o(W,{items:L,onSelect:Y})]})]}),o(fe,{logs:C})]})},fe=({logs:f})=>f.length?r(m,{flexDirection:"column",marginTop:1,children:[o(e,{color:"gray",children:"Output:"}),f.map((L,V)=>o(e,{color:"gray",children:L},V))]}):null;export{Pe as a};
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
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(`
2
+ import{b as t}from"./chunk-QPYNK3X7.js";import{a as r}from"./chunk-TP2HSEPI.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
3
  Usage
4
4
  $ process-element [path]
5
5
 
package/dist/ui.js CHANGED
@@ -1 +1 @@
1
- import{a}from"./chunk-NT4V5NUJ.js";export{a as DevUI};
1
+ import{a}from"./chunk-TP2HSEPI.js";export{a as DevUI};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@process.co/element-dev-server",
3
- "version": "0.0.1-test-5",
3
+ "version": "0.0.2",
4
4
  "description": "Helper Library for developing elements for Process.co",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -22,6 +22,10 @@
22
22
  "registry": "https://registry.npmjs.org",
23
23
  "scope": "@process.co"
24
24
  },
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/process-co/npm-element-dev-server.git"
28
+ },
25
29
  "release": {
26
30
  "branches": [
27
31
  "main"
@@ -46,9 +50,36 @@
46
50
  },
47
51
  "license": "ISC",
48
52
  "dependencies": {
53
+ "@fortawesome/pro-duotone-svg-icons": "^6.7.2",
54
+ "@fortawesome/pro-light-svg-icons": "^6.7.2",
55
+ "@fortawesome/pro-regular-svg-icons": "^6.7.2",
56
+ "@fortawesome/pro-solid-svg-icons": "^6.7.2",
57
+ "@fortawesome/react-fontawesome": "^0.2.2",
58
+ "@monaco-editor/react": "^4.7.0",
49
59
  "@process.co/element-dev-support": "github:process-co/npm-element-dev-support#main",
60
+ "@process.co/ui": "workspace:0.0.0-development",
61
+ "@process.co/utilities": "workspace:0.0.0-development",
62
+ "uuid": "^11.1.0",
63
+ "@radix-ui/react-accordion": "^1.2.3",
64
+ "@radix-ui/react-dialog": "^1.1.6",
65
+ "@radix-ui/react-dropdown-menu": "^2.1.6",
66
+ "@radix-ui/react-popover": "^1.1.6",
67
+ "@radix-ui/react-progress": "^1.1.2",
68
+ "@radix-ui/react-radio-group": "^1.2.3",
69
+ "@radix-ui/react-scroll-area": "^1.2.3",
70
+ "@radix-ui/react-select": "^2.1.6",
71
+ "@radix-ui/react-separator": "^1.1.2",
72
+ "@radix-ui/react-slider": "^1.2.3",
73
+ "@radix-ui/react-slot": "^1.1.2",
74
+ "@radix-ui/react-switch": "^1.1.3",
75
+ "@radix-ui/react-tabs": "^1.1.3",
76
+ "@radix-ui/react-toggle": "^1.1.2",
77
+ "@radix-ui/react-toggle-group": "^1.1.2",
78
+ "@radix-ui/react-tooltip": "^1.1.8",
50
79
  "@vitejs/plugin-react": "^4.3.4",
51
80
  "axios": "^1.8.4",
81
+ "class-variance-authority": "^0.7.1",
82
+ "clsx": "^2.1.1",
52
83
  "ink": "^6.0.1",
53
84
  "ink-select-input": "^6.2.0",
54
85
  "io-ts": "^2.2.22",
@@ -56,10 +87,13 @@
56
87
  "querystring": "^0.2.1",
57
88
  "react": "^19.1.0",
58
89
  "react-dom": "^19.1.0",
90
+ "react-error-boundary": "^5.0.0",
91
+ "tailwind-merge": "^3.0.2",
59
92
  "tsup": "^8.5.0",
60
93
  "tsx": "^4.19.1",
61
94
  "type-fest": "^4.21.0",
62
- "vite": "^7.0.2"
95
+ "vite": "^7.0.2",
96
+ "zustand": "^5.0.3"
63
97
  },
64
98
  "devDependencies": {
65
99
  "@babel/core": "^7.26.9",
package/src/ui.tsx CHANGED
@@ -49,6 +49,7 @@ export const DevUI = ({ rootDir }: { rootDir: string }) => {
49
49
  const [viteUrl, setViteUrl] = useState<string | null>(null);
50
50
  // Add a serverMessage state for display messages
51
51
  const [serverMessage, setServerMessage] = useState<string | null>(null);
52
+ const [usingLocalUI, setUsingLocalUI] = useState<boolean>(false);
52
53
 
53
54
  // Function to get the actual module path for an action/signal
54
55
  const getModulePath = async (elementPath: string, actionSignalKey: string, type: 'action' | 'signal'): Promise<{ modulePath: string; uiDir: string } | null> => {
@@ -282,6 +283,25 @@ export const DevUI = ({ rootDir }: { rootDir: string }) => {
282
283
  const currentActionSignal = (elementModule.actions as any[])?.find(action => action.key === actionSignal.value) ||
283
284
  (elementModule.signals as any[])?.find(signal => signal.key === actionSignal.value);
284
285
 
286
+ // Check if we're in monorepo with local UI source (from Node.js context)
287
+ // Try multiple possible paths to find the UI source
288
+ const possibleUIPaths = [
289
+ path.resolve(process.cwd(), 'process-co', 'ui', 'src'),
290
+ path.resolve(__dirname, '..', '..', 'ui', 'src'),
291
+ path.resolve(__dirname, '..', 'ui', 'src'),
292
+ ];
293
+
294
+ let hasLocalUISource = false;
295
+ for (const uiPath of possibleUIPaths) {
296
+ if (fs.existsSync(uiPath)) {
297
+ hasLocalUISource = true;
298
+ break;
299
+ }
300
+ }
301
+
302
+ // Update state to show in banner
303
+ setUsingLocalUI(hasLocalUISource);
304
+
285
305
  // Set process.env variables for Vite config
286
306
  process.env.VITE_ELEMENT_PATH = element.path;
287
307
  process.env.VITE_ELEMENT_TYPE = actionSignal.type;
@@ -292,6 +312,7 @@ export const DevUI = ({ rootDir }: { rootDir: string }) => {
292
312
  process.env.VITE_PROPERTY_UI_PATH = property?.uiPath || '';
293
313
  process.env.VITE_MODULE_PATH = moduleInfo.modulePath;
294
314
  process.env.VITE_UI_DIRECTORY = moduleInfo.uiDir;
315
+ process.env.VITE_HAS_LOCAL_UI_SOURCE = hasLocalUISource ? 'true' : 'false';
295
316
 
296
317
 
297
318
  const viteLogLength = 1;
@@ -319,13 +340,25 @@ export const DevUI = ({ rootDir }: { rootDir: string }) => {
319
340
  // Pass the complete element data from compatibility module
320
341
  'import.meta.env.VITE_ELEMENT_MODULE': JSON.stringify(elementModule),
321
342
  'import.meta.env.VITE_CURRENT_ACTION_SIGNAL': JSON.stringify(currentActionSignal),
322
- 'import.meta.env.VITE_SELECTED_PROPERTY': JSON.stringify(property)
343
+ 'import.meta.env.VITE_SELECTED_PROPERTY': JSON.stringify(property),
344
+ 'import.meta.env.VITE_HAS_LOCAL_UI_SOURCE': JSON.stringify(hasLocalUISource)
323
345
  },
324
346
  logLevel: 'info',
325
347
  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)); },
348
+ info(msg) {
349
+ // Only capture in UI, don't output to console (prevents HMR noise)
350
+ viteLogsRef.current(logs => [...logs, msg].slice(-viteLogLength));
351
+ },
352
+ warn(msg) {
353
+ // Show warnings in console
354
+ console.warn(`[warn] ${msg}`);
355
+ viteLogsRef.current(logs => [...logs, `[warn] ${msg}`].slice(-viteLogLength));
356
+ },
357
+ error(msg) {
358
+ // Show errors in console
359
+ console.error(`[error] ${msg}`);
360
+ viteLogsRef.current(logs => [...logs, `[error] ${msg}`].slice(-viteLogLength));
361
+ },
329
362
  clearScreen() { },
330
363
  hasWarned: false,
331
364
  warnOnce(msg) { },
@@ -574,6 +607,7 @@ export const DevUI = ({ rootDir }: { rootDir: string }) => {
574
607
  ) : (
575
608
  <Text>UI Mode: <Text color="#01D4E7">Action/Signal Level</Text></Text>
576
609
  )}
610
+ {usingLocalUI && <Text>UI Source: <Text color="#8759F2">Local Monorepo (Hot Reload Enabled)</Text></Text>}
577
611
  <Text color="yellow">Press Ctrl+C to stop the server</Text>
578
612
  </Box>
579
613
  <Box marginTop={1} marginLeft={4} flexDirection="column">