vimd 0.1.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/LICENSE +21 -0
- package/README.md +270 -0
- package/dist/cli/commands/build.d.ts +7 -0
- package/dist/cli/commands/build.d.ts.map +1 -0
- package/dist/cli/commands/build.js +54 -0
- package/dist/cli/commands/config.d.ts +6 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +117 -0
- package/dist/cli/commands/dev.d.ts +8 -0
- package/dist/cli/commands/dev.d.ts.map +1 -0
- package/dist/cli/commands/dev.js +95 -0
- package/dist/cli/commands/theme.d.ts +2 -0
- package/dist/cli/commands/theme.d.ts.map +1 -0
- package/dist/cli/commands/theme.js +46 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +38 -0
- package/dist/cli/setup.d.ts +2 -0
- package/dist/cli/setup.d.ts.map +1 -0
- package/dist/cli/setup.js +50 -0
- package/dist/config/defaults.d.ts +3 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +19 -0
- package/dist/config/loader.d.ts +9 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +78 -0
- package/dist/config/types.d.ts +47 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +4 -0
- package/dist/config/validator.d.ts +11 -0
- package/dist/config/validator.d.ts.map +1 -0
- package/dist/config/validator.js +38 -0
- package/dist/core/converter.d.ts +10 -0
- package/dist/core/converter.d.ts.map +1 -0
- package/dist/core/converter.js +81 -0
- package/dist/core/pandoc-detector.d.ts +8 -0
- package/dist/core/pandoc-detector.d.ts.map +1 -0
- package/dist/core/pandoc-detector.js +70 -0
- package/dist/core/server.d.ts +11 -0
- package/dist/core/server.d.ts.map +1 -0
- package/dist/core/server.js +57 -0
- package/dist/core/watcher.d.ts +16 -0
- package/dist/core/watcher.d.ts.map +1 -0
- package/dist/core/watcher.js +44 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/templates/.gitkeep +0 -0
- package/dist/templates/default.html +22 -0
- package/dist/templates/standalone.html +31 -0
- package/dist/themes/index.d.ts +7 -0
- package/dist/themes/index.d.ts.map +1 -0
- package/dist/themes/index.js +29 -0
- package/dist/themes/registry.d.ts +4 -0
- package/dist/themes/registry.d.ts.map +1 -0
- package/dist/themes/registry.js +40 -0
- package/dist/themes/styles/.gitkeep +0 -0
- package/dist/themes/styles/academic.css +68 -0
- package/dist/themes/styles/dark.css +54 -0
- package/dist/themes/styles/github.css +1 -0
- package/dist/themes/styles/minimal.css +35 -0
- package/dist/themes/styles/technical.css +84 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/utils/logger.d.ts +7 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +16 -0
- package/dist/utils/os-detector.d.ts +8 -0
- package/dist/utils/os-detector.d.ts.map +1 -0
- package/dist/utils/os-detector.js +34 -0
- package/dist/utils/path-resolver.d.ts +7 -0
- package/dist/utils/path-resolver.d.ts.map +1 -0
- package/dist/utils/path-resolver.js +26 -0
- package/dist/utils/process-manager.d.ts +12 -0
- package/dist/utils/process-manager.d.ts.map +1 -0
- package/dist/utils/process-manager.js +35 -0
- package/package.json +75 -0
- package/templates/.gitkeep +0 -0
- package/templates/default.html +22 -0
- package/templates/standalone.html +31 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 notokeishou
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# vimd
|
|
2
|
+
|
|
3
|
+
🌐 [English](README-en.md) | 日本語
|
|
4
|
+
|
|
5
|
+
> pandocを使ったリアルタイムMarkdownプレビューツール (view markdown)
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/vimd)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
|
|
10
|
+
**vimd** は、pandocを使用した高速でシンプルなMarkdownプレビューツールです。複数のテーマに対応したリアルタイムHTMLプレビューを提供します。
|
|
11
|
+
|
|
12
|
+
## 特徴
|
|
13
|
+
|
|
14
|
+
- **リアルタイムプレビュー**: ファイル保存時にブラウザが即座に更新 (live-server)
|
|
15
|
+
- **複数テーマ**: 5つの組み込みテーマ (GitHub, Minimal, Dark, Academic, Technical)
|
|
16
|
+
- **pandoc連携**: pandocによる高品質なMarkdown変換
|
|
17
|
+
- **グローバル設定**: プロジェクトディレクトリを汚さない `~/.vimd/config.js`
|
|
18
|
+
- **対話的セットアップ**: 初回起動時にテーマ選択をガイド
|
|
19
|
+
- **クロスプラットフォーム**: macOS, Linux, Windows で動作
|
|
20
|
+
|
|
21
|
+
## インストール
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install -g vimd
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
インストール後、対話的なセットアップで初期設定を行います。
|
|
28
|
+
|
|
29
|
+
### 必要要件
|
|
30
|
+
|
|
31
|
+
- **Node.js** >= 18.0.0 (ESMサポートが必要)
|
|
32
|
+
- **pandoc** >= 2.0 (別途インストールが必要)
|
|
33
|
+
|
|
34
|
+
pandocのインストール方法:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# macOS
|
|
38
|
+
brew install pandoc
|
|
39
|
+
|
|
40
|
+
# Ubuntu/Debian
|
|
41
|
+
sudo apt install pandoc
|
|
42
|
+
|
|
43
|
+
# Windows
|
|
44
|
+
choco install pandoc
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## クイックスタート
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# ライブプレビューを開始
|
|
51
|
+
vimd dev README.md
|
|
52
|
+
|
|
53
|
+
# 静的HTMLを生成
|
|
54
|
+
vimd build README.md
|
|
55
|
+
|
|
56
|
+
# テーマを変更
|
|
57
|
+
vimd theme
|
|
58
|
+
|
|
59
|
+
# 設定を編集
|
|
60
|
+
vimd config
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## コマンド
|
|
64
|
+
|
|
65
|
+
### `vimd dev <file>`
|
|
66
|
+
|
|
67
|
+
ホットリロード対応のライブプレビューサーバーを起動します。ブラウザを自動で開き、ファイルの変更を監視します。
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
vimd dev README.md
|
|
71
|
+
vimd dev docs/guide.md --port 3000
|
|
72
|
+
vimd dev spec.md --theme dark --no-open
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**オプション:**
|
|
76
|
+
|
|
77
|
+
- `-p, --port <port>`: ポート番号 (デフォルト: 8080)
|
|
78
|
+
- `-t, --theme <theme>`: テーマ名 (グローバル設定を上書き)
|
|
79
|
+
- `--no-open`: ブラウザを自動で開かない
|
|
80
|
+
|
|
81
|
+
### `vimd build <file>`
|
|
82
|
+
|
|
83
|
+
静的HTMLファイルを生成します。スタイルが埋め込まれたスタンドアロンHTMLを出力します。
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
vimd build README.md
|
|
87
|
+
vimd build docs/guide.md -o dist/guide.html
|
|
88
|
+
vimd build spec.md --theme academic
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**オプション:**
|
|
92
|
+
|
|
93
|
+
- `-o, --output <path>`: 出力ファイルパス (デフォルト: 同名で拡張子.html)
|
|
94
|
+
- `-t, --theme <theme>`: テーマ名 (グローバル設定を上書き)
|
|
95
|
+
|
|
96
|
+
### `vimd theme`
|
|
97
|
+
|
|
98
|
+
対話的にテーマを変更します。5つの組み込みテーマから選択できます。
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
vimd theme
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### `vimd config`
|
|
105
|
+
|
|
106
|
+
対話的に設定を編集します。テーマ、ポート、その他の設定を変更できます。
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# 対話的な設定エディタ
|
|
110
|
+
vimd config
|
|
111
|
+
|
|
112
|
+
# 現在の設定を表示
|
|
113
|
+
vimd config --list
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## テーマ
|
|
117
|
+
|
|
118
|
+
vimdには5つの組み込みテーマがあります:
|
|
119
|
+
|
|
120
|
+
| テーマ | 説明 | 用途 |
|
|
121
|
+
| ------------- | --------------------------------- | -------------------- |
|
|
122
|
+
| **GitHub** | GitHub Markdownスタイル (推奨) | 一般的なドキュメント |
|
|
123
|
+
| **Minimal** | シンプルな白背景 | 集中して書きたいとき |
|
|
124
|
+
| **Dark** | VS Codeインスパイアのダークモード | 夜間の作業 |
|
|
125
|
+
| **Academic** | 論文スタイルのレイアウト | 学術論文、研究文書 |
|
|
126
|
+
| **Technical** | APIドキュメントスタイル | 技術仕様書、API文書 |
|
|
127
|
+
|
|
128
|
+
## 設定
|
|
129
|
+
|
|
130
|
+
グローバル設定は `~/.vimd/config.js` に保存されます。
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
export default {
|
|
134
|
+
theme: 'github',
|
|
135
|
+
port: 8080,
|
|
136
|
+
host: 'localhost',
|
|
137
|
+
open: true,
|
|
138
|
+
pandoc: {
|
|
139
|
+
standalone: true,
|
|
140
|
+
toc: false,
|
|
141
|
+
highlightStyle: 'github',
|
|
142
|
+
},
|
|
143
|
+
watch: {
|
|
144
|
+
ignored: ['node_modules', '.git'],
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### 設定オプション
|
|
150
|
+
|
|
151
|
+
- `theme`: デフォルトテーマ名 (文字列)
|
|
152
|
+
- `port`: 開発サーバーのポート (数値、デフォルト: 8080)
|
|
153
|
+
- `host`: 開発サーバーのホスト (文字列、デフォルト: 'localhost')
|
|
154
|
+
- `open`: ブラウザを自動で開く (真偽値、デフォルト: true)
|
|
155
|
+
- `pandoc.standalone`: スタンドアロンHTMLを生成 (真偽値)
|
|
156
|
+
- `pandoc.toc`: 目次を生成 (真偽値)
|
|
157
|
+
- `pandoc.highlightStyle`: コードハイライトスタイル (文字列)
|
|
158
|
+
- `watch.ignored`: ファイル監視で無視するパターン (配列)
|
|
159
|
+
|
|
160
|
+
## API使用
|
|
161
|
+
|
|
162
|
+
vimdはNode.jsライブラリとしても使用できます:
|
|
163
|
+
|
|
164
|
+
```javascript
|
|
165
|
+
import { MarkdownConverter, ConfigLoader, ThemeManager } from 'vimd';
|
|
166
|
+
|
|
167
|
+
// 設定を読み込む
|
|
168
|
+
const config = await ConfigLoader.loadGlobal();
|
|
169
|
+
|
|
170
|
+
// コンバーターを作成
|
|
171
|
+
const converter = new MarkdownConverter({
|
|
172
|
+
theme: 'github',
|
|
173
|
+
pandocOptions: config.pandoc,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// MarkdownをHTMLに変換
|
|
177
|
+
const html = await converter.convertWithTemplate('README.md');
|
|
178
|
+
|
|
179
|
+
// 利用可能なテーマを一覧表示
|
|
180
|
+
const themes = ThemeManager.list();
|
|
181
|
+
console.log(themes); // [{ name: 'github', displayName: 'GitHub' }, ...]
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## 開発
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
# リポジトリをクローン
|
|
188
|
+
git clone https://github.com/notokeishou/vimd.git
|
|
189
|
+
cd vimd
|
|
190
|
+
|
|
191
|
+
# 依存関係をインストール
|
|
192
|
+
npm install
|
|
193
|
+
|
|
194
|
+
# ビルド
|
|
195
|
+
npm run build
|
|
196
|
+
|
|
197
|
+
# テストを実行
|
|
198
|
+
npm test
|
|
199
|
+
|
|
200
|
+
# カバレッジ付きでテスト
|
|
201
|
+
npm run test:coverage
|
|
202
|
+
|
|
203
|
+
# 開発モード
|
|
204
|
+
npm run dev -- dev test.md
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### プロジェクト構造
|
|
208
|
+
|
|
209
|
+
```
|
|
210
|
+
vimd/
|
|
211
|
+
├── src/
|
|
212
|
+
│ ├── cli/ # CLIコマンド
|
|
213
|
+
│ ├── config/ # 設定管理
|
|
214
|
+
│ ├── core/ # コア機能 (converter, watcher, server)
|
|
215
|
+
│ ├── themes/ # テーマシステム
|
|
216
|
+
│ └── utils/ # ユーティリティ関数
|
|
217
|
+
├── tests/
|
|
218
|
+
│ ├── unit/ # ユニットテスト
|
|
219
|
+
│ └── integration/ # 統合テスト
|
|
220
|
+
├── templates/ # HTMLテンプレート
|
|
221
|
+
└── dist/ # ビルド済みファイル
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## トラブルシューティング
|
|
225
|
+
|
|
226
|
+
### pandocが見つからない
|
|
227
|
+
|
|
228
|
+
「pandoc not found」エラーが出る場合:
|
|
229
|
+
|
|
230
|
+
1. パッケージマネージャーでpandocをインストール
|
|
231
|
+
2. インストールを確認: `pandoc --version`
|
|
232
|
+
3. ターミナルを再起動
|
|
233
|
+
|
|
234
|
+
### ポートが使用中
|
|
235
|
+
|
|
236
|
+
ポート8080が既に使用中の場合:
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
vimd dev README.md --port 3000
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
または `~/.vimd/config.js` でデフォルトポートを変更してください。
|
|
243
|
+
|
|
244
|
+
### テーマが適用されない
|
|
245
|
+
|
|
246
|
+
以下を確認してください:
|
|
247
|
+
|
|
248
|
+
1. 利用可能なテーマを確認: `vimd theme`
|
|
249
|
+
2. 設定を確認: `vimd config --list`
|
|
250
|
+
3. ソースからの場合は再ビルド: `npm run build`
|
|
251
|
+
|
|
252
|
+
## ライセンス
|
|
253
|
+
|
|
254
|
+
MIT © notokeishou
|
|
255
|
+
|
|
256
|
+
## コントリビューション
|
|
257
|
+
|
|
258
|
+
コントリビューションを歓迎します! プルリクエストの提出方法については [CONTRIBUTING.md](CONTRIBUTING.md) をお読みください。
|
|
259
|
+
|
|
260
|
+
## 変更履歴
|
|
261
|
+
|
|
262
|
+
リリース履歴は [CHANGELOG.md](CHANGELOG.md) を参照してください。
|
|
263
|
+
|
|
264
|
+
## リンク
|
|
265
|
+
|
|
266
|
+
- [GitHubリポジトリ](https://github.com/notokeishou/vimd)
|
|
267
|
+
- [npmパッケージ](https://www.npmjs.com/package/vimd)
|
|
268
|
+
- [Issue Tracker](https://github.com/notokeishou/vimd/issues)
|
|
269
|
+
|
|
270
|
+
---
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/build.ts"],"names":[],"mappings":"AAQA,UAAU,YAAY;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,IAAI,CAAC,CA0Df"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// src/cli/commands/build.ts
|
|
2
|
+
import { ConfigLoader } from '../../config/loader.js';
|
|
3
|
+
import { MarkdownConverter } from '../../core/converter.js';
|
|
4
|
+
import { PandocDetector } from '../../core/pandoc-detector.js';
|
|
5
|
+
import { Logger } from '../../utils/logger.js';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import fs from 'fs-extra';
|
|
8
|
+
export async function buildCommand(filePath, options) {
|
|
9
|
+
try {
|
|
10
|
+
Logger.info('Building HTML...');
|
|
11
|
+
// 1. Load configuration
|
|
12
|
+
const config = await ConfigLoader.loadGlobal();
|
|
13
|
+
// Override with command line options
|
|
14
|
+
if (options.theme) {
|
|
15
|
+
config.theme = options.theme;
|
|
16
|
+
}
|
|
17
|
+
Logger.info(`Theme: ${config.theme}`);
|
|
18
|
+
// 2. Check pandoc installation
|
|
19
|
+
PandocDetector.ensureInstalled();
|
|
20
|
+
// 3. Check file exists
|
|
21
|
+
const absolutePath = path.resolve(filePath);
|
|
22
|
+
if (!(await fs.pathExists(absolutePath))) {
|
|
23
|
+
Logger.error(`File not found: ${filePath}`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
// 4. Determine output path
|
|
27
|
+
const outputPath = options.output
|
|
28
|
+
? path.resolve(options.output)
|
|
29
|
+
: path.join(path.dirname(absolutePath), path.basename(filePath, path.extname(filePath)) + '.html');
|
|
30
|
+
Logger.info(`Output: ${outputPath}`);
|
|
31
|
+
// 5. Prepare converter
|
|
32
|
+
const converter = new MarkdownConverter({
|
|
33
|
+
theme: config.theme,
|
|
34
|
+
pandocOptions: {
|
|
35
|
+
...config.pandoc,
|
|
36
|
+
standalone: true, // build always uses standalone
|
|
37
|
+
},
|
|
38
|
+
customCSS: config.css,
|
|
39
|
+
template: config.template || undefined,
|
|
40
|
+
});
|
|
41
|
+
// 6. Execute conversion
|
|
42
|
+
Logger.info('Converting...');
|
|
43
|
+
const html = await converter.convertWithTemplate(absolutePath);
|
|
44
|
+
await converter.writeHTML(html, outputPath);
|
|
45
|
+
Logger.success(`Build complete: ${outputPath}`);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
Logger.error('Build failed');
|
|
49
|
+
if (error instanceof Error) {
|
|
50
|
+
Logger.error(error.message);
|
|
51
|
+
}
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/config.ts"],"names":[],"mappings":"AAQA,UAAU,aAAa;IACrB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBzE"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// src/cli/commands/config.ts
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import { ConfigLoader } from '../../config/loader.js';
|
|
4
|
+
import { ThemeManager } from '../../themes/index.js';
|
|
5
|
+
import { Logger } from '../../utils/logger.js';
|
|
6
|
+
import { PathResolver } from '../../utils/path-resolver.js';
|
|
7
|
+
export async function configCommand(options) {
|
|
8
|
+
try {
|
|
9
|
+
// --list オプション: 現在の設定を表示
|
|
10
|
+
if (options.list) {
|
|
11
|
+
await showCurrentConfig();
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
// 対話的設定変更
|
|
15
|
+
await interactiveConfig();
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
Logger.error('Failed to update configuration');
|
|
19
|
+
if (error instanceof Error) {
|
|
20
|
+
Logger.error(error.message);
|
|
21
|
+
}
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async function showCurrentConfig() {
|
|
26
|
+
const config = await ConfigLoader.loadGlobal();
|
|
27
|
+
const configPath = PathResolver.getConfigPath();
|
|
28
|
+
console.log('\nCurrent configuration:');
|
|
29
|
+
console.log(` Theme: ${config.theme}`);
|
|
30
|
+
console.log(` Port: ${config.port}`);
|
|
31
|
+
console.log(` Host: ${config.host}`);
|
|
32
|
+
console.log(` Open Browser: ${config.open}`);
|
|
33
|
+
console.log(` Config File: ${configPath}\n`);
|
|
34
|
+
}
|
|
35
|
+
async function interactiveConfig() {
|
|
36
|
+
const config = await ConfigLoader.loadGlobal();
|
|
37
|
+
// 変更したい項目を選択
|
|
38
|
+
const { item } = await inquirer.prompt([
|
|
39
|
+
{
|
|
40
|
+
type: 'list',
|
|
41
|
+
name: 'item',
|
|
42
|
+
message: 'What would you like to change?',
|
|
43
|
+
choices: [
|
|
44
|
+
{ name: 'Theme', value: 'theme' },
|
|
45
|
+
{ name: 'Port number', value: 'port' },
|
|
46
|
+
{ name: 'Auto-open browser', value: 'open' },
|
|
47
|
+
{ name: 'Cancel', value: 'cancel' },
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
]);
|
|
51
|
+
if (item === 'cancel') {
|
|
52
|
+
Logger.info('Configuration unchanged');
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
// 項目別の変更処理
|
|
56
|
+
switch (item) {
|
|
57
|
+
case 'theme':
|
|
58
|
+
await changeTheme(config);
|
|
59
|
+
break;
|
|
60
|
+
case 'port':
|
|
61
|
+
await changePort(config);
|
|
62
|
+
break;
|
|
63
|
+
case 'open':
|
|
64
|
+
await changeOpen(config);
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
// 設定保存
|
|
68
|
+
await ConfigLoader.save(config);
|
|
69
|
+
Logger.success('Configuration updated');
|
|
70
|
+
}
|
|
71
|
+
async function changeTheme(config) {
|
|
72
|
+
const themes = ThemeManager.list();
|
|
73
|
+
const { theme } = await inquirer.prompt([
|
|
74
|
+
{
|
|
75
|
+
type: 'list',
|
|
76
|
+
name: 'theme',
|
|
77
|
+
message: 'Select a theme:',
|
|
78
|
+
choices: themes.map((t) => ({
|
|
79
|
+
name: t.name === config.theme
|
|
80
|
+
? `${t.displayName} (current)`
|
|
81
|
+
: t.displayName,
|
|
82
|
+
value: t.name,
|
|
83
|
+
})),
|
|
84
|
+
default: config.theme,
|
|
85
|
+
},
|
|
86
|
+
]);
|
|
87
|
+
config.theme = theme;
|
|
88
|
+
}
|
|
89
|
+
async function changePort(config) {
|
|
90
|
+
const { port } = await inquirer.prompt([
|
|
91
|
+
{
|
|
92
|
+
type: 'input',
|
|
93
|
+
name: 'port',
|
|
94
|
+
message: 'Enter port number:',
|
|
95
|
+
default: config.port.toString(),
|
|
96
|
+
validate: (input) => {
|
|
97
|
+
const num = parseInt(input, 10);
|
|
98
|
+
if (isNaN(num) || num < 1 || num > 65535) {
|
|
99
|
+
return 'Port must be between 1 and 65535';
|
|
100
|
+
}
|
|
101
|
+
return true;
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
]);
|
|
105
|
+
config.port = parseInt(port, 10);
|
|
106
|
+
}
|
|
107
|
+
async function changeOpen(config) {
|
|
108
|
+
const { open } = await inquirer.prompt([
|
|
109
|
+
{
|
|
110
|
+
type: 'confirm',
|
|
111
|
+
name: 'open',
|
|
112
|
+
message: 'Auto-open browser in preview?',
|
|
113
|
+
default: config.open,
|
|
114
|
+
},
|
|
115
|
+
]);
|
|
116
|
+
config.open = open;
|
|
117
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/dev.ts"],"names":[],"mappings":"AAWA,UAAU,UAAU;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,CAoGf"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// src/cli/commands/dev.ts
|
|
2
|
+
import { ConfigLoader } from '../../config/loader.js';
|
|
3
|
+
import { FileWatcher } from '../../core/watcher.js';
|
|
4
|
+
import { MarkdownConverter } from '../../core/converter.js';
|
|
5
|
+
import { LiveServer } from '../../core/server.js';
|
|
6
|
+
import { PandocDetector } from '../../core/pandoc-detector.js';
|
|
7
|
+
import { Logger } from '../../utils/logger.js';
|
|
8
|
+
import { ProcessManager } from '../../utils/process-manager.js';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
import fs from 'fs-extra';
|
|
11
|
+
export async function devCommand(filePath, options) {
|
|
12
|
+
try {
|
|
13
|
+
Logger.info('Starting vimd dev...');
|
|
14
|
+
// 1. Load configuration
|
|
15
|
+
const config = await ConfigLoader.loadGlobal();
|
|
16
|
+
// Override with command line options
|
|
17
|
+
if (options.port) {
|
|
18
|
+
config.port = parseInt(options.port, 10);
|
|
19
|
+
}
|
|
20
|
+
if (options.theme) {
|
|
21
|
+
config.theme = options.theme;
|
|
22
|
+
}
|
|
23
|
+
if (options.open !== undefined) {
|
|
24
|
+
config.open = options.open;
|
|
25
|
+
}
|
|
26
|
+
Logger.info(`Theme: ${config.theme}`);
|
|
27
|
+
Logger.info(`Port: ${config.port}`);
|
|
28
|
+
// 2. Check pandoc installation
|
|
29
|
+
PandocDetector.ensureInstalled();
|
|
30
|
+
// 3. Check file exists
|
|
31
|
+
const absolutePath = path.resolve(filePath);
|
|
32
|
+
if (!(await fs.pathExists(absolutePath))) {
|
|
33
|
+
Logger.error(`File not found: ${filePath}`);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
// 4. Prepare output directory
|
|
37
|
+
const outputDir = path.join(process.cwd(), '.vimd-tmp');
|
|
38
|
+
await fs.ensureDir(outputDir);
|
|
39
|
+
const htmlPath = path.join(outputDir, path.basename(filePath, path.extname(filePath)) + '.html');
|
|
40
|
+
// 5. Prepare converter
|
|
41
|
+
const converter = new MarkdownConverter({
|
|
42
|
+
theme: config.theme,
|
|
43
|
+
pandocOptions: config.pandoc,
|
|
44
|
+
customCSS: config.css,
|
|
45
|
+
template: config.template,
|
|
46
|
+
});
|
|
47
|
+
// 6. Initial conversion
|
|
48
|
+
Logger.info('Converting markdown...');
|
|
49
|
+
const html = await converter.convertWithTemplate(absolutePath);
|
|
50
|
+
await converter.writeHTML(html, htmlPath);
|
|
51
|
+
Logger.success('Conversion complete');
|
|
52
|
+
// 7. Start live server
|
|
53
|
+
const server = new LiveServer({
|
|
54
|
+
port: config.port,
|
|
55
|
+
host: config.host,
|
|
56
|
+
open: config.open,
|
|
57
|
+
root: outputDir,
|
|
58
|
+
});
|
|
59
|
+
await server.start(htmlPath);
|
|
60
|
+
Logger.info(`Watching: ${filePath}`);
|
|
61
|
+
Logger.info('Press Ctrl+C to stop');
|
|
62
|
+
// 8. Start file watching
|
|
63
|
+
const watcher = new FileWatcher(absolutePath, config.watch);
|
|
64
|
+
watcher.onChange(async (changedPath) => {
|
|
65
|
+
Logger.info('File changed, reconverting...');
|
|
66
|
+
try {
|
|
67
|
+
const newHtml = await converter.convertWithTemplate(changedPath);
|
|
68
|
+
await converter.writeHTML(newHtml, htmlPath);
|
|
69
|
+
Logger.success('Reconversion complete');
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
Logger.error('Reconversion failed');
|
|
73
|
+
if (error instanceof Error) {
|
|
74
|
+
Logger.error(error.message);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
watcher.start();
|
|
79
|
+
// 9. Register cleanup
|
|
80
|
+
ProcessManager.onExit(async () => {
|
|
81
|
+
Logger.info('Shutting down...');
|
|
82
|
+
await watcher.stop();
|
|
83
|
+
await server.stop();
|
|
84
|
+
await fs.remove(outputDir);
|
|
85
|
+
Logger.info('Cleanup complete');
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
Logger.error('Failed to start dev server');
|
|
90
|
+
if (error instanceof Error) {
|
|
91
|
+
Logger.error(error.message);
|
|
92
|
+
}
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/theme.ts"],"names":[],"mappings":"AAMA,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CA6ClD"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// src/cli/commands/theme.ts
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import { ConfigLoader } from '../../config/loader.js';
|
|
4
|
+
import { ThemeManager } from '../../themes/index.js';
|
|
5
|
+
import { Logger } from '../../utils/logger.js';
|
|
6
|
+
export async function themeCommand() {
|
|
7
|
+
try {
|
|
8
|
+
// 1. 現在の設定読み込み
|
|
9
|
+
const config = await ConfigLoader.loadGlobal();
|
|
10
|
+
const currentTheme = config.theme;
|
|
11
|
+
// 2. テーマ一覧取得
|
|
12
|
+
const themes = ThemeManager.list();
|
|
13
|
+
// 3. 対話的選択
|
|
14
|
+
const answers = await inquirer.prompt([
|
|
15
|
+
{
|
|
16
|
+
type: 'list',
|
|
17
|
+
name: 'theme',
|
|
18
|
+
message: 'Select a theme:',
|
|
19
|
+
choices: themes.map((t) => ({
|
|
20
|
+
name: t.name === currentTheme
|
|
21
|
+
? `${t.displayName} (current)`
|
|
22
|
+
: t.displayName,
|
|
23
|
+
value: t.name,
|
|
24
|
+
})),
|
|
25
|
+
default: currentTheme,
|
|
26
|
+
},
|
|
27
|
+
]);
|
|
28
|
+
// 変更がない場合
|
|
29
|
+
if (answers.theme === currentTheme) {
|
|
30
|
+
Logger.info('Theme unchanged');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
// 4. 設定更新
|
|
34
|
+
config.theme = answers.theme;
|
|
35
|
+
await ConfigLoader.save(config);
|
|
36
|
+
Logger.success(`Theme updated to '${answers.theme}'`);
|
|
37
|
+
Logger.info('All projects will use this theme.');
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
Logger.error('Failed to change theme');
|
|
41
|
+
if (error instanceof Error) {
|
|
42
|
+
Logger.error(error.message);
|
|
43
|
+
}
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
|