vimd 0.4.1 → 0.5.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.
package/README.md CHANGED
@@ -65,10 +65,10 @@
65
65
  ### インストール
66
66
 
67
67
  ```bash
68
- npm install -g vimd@0.3.11
68
+ npm install -g vimd
69
69
  ```
70
70
 
71
- **現在の安定版: v0.2.4**
71
+ **現在のバージョン: v0.5.0**
72
72
 
73
73
  v0.2.0 からは **pandoc なしで利用可能** になりました。
74
74
  高品質な出力が必要な場合のみ pandoc をインストールしてください。
@@ -103,21 +103,12 @@ vimd config
103
103
 
104
104
  ## バージョン情報
105
105
 
106
- **安定版: v0.2.4**
106
+ **現在のバージョン: v0.5.0**
107
107
 
108
- v0.3.x は内部アーキテクチャを刷新した実験的バージョンです。
109
- 安定した動作を求める場合は v0.2.4 をご利用ください。
110
-
111
- 安定版のインストール:
112
-
113
- ```bash
114
- npm install -g vimd@0.2.4
115
- ```
116
-
117
- 最新版(実験的)のインストール:
108
+ v0.5.x はフォルダモード(複数ファイルプレビュー)を追加したバージョンです。
118
109
 
119
110
  ```bash
120
- npm install -g vimd@latest
111
+ npm install -g vimd
121
112
  ```
122
113
 
123
114
  ---
@@ -127,6 +118,7 @@ npm install -g vimd@latest
127
118
  | コマンド | 説明 |
128
119
  | ------------------- | ------------------------------ |
129
120
  | `vimd dev <file>` | ライブプレビューサーバーを起動 |
121
+ | `vimd dev <folder>` | フォルダモードを起動 |
130
122
  | `vimd build <file>` | 静的HTMLを生成 |
131
123
  | `vimd theme` | テーマを対話的に変更 |
132
124
  | `vimd config` | 設定を対話的に編集 |
@@ -238,6 +230,30 @@ LaTeX ファイルは自動的に検出され、pandoc を使用して変換さ
238
230
 
239
231
  **注意**: LaTeX プレビューには pandoc が必須です。pandoc がインストールされていない場合、インストールガイドが表示されます。
240
232
 
233
+ ### フォルダモード
234
+
235
+ vimd はフォルダ内の複数ファイルを同一ポートで切り替えながらプレビューできます(v0.5.0以降)。
236
+
237
+ ```bash
238
+ # カレントディレクトリをプレビュー
239
+ vimd dev .
240
+
241
+ # 指定フォルダをプレビュー
242
+ vimd dev ./docs
243
+ ```
244
+
245
+ VSCode風のサイドバーでファイルを選択し、リアルタイムでプレビューを確認できます。
246
+
247
+ **対応する拡張子**:
248
+ - `.md` (Markdown)
249
+ - `.tex`, `.latex` (LaTeX)
250
+
251
+ **機能**:
252
+ - ツリー形式のファイル表示(フォルダ展開/折りたたみ可能)
253
+ - サイドバーのリサイズ(ドラッグで幅変更)
254
+ - キーボードショートカット(Ctrl+B でサイドバー表示/非表示)
255
+ - ファイル変更時の自動リロード
256
+
241
257
  ---
242
258
 
243
259
  ## Why vimd?
@@ -277,3 +293,7 @@ LaTeX ファイルは自動的に検出され、pandoc を使用して変換さ
277
293
  ## ライセンス
278
294
 
279
295
  MIT © notokeishou
296
+
297
+ ### サードパーティライセンス
298
+
299
+ フォルダモードのアイコンは [vscode-icons](https://github.com/vscode-icons/vscode-icons) を使用しています(CC BY-SA 4.0)。
@@ -4,6 +4,6 @@ interface DevOptions {
4
4
  open?: boolean;
5
5
  pandoc?: boolean;
6
6
  }
7
- export declare function devCommand(filePath: string, options: DevOptions): Promise<void>;
7
+ export declare function devCommand(targetPath: string, options: DevOptions): Promise<void>;
8
8
  export {};
9
9
  //# sourceMappingURL=dev.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/dev.ts"],"names":[],"mappings":"AAuBA,UAAU,UAAU;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,CAgLf"}
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/dev.ts"],"names":[],"mappings":"AAwBA,UAAU,UAAU;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,UAAU,CAC9B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,CA2Lf"}
@@ -3,6 +3,7 @@ import { ConfigLoader } from '../../config/loader.js';
3
3
  import { FileWatcher } from '../../core/watcher.js';
4
4
  import { MarkdownConverter } from '../../core/converter.js';
5
5
  import { WebSocketServer } from '../../core/websocket-server.js';
6
+ import { FolderModeServer } from '../../core/folder-mode/index.js';
6
7
  import { PandocDetector } from '../../core/pandoc-detector.js';
7
8
  import { ParserFactory } from '../../core/parser/index.js';
8
9
  import { Logger } from '../../utils/logger.js';
@@ -18,7 +19,7 @@ function isLatexFile(filePath) {
18
19
  const ext = path.extname(filePath).toLowerCase();
19
20
  return ext === '.tex' || ext === '.latex';
20
21
  }
21
- export async function devCommand(filePath, options) {
22
+ export async function devCommand(targetPath, options) {
22
23
  try {
23
24
  Logger.info('Starting vimd dev...');
24
25
  // 1. Load configuration
@@ -56,13 +57,21 @@ export async function devCommand(filePath, options) {
56
57
  }
57
58
  Logger.info(`Theme: ${config.theme}`);
58
59
  Logger.info(`Port: ${port}`);
59
- // 5. Check file exists first (needed for extension detection)
60
- const absolutePath = path.resolve(filePath);
60
+ // 5. Check target exists and determine mode
61
+ const absolutePath = path.resolve(targetPath);
61
62
  if (!(await fs.pathExists(absolutePath))) {
62
- Logger.error(`File not found: ${filePath}`);
63
+ Logger.error(`Path not found: ${targetPath}`);
63
64
  process.exit(1);
64
65
  }
65
- // 6. Detect file type and determine parser/format
66
+ const stat = await fs.stat(absolutePath);
67
+ // 6. Branch: Folder mode or Single file mode
68
+ if (stat.isDirectory()) {
69
+ await startFolderMode(absolutePath, port, config, options);
70
+ return;
71
+ }
72
+ // Continue with single file mode
73
+ const filePath = targetPath;
74
+ // 7. Detect file type and determine parser/format
66
75
  const isLatex = isLatexFile(filePath);
67
76
  const fromFormat = isLatex ? 'latex' : 'markdown';
68
77
  // 7. Determine parser type (LaTeX requires pandoc)
@@ -172,3 +181,46 @@ export async function devCommand(filePath, options) {
172
181
  process.exit(1);
173
182
  }
174
183
  }
184
+ /**
185
+ * Start folder mode server
186
+ */
187
+ async function startFolderMode(folderPath, port, config, _options) {
188
+ Logger.info('Mode: Folder');
189
+ Logger.info(`Folder: ${folderPath}`);
190
+ // Create folder mode server
191
+ const server = new FolderModeServer({
192
+ rootPath: folderPath,
193
+ port: port,
194
+ theme: config.theme,
195
+ open: config.open,
196
+ });
197
+ const startResult = await server.start();
198
+ const actualPort = startResult.actualPort;
199
+ // Open browser if configured
200
+ if (config.open) {
201
+ const url = `http://localhost:${actualPort}/`;
202
+ try {
203
+ await open(url);
204
+ Logger.info('Browser opened');
205
+ }
206
+ catch {
207
+ Logger.warn('Failed to open browser automatically');
208
+ }
209
+ }
210
+ // Save session
211
+ await SessionManager.saveSession({
212
+ pid: process.pid,
213
+ port: actualPort,
214
+ htmlPath: '', // No HTML file in folder mode
215
+ sourcePath: folderPath,
216
+ startedAt: new Date().toISOString(),
217
+ });
218
+ Logger.info('Press Ctrl+C to stop');
219
+ // Register cleanup
220
+ ProcessManager.onExit(async () => {
221
+ Logger.info('Shutting down...');
222
+ await server.stop();
223
+ await SessionManager.removeSession(actualPort);
224
+ Logger.info('Cleanup complete');
225
+ });
226
+ }
@@ -0,0 +1,308 @@
1
+ /**
2
+ * Folder mode styles - VSCode-style dark sidebar
3
+ */
4
+
5
+ :root {
6
+ --sidebar-bg: #252526;
7
+ --sidebar-text: #cccccc;
8
+ --sidebar-text-muted: #858585;
9
+ --sidebar-hover: #2a2d2e;
10
+ --sidebar-selected: #094771;
11
+ --sidebar-border: #3c3c3c;
12
+ --sidebar-header-bg: #3c3c3c;
13
+ --sidebar-width: 250px;
14
+ --sidebar-min-width: 150px;
15
+ --sidebar-max-width: 500px;
16
+ --toggle-bar-width: 20px;
17
+ --resizer-width: 4px;
18
+ }
19
+
20
+ * {
21
+ box-sizing: border-box;
22
+ }
23
+
24
+ html, body {
25
+ margin: 0;
26
+ padding: 0;
27
+ height: 100%;
28
+ overflow: hidden;
29
+ }
30
+
31
+ /* Container */
32
+ .vimd-container {
33
+ display: flex;
34
+ height: 100vh;
35
+ overflow: hidden;
36
+ background: #1e1e1e;
37
+ }
38
+
39
+ /* Toggle bar (visible when sidebar is collapsed) */
40
+ .vimd-sidebar-toggle-bar {
41
+ width: var(--toggle-bar-width);
42
+ min-width: var(--toggle-bar-width);
43
+ background: var(--sidebar-bg);
44
+ border-right: 1px solid var(--sidebar-border);
45
+ display: none;
46
+ flex-direction: column;
47
+ align-items: center;
48
+ padding-top: 8px;
49
+ }
50
+
51
+ .vimd-sidebar-toggle-bar.visible {
52
+ display: flex;
53
+ }
54
+
55
+ /* Sidebar */
56
+ .vimd-sidebar {
57
+ width: var(--sidebar-width);
58
+ min-width: var(--sidebar-min-width);
59
+ max-width: var(--sidebar-max-width);
60
+ background: var(--sidebar-bg);
61
+ color: var(--sidebar-text);
62
+ display: flex;
63
+ flex-direction: column;
64
+ position: relative;
65
+ border-right: 1px solid var(--sidebar-border);
66
+ transition: width 0.15s ease, min-width 0.15s ease, opacity 0.15s ease;
67
+ }
68
+
69
+ .vimd-sidebar.collapsed {
70
+ width: 0;
71
+ min-width: 0;
72
+ border-right: none;
73
+ overflow: hidden;
74
+ opacity: 0;
75
+ }
76
+
77
+ /* Sidebar header */
78
+ .vimd-sidebar-header {
79
+ display: flex;
80
+ align-items: center;
81
+ justify-content: space-between;
82
+ padding: 8px 12px;
83
+ background: var(--sidebar-header-bg);
84
+ border-bottom: 1px solid var(--sidebar-border);
85
+ min-height: 35px;
86
+ }
87
+
88
+ .vimd-folder-name {
89
+ font-size: 11px;
90
+ font-weight: 600;
91
+ text-transform: uppercase;
92
+ letter-spacing: 0.5px;
93
+ color: var(--sidebar-text-muted);
94
+ overflow: hidden;
95
+ text-overflow: ellipsis;
96
+ white-space: nowrap;
97
+ flex: 1;
98
+ }
99
+
100
+ /* Toggle button */
101
+ .vimd-toggle-btn {
102
+ background: transparent;
103
+ border: none;
104
+ color: var(--sidebar-text);
105
+ cursor: pointer;
106
+ padding: 4px;
107
+ display: flex;
108
+ align-items: center;
109
+ justify-content: center;
110
+ border-radius: 3px;
111
+ opacity: 0.7;
112
+ transition: opacity 0.1s, background 0.1s;
113
+ }
114
+
115
+ .vimd-toggle-btn:hover {
116
+ opacity: 1;
117
+ background: var(--sidebar-hover);
118
+ }
119
+
120
+ /* File tree */
121
+ .vimd-tree {
122
+ flex: 1;
123
+ overflow-y: auto;
124
+ overflow-x: hidden;
125
+ padding: 4px 0;
126
+ }
127
+
128
+ .vimd-tree::-webkit-scrollbar {
129
+ width: 10px;
130
+ }
131
+
132
+ .vimd-tree::-webkit-scrollbar-track {
133
+ background: transparent;
134
+ }
135
+
136
+ .vimd-tree::-webkit-scrollbar-thumb {
137
+ background: rgba(121, 121, 121, 0.4);
138
+ border-radius: 5px;
139
+ }
140
+
141
+ .vimd-tree::-webkit-scrollbar-thumb:hover {
142
+ background: rgba(121, 121, 121, 0.7);
143
+ }
144
+
145
+ /* Tree item (file or folder) */
146
+ .vimd-tree-item {
147
+ display: flex;
148
+ align-items: center;
149
+ padding: 3px 8px;
150
+ cursor: pointer;
151
+ user-select: none;
152
+ font-size: 13px;
153
+ line-height: 22px;
154
+ white-space: nowrap;
155
+ overflow: hidden;
156
+ text-overflow: ellipsis;
157
+ }
158
+
159
+ .vimd-tree-item:hover {
160
+ background: var(--sidebar-hover);
161
+ }
162
+
163
+ .vimd-tree-item.selected {
164
+ background: var(--sidebar-selected);
165
+ }
166
+
167
+ .vimd-tree-item.selected:hover {
168
+ background: var(--sidebar-selected);
169
+ }
170
+
171
+ /* Tree item icon */
172
+ .vimd-tree-icon {
173
+ width: 16px;
174
+ height: 16px;
175
+ margin-right: 6px;
176
+ flex-shrink: 0;
177
+ display: flex;
178
+ align-items: center;
179
+ justify-content: center;
180
+ }
181
+
182
+ .vimd-tree-icon img {
183
+ width: 16px;
184
+ height: 16px;
185
+ }
186
+
187
+ /* Folder chevron */
188
+ .vimd-tree-chevron {
189
+ width: 16px;
190
+ height: 16px;
191
+ margin-right: 2px;
192
+ flex-shrink: 0;
193
+ display: flex;
194
+ align-items: center;
195
+ justify-content: center;
196
+ color: var(--sidebar-text-muted);
197
+ transition: transform 0.1s;
198
+ }
199
+
200
+ .vimd-tree-chevron.collapsed {
201
+ transform: rotate(-90deg);
202
+ }
203
+
204
+ /* Tree children (nested items) */
205
+ .vimd-tree-children {
206
+ display: block;
207
+ }
208
+
209
+ .vimd-tree-children.collapsed {
210
+ display: none;
211
+ }
212
+
213
+ /* Indentation */
214
+ .vimd-tree-item[data-depth="0"] { padding-left: 8px; }
215
+ .vimd-tree-item[data-depth="1"] { padding-left: 24px; }
216
+ .vimd-tree-item[data-depth="2"] { padding-left: 40px; }
217
+ .vimd-tree-item[data-depth="3"] { padding-left: 56px; }
218
+ .vimd-tree-item[data-depth="4"] { padding-left: 72px; }
219
+ .vimd-tree-item[data-depth="5"] { padding-left: 88px; }
220
+
221
+ /* Resizer */
222
+ .vimd-resizer {
223
+ position: absolute;
224
+ right: 0;
225
+ top: 0;
226
+ bottom: 0;
227
+ width: var(--resizer-width);
228
+ cursor: ew-resize;
229
+ background: transparent;
230
+ z-index: 10;
231
+ }
232
+
233
+ .vimd-resizer:hover,
234
+ .vimd-resizer.active {
235
+ background: #007acc;
236
+ }
237
+
238
+ /* Preview area */
239
+ .vimd-preview {
240
+ flex: 1;
241
+ overflow-y: auto;
242
+ overflow-x: hidden;
243
+ background: #ffffff;
244
+ }
245
+
246
+ /* Welcome screen */
247
+ .vimd-welcome {
248
+ display: flex;
249
+ align-items: center;
250
+ justify-content: center;
251
+ height: 100%;
252
+ text-align: center;
253
+ }
254
+
255
+ .vimd-welcome.hidden {
256
+ display: none;
257
+ }
258
+
259
+ .vimd-welcome-content h1 {
260
+ font-size: 48px;
261
+ font-weight: 300;
262
+ color: #999;
263
+ margin: 0 0 16px 0;
264
+ }
265
+
266
+ .vimd-welcome-content p {
267
+ font-size: 16px;
268
+ color: #666;
269
+ margin: 0;
270
+ }
271
+
272
+ /* Content area */
273
+ .vimd-content {
274
+ display: none;
275
+ padding: 32px;
276
+ max-width: 900px;
277
+ margin: 0 auto;
278
+ }
279
+
280
+ .vimd-content.visible {
281
+ display: block;
282
+ }
283
+
284
+ /* Loading state */
285
+ .vimd-loading {
286
+ display: flex;
287
+ align-items: center;
288
+ justify-content: center;
289
+ height: 100%;
290
+ color: #666;
291
+ font-size: 14px;
292
+ }
293
+
294
+ /* Error state */
295
+ .vimd-error {
296
+ padding: 32px;
297
+ color: #d32f2f;
298
+ }
299
+
300
+ .vimd-error h2 {
301
+ margin: 0 0 8px 0;
302
+ font-size: 18px;
303
+ }
304
+
305
+ .vimd-error p {
306
+ margin: 0;
307
+ font-size: 14px;
308
+ }