visualifyjs 2.5.3-2.dev → 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 -241
  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 -298
  63. package/docs/docs/_404.md +51 -51
  64. package/docs/docs/_coverpage.md +11 -11
  65. package/docs/docs/_sidebar.md +54 -44
  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 -121
  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 -9
  81. package/docs/docs/more-pages.md +23 -23
  82. package/docs/docs/quickstart.md +148 -124
  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 -587
  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 -147
  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,521 @@
1
+ /**
2
+ * @fileoverview Documentation commands for Visualify CLI
3
+ * @module cli/commands/docs
4
+ *
5
+ * Implements `visualify docs dev` and `visualify docs build` commands
6
+ * with full Docsify integration and Visualify plugin support.
7
+ */
8
+
9
+ const { Command } = require('commander');
10
+ const fs = require('fs').promises;
11
+ const path = require('path');
12
+ const { spawn, exec } = require('child_process');
13
+ const logger = require('../utils/logger');
14
+ const { loadConfig, fileExists } = require('../utils/config');
15
+
16
+ /**
17
+ * Valid documentation actions
18
+ * @readonly
19
+ * @type {string[]}
20
+ */
21
+ const VALID_ACTIONS = ['dev', 'build'];
22
+
23
+ /**
24
+ * Default Docsify configuration
25
+ * @readonly
26
+ * @type {Object}
27
+ */
28
+ const DEFAULT_DOCSIFY_CONFIG = {
29
+ name: 'Documentation',
30
+ repo: '',
31
+ loadSidebar: true,
32
+ loadNavbar: true,
33
+ coverpage: false,
34
+ onlyCover: false,
35
+ auto2top: true,
36
+ maxLevel: 4,
37
+ subMaxLevel: 2,
38
+ mergeNavbar: true,
39
+ search: {
40
+ maxAge: 86400000,
41
+ paths: 'auto',
42
+ placeholder: 'Type to search',
43
+ noData: 'No results!',
44
+ depth: 2,
45
+ hideOtherSidebarContent: false,
46
+ },
47
+ plugins: [],
48
+ };
49
+
50
+ /**
51
+ * Find the docs directory
52
+ * @param {string} [customPath] - Custom path provided by user
53
+ * @returns {Promise<string|null>} Path to docs directory or null
54
+ */
55
+ async function findDocsDirectory(customPath) {
56
+ if (customPath) {
57
+ const resolvedPath = path.resolve(customPath);
58
+ if (await fileExists(resolvedPath)) {
59
+ const stat = await fs.stat(resolvedPath);
60
+ if (stat.isDirectory()) {
61
+ return resolvedPath;
62
+ }
63
+ }
64
+ logger.error(`Docs directory not found: ${resolvedPath}`);
65
+ return null;
66
+ }
67
+
68
+ // Try common docs directory names
69
+ const candidates = ['docs', 'doc', 'documentation', 'md'];
70
+ const cwd = process.cwd();
71
+
72
+ for (const dir of candidates) {
73
+ const dirPath = path.join(cwd, dir);
74
+ if (await fileExists(dirPath)) {
75
+ const stat = await fs.stat(dirPath);
76
+ if (stat.isDirectory()) {
77
+ logger.debug(`Found docs directory: ${dirPath}`);
78
+ return dirPath;
79
+ }
80
+ }
81
+ }
82
+
83
+ // Default to current directory if it has markdown files
84
+ try {
85
+ const files = await fs.readdir(cwd);
86
+ const hasMarkdown = files.some((f) => f.endsWith('.md'));
87
+ if (hasMarkdown) {
88
+ logger.debug('Using current directory as docs root (found markdown files)');
89
+ return cwd;
90
+ }
91
+ } catch (err) {
92
+ logger.debug('Error checking current directory:', err.message);
93
+ }
94
+
95
+ return null;
96
+ }
97
+
98
+ /**
99
+ * Ensure docsify-cli is installed
100
+ * @returns {Promise<boolean>}
101
+ */
102
+ async function ensureDocsifyCLI() {
103
+ return new Promise((resolve) => {
104
+ exec('npx docsify --version', (error) => {
105
+ if (error) {
106
+ logger.warn('docsify-cli not found. It will be installed when needed.');
107
+ resolve(false);
108
+ } else {
109
+ resolve(true);
110
+ }
111
+ });
112
+ });
113
+ }
114
+
115
+ /**
116
+ * Generate Visualify plugin script for Docsify
117
+ * @returns {string} Script tag HTML
118
+ */
119
+ function generateVisualifyScript() {
120
+ const visualifyPath = path.join(__dirname, '../../../dist/visualify-docsify.js');
121
+
122
+ // Check if built bundle exists, otherwise use source
123
+ return `
124
+ <!-- Visualify Plugin for Docsify -->
125
+ <script>
126
+ window.$docsify = window.$docsify || {};
127
+ window.$docsify.plugins = [].concat(
128
+ window.$docsify.plugins || [],
129
+ function(hook, vm) {
130
+ hook.init(function() {
131
+ console.log('[Visualify] Plugin initialized');
132
+ });
133
+
134
+ hook.mounted(function() {
135
+ if (window.VisualifyDocsify) {
136
+ window.VisualifyDocsify.mountAll();
137
+ }
138
+ });
139
+
140
+ hook.doneEach(function() {
141
+ if (window.VisualifyDocsify) {
142
+ window.VisualifyDocsify.mountAll();
143
+ }
144
+ });
145
+ }
146
+ );
147
+ </script>
148
+ <script src="https://cdn.jsdelivr.net/npm/visualifyjs@latest/dist/visualify-docsify.min.js"></script>
149
+ `;
150
+ }
151
+
152
+ /**
153
+ * Generate index.html for Docsify
154
+ * @param {Object} options - Generation options
155
+ * @param {string} options.docsPath - Path to docs directory
156
+ * @param {Object} [options.config] - Custom Docsify configuration
157
+ * @returns {string} HTML content
158
+ */
159
+ async function generateIndexHtml(options) {
160
+ const { docsPath, config = {} } = options;
161
+
162
+ // Try to load existing configuration
163
+ const visualifyConfig = await loadConfig().catch(() => ({}));
164
+ const docsifyConfig = {
165
+ ...DEFAULT_DOCSIFY_CONFIG,
166
+ ...visualifyConfig.docsify,
167
+ ...config,
168
+ };
169
+
170
+ // Check for README.md
171
+ const readmePath = path.join(docsPath, 'README.md');
172
+ const hasReadme = await fileExists(readmePath);
173
+
174
+ if (!hasReadme) {
175
+ logger.warn('No README.md found in docs directory');
176
+ }
177
+
178
+ const configJson = JSON.stringify(docsifyConfig, null, 2);
179
+
180
+ return `<!DOCTYPE html>
181
+ <html lang="en">
182
+ <head>
183
+ <meta charset="UTF-8">
184
+ <title>${docsifyConfig.name}</title>
185
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
186
+ <meta name="description" content="${docsifyConfig.name}">
187
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
188
+ <link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/vue.css">
189
+ <style>
190
+ .visualify-chart-wrapper {
191
+ margin: 1em 0;
192
+ border: 1px solid #e8e8e8;
193
+ border-radius: 4px;
194
+ overflow: hidden;
195
+ }
196
+ .visualify-chart-container {
197
+ width: 100%;
198
+ min-height: 400px;
199
+ }
200
+ .visualify-error {
201
+ padding: 16px;
202
+ border: 1px solid #ff4d4f;
203
+ border-radius: 4px;
204
+ background: #fff2f0;
205
+ color: #cf1322;
206
+ margin: 16px 0;
207
+ }
208
+ </style>
209
+ </head>
210
+ <body>
211
+ <div id="app"></div>
212
+ <script>
213
+ window.$docsify = ${configJson};
214
+ </script>
215
+ <!-- Docsify v4 -->
216
+ <script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
217
+ <!-- Search plugin -->
218
+ <script src="//cdn.jsdelivr.net/npm/docsify@4/lib/plugins/search.min.js"></script>
219
+ <!-- Copy code plugin -->
220
+ <script src="//cdn.jsdelivr.net/npm/docsify-copy-code@2"></script>
221
+ ${generateVisualifyScript()}
222
+ </body>
223
+ </html>
224
+ `;
225
+ }
226
+
227
+ /**
228
+ * Execute the docs dev command
229
+ * @param {string} [docsPath] - Path to docs directory
230
+ * @param {Object} options - Command options
231
+ * @param {boolean} options.verbose - Enable verbose logging
232
+ * @param {string} options.port - Port to run server on
233
+ * @param {string} options.host - Host to bind server to
234
+ * @param {boolean} options.open - Open browser automatically
235
+ * @returns {Promise<void>}
236
+ */
237
+ async function executeDocsDev(docsPath, options) {
238
+ try {
239
+ if (options.verbose) {
240
+ logger.enableVerbose();
241
+ }
242
+
243
+ logger.debug('Starting docs dev server');
244
+ logger.debug('Options:', options);
245
+
246
+ // Find docs directory
247
+ const targetPath = await findDocsDirectory(docsPath);
248
+ if (!targetPath) {
249
+ logger.error('Could not find docs directory');
250
+ logger.tip('Create a docs/ directory or specify a path: visualify docs dev ./my-docs');
251
+ process.exit(1);
252
+ }
253
+
254
+ logger.header('Documentation Development Server');
255
+ logger.info(`Docs directory: ${targetPath}`);
256
+
257
+ // Ensure index.html exists
258
+ const indexPath = path.join(targetPath, 'index.html');
259
+ if (!(await fileExists(indexPath))) {
260
+ logger.info('Creating index.html...');
261
+ const html = await generateIndexHtml({ docsPath: targetPath });
262
+ await fs.writeFile(indexPath, html, 'utf-8');
263
+ logger.success('Created index.html');
264
+ }
265
+
266
+ // Check for docsify-cli
267
+ await ensureDocsifyCLI();
268
+
269
+ const port = options.port || '3000';
270
+ const host = options.host || 'localhost';
271
+
272
+ logger.info('Starting Docsify dev server...');
273
+ logger.debug(`Port: ${port}, Host: ${host}`);
274
+
275
+ // Build docsify-cli command
276
+ const args = ['docsify', 'serve', targetPath, '--port', port, '--host', host];
277
+
278
+ if (options.open) {
279
+ args.push('--open');
280
+ }
281
+
282
+ // Spawn docsify serve process
283
+ const child = spawn('npx', args, {
284
+ stdio: 'pipe',
285
+ shell: true,
286
+ });
287
+
288
+ let serverStarted = false;
289
+
290
+ child.stdout.on('data', (data) => {
291
+ const output = data.toString();
292
+
293
+ // Filter and format Docsify output
294
+ if (output.includes('Listening') || output.includes('http')) {
295
+ if (!serverStarted) {
296
+ serverStarted = true;
297
+ logger.success(`Server running at http://${host}:${port}`);
298
+ logger.newline();
299
+ logger.info('Features:');
300
+ logger.example('Hot reload', 'Changes are automatically refreshed');
301
+ logger.example('Visualify charts', 'Code blocks with ```visualify are rendered as charts');
302
+ logger.newline();
303
+ logger.tip('Press Ctrl+C to stop the server');
304
+ }
305
+ }
306
+
307
+ if (options.verbose) {
308
+ process.stdout.write(output);
309
+ }
310
+ });
311
+
312
+ child.stderr.on('data', (data) => {
313
+ const output = data.toString();
314
+
315
+ // Only show errors unless verbose
316
+ if (options.verbose || output.includes('error') || output.includes('Error')) {
317
+ process.stderr.write(output);
318
+ }
319
+ });
320
+
321
+ child.on('close', (code) => {
322
+ if (code !== 0 && code !== null) {
323
+ logger.error(`Docsify server exited with code ${code}`);
324
+ process.exit(1);
325
+ }
326
+ });
327
+
328
+ child.on('error', (err) => {
329
+ logger.error('Failed to start Docsify server:', err.message);
330
+ logger.tip('Try installing docsify-cli globally: npm i -g docsify-cli');
331
+ process.exit(1);
332
+ });
333
+
334
+ // Handle graceful shutdown
335
+ process.on('SIGINT', () => {
336
+ logger.newline();
337
+ logger.info('Shutting down server...');
338
+ child.kill('SIGINT');
339
+ });
340
+
341
+ process.on('SIGTERM', () => {
342
+ child.kill('SIGTERM');
343
+ });
344
+
345
+ } catch (err) {
346
+ logger.error('Failed to start docs dev server:', err.message);
347
+ logger.debug('Stack trace:', err.stack);
348
+ process.exit(1);
349
+ }
350
+ }
351
+
352
+ /**
353
+ * Execute the docs build command
354
+ * @param {string} [docsPath] - Path to docs directory
355
+ * @param {string} [destPath] - Destination path for built files
356
+ * @param {Object} options - Command options
357
+ * @param {boolean} options.verbose - Enable verbose logging
358
+ * @returns {Promise<void>}
359
+ */
360
+ async function executeDocsBuild(docsPath, destPath, options) {
361
+ try {
362
+ if (options.verbose) {
363
+ logger.enableVerbose();
364
+ }
365
+
366
+ logger.debug('Building documentation');
367
+ logger.debug('Options:', options);
368
+
369
+ // Find docs directory
370
+ const sourcePath = await findDocsDirectory(docsPath);
371
+ if (!sourcePath) {
372
+ logger.error('Could not find docs directory');
373
+ logger.tip('Create a docs/ directory or specify a path: visualify docs build ./my-docs');
374
+ process.exit(1);
375
+ }
376
+
377
+ // Determine output directory
378
+ const outputDir = destPath
379
+ ? path.resolve(destPath)
380
+ : path.join(process.cwd(), 'dist-docs');
381
+
382
+ logger.header('Documentation Build');
383
+ logger.info(`Source: ${sourcePath}`);
384
+ logger.info(`Output: ${outputDir}`);
385
+
386
+ // Ensure index.html exists
387
+ const indexPath = path.join(sourcePath, 'index.html');
388
+ if (!(await fileExists(indexPath))) {
389
+ logger.info('Creating index.html...');
390
+ const html = await generateIndexHtml({ docsPath: sourcePath });
391
+ await fs.writeFile(indexPath, html, 'utf-8');
392
+ logger.success('Created index.html');
393
+ }
394
+
395
+ // Create output directory
396
+ await fs.mkdir(outputDir, { recursive: true });
397
+
398
+ // Copy all files from source to output
399
+ logger.info('Copying files...');
400
+ await copyDirectory(sourcePath, outputDir);
401
+
402
+ logger.success('Documentation built successfully!');
403
+ logger.newline();
404
+ logger.info('To serve the built documentation:');
405
+ logger.example(`npx serve ${outputDir}`, 'Serve with npx serve');
406
+ logger.example(`python -m http.server -d ${outputDir}`, 'Serve with Python');
407
+
408
+ } catch (err) {
409
+ logger.error('Failed to build documentation:', err.message);
410
+ logger.debug('Stack trace:', err.stack);
411
+ process.exit(1);
412
+ }
413
+ }
414
+
415
+ /**
416
+ * Copy directory recursively
417
+ * @param {string} src - Source directory
418
+ * @param {string} dest - Destination directory
419
+ */
420
+ async function copyDirectory(src, dest) {
421
+ const entries = await fs.readdir(src, { withFileTypes: true });
422
+
423
+ for (const entry of entries) {
424
+ const srcPath = path.join(src, entry.name);
425
+ const destPath = path.join(dest, entry.name);
426
+
427
+ if (entry.isDirectory()) {
428
+ await fs.mkdir(destPath, { recursive: true });
429
+ await copyDirectory(srcPath, destPath);
430
+ } else {
431
+ await fs.copyFile(srcPath, destPath);
432
+ }
433
+ }
434
+ }
435
+
436
+ /**
437
+ * Execute the docs command with a subcommand
438
+ * @param {string} action - The action to perform
439
+ * @param {string} [docsPath] - Path to docs directory
440
+ * @param {string} [destPath] - Destination path (for build)
441
+ * @param {Object} options - Command options
442
+ * @returns {Promise<void>}
443
+ */
444
+ async function executeDocs(action, docsPath, destPath, options) {
445
+ if (!VALID_ACTIONS.includes(action)) {
446
+ logger.error(`Invalid action: "${action}"`);
447
+ logger.tip(`Valid actions are: ${VALID_ACTIONS.join(', ')}`);
448
+ process.exit(1);
449
+ }
450
+
451
+ switch (action) {
452
+ case 'dev':
453
+ await executeDocsDev(docsPath, options);
454
+ break;
455
+ case 'build':
456
+ await executeDocsBuild(docsPath, destPath, options);
457
+ break;
458
+ }
459
+ }
460
+
461
+ /**
462
+ * Create and configure the docs command
463
+ * @returns {Command} The configured command
464
+ */
465
+ function createDocsCommand() {
466
+ const command = new Command('docs')
467
+ .description('Documentation management commands')
468
+ .addHelpText(
469
+ 'after',
470
+ `
471
+ Examples:
472
+ $ visualify docs dev Start dev server (auto-detect docs dir)
473
+ $ visualify docs dev ./docs Start dev server with specific path
474
+ $ visualify docs dev -p 8080 Start on port 8080
475
+ $ visualify docs build Build to ./dist-docs
476
+ $ visualify docs build ./docs ./site Build to custom output directory
477
+ $ visualify docs dev --open Start and open browser
478
+
479
+ Configuration:
480
+ Create visualify.json to customize Docsify behavior:
481
+ {
482
+ "docsify": {
483
+ "name": "My Documentation",
484
+ "themeColor": "#3F51B5",
485
+ "search": { "depth": 3 }
486
+ }
487
+ }
488
+ `
489
+ );
490
+
491
+ // Add subcommands
492
+ command
493
+ .command('dev')
494
+ .description('Start the documentation development server')
495
+ .argument('[path]', 'Path to docs directory (default: auto-detect)')
496
+ .option('-v, --verbose', 'Enable verbose logging')
497
+ .option('-p, --port <number>', 'Port to run the server on', '3000')
498
+ .option('-h, --host <host>', 'Host to bind the server to', 'localhost')
499
+ .option('-o, --open', 'Open browser automatically')
500
+ .action((path, options) => executeDocsDev(path, options));
501
+
502
+ command
503
+ .command('build')
504
+ .description('Build static documentation')
505
+ .argument('[path]', 'Path to docs directory (default: auto-detect)')
506
+ .argument('[dest]', 'Output directory (default: ./dist-docs)')
507
+ .option('-v, --verbose', 'Enable verbose logging')
508
+ .action((path, dest, options) => executeDocsBuild(path, dest, options));
509
+
510
+ return command;
511
+ }
512
+
513
+ module.exports = {
514
+ createDocsCommand,
515
+ executeDocs,
516
+ executeDocsDev,
517
+ executeDocsBuild,
518
+ VALID_ACTIONS,
519
+ generateIndexHtml,
520
+ findDocsDirectory,
521
+ };