mdv-live 0.1.0__tar.gz
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.
- mdv_live-0.1.0/PKG-INFO +75 -0
- mdv_live-0.1.0/README.md +47 -0
- mdv_live-0.1.0/mdv/__init__.py +6 -0
- mdv_live-0.1.0/mdv/__main__.py +9 -0
- mdv_live-0.1.0/mdv/cli.py +339 -0
- mdv_live-0.1.0/mdv/file_types.py +103 -0
- mdv_live-0.1.0/mdv/models.py +11 -0
- mdv_live-0.1.0/mdv/server.py +516 -0
- mdv_live-0.1.0/mdv/static/index.html +1327 -0
- mdv_live-0.1.0/mdv_live.egg-info/PKG-INFO +75 -0
- mdv_live-0.1.0/mdv_live.egg-info/SOURCES.txt +15 -0
- mdv_live-0.1.0/mdv_live.egg-info/dependency_links.txt +1 -0
- mdv_live-0.1.0/mdv_live.egg-info/entry_points.txt +2 -0
- mdv_live-0.1.0/mdv_live.egg-info/requires.txt +5 -0
- mdv_live-0.1.0/mdv_live.egg-info/top_level.txt +2 -0
- mdv_live-0.1.0/pyproject.toml +49 -0
- mdv_live-0.1.0/setup.cfg +4 -0
mdv_live-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mdv-live
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Markdown Viewer - File tree + Live preview + Hot reload
|
|
5
|
+
Author-email: PanHouse <hirono.okamoto@panhouse.jp>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/panhouse/mdv
|
|
8
|
+
Project-URL: Repository, https://github.com/panhouse/mdv
|
|
9
|
+
Keywords: markdown,viewer,live-reload,preview,hot-reload
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Environment :: Web Environment
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Text Processing :: Markup :: Markdown
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
Requires-Dist: fastapi>=0.104.0
|
|
24
|
+
Requires-Dist: uvicorn>=0.24.0
|
|
25
|
+
Requires-Dist: websockets>=12.0
|
|
26
|
+
Requires-Dist: watchdog>=3.0.0
|
|
27
|
+
Requires-Dist: markdown>=3.5.0
|
|
28
|
+
|
|
29
|
+
# MDV - Markdown Viewer
|
|
30
|
+
|
|
31
|
+
ファイルツリー + ライブプレビュー付きマークダウンビューア
|
|
32
|
+
|
|
33
|
+
## Features
|
|
34
|
+
|
|
35
|
+
- 📁 左側にフォルダツリー表示
|
|
36
|
+
- 📄 マークダウンをHTMLでレンダリング
|
|
37
|
+
- 🔄 ファイル更新時に自動リロード(WebSocket)
|
|
38
|
+
- 🎨 シンタックスハイライト(highlight.js)
|
|
39
|
+
- 📊 Mermaid図のレンダリング対応
|
|
40
|
+
- 🌙 ダークテーマ
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# リポジトリをクローン
|
|
46
|
+
git clone https://github.com/panhouse/mdv.git
|
|
47
|
+
cd mdv
|
|
48
|
+
|
|
49
|
+
# グローバルインストール
|
|
50
|
+
pip install -e .
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Usage
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# カレントディレクトリを表示
|
|
57
|
+
mdv
|
|
58
|
+
|
|
59
|
+
# 特定のディレクトリを表示
|
|
60
|
+
mdv ./project/
|
|
61
|
+
|
|
62
|
+
# ポート指定
|
|
63
|
+
mdv -p 9000
|
|
64
|
+
|
|
65
|
+
# ブラウザを自動で開かない
|
|
66
|
+
mdv --no-browser
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Requirements
|
|
70
|
+
|
|
71
|
+
- Python 3.9+
|
|
72
|
+
- FastAPI
|
|
73
|
+
- uvicorn
|
|
74
|
+
- watchdog
|
|
75
|
+
- markdown
|
mdv_live-0.1.0/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# MDV - Markdown Viewer
|
|
2
|
+
|
|
3
|
+
ファイルツリー + ライブプレビュー付きマークダウンビューア
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 📁 左側にフォルダツリー表示
|
|
8
|
+
- 📄 マークダウンをHTMLでレンダリング
|
|
9
|
+
- 🔄 ファイル更新時に自動リロード(WebSocket)
|
|
10
|
+
- 🎨 シンタックスハイライト(highlight.js)
|
|
11
|
+
- 📊 Mermaid図のレンダリング対応
|
|
12
|
+
- 🌙 ダークテーマ
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# リポジトリをクローン
|
|
18
|
+
git clone https://github.com/panhouse/mdv.git
|
|
19
|
+
cd mdv
|
|
20
|
+
|
|
21
|
+
# グローバルインストール
|
|
22
|
+
pip install -e .
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# カレントディレクトリを表示
|
|
29
|
+
mdv
|
|
30
|
+
|
|
31
|
+
# 特定のディレクトリを表示
|
|
32
|
+
mdv ./project/
|
|
33
|
+
|
|
34
|
+
# ポート指定
|
|
35
|
+
mdv -p 9000
|
|
36
|
+
|
|
37
|
+
# ブラウザを自動で開かない
|
|
38
|
+
mdv --no-browser
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Requirements
|
|
42
|
+
|
|
43
|
+
- Python 3.9+
|
|
44
|
+
- FastAPI
|
|
45
|
+
- uvicorn
|
|
46
|
+
- watchdog
|
|
47
|
+
- markdown
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
MDV - Markdown Viewer CLI
|
|
4
|
+
どこからでも呼び出せるマークダウンビューア
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import subprocess
|
|
11
|
+
import sys
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import List, Optional
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class ProcessInfo:
|
|
20
|
+
"""プロセス情報"""
|
|
21
|
+
pid: str
|
|
22
|
+
port: str
|
|
23
|
+
command: str
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_mdv_processes() -> List[ProcessInfo]:
|
|
27
|
+
"""稼働中のMDVサーバープロセスを取得"""
|
|
28
|
+
try:
|
|
29
|
+
result = subprocess.run(
|
|
30
|
+
["lsof", "-i", "-P", "-n"],
|
|
31
|
+
capture_output=True,
|
|
32
|
+
text=True,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
processes = []
|
|
36
|
+
for line in result.stdout.strip().split("\n"):
|
|
37
|
+
if "python" not in line.lower() or "LISTEN" not in line:
|
|
38
|
+
continue
|
|
39
|
+
|
|
40
|
+
parts = line.split()
|
|
41
|
+
if len(parts) < 9:
|
|
42
|
+
continue
|
|
43
|
+
|
|
44
|
+
pid = parts[1]
|
|
45
|
+
|
|
46
|
+
# プロセスのコマンドラインを確認
|
|
47
|
+
try:
|
|
48
|
+
cmd_result = subprocess.run(
|
|
49
|
+
["ps", "-p", pid, "-o", "command="],
|
|
50
|
+
capture_output=True,
|
|
51
|
+
text=True,
|
|
52
|
+
)
|
|
53
|
+
cmd = cmd_result.stdout.strip()
|
|
54
|
+
|
|
55
|
+
if "mdv" not in cmd.lower():
|
|
56
|
+
continue
|
|
57
|
+
|
|
58
|
+
# ポート番号を抽出
|
|
59
|
+
port_info = parts[8] if len(parts) > 8 else ""
|
|
60
|
+
port = ""
|
|
61
|
+
if ":" in port_info:
|
|
62
|
+
port = port_info.split(":")[-1].split("->")[0]
|
|
63
|
+
|
|
64
|
+
# コマンドを短縮
|
|
65
|
+
display_cmd = cmd[:80] + "..." if len(cmd) > 80 else cmd
|
|
66
|
+
|
|
67
|
+
processes.append(ProcessInfo(
|
|
68
|
+
pid=pid,
|
|
69
|
+
port=port,
|
|
70
|
+
command=display_cmd,
|
|
71
|
+
))
|
|
72
|
+
except subprocess.SubprocessError:
|
|
73
|
+
pass
|
|
74
|
+
|
|
75
|
+
return processes
|
|
76
|
+
|
|
77
|
+
except Exception as e:
|
|
78
|
+
print(f"Error getting processes: {e}")
|
|
79
|
+
return []
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def list_servers() -> int:
|
|
83
|
+
"""稼働中のMDVサーバーを一覧表示"""
|
|
84
|
+
processes = get_mdv_processes()
|
|
85
|
+
|
|
86
|
+
if not processes:
|
|
87
|
+
print("稼働中のMDVサーバーはありません")
|
|
88
|
+
return 0
|
|
89
|
+
|
|
90
|
+
print(f"稼働中のMDVサーバー: {len(processes)}件")
|
|
91
|
+
print("-" * 60)
|
|
92
|
+
print(f"{'PID':<8} {'Port':<8} {'Command'}")
|
|
93
|
+
print("-" * 60)
|
|
94
|
+
|
|
95
|
+
for proc in processes:
|
|
96
|
+
print(f"{proc.pid:<8} {proc.port:<8} {proc.command}")
|
|
97
|
+
|
|
98
|
+
print("-" * 60)
|
|
99
|
+
print("\n停止: mdv -k -a (全停止) / mdv -k <PID> (個別停止)")
|
|
100
|
+
return 0
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def kill_server_by_pid(pid: str) -> int:
|
|
104
|
+
"""特定のPIDのサーバーを停止"""
|
|
105
|
+
try:
|
|
106
|
+
subprocess.run(["kill", pid], check=True)
|
|
107
|
+
print(f"PID {pid} を停止しました")
|
|
108
|
+
return 0
|
|
109
|
+
except ValueError:
|
|
110
|
+
print(f"無効なPID: {pid}")
|
|
111
|
+
return 1
|
|
112
|
+
except subprocess.CalledProcessError:
|
|
113
|
+
print(f"PID {pid} の停止に失敗しました")
|
|
114
|
+
return 1
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def kill_all_servers() -> int:
|
|
118
|
+
"""全サーバーを停止"""
|
|
119
|
+
processes = get_mdv_processes()
|
|
120
|
+
|
|
121
|
+
if not processes:
|
|
122
|
+
print("稼働中のMDVサーバーはありません")
|
|
123
|
+
return 0
|
|
124
|
+
|
|
125
|
+
print(f"{len(processes)}件のMDVサーバーを停止します...")
|
|
126
|
+
|
|
127
|
+
killed = 0
|
|
128
|
+
for proc in processes:
|
|
129
|
+
try:
|
|
130
|
+
subprocess.run(["kill", proc.pid], check=True)
|
|
131
|
+
print(f" PID {proc.pid} (port {proc.port}) を停止")
|
|
132
|
+
killed += 1
|
|
133
|
+
except subprocess.CalledProcessError:
|
|
134
|
+
print(f" PID {proc.pid} の停止に失敗")
|
|
135
|
+
|
|
136
|
+
print(f"\n完了: {killed}/{len(processes)} 件を停止しました")
|
|
137
|
+
return 0 if killed == len(processes) else 1
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def kill_servers(target: Optional[str] = None, kill_all: bool = False) -> int:
|
|
141
|
+
"""MDVサーバーを停止"""
|
|
142
|
+
if target:
|
|
143
|
+
return kill_server_by_pid(target)
|
|
144
|
+
|
|
145
|
+
if not kill_all:
|
|
146
|
+
print("全サーバーを停止するには -a オプションが必要です")
|
|
147
|
+
print(" mdv -k -a 全サーバーを停止")
|
|
148
|
+
print(" mdv -k <PID> 特定のサーバーを停止")
|
|
149
|
+
return 1
|
|
150
|
+
|
|
151
|
+
return kill_all_servers()
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def convert_to_pdf(input_path: Path, output_path: Optional[Path] = None) -> int:
|
|
155
|
+
"""MarkdownファイルをPDFに変換(md-to-pdfを使用)"""
|
|
156
|
+
if not input_path.exists():
|
|
157
|
+
print(f"Error: File not found: {input_path}")
|
|
158
|
+
return 1
|
|
159
|
+
|
|
160
|
+
if not input_path.is_file():
|
|
161
|
+
print(f"Error: Not a file: {input_path}")
|
|
162
|
+
return 1
|
|
163
|
+
|
|
164
|
+
if input_path.suffix.lower() not in [".md", ".markdown"]:
|
|
165
|
+
print(f"Error: Not a markdown file: {input_path}")
|
|
166
|
+
return 1
|
|
167
|
+
|
|
168
|
+
# md-to-pdfコマンドを構築
|
|
169
|
+
cmd = ["npx", "md-to-pdf", str(input_path)]
|
|
170
|
+
if output_path:
|
|
171
|
+
# md-to-pdfは--out-dirオプションを使用
|
|
172
|
+
cmd.extend(["--out-dir", str(output_path.parent)])
|
|
173
|
+
|
|
174
|
+
try:
|
|
175
|
+
result = subprocess.run(
|
|
176
|
+
cmd,
|
|
177
|
+
capture_output=True,
|
|
178
|
+
text=True,
|
|
179
|
+
cwd=str(input_path.parent),
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
if result.returncode != 0:
|
|
183
|
+
if "npx: command not found" in result.stderr or "not found" in result.stderr:
|
|
184
|
+
print("Error: npx (Node.js) is required for PDF conversion")
|
|
185
|
+
print("Install Node.js: https://nodejs.org/")
|
|
186
|
+
return 1
|
|
187
|
+
print(f"Error: {result.stderr}")
|
|
188
|
+
return 1
|
|
189
|
+
|
|
190
|
+
# 出力ファイルパスを特定
|
|
191
|
+
default_output = input_path.with_suffix(".pdf")
|
|
192
|
+
if output_path and output_path != default_output:
|
|
193
|
+
# リネームが必要な場合
|
|
194
|
+
if default_output.exists():
|
|
195
|
+
default_output.rename(output_path)
|
|
196
|
+
print(f"✅ PDF saved: {output_path}")
|
|
197
|
+
else:
|
|
198
|
+
print(f"✅ PDF saved: {output_path}")
|
|
199
|
+
else:
|
|
200
|
+
print(f"✅ PDF saved: {default_output}")
|
|
201
|
+
|
|
202
|
+
return 0
|
|
203
|
+
|
|
204
|
+
except FileNotFoundError:
|
|
205
|
+
print("Error: npx (Node.js) is required for PDF conversion")
|
|
206
|
+
print("Install Node.js: https://nodejs.org/")
|
|
207
|
+
return 1
|
|
208
|
+
except Exception as e:
|
|
209
|
+
print(f"Error creating PDF: {e}")
|
|
210
|
+
return 1
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def start_viewer(
|
|
214
|
+
path: str = ".",
|
|
215
|
+
port: int = 8642,
|
|
216
|
+
open_browser: bool = True
|
|
217
|
+
) -> None:
|
|
218
|
+
"""MDVサーバーを起動"""
|
|
219
|
+
target_path = Path(path).resolve()
|
|
220
|
+
|
|
221
|
+
if not target_path.exists():
|
|
222
|
+
print(f"Error: Path does not exist: {target_path}")
|
|
223
|
+
sys.exit(1)
|
|
224
|
+
|
|
225
|
+
# ファイルが指定された場合、親ディレクトリをルートにして、そのファイルを開く
|
|
226
|
+
initial_file: Optional[str] = None
|
|
227
|
+
if target_path.is_file():
|
|
228
|
+
initial_file = target_path.name
|
|
229
|
+
target_path = target_path.parent
|
|
230
|
+
|
|
231
|
+
from .server import start_server
|
|
232
|
+
start_server(
|
|
233
|
+
root_path=str(target_path),
|
|
234
|
+
port=port,
|
|
235
|
+
open_browser=open_browser,
|
|
236
|
+
initial_file=initial_file,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def create_parser() -> argparse.ArgumentParser:
|
|
241
|
+
"""引数パーサーを作成"""
|
|
242
|
+
parser = argparse.ArgumentParser(
|
|
243
|
+
prog="mdv",
|
|
244
|
+
description="MDV - Markdown Viewer with file tree + live preview",
|
|
245
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
246
|
+
epilog="""
|
|
247
|
+
Examples:
|
|
248
|
+
mdv Start viewer in current directory
|
|
249
|
+
mdv /path/to/dir Start viewer in specified directory
|
|
250
|
+
mdv README.md Open specific file
|
|
251
|
+
mdv --pdf README.md Convert markdown to PDF
|
|
252
|
+
mdv -p 3000 Start on port 3000
|
|
253
|
+
mdv -l List running servers
|
|
254
|
+
mdv -k -a Stop all servers
|
|
255
|
+
""",
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
# サーバー管理オプション
|
|
259
|
+
parser.add_argument(
|
|
260
|
+
"-l", "--list",
|
|
261
|
+
action="store_true",
|
|
262
|
+
help="List running MDV servers",
|
|
263
|
+
)
|
|
264
|
+
parser.add_argument(
|
|
265
|
+
"-k", "--kill",
|
|
266
|
+
nargs="?",
|
|
267
|
+
const="__no_pid__",
|
|
268
|
+
metavar="PID",
|
|
269
|
+
help="Stop server (-k -a for all, -k <PID> for specific)",
|
|
270
|
+
)
|
|
271
|
+
parser.add_argument(
|
|
272
|
+
"-a", "--all",
|
|
273
|
+
action="store_true",
|
|
274
|
+
help="Use with -k to stop all servers",
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
# ビューア起動オプション
|
|
278
|
+
parser.add_argument(
|
|
279
|
+
"path",
|
|
280
|
+
nargs="?",
|
|
281
|
+
default=".",
|
|
282
|
+
help="Directory or file path to view (default: current directory)",
|
|
283
|
+
)
|
|
284
|
+
parser.add_argument(
|
|
285
|
+
"-p", "--port",
|
|
286
|
+
type=int,
|
|
287
|
+
default=8642,
|
|
288
|
+
help="Server port (default: 8642)",
|
|
289
|
+
)
|
|
290
|
+
parser.add_argument(
|
|
291
|
+
"--no-browser",
|
|
292
|
+
action="store_true",
|
|
293
|
+
help="Don't open browser automatically",
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
# PDF変換オプション
|
|
297
|
+
parser.add_argument(
|
|
298
|
+
"--pdf",
|
|
299
|
+
action="store_true",
|
|
300
|
+
help="Convert markdown file to PDF",
|
|
301
|
+
)
|
|
302
|
+
parser.add_argument(
|
|
303
|
+
"-o", "--output",
|
|
304
|
+
type=str,
|
|
305
|
+
metavar="FILE",
|
|
306
|
+
help="Output PDF file path (default: same name as input with .pdf extension)",
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
return parser
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def main() -> None:
|
|
313
|
+
"""メインエントリーポイント"""
|
|
314
|
+
parser = create_parser()
|
|
315
|
+
args = parser.parse_args()
|
|
316
|
+
|
|
317
|
+
# -l: サーバー一覧
|
|
318
|
+
if args.list:
|
|
319
|
+
sys.exit(list_servers())
|
|
320
|
+
|
|
321
|
+
# -k: サーバー停止
|
|
322
|
+
if args.kill is not None:
|
|
323
|
+
if args.kill != "__no_pid__":
|
|
324
|
+
sys.exit(kill_servers(target=args.kill))
|
|
325
|
+
else:
|
|
326
|
+
sys.exit(kill_servers(kill_all=args.all))
|
|
327
|
+
|
|
328
|
+
# --pdf: PDF変換
|
|
329
|
+
if args.pdf:
|
|
330
|
+
input_path = Path(args.path).resolve()
|
|
331
|
+
output_path = Path(args.output).resolve() if args.output else None
|
|
332
|
+
sys.exit(convert_to_pdf(input_path, output_path))
|
|
333
|
+
|
|
334
|
+
# デフォルト: ビューア起動
|
|
335
|
+
start_viewer(args.path, args.port, not args.no_browser)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
if __name__ == "__main__":
|
|
339
|
+
main()
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ファイルタイプの定義
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Dict, Optional
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class FileTypeInfo:
|
|
11
|
+
"""ファイルタイプ情報"""
|
|
12
|
+
type: str # 'markdown', 'text', 'code', 'image'
|
|
13
|
+
icon: str
|
|
14
|
+
lang: Optional[str] = None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# ファイル拡張子とタイプのマッピング
|
|
18
|
+
FILE_TYPES: Dict[str, FileTypeInfo] = {
|
|
19
|
+
# Markdown
|
|
20
|
+
".md": FileTypeInfo("markdown", "markdown"),
|
|
21
|
+
".markdown": FileTypeInfo("markdown", "markdown"),
|
|
22
|
+
|
|
23
|
+
# Text
|
|
24
|
+
".txt": FileTypeInfo("text", "text"),
|
|
25
|
+
".text": FileTypeInfo("text", "text"),
|
|
26
|
+
".rst": FileTypeInfo("text", "text"),
|
|
27
|
+
".log": FileTypeInfo("text", "text"),
|
|
28
|
+
|
|
29
|
+
# Config files
|
|
30
|
+
".json": FileTypeInfo("code", "json", "json"),
|
|
31
|
+
".yaml": FileTypeInfo("code", "yaml", "yaml"),
|
|
32
|
+
".yml": FileTypeInfo("code", "yaml", "yaml"),
|
|
33
|
+
".toml": FileTypeInfo("code", "toml", "toml"),
|
|
34
|
+
".ini": FileTypeInfo("code", "config", "ini"),
|
|
35
|
+
".cfg": FileTypeInfo("code", "config", "ini"),
|
|
36
|
+
".conf": FileTypeInfo("code", "config", "ini"),
|
|
37
|
+
".env": FileTypeInfo("code", "config", "bash"),
|
|
38
|
+
|
|
39
|
+
# Code files
|
|
40
|
+
".py": FileTypeInfo("code", "python", "python"),
|
|
41
|
+
".js": FileTypeInfo("code", "javascript", "javascript"),
|
|
42
|
+
".ts": FileTypeInfo("code", "typescript", "typescript"),
|
|
43
|
+
".jsx": FileTypeInfo("code", "react", "javascript"),
|
|
44
|
+
".tsx": FileTypeInfo("code", "react", "typescript"),
|
|
45
|
+
".html": FileTypeInfo("code", "html", "html"),
|
|
46
|
+
".css": FileTypeInfo("code", "css", "css"),
|
|
47
|
+
".scss": FileTypeInfo("code", "css", "scss"),
|
|
48
|
+
".less": FileTypeInfo("code", "css", "less"),
|
|
49
|
+
".java": FileTypeInfo("code", "java", "java"),
|
|
50
|
+
".c": FileTypeInfo("code", "c", "c"),
|
|
51
|
+
".cpp": FileTypeInfo("code", "cpp", "cpp"),
|
|
52
|
+
".h": FileTypeInfo("code", "c", "c"),
|
|
53
|
+
".hpp": FileTypeInfo("code", "cpp", "cpp"),
|
|
54
|
+
".go": FileTypeInfo("code", "go", "go"),
|
|
55
|
+
".rs": FileTypeInfo("code", "rust", "rust"),
|
|
56
|
+
".rb": FileTypeInfo("code", "ruby", "ruby"),
|
|
57
|
+
".php": FileTypeInfo("code", "php", "php"),
|
|
58
|
+
".swift": FileTypeInfo("code", "swift", "swift"),
|
|
59
|
+
".kt": FileTypeInfo("code", "kotlin", "kotlin"),
|
|
60
|
+
".sh": FileTypeInfo("code", "shell", "bash"),
|
|
61
|
+
".bash": FileTypeInfo("code", "shell", "bash"),
|
|
62
|
+
".zsh": FileTypeInfo("code", "shell", "bash"),
|
|
63
|
+
".sql": FileTypeInfo("code", "database", "sql"),
|
|
64
|
+
".xml": FileTypeInfo("code", "xml", "xml"),
|
|
65
|
+
".graphql": FileTypeInfo("code", "graphql", "graphql"),
|
|
66
|
+
".vue": FileTypeInfo("code", "vue", "html"),
|
|
67
|
+
".svelte": FileTypeInfo("code", "svelte", "html"),
|
|
68
|
+
|
|
69
|
+
# Images
|
|
70
|
+
".png": FileTypeInfo("image", "image"),
|
|
71
|
+
".jpg": FileTypeInfo("image", "image"),
|
|
72
|
+
".jpeg": FileTypeInfo("image", "image"),
|
|
73
|
+
".gif": FileTypeInfo("image", "image"),
|
|
74
|
+
".svg": FileTypeInfo("image", "image"),
|
|
75
|
+
".webp": FileTypeInfo("image", "image"),
|
|
76
|
+
".ico": FileTypeInfo("image", "image"),
|
|
77
|
+
".bmp": FileTypeInfo("image", "image"),
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# サポートする拡張子のセット
|
|
81
|
+
SUPPORTED_EXTENSIONS = frozenset(FILE_TYPES.keys())
|
|
82
|
+
|
|
83
|
+
# スキップするディレクトリ
|
|
84
|
+
SKIP_DIRECTORIES = frozenset([
|
|
85
|
+
"node_modules",
|
|
86
|
+
"__pycache__",
|
|
87
|
+
"venv",
|
|
88
|
+
".venv",
|
|
89
|
+
".git",
|
|
90
|
+
"dist",
|
|
91
|
+
"build",
|
|
92
|
+
".next",
|
|
93
|
+
".nuxt",
|
|
94
|
+
".cache",
|
|
95
|
+
"coverage",
|
|
96
|
+
".pytest_cache",
|
|
97
|
+
".mypy_cache",
|
|
98
|
+
])
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def get_file_type(extension: str) -> Optional[FileTypeInfo]:
|
|
102
|
+
"""拡張子からファイルタイプ情報を取得"""
|
|
103
|
+
return FILE_TYPES.get(extension.lower())
|