# app.py import streamlit as st from pathlib import Path from core.file_scanner import FileScanner, FileInfo # セッション状態を初期化 if 'scanned_files' not in st.session_state: st.session_state.scanned_files = [] # List[FileInfo] if 'selected_files' not in st.session_state: st.session_state.selected_files = set() # {Path} 相対パス or 絶対パス st.title("指定拡張子のみディレクトリスキャン & 選択ダウンロード") # --- 1) ユーザー入力: スキャンしたいローカルディレクトリパス --- dir_path_str = st.text_input("ローカルディレクトリパス", value=".") # --- 2) ユーザー入力: スキャン対象拡張子選択 --- st.subheader("スキャン対象拡張子") available_exts = [".py", ".js", ".ts", ".md", ".txt", ".java", ".cpp"] chosen_exts = [] for ext in available_exts: if st.checkbox(ext, key=f"ext_{ext}", value=(ext in [".py", ".md"])): chosen_exts.append(ext) # --- 3) 「スキャン実行」ボタン --- if st.button("スキャン開始"): base_path = Path(dir_path_str).resolve() if not base_path.is_dir(): st.error("有効なディレクトリを指定してください") else: scanner = FileScanner(base_path, set(chosen_exts)) found_files = scanner.scan_files() st.session_state.scanned_files = found_files st.session_state.selected_files = set() # スキャン毎に選択リセット st.success(f"スキャン完了: {len(found_files)}個のファイル") # --- 4) ファイルを表示するUI(ディレクトリ構造 or リスト) + 全選択 / 全解除 --- if st.session_state.scanned_files: # 全選択 / 全解除ボタン col1, col2 = st.columns(2) with col1: if st.button("すべて選択"): # すべてのファイルを選択 base_path = Path(dir_path_str).resolve() # relative_to で相対パスを使うか、絶対パスを使うかは好みでOK st.session_state.selected_files = { f.path.relative_to(base_path) for f in st.session_state.scanned_files } with col2: if st.button("すべて解除"): st.session_state.selected_files = set() st.write("### ファイル一覧 (指定拡張子のみ)") base_path = Path(dir_path_str).resolve() for file_info in st.session_state.scanned_files: # 相対パスで扱う例 rel_path = file_info.path.relative_to(base_path) checked = (rel_path in st.session_state.selected_files) # チェックボックス + ファイル名表示 new_checked = st.checkbox( f"{rel_path} ({file_info.formatted_size})", value=checked, key=str(rel_path) # keyは重複しないように文字列にしておく ) # 選択状態を更新 if new_checked: st.session_state.selected_files.add(rel_path) else: st.session_state.selected_files.discard(rel_path) # --- 5) 選択ファイルをまとめてMarkdown化 & ダウンロード --- def create_markdown_for_selected(files, selected_paths, base_dir: Path) -> str: """ 選択されたファイルだけをMarkdownテキストとして返す """ output = [] for f in files: rel_path = f.path.relative_to(base_dir) if rel_path in selected_paths: output.append(f"## {rel_path}") output.append("------------") if f.content is not None: output.append(f.content) else: output.append("# Failed to read content") output.append("") # 空行 return "\n".join(output) if st.session_state.scanned_files: st.write("### 選択ファイルをMarkdownダウンロード") if st.button("選択ファイルをまとめてダウンロード"): base_path = Path(dir_path_str).resolve() markdown_text = create_markdown_for_selected( st.session_state.scanned_files, st.session_state.selected_files, base_path ) st.download_button( label="ダウンロード", data=markdown_text, file_name="selected_files.md", mime="text/markdown" )