@process.co/element-dev-server 0.0.1 → 0.0.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.
@@ -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,107 +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
6
 
7
- ```bash
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
8
12
 
9
- npm i @process.co/element-dev-server -g
13
+ ## 📦 Installation
10
14
 
15
+ ```bash
16
+ # In your element project
17
+ pnpm add -D @process.co/element-dev-server
11
18
  ```
12
19
 
13
- ## 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.
14
25
 
15
- The CLI accepts an optional path argument to specify which directory to scan for Process.co element modules:
26
+ All dependencies are automatically provided by the dev server! 🎉
16
27
 
17
- or you can run from within your element's directory or one directory above run the following command
28
+ ## 🚀 Usage
29
+
30
+ ### Basic Usage
18
31
 
19
32
  ```bash
33
+ npx proc-dev
34
+ ```
20
35
 
21
- proc-dev
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`
22
41
 
42
+ ### What You'll See
43
+
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)
23
62
  ```
24
63
 
64
+ The dev server will then launch with your selected UI component loaded and ready to test!
25
65
 
26
- ## Features
66
+ ## 🔍 How It Works
27
67
 
28
- - **Interactive UI**: Uses Ink to provide a beautiful terminal interface
29
- - **Path Validation**: Automatically validates that the provided path exists and is a directory
30
- - **Element Discovery**: Uses the @process.co/elements library to discover and load element modules
31
- - **Dev Server Launch**: Launches a Vite development server for the selected element
32
- - **Smart Directory Detection**: Automatically finds the appropriate dev directory for each element
68
+ ### Automatic Dependency Resolution
33
69
 
34
- ## How It Works
70
+ The dev server uses a smart resolution strategy to find UI dependencies:
35
71
 
36
- The CLI uses the `@process.co/elements` library to:
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)
37
76
 
38
- 1. **Scan for Elements**: Looks for `.mjs` or `.mts` files in the specified directory
39
- 2. **Load Modules**: Imports each element module using the elements library
40
- 3. **Display Information**: Shows element names and types in an interactive list
41
- 4. **Launch Dev Server**: Starts a Vite server in the appropriate directory
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`)
42
84
 
43
- ## Development
85
+ 3. **Logs the resolution**:
86
+ ```
87
+ 🔍 Resolving UI dependencies from: /path/to/node_modules
88
+ ```
44
89
 
45
- ```bash
46
- # Build the project
47
- pnpm build
90
+ ### Vite Configuration
91
+
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
48
97
 
49
- # Run in development mode
50
- pnpm cli
98
+ ## 📁 Project Structure Expected
51
99
 
52
- # Watch for changes
53
- pnpm dev
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
54
120
  ```
55
121
 
56
- ## 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
+ ```
57
149
 
58
- - Node.js 18+
59
- - A directory containing Process.co element modules (`.mjs` or `.mts` files)
60
- - Each element should have proper exports with `name` and `type` properties
61
- - Elements should have a `dev/` directory with a `vite.config.js` file (optional)
150
+ All imports work automatically - no additional setup required!
62
151
 
63
- ## Element Module Format
152
+ ## 🛠️ Troubleshooting
64
153
 
65
- Elements should be exported as modules with the following structure:
154
+ ### Dependencies Not Found
66
155
 
67
- ```javascript
68
- // example-element.mjs
69
- export default {
70
- name: 'Example Element',
71
- type: 'app', // or 'action', 'signal', 'credential'
72
- description: 'An example element',
73
- // ... other properties
74
- };
156
+ If you see errors like:
157
+ ```
158
+ ERROR: Could not resolve "@radix-ui/react-slot"
75
159
  ```
76
160
 
77
- ## Error Handling
161
+ **Solution**: Check the console output for the dependency resolution path:
162
+ ```
163
+ 🔍 Resolving UI dependencies from: /path/to/node_modules
164
+ ```
78
165
 
79
- 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
+ ```
80
171
 
81
- - **Path doesn't exist**: Shows the full path that was not found
82
- - **Path is not a directory**: Validates that the path points to a directory
83
- - **No elements found**: Provides guidance when no valid element modules are found
84
- - **Module loading errors**: Shows specific errors when element modules fail to load
172
+ ### Vite Port Already in Use
85
173
 
86
- ## Examples
174
+ The dev server uses port `5173` by default. If it's in use, you'll see an error.
87
175
 
88
- ```bash
89
- # From a project root with element modules
90
- 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
185
+
186
+ ### pnpm (Recommended) ✅
187
+ Works perfectly with pnpm's hoisting strategy.
91
188
 
92
- # From anywhere, pointing to a specific project
93
- process-element ~/projects/my-process-app
189
+ ### npm
190
+ Works with npm's nested node_modules.
94
191
 
95
- # Using relative paths
96
- process-element ./elements
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
97
202
  ```
98
203
 
99
- ## 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
100
223
 
101
- The CLI will automatically discover elements that:
224
+ The `tsup` build only bundles the CLI code - dependencies are included via package.json.
102
225
 
103
- - Are `.mjs` or `.mts` files in the specified directory
104
- - Have proper exports with `name` and `type` properties
105
- - Can be successfully imported by the `@process.co/elements` library
226
+ ## 📄 License
106
227
 
107
- 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",
3
+ "version": "0.0.3",
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": "^0.0.14",
61
+ "@process.co/utilities": "^0.0.1",
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,17 +87,18 @@
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",
66
100
  "@babel/preset-env": "^7.26.9",
67
101
  "@babel/preset-typescript": "^7.26.0",
68
- "@repo/config-jest": "workspace:^",
69
- "@repo/config-typescript": "workspace:^",
70
102
  "@types/jest": "^29.5.14",
71
103
  "@types/node": "22.13.17",
72
104
  "@types/react": "19.1.0",
@@ -77,4 +109,4 @@
77
109
  "typescript": "^5.7.3"
78
110
  },
79
111
  "registry": "https://registry.npmjs.org"
80
- }
112
+ }
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">