visualifyjs 2.5.3 → 3.0.0

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.
Files changed (252) hide show
  1. package/.claude/mem/TIMELINE.md +36 -0
  2. package/.claude/mem/notes/2026-02-11-3d-visualization-docs-fix-external-script-solution.md +24 -0
  3. package/.claude/mem/notes/2026-02-11-3d-visualization-docs-fix-session-summary.md +43 -0
  4. package/.claude/mem/notes/2026-02-11-cli-fix-editor-command-alias.md +26 -0
  5. package/.claude/mem/notes/2026-02-11-phase-3-developer-experience-completed.md +51 -0
  6. package/.claude/mem/notes/2026-02-11-phase-4-web-workers-implementation-complete.md +59 -0
  7. package/.claude/mem/notes/2026-02-11-visualify-phase-2-3d-visualization-complete.md +50 -0
  8. package/.claude/mem/notes/2026-02-11-visualify-phase-2-committed-ready-for-phase-3.md +33 -0
  9. package/.claude/mem/notes/2026-02-11-visualify-phase-3-complete-developer-experience.md +52 -0
  10. package/.claude/mem/notes/2026-02-11-visualify-repository-cleanup-complete.md +28 -0
  11. package/.claude/mem/notes/2026-02-18-codebase-cleanup-docsify-plugin-documentation.md +37 -0
  12. package/.claude/mem/notes/2026-02-19-css-grid-layout-fix-displaycontents-on-vcontroller.md +18 -0
  13. package/.claude/mem/notes/2026-02-19-docsify-plugin-fixes-latex-and-visualify-code-bloc.md +26 -0
  14. package/.claude/mem/notes/2026-02-19-page-mode-docs-update-decisions.md +23 -0
  15. package/.claude/mem/notes/2026-02-19-react-context-infinite-re-render-loop-fix-pattern.md +31 -0
  16. package/.claude/mem/notes/2026-02-19-version-300-bump-and-build-fixes.md +32 -0
  17. package/.claude/mem/notes/2026-02-19-visualify-build-deployment-architecture-bug-fixes.md +25 -0
  18. package/.claude/mem/notes/2026-02-19-visualify-dist-iife-self-contained-build-config.md +30 -0
  19. package/.claude/mem/notes/2026-02-19-visualify-infinite-loop-i18n-fixes.md +31 -0
  20. package/.claude/mem/notes/2026-02-19-visualify-v3-bundle-splitting-docs-restructuring.md +32 -0
  21. package/.claude/mem/notes/2026-02-20-bundle-externalization-final-architecture.md +29 -0
  22. package/.claude/mem/notes/2026-02-20-chromium-page-fix-unstable-keys-and-double-event-b.md +27 -0
  23. package/.claude/mem/notes/2026-02-20-console-cleanup-bundle-optimization-commit.md +20 -0
  24. package/.claude/mem/notes/2026-02-20-dotbio-dot-plot-fix-useeffect-dependency.md +21 -0
  25. package/.claude/mem/notes/2026-02-20-public-folder-cleanup-and-readme-rewrite.md +25 -0
  26. package/.claude/mem/notes/2026-02-20-v300-release-and-beta-channel-strategy.md +29 -0
  27. package/.claude/mem/notes/2026-02-20-visium-background-image-unknown-legend-fix.md +19 -0
  28. package/.claude/mem/notes/2026-02-20-visualify-cdn-loader-bundle-externalization.md +34 -0
  29. package/.claude/mem/sessions/session-2026-02-20-031524.md +54 -0
  30. package/.claude/settings.local.json +21 -0
  31. package/.github/workflows/static.yml.bak +51 -51
  32. package/.sisyphus/boulder.json +65 -0
  33. package/.sisyphus/plans/phase-4-advanced-optimizations.md +217 -0
  34. package/LICENSE +674 -674
  35. package/README.md +94 -59
  36. package/config-overrides.js +31 -31
  37. package/dist/stats.html +4949 -0
  38. package/dist/visualify-3d.esm.js +1 -0
  39. package/dist/visualify-3d.js +1 -0
  40. package/dist/visualify-core.esm.js +1 -0
  41. package/dist/visualify-core.js +1 -0
  42. package/dist/visualify-docs.esm.js +1 -0
  43. package/dist/visualify-docs.js +1 -0
  44. package/dist/visualify-loader.js +1 -0
  45. package/dist/visualify-pages.esm.js +1 -0
  46. package/dist/visualify-pages.js +1 -0
  47. package/dist/visualify-portal.esm.js +1 -0
  48. package/dist/visualify-portal.js +1 -0
  49. package/dist/visualify-shared.js +26571 -0
  50. package/dist/visualify.js +1 -188
  51. package/docs/CHANGELOG.md +148 -0
  52. package/docs/cli/commands.md +513 -0
  53. package/docs/configuration/visualify-json.md +474 -0
  54. package/docs/docs/3d-visualization.md +374 -0
  55. package/docs/docs/CLI.md +303 -34
  56. package/docs/docs/README.md +65 -65
  57. package/docs/docs/Rechart/bar.md +190 -190
  58. package/docs/docs/Rechart/funnel.md +241 -193
  59. package/docs/docs/Rechart/line.md +355 -355
  60. package/docs/docs/Rechart/pie.md +225 -225
  61. package/docs/docs/Rechart/radar.md +253 -253
  62. package/docs/docs/Rechart/scatter.md +298 -0
  63. package/docs/docs/_404.md +51 -51
  64. package/docs/docs/_coverpage.md +11 -11
  65. package/docs/docs/_sidebar.md +54 -43
  66. package/docs/docs/components/dotBio.md +87 -34
  67. package/docs/docs/components/echart.md +171 -82
  68. package/docs/docs/components/html.md +61 -34
  69. package/docs/docs/components/macaron.md +156 -145
  70. package/docs/docs/components/markdown.md +42 -0
  71. package/docs/docs/components/more.md +183 -142
  72. package/docs/docs/components/plotly.md +132 -62
  73. package/docs/docs/components/scatterL.md +171 -70
  74. package/docs/docs/components/visium.md +112 -57
  75. package/docs/docs/configuration.md +121 -123
  76. package/docs/docs/deploy.md +31 -31
  77. package/docs/docs/docsify-plugin.md +655 -0
  78. package/docs/docs/hmr.md +165 -0
  79. package/docs/docs/i18n.md +332 -0
  80. package/docs/docs/log.md +30 -1
  81. package/docs/docs/more-pages.md +23 -23
  82. package/docs/docs/quickstart.md +148 -119
  83. package/docs/docs/rechart-attributes.md +74 -74
  84. package/docs/docs/rechart-basic-usage.md +160 -162
  85. package/docs/docs/theme.md +5 -5
  86. package/docs/docs/typescript.md +306 -0
  87. package/docs/docs/visual-editor.md +359 -0
  88. package/docs/index.html +85 -71
  89. package/docs/manifest.json +23 -23
  90. package/docs/migration/v3-migration.md +392 -0
  91. package/docs/static/css/fluff-stuff.css +169 -169
  92. package/docs/static/css/font-awesome.min.css +4 -4
  93. package/docs/static/css/visualify.css +6 -25
  94. package/docs/static/js/3d-viz-examples.js +181 -0
  95. package/docs/static/js/configuration.js +630 -448
  96. package/docs/static/js/visualify.js +1 -188
  97. package/package.json +106 -84
  98. package/rollup.config.mjs +766 -76
  99. package/src/_css/404.css +115 -115
  100. package/src/_css/App.css +37 -37
  101. package/src/_css/autoSuggestion.css +26 -26
  102. package/src/_css/circular-progress.css +32 -32
  103. package/src/_css/index.css +36 -36
  104. package/src/_css/modern.css +350 -25
  105. package/src/_media/corner.svg +8 -8
  106. package/src/_media/download.svg +3 -3
  107. package/src/_media/logo.svg +14 -14
  108. package/src/_test/App.test.js +15 -15
  109. package/src/_utils/reportWebVitals.js +13 -13
  110. package/src/a11y/README.md +177 -0
  111. package/src/a11y/aria-labels.js +339 -0
  112. package/src/a11y/color-contrast.js +535 -0
  113. package/src/a11y/index.js +197 -0
  114. package/src/a11y/keyboard-nav.js +523 -0
  115. package/src/a11y/styles.css +165 -0
  116. package/src/cli/commands/dev.js +214 -0
  117. package/src/cli/commands/docs.js +521 -0
  118. package/src/cli/commands/edit.js +379 -0
  119. package/src/cli/commands/init.js +213 -0
  120. package/src/cli/commands/portal.js +236 -0
  121. package/src/cli/dev-server.js +530 -0
  122. package/src/cli/hmr.js +456 -0
  123. package/src/cli/index.js +180 -0
  124. package/src/cli/utils/config.js +207 -0
  125. package/src/cli/utils/logger.js +241 -0
  126. package/src/config/defaults.ts +122 -0
  127. package/src/config/index.ts +72 -0
  128. package/src/config/loader.ts +478 -0
  129. package/src/config/schema.ts +227 -0
  130. package/src/config/validator.ts +337 -0
  131. package/src/core/appContext.js +34 -27
  132. package/src/core/components/Bar.js +383 -0
  133. package/src/core/components/Bar3D.js +473 -0
  134. package/src/core/components/LargeDatasetChart.js +296 -0
  135. package/src/core/components/Line3D.js +310 -0
  136. package/src/core/components/Scatter.js +392 -188
  137. package/src/core/components/Scatter3D.js +455 -0
  138. package/src/core/components/ScatterBio.js +601 -572
  139. package/src/core/components/Surface3D.js +326 -0
  140. package/src/core/components/ThreeCustom.js +648 -0
  141. package/src/core/components/ThreeScene.js +459 -0
  142. package/src/core/components/VisiumPlot.js +191 -165
  143. package/src/core/components/browser.js +42 -42
  144. package/src/core/components/dotplot.js +413 -413
  145. package/src/core/components/html.js +29 -29
  146. package/src/core/components/list.js +178 -178
  147. package/src/core/components/macaron.js +206 -201
  148. package/src/core/components/markdown.js +56 -56
  149. package/src/core/components/parser.scatterBio.js +582 -579
  150. package/src/core/components/ratio.js +82 -80
  151. package/src/core/components/scatterL.js +206 -173
  152. package/src/core/components/searchbar.js +156 -131
  153. package/src/core/components/selection.js +310 -193
  154. package/src/core/components/timeline.js +236 -281
  155. package/src/core/components/visium.js +114 -97
  156. package/src/core/data-processor.js +413 -0
  157. package/src/core/fetch/condfetch.js +82 -82
  158. package/src/core/fetch/fetch.js +92 -92
  159. package/src/core/fetch/json.js +29 -29
  160. package/src/core/fetch/vfetch.js +42 -42
  161. package/src/core/hmr-client.js +724 -0
  162. package/src/core/liveEditor.js +44 -44
  163. package/src/core/modules/codeEditorWithPreview.js +104 -104
  164. package/src/core/modules/echarts/common.js +20 -20
  165. package/src/core/modules/echarts/gl.js +228 -0
  166. package/src/core/modules/echarts/presetHandler.js +41 -41
  167. package/src/core/modules/echarts/presets/esodev.chromium.js +172 -172
  168. package/src/core/modules/echarts/presets/esodev.codex.js +130 -130
  169. package/src/core/modules/echarts/presets/esodev.visium.js +123 -123
  170. package/src/core/modules/echarts/presets/mmtrbc.js +186 -186
  171. package/src/core/modules/echarts.js +70 -71
  172. package/src/core/modules/echartsUtils.js +43 -43
  173. package/src/core/modules/echartswitcher.js +227 -152
  174. package/src/core/modules/replotly/presetHandler.js +24 -24
  175. package/src/core/modules/replotly/presets/minimum.js +18 -18
  176. package/src/core/modules/replotly/presets/mmtrbc.dot.js +114 -114
  177. package/src/core/modules/replotly/presets/mmtrbc.violin.js +100 -100
  178. package/src/core/modules/replotly.js +74 -71
  179. package/src/core/modules/threejs/Camera.js +373 -0
  180. package/src/core/modules/threejs/Lighting.js +459 -0
  181. package/src/core/modules/threejs/Renderer.js +364 -0
  182. package/src/core/modules/threejs/Scene.js +266 -0
  183. package/src/core/modules/threejs/index.js +155 -0
  184. package/src/core/pages/404.js +50 -50
  185. package/src/core/pages/error.js +27 -27
  186. package/src/core/pages/jsonPage.js +62 -62
  187. package/src/core/pages/loading.js +44 -44
  188. package/src/core/parser/echart.data.js +204 -183
  189. package/src/core/parser/echart.features.js +125 -125
  190. package/src/core/parser/echart.general.js +147 -143
  191. package/src/core/parser/echart.hilbert.js +57 -57
  192. package/src/core/parser/echart.parser.js +210 -210
  193. package/src/core/parser/echart.series.js +67 -67
  194. package/src/core/parser/echart.types.js +76 -76
  195. package/src/core/parser/plotly.config.js +10 -10
  196. package/src/core/parser/plotly.data.js +132 -132
  197. package/src/core/parser/plotly.layout.js +9 -9
  198. package/src/core/parser/plotly.violin.js +18 -18
  199. package/src/core/recharts.js +361 -62
  200. package/src/core/router/alias.js +49 -49
  201. package/src/core/router/jsonRouter.js +31 -31
  202. package/src/core/themes/modern.js +32 -32
  203. package/src/core/themes/themeSelector.js +33 -33
  204. package/src/core/visualify.js +213 -47
  205. package/src/core/widgets/circularProgress.js +23 -23
  206. package/src/core/widgets/controller.js +116 -83
  207. package/src/core/widgets/errorBoundary.js +36 -36
  208. package/src/core/widgets/footer.js +185 -177
  209. package/src/core/widgets/header.js +238 -234
  210. package/src/core/widgets/layout/Grid.js +31 -31
  211. package/src/core/widgets/layout.js +36 -36
  212. package/src/core/widgets/mapping.js +56 -42
  213. package/src/core/workers/data-worker.js +349 -0
  214. package/src/core/workers/worker-pool.js +396 -0
  215. package/src/docsify/bundle.js +215 -0
  216. package/src/docsify/markdown.js +271 -0
  217. package/src/docsify/plugin.js +268 -0
  218. package/src/editor/README.md +172 -0
  219. package/src/editor/components/ChartBuilder.jsx +341 -0
  220. package/src/editor/components/ChartTypeSidebar.jsx +91 -0
  221. package/src/editor/components/Editor.jsx +367 -0
  222. package/src/editor/components/Preview.jsx +446 -0
  223. package/src/editor/components/PropertyPanel.jsx +468 -0
  224. package/src/editor/components/StatusBar.jsx +85 -0
  225. package/src/editor/context/EditorContext.js +248 -0
  226. package/src/editor/hooks/useDebounce.js +32 -0
  227. package/src/editor/index.js +315 -0
  228. package/src/editor/styles/editor.css +637 -0
  229. package/src/editor/utils/chartValidator.js +263 -0
  230. package/src/entries/charts3d.js +70 -0
  231. package/src/entries/core.js +78 -0
  232. package/src/entries/docs.js +154 -0
  233. package/src/entries/pages.js +93 -0
  234. package/src/entries/portal.js +204 -0
  235. package/src/entries/shared.js +50 -0
  236. package/src/i18n/formatters.js +455 -0
  237. package/src/i18n/index.js +169 -0
  238. package/src/i18n/locales/ar.json +137 -0
  239. package/src/i18n/locales/de.json +137 -0
  240. package/src/i18n/locales/en.json +137 -0
  241. package/src/i18n/locales/es.json +137 -0
  242. package/src/i18n/locales/he.json +137 -0
  243. package/src/i18n/locales/zh.json +137 -0
  244. package/src/i18n/rtl.css +183 -0
  245. package/src/index.js +82 -62
  246. package/src/loader.js +103 -0
  247. package/src/setupTests.js +5 -5
  248. package/tsconfig.json +51 -0
  249. package/types/charts.d.ts +569 -0
  250. package/types/components.d.ts +441 -0
  251. package/types/config.d.ts +199 -0
  252. package/types/index.d.ts +353 -0
@@ -0,0 +1,379 @@
1
+ /**
2
+ * @fileoverview Visual Editor Command for Visualify CLI
3
+ * @module cli/commands/edit
4
+ *
5
+ * Starts the visual configuration editor for creating and editing
6
+ * Visualify configurations without writing JSON.
7
+ */
8
+
9
+ const { Command } = require('commander');
10
+ const path = require('path');
11
+ const fs = require('fs');
12
+ const http = require('http');
13
+ const logger = require('../utils/logger');
14
+
15
+ /**
16
+ * Default port for the editor server
17
+ * @readonly
18
+ */
19
+ const DEFAULT_PORT = 3456;
20
+
21
+ /**
22
+ * Default host for the editor server
23
+ * @readonly
24
+ */
25
+ const DEFAULT_HOST = 'localhost';
26
+
27
+ /**
28
+ * HTML template for the editor page
29
+ */
30
+ const EDITOR_HTML_TEMPLATE = `<!DOCTYPE html>
31
+ <html lang="en">
32
+ <head>
33
+ <meta charset="UTF-8">
34
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
35
+ <title>Visualify Editor</title>
36
+ <link
37
+ rel="stylesheet"
38
+ href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
39
+ integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN"
40
+ crossorigin="anonymous"
41
+ />
42
+ <style>
43
+ body {
44
+ margin: 0;
45
+ padding: 0;
46
+ overflow: hidden;
47
+ }
48
+ #root {
49
+ height: 100vh;
50
+ width: 100vw;
51
+ }
52
+ .loading-screen {
53
+ display: flex;
54
+ flex-direction: column;
55
+ align-items: center;
56
+ justify-content: center;
57
+ height: 100vh;
58
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
59
+ }
60
+ .loading-spinner {
61
+ width: 50px;
62
+ height: 50px;
63
+ border: 4px solid #f3f3f3;
64
+ border-top: 4px solid #3498db;
65
+ border-radius: 50%;
66
+ animation: spin 1s linear infinite;
67
+ margin-bottom: 20px;
68
+ }
69
+ @keyframes spin {
70
+ 0% { transform: rotate(0deg); }
71
+ 100% { transform: rotate(360deg); }
72
+ }
73
+ </style>
74
+ </head>
75
+ <body>
76
+ <div id="root">
77
+ <div class="loading-screen">
78
+ <div class="loading-spinner"></div>
79
+ <h3>Loading Visualify Editor...</h3>
80
+ <p>Please wait while the editor initializes</p>
81
+ </div>
82
+ </div>
83
+ <script>
84
+ // Embedded editor configuration
85
+ window.VISUALIFY_EDITOR_CONFIG = {{EDITOR_CONFIG}};
86
+ </script>
87
+ <script src="/editor/static/js/editor.js"></script>
88
+ </body>
89
+ </html>`;
90
+
91
+ /**
92
+ * Serve static file
93
+ * @param {http.ServerResponse} res - Response object
94
+ * @param {string} filePath - Path to file
95
+ * @param {string} contentType - MIME type
96
+ */
97
+ function serveStaticFile(res, filePath, contentType) {
98
+ try {
99
+ const content = fs.readFileSync(filePath);
100
+ res.writeHead(200, { 'Content-Type': contentType });
101
+ res.end(content);
102
+ } catch (error) {
103
+ res.writeHead(404);
104
+ res.end('Not found');
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Load configuration from file
110
+ * @param {string} filePath - Path to config file
111
+ * @returns {Object|null} Loaded configuration
112
+ */
113
+ function loadConfigFile(filePath) {
114
+ try {
115
+ const content = fs.readFileSync(filePath, 'utf-8');
116
+ return JSON.parse(content);
117
+ } catch (error) {
118
+ logger.warn(`Failed to load config file: ${error.message}`);
119
+ return null;
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Create the editor server
125
+ * @param {Object} options - Server options
126
+ * @param {number} options.port - Server port
127
+ * @param {string} options.host - Server host
128
+ * @param {string|null} options.configFile - Path to config file
129
+ * @returns {http.Server}
130
+ */
131
+ function createEditorServer(options) {
132
+ const { port, host, configFile } = options;
133
+
134
+ // Load config if specified
135
+ let editorConfig = {
136
+ version: '3.0.0',
137
+ charts: [],
138
+ layout: { type: 'grid', rows: 1, cols: 1, gap: '10px' },
139
+ theme: 'modern',
140
+ };
141
+
142
+ if (configFile) {
143
+ const loadedConfig = loadConfigFile(configFile);
144
+ if (loadedConfig) {
145
+ editorConfig = { ...editorConfig, ...loadedConfig };
146
+ logger.success(`Loaded configuration from ${configFile}`);
147
+ }
148
+ }
149
+
150
+ const server = http.createServer((req, res) => {
151
+ // Enable CORS
152
+ res.setHeader('Access-Control-Allow-Origin', '*');
153
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
154
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
155
+
156
+ if (req.method === 'OPTIONS') {
157
+ res.writeHead(200);
158
+ res.end();
159
+ return;
160
+ }
161
+
162
+ const url = new URL(req.url, `http://${host}:${port}`);
163
+ const pathname = url.pathname;
164
+
165
+ // Main editor page
166
+ if (pathname === '/' || pathname === '/editor') {
167
+ const html = EDITOR_HTML_TEMPLATE.replace(
168
+ '{{EDITOR_CONFIG}}',
169
+ JSON.stringify(editorConfig).replace(/</g, '\\u003c')
170
+ );
171
+ res.writeHead(200, { 'Content-Type': 'text/html' });
172
+ res.end(html);
173
+ return;
174
+ }
175
+
176
+ // Static assets - editor bundle
177
+ if (pathname === '/editor/static/js/editor.js') {
178
+ const editorJsPath = path.join(__dirname, '../../../editor/static/js/editor.js');
179
+ if (fs.existsSync(editorJsPath)) {
180
+ serveStaticFile(res, editorJsPath, 'application/javascript');
181
+ } else {
182
+ // Return placeholder if bundle doesn't exist yet
183
+ res.writeHead(200, { 'Content-Type': 'application/javascript' });
184
+ res.end(`
185
+ // Visualify Editor Bundle
186
+ // This is a placeholder. In production, this would be the compiled editor bundle.
187
+ console.log('Visualify Editor loaded');
188
+
189
+ // Simple editor implementation for demo
190
+ const root = document.getElementById('root');
191
+ const config = window.VISUALIFY_EDITOR_CONFIG || {};
192
+
193
+ root.innerHTML = \`
194
+ <div style="padding: 20px; font-family: sans-serif;">
195
+ <h1>Visualify Editor</h1>
196
+ <p>Configuration loaded with \${config.charts?.length || 0} charts</p>
197
+ <div style="background: #f5f5f5; padding: 15px; border-radius: 8px; margin-top: 20px;">
198
+ <pre style="margin: 0; overflow: auto;">\${JSON.stringify(config, null, 2)}</pre>
199
+ </div>
200
+ <p style="margin-top: 20px; color: #666;">
201
+ <strong>Note:</strong> This is a development preview.
202
+ The full editor requires building the React application.
203
+ </p>
204
+ </div>
205
+ \`;
206
+ `);
207
+ }
208
+ return;
209
+ }
210
+
211
+ // API endpoints
212
+ if (pathname === '/api/config') {
213
+ if (req.method === 'GET') {
214
+ res.writeHead(200, { 'Content-Type': 'application/json' });
215
+ res.end(JSON.stringify(editorConfig));
216
+ return;
217
+ }
218
+
219
+ if (req.method === 'POST') {
220
+ let body = '';
221
+ req.on('data', chunk => body += chunk);
222
+ req.on('end', () => {
223
+ try {
224
+ const newConfig = JSON.parse(body);
225
+ Object.assign(editorConfig, newConfig);
226
+ res.writeHead(200, { 'Content-Type': 'application/json' });
227
+ res.end(JSON.stringify({ success: true }));
228
+ } catch (error) {
229
+ res.writeHead(400, { 'Content-Type': 'application/json' });
230
+ res.end(JSON.stringify({ error: error.message }));
231
+ }
232
+ });
233
+ return;
234
+ }
235
+ }
236
+
237
+ // 404
238
+ res.writeHead(404, { 'Content-Type': 'application/json' });
239
+ res.end(JSON.stringify({ error: 'Not found' }));
240
+ });
241
+
242
+ return server;
243
+ }
244
+
245
+ /**
246
+ * Open browser
247
+ * @param {string} url - URL to open
248
+ */
249
+ function openBrowser(url) {
250
+ const { exec } = require('child_process');
251
+ const platform = process.platform;
252
+
253
+ let command;
254
+ switch (platform) {
255
+ case 'darwin':
256
+ command = `open "${url}"`;
257
+ break;
258
+ case 'win32':
259
+ command = `start "" "${url}"`;
260
+ break;
261
+ default:
262
+ command = `xdg-open "${url}"`;
263
+ }
264
+
265
+ exec(command, (error) => {
266
+ if (error) {
267
+ logger.debug('Failed to open browser:', error.message);
268
+ }
269
+ });
270
+ }
271
+
272
+ /**
273
+ * Execute the edit command
274
+ * @param {string|null} configFile - Path to config file
275
+ * @param {Object} options - Command options
276
+ * @param {number} options.port - Server port
277
+ * @param {string} options.host - Server host
278
+ * @param {boolean} options.open - Whether to open browser
279
+ * @returns {Promise<void>}
280
+ */
281
+ async function executeEdit(configFile, options) {
282
+ try {
283
+ const port = parseInt(options.port, 10) || DEFAULT_PORT;
284
+ const host = options.host || DEFAULT_HOST;
285
+ const shouldOpen = options.open !== false;
286
+
287
+ logger.header('Visualify Editor');
288
+ logger.info('Starting visual configuration editor...');
289
+
290
+ // Validate config file if provided
291
+ if (configFile && !fs.existsSync(configFile)) {
292
+ logger.warn(`Config file not found: ${configFile}`);
293
+ logger.tip('Starting with default configuration');
294
+ configFile = null;
295
+ }
296
+
297
+ // Create and start server
298
+ const server = createEditorServer({ port, host, configFile });
299
+
300
+ server.listen(port, host, () => {
301
+ const url = `http://${host}:${port}`;
302
+
303
+ logger.success('Editor server started');
304
+ logger.newline();
305
+ logger.info(`Local: ${url}`);
306
+ logger.info(`Network: http://${require('os').networkInterfaces()['en0']?.[0]?.address || host}:${port}`);
307
+ logger.newline();
308
+
309
+ if (configFile) {
310
+ logger.info(`Editing: ${path.resolve(configFile)}`);
311
+ } else {
312
+ logger.info('Editing: New configuration');
313
+ }
314
+
315
+ logger.newline();
316
+ logger.tip('Press Ctrl+C to stop the server');
317
+ logger.newline();
318
+
319
+ // Open browser
320
+ if (shouldOpen) {
321
+ setTimeout(() => {
322
+ openBrowser(url);
323
+ }, 1000);
324
+ }
325
+ });
326
+
327
+ // Handle server errors
328
+ server.on('error', (error) => {
329
+ if (error.code === 'EADDRINUSE') {
330
+ logger.error(`Port ${port} is already in use`);
331
+ logger.tip(`Try a different port with: visualify edit --port ${port + 1}`);
332
+ } else {
333
+ logger.error('Server error:', error.message);
334
+ }
335
+ process.exit(1);
336
+ });
337
+
338
+ // Graceful shutdown
339
+ process.on('SIGINT', () => {
340
+ logger.newline();
341
+ logger.info('Shutting down editor server...');
342
+ server.close(() => {
343
+ logger.success('Server stopped');
344
+ process.exit(0);
345
+ });
346
+ });
347
+
348
+ } catch (error) {
349
+ logger.error('Failed to start editor:', error.message);
350
+ logger.debug('Stack trace:', error.stack);
351
+ process.exit(1);
352
+ }
353
+ }
354
+
355
+ /**
356
+ * Create and configure the edit command
357
+ * @returns {Command} The configured command
358
+ */
359
+ function createEditCommand() {
360
+ const command = new Command('edit')
361
+ .description('Start the visual configuration editor')
362
+ .argument('[file]', 'Configuration file to edit (optional)')
363
+ .option('-p, --port <number>', 'Port to run the editor on', String(DEFAULT_PORT))
364
+ .option('-h, --host <host>', 'Host to bind the server to', DEFAULT_HOST)
365
+ .option('--no-open', 'Do not open browser automatically')
366
+ .action(executeEdit);
367
+
368
+ // Add alias 'editor' for convenience
369
+ command.alias('editor');
370
+
371
+ return command;
372
+ }
373
+
374
+ module.exports = {
375
+ createEditCommand,
376
+ executeEdit,
377
+ DEFAULT_PORT,
378
+ DEFAULT_HOST,
379
+ };
@@ -0,0 +1,213 @@
1
+ /**
2
+ * @fileoverview Project initialization command for Visualify CLI
3
+ * @module cli/commands/init
4
+ */
5
+
6
+ const { Command } = require('commander');
7
+ const logger = require('../utils/logger');
8
+
9
+ /**
10
+ * Valid template types
11
+ * @readonly
12
+ * @type {string[]}
13
+ */
14
+ const VALID_TEMPLATES = ['docs', 'portal', 'full'];
15
+
16
+ /**
17
+ * Default template when not specified
18
+ * @readonly
19
+ * @type {string}
20
+ */
21
+ const DEFAULT_TEMPLATE = 'full';
22
+
23
+ /**
24
+ * Template descriptions for help text
25
+ * @readonly
26
+ * @type {Object<string, string>}
27
+ */
28
+ const TEMPLATE_DESCRIPTIONS = {
29
+ docs: 'Documentation-only project with markdown support',
30
+ portal: 'Data portal with visualization components',
31
+ full: 'Complete project with both docs and portal features',
32
+ };
33
+
34
+ /**
35
+ * Execute the init command
36
+ * @param {string} template - The template to use
37
+ * @param {Object} options - Command options
38
+ * @param {boolean} options.verbose - Enable verbose logging
39
+ * @param {string} [options.name] - Project name
40
+ * @param {string} [options.dir] - Target directory
41
+ * @returns {Promise<void>}
42
+ */
43
+ async function executeInit(template, options) {
44
+ try {
45
+ if (options.verbose) {
46
+ logger.enableVerbose();
47
+ }
48
+
49
+ logger.debug('Initializing new project');
50
+ logger.debug('Template:', template);
51
+ logger.debug('Options:', options);
52
+
53
+ // Validate template
54
+ if (!VALID_TEMPLATES.includes(template)) {
55
+ logger.error(`Invalid template: "${template}"`);
56
+ logger.tip(`Valid templates are: ${VALID_TEMPLATES.join(', ')}`);
57
+ logger.newline();
58
+ logger.info('Template descriptions:');
59
+ Object.entries(TEMPLATE_DESCRIPTIONS).forEach(([key, desc]) => {
60
+ logger.example(`${key}`, desc);
61
+ });
62
+ process.exit(1);
63
+ }
64
+
65
+ const projectName = options.name || 'my-visualify-project';
66
+ const targetDir = options.dir || `./${projectName}`;
67
+
68
+ logger.header('Project Initialization');
69
+ logger.info(`Creating new ${chalk.cyan(template)} project...`);
70
+ logger.info(`Project name: ${chalk.cyan(projectName)}`);
71
+ logger.info(`Target directory: ${chalk.cyan(targetDir)}`);
72
+
73
+ // TODO: Implement actual project scaffolding
74
+ await scaffoldProject(template, projectName, targetDir, options);
75
+
76
+ logger.success('Project initialized successfully!');
77
+ logger.newline();
78
+ logger.info('Next steps:');
79
+ logger.example(`cd ${projectName}`, 'Navigate to project directory');
80
+ logger.example('npm install', 'Install dependencies');
81
+ logger.example('visualify dev', 'Start development server');
82
+ } catch (err) {
83
+ logger.error('Failed to initialize project:', err.message);
84
+ logger.debug('Stack trace:', err.stack);
85
+ process.exit(1);
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Scaffold a new project
91
+ * @param {string} template - The template type
92
+ * @param {string} projectName - The project name
93
+ * @param {string} targetDir - The target directory
94
+ * @param {Object} options - Command options
95
+ * @returns {Promise<void>}
96
+ */
97
+ async function scaffoldProject(template, projectName, targetDir, options) {
98
+ logger.debug('Scaffolding project...');
99
+
100
+ // Simulate project creation steps
101
+ const steps = [
102
+ 'Creating directory structure',
103
+ 'Generating configuration files',
104
+ 'Copying template files',
105
+ 'Creating package.json',
106
+ ];
107
+
108
+ for (const step of steps) {
109
+ logger.info(` ${chalk.gray('→')} ${step}`);
110
+ // TODO: Implement actual file operations
111
+ await simulateDelay(100);
112
+ }
113
+
114
+ // Create template-specific files
115
+ switch (template) {
116
+ case 'docs':
117
+ await scaffoldDocsTemplate(targetDir);
118
+ break;
119
+ case 'portal':
120
+ await scaffoldPortalTemplate(targetDir);
121
+ break;
122
+ case 'full':
123
+ await scaffoldFullTemplate(targetDir);
124
+ break;
125
+ }
126
+
127
+ logger.debug('Scaffolding complete');
128
+ }
129
+
130
+ /**
131
+ * Scaffold a docs-only template
132
+ * @param {string} targetDir - The target directory
133
+ * @returns {Promise<void>}
134
+ */
135
+ async function scaffoldDocsTemplate(targetDir) {
136
+ logger.debug('Creating docs template files');
137
+ // TODO: Implement docs template scaffolding
138
+ }
139
+
140
+ /**
141
+ * Scaffold a portal-only template
142
+ * @param {string} targetDir - The target directory
143
+ * @returns {Promise<void>}
144
+ */
145
+ async function scaffoldPortalTemplate(targetDir) {
146
+ logger.debug('Creating portal template files');
147
+ // TODO: Implement portal template scaffolding
148
+ }
149
+
150
+ /**
151
+ * Scaffold a full template with both docs and portal
152
+ * @param {string} targetDir - The target directory
153
+ * @returns {Promise<void>}
154
+ */
155
+ async function scaffoldFullTemplate(targetDir) {
156
+ logger.debug('Creating full template files');
157
+ // TODO: Implement full template scaffolding
158
+ }
159
+
160
+ /**
161
+ * Simulate a delay for async operations
162
+ * @param {number} ms - Milliseconds to delay
163
+ * @returns {Promise<void>}
164
+ */
165
+ function simulateDelay(ms) {
166
+ return new Promise((resolve) => setTimeout(resolve, ms));
167
+ }
168
+
169
+ /**
170
+ * Create and configure the init command
171
+ * @returns {Command} The configured command
172
+ */
173
+ function createInitCommand() {
174
+ const command = new Command('init')
175
+ .description('Initialize a new Visualify project')
176
+ .argument(
177
+ '[template]',
178
+ `Project template: ${VALID_TEMPLATES.join(', ')}`,
179
+ DEFAULT_TEMPLATE
180
+ )
181
+ .option('-v, --verbose', 'Enable verbose logging')
182
+ .option('-n, --name <name>', 'Project name')
183
+ .option('-d, --dir <directory>', 'Target directory')
184
+ .addHelpText(
185
+ 'after',
186
+ `
187
+ Templates:
188
+ docs ${TEMPLATE_DESCRIPTIONS.docs}
189
+ portal ${TEMPLATE_DESCRIPTIONS.portal}
190
+ full ${TEMPLATE_DESCRIPTIONS.full} (default)
191
+
192
+ Examples:
193
+ $ visualify init Initialize with default (full) template
194
+ $ visualify init docs Initialize docs-only project
195
+ $ visualify init portal -n my-app Initialize portal with custom name
196
+ $ visualify init full -d ./my-dir Initialize in specific directory
197
+ `
198
+ )
199
+ .action(executeInit);
200
+
201
+ return command;
202
+ }
203
+
204
+ // Import chalk for use in this module
205
+ const chalk = require('chalk');
206
+
207
+ module.exports = {
208
+ createInitCommand,
209
+ executeInit,
210
+ VALID_TEMPLATES,
211
+ DEFAULT_TEMPLATE,
212
+ TEMPLATE_DESCRIPTIONS,
213
+ };