diff --git a/js/single_line_picker.js b/js/single_line_picker.js index 5349c7b..09e3265 100644 --- a/js/single_line_picker.js +++ b/js/single_line_picker.js @@ -57,6 +57,21 @@ app.registerExtension({ `; document.head.appendChild(style); + const base64prefix = ":::base64:::"; + const decodeBase64Unicode = function (input) { + let rawBase64 = input; + + if (input.startsWith(base64prefix)) { + rawBase64 = input.slice(base64prefix.length); + }else{ + return ""; + } + //bytes = Uint8Array.fromBase64(rawBase64); + const binString = atob(rawBase64); + const bytes = Uint8Array.from(binString, (m) => m.charCodeAt(0)); + return new TextDecoder().decode(bytes); + } + const addListItem = function (container) { const itm = document.createElement("div"); itm.className = "single-line-picker-list-item"; @@ -395,6 +410,7 @@ app.registerExtension({ const result = []; let currentBody = []; let currentTitle = null; + let includesBase64 = false; lineArray.forEach((line) => { const trimmedLine = line.trim(); @@ -411,6 +427,9 @@ app.registerExtension({ } else { // accumulate the body if (currentTitle !== null) { + if (currentBody.length == 0 && trimmedLine === base64prefix) { + includesBase64 = true; + } currentBody.push(trimmedLine); } } @@ -421,6 +440,16 @@ app.registerExtension({ result.push(currentTitle, currentBody.join('\n')); } + if (includesBase64) { + for (let index = 0; index < result.length; index++) { + const itm = result[index]; + if (itm.startsWith(base64prefix)) { + result[index] = decodeBase64Unicode(result[index]); + } + + } + } + return result; } diff --git a/tools/slp_build_folder.py b/tools/slp_build_folder.py new file mode 100644 index 0000000..d7d95e2 --- /dev/null +++ b/tools/slp_build_folder.py @@ -0,0 +1,98 @@ +import argparse +import base64 +from pathlib import Path + +def output_to_file(output_content, target_dir): + target_dir = Path(target_dir) + if output_content: + # 既存ファイルは上書きされる + target_dir.write_text(output_content, encoding='utf-8') + print(f"Successfully saved to: {target_dir}") + else: + print("No content generated. Check if the directory contains .txt files.") + +def get_folder_contents(target_dir): + """ + フォルダ直下のtxtファイルを読み込み、指定フォーマットの文字列を返します。 + """ + target_dir = Path(target_dir) + if not target_dir.is_dir(): + return "" + + results = ["#section"] + # フォルダ直下の .txt ファイルを昇順で取得 + for file_path in sorted(target_dir.glob("*.txt")): + try: + # 改行コードを \n に統一して読み込み + if file_path.name.endswith(".slp.txt"): + continue + content = file_path.read_text(encoding='utf-8').replace('\r\n', '\n').replace('\r', '\n') + results.append(f"[{file_path.stem}]") + results.append(content) + except Exception as e: + results.append(f"[{file_path.stem}]\nError reading file: {e}") + + if len(results) <= 1: + return "" + return "\n".join(results) + +def process_folder(target_dir): + """-d: フォルダ内のテキストファイルを処理して保存""" + target_dir = Path(target_dir) + output_file = target_dir / "output.slp.txt" + output_to_file(get_folder_contents(target_dir), output_file) + +def merge_subfolder_files(target_dir): + """ + -dd: サブフォルダ(1階層)ごとに get_folder_contents を実行し、 + 結果をbase64エンコードしてまとめます。 + """ + target_dir = Path(target_dir) + if not target_dir.is_dir(): + print(f"Error: {target_dir} is not a directory.") + return + + final_results = ["#section"] + + # 1階層下のディレクトリのみを抽出 + subdirectories = sorted([d for d in target_dir.iterdir() if d.is_dir()]) + for sub_dir in subdirectories: + # サブフォルダ内のテキスト情報を取得 + folder_text = get_folder_contents(sub_dir) + + # テキストが存在する場合のみ処理 + if folder_text: + # 文字列をバイトに変換してBase64エンコード + b64_encoded = base64.b64encode(folder_text.encode('utf-8')).decode('utf-8') + + final_results.append(f"[{sub_dir.name}]") + final_results.append(":::base64:::") + final_results.append(b64_encoded) + + output_content = "" + if len(final_results) > 1: + output_content = "\n".join(final_results) + output_file = target_dir / "output.slp.txt" + output_to_file(output_content, output_file) + +def main(): + parser = argparse.ArgumentParser(description='Converts text files to SLP compatible text file.') + parser.add_argument('path', help='Path to the target file or directory') + + group = parser.add_mutually_exclusive_group() + group.add_argument('-d', action='store_true', help='Process text files in the folder') + group.add_argument('-dd', action='store_true', help='Process text files in subfolders and merge them into a single file') + + args = parser.parse_args() + target_path = Path(args.path) + + if args.d: + process_folder(target_path) + elif args.dd: + merge_subfolder_files(target_path) + else: + # オプションなしの場合は-d + process_folder(target_path) + +if __name__ == '__main__': + main() diff --git a/tools/slp_buld_csv.py b/tools/slp_buld_csv.py new file mode 100644 index 0000000..5dcddc6 --- /dev/null +++ b/tools/slp_buld_csv.py @@ -0,0 +1,105 @@ +import argparse +import base64 +import csv +from pathlib import Path + +def output_to_file(output_content, file_path): + """指定されたファイル名で内容を保存""" + file_path = Path(file_path) + if not output_content: + print(f"No content to write for {file_path}") + return + file_path.write_text(output_content, encoding='utf-8') + print(f"Successfully saved to: {file_path}") + +def process_csv_or_tsv(file_path): + """ + CSV/TSVを処理し、1行ずつリストに追加して最後に結合します。 + """ + path = Path(file_path) + if not path.is_file(): + return "" + + delim = '\t' if path.suffix.lower() == '.tsv' else ',' + results = ["#section"] + + try: + with path.open(encoding='utf-8', newline='') as f: + reader = csv.reader(f, delimiter=delim) + for row in reader: + if len(row) >= 2 and row[0] and row[1]: + # [title] + results.append(f"[{row[0]}]") + # body (改行コード正規化) + results.append(row[1].replace('\r\n', '\n').replace('\r', '\n')) + + # 3列目がある場合の追加処理 + if len(row) >= 3: + results.append("---") + results.append(row[2].replace('\r\n', '\n').replace('\r', '\n')) + except Exception as e: + return f"Error processing {path.name}: {e}" + + if len(results) <= 1: + return "" + return "\n".join(results) + +def merge_folder_files(target_dir): + """フォルダ内の全ファイルを処理し、結果をBase64化して連結""" + target_dir = Path(target_dir) + final_results = ["#section"] + + files = sorted([f for f in target_dir.iterdir() if f.suffix.lower() in ['.csv', '.tsv']]) + + for file in files: + content = process_csv_or_tsv(file) + if content: + b64_encoded = base64.b64encode(content.encode('utf-8')).decode('utf-8') + final_results.append(f"[{file.stem}]") + final_results.append(":::base64:::") + final_results.append(b64_encoded) + if len(final_results) <= 1: + return "" + return "\n".join(final_results) + +def process_csv(target_path): + # 単体ファイル処理 + target_path = Path(target_path) + if target_path.suffix.lower() not in ['.csv', '.tsv']: + print("Error: Target path must be a .csv or .tsv file") + return + + content = process_csv_or_tsv(target_path) + output_file_path = target_path.with_suffix('.slp.txt') + output_to_file(content, output_file_path) + +def process_folder(target_path): + target_path = Path(target_path) + if not target_path.is_dir(): + print("Error: Target path must be a directory.") + return + + content = merge_folder_files(target_path) + output_file_path = target_path / "output.slp.txt" + output_to_file(content, output_file_path) + +def main(): + parser = argparse.ArgumentParser(description='Converts CSV/TSV to SLP compatible text file.') + parser.add_argument('path', help='Path to the target CSV/TSV file or directory') + + args = parser.parse_args() + target_path = Path(args.path) + + if target_path.is_dir(): + # フォルダ一括処理 + process_folder(target_path) + elif target_path.suffix.lower() in ['.csv', '.tsv']: + # 単体ファイル処理 + process_csv(target_path) + + else: + print("Error: Target path must be a .csv or .tsv file or directory.") + + +if __name__ == '__main__': + main() \ No newline at end of file