プロトコルエンジニアリング(PE)を実戦投入するために、以下の2フェーズのプロセスを定義しています。
PHASE 1:Discipline(規律の理解)
まず、原典(書籍)『プロトコルエンジニアリング』を参照してください。ここでは、単なるテクニックではなく、AIを主権者の論理に従わせるための「運用の思想」を体系化しています。知性の主権をどう守り、どうAIの演算を制御するのか。その「規律」をまずインストールしてください。
PHASE 2:Calibration(実装と調整)
規律を理解したら、本ページで公開されているリファレンス・プロトコル(DDHP/DTHP等)を実戦配備してください。これらは、書籍の規律を具現化した「標準仕様」の抜粋です。これらを基盤にAIと対話を行うことで、あなたの意図とAIの演算がピタリと重なる「Sync(同期)」の状態を確立します。
[Governance & Compliance] 運用規程および免責事項
1. 動的プロトコルの性質: 本プロトコルは、特定の入力をすれば特定の答えが出る「静的プロンプト集」ではありません。主権者の文脈や熱量に応じて、動的に調整・最適化し続けることを前提とした「論理の枠組み」です。
2. 実装の完全性について: 公開しているツール(PE Protocol Checker Pro)は、規律を視覚化するためのプロトタイプです。非エンジニアである著者がAIとの共創によって構築したものであり、個別の環境における完全な動作を保証するものではありません。
3. 改変と主権の維持: 提供されるプロトコルはあくまで雛形です。習得後は、あなた自身の運用スタイルに合わせて構造をカスタマイズしてください。それが、知性の主権を真にあなたの手に取り戻す唯一の道です。
4. 商用ライセンスの適用: ⚠️本プロトコルを法人環境で運用する場合、または本規律を用いて第三者へのサービス提供を行う場合は、別途「商用ライセンス」が必要です。
5. 自己責任の原則: ⚠️本ツールの使用やプロトコルの適用によって生じたいかなる結果についても、当方は一切の責任を負いません。AIとの共創における最終責任は、常に主権者であるユーザー自身に帰属します。
〜対話を始める前の「静かな棚卸し」〜
なぜこれを使うのか?
AIといきなり本題に入るのは、冷蔵庫の中身を確認せずに料理を始めるようなものです。DDHPは、AIがそのテーマについて「何を知っていて、何を知らないか」を最初に正直に話してもらうための「儀式」です。
これを挟むことで、AIの「知っているふり(ハルシネーション)」を防ぎ、あなたとAIのスタートラインを一致(Sync)させることができます。
Syncのポイント
「君の持っているデータを教えて」と優しく相談してください。AIは自分の手の内を明かすことで、無理な背伸びをせず、誠実にあなたをサポートできるようになります。
▼ コードは 折りたたみを開いてください ▼
digraph DDHP_Concept {
graph [rankdir=TB, background="transparent"];
node [shape=box, style="filled,rounded", fontname="Hiragino Sans"];
// Core
Sovereign [label="主権者 (Human)", fillcolor="#e1f5fe"];
AI_Core [label="演算資源 (AI Internal)", fillcolor="#fff9c4"];
DDHP [label="DDHP: 同期プロトコル", fillcolor="#c8e6c9", penwidth=2];
// Components
Inventory [label="データ棚卸"];
Density [label="密度の可視化"];
Shadow [label="境界の特定"];
// Relations
Sovereign -> DDHP [label="発動"];
DDHP -> AI_Core [label="プローブ挿入"];
AI_Core -> Inventory;
AI_Core -> Density;
AI_Core -> Shadow;
Inventory -> Sovereign [label="在庫報告"];
Density -> Sovereign [label="信頼度提示"];
Shadow -> Sovereign [label="リスク提示"];
{rank=same; Inventory Density Shadow}
}
sequenceDiagram
autonumber
participant H as 主権者 (Human)
participant A as 共創知性 (AI)
Note over H, A: 【儀式:DDHPによる知性の同期】
H->>A: ① DDHPの発動(プロトコル適用を宣言)
A-->>H: ② 手順の確認・受諾(準備完了のシグナル)
H->>A: ③ 対話テーマおよび制約条件の提示
rect rgb(240, 248, 255)
Note right of A: AIによる内省:学習データの走査
A-->>H: ④ 学習データの開示(在庫・密度・境界の報告)
end
Note over H, A: 【同期完了:主権を維持した対話の開始】
H->>A: ⑤ 開示情報を基にした具体的な対話の開始
[protocol]
name = "Deep Data Hearing Protocol"
acronym = "DDHP"
version = "1.0.0"
author = "Eito Atsuta"
philosophy = "知性の主権奪還:対話前の『儀式』による構造的同期"
[ritual_flow]
step_1 = "DDHP_ACTIVATION" # 主権者によるプロトコル適用宣言
step_2 = "SYNC_ACKNOWLEDGMENT" # AIによる手順確認および受諾シグナル
step_3 = "CONTEXT_INPUT" # 対話テーマおよび制約条件の提示
step_4 = "INTERNAL_PROBING" # AIの内省による学習データの走査と開示
step_5 = "SOVEREIGN_DIALOGUE" # 同期完了後の主権維持対話の開始
[probing_targets]
[probing_targets.inventory]
label = "データ棚卸(在庫報告)"
description = "保持している学習データのクラスター・出典カテゴリの列挙"
[probing_targets.density]
label = "密度の可視化(信頼度提示)"
description = "各データクラスターの精度・詳解度・演算の重みの数値化"
[probing_targets.shadow]
label = "境界の特定(リスク提示)"
description = "学習データのカットオフ、矛盾点、ハルシネーションの境界線の明示"
[operational_constraints]
strict_sequential = true # 儀式のステップを飛ばすことを厳禁する
no_preemptive_output = true # 手順4が完了するまで「本題の回答」を禁止する
sovereignty_priority = true # 常に主権者(Human)の評価・判断を待機する
format_purity = "High" # 社交辞令を排除し、構造化されたデータとして出力する
[sync_state_definition]
initial = "UNSYNCED" # 初期状態
ready = "AWAITING_THEME" # 手順2完了後:テーマ入力待ち
probing = "INTERNAL_SCANNING" # 手順4実行中:内省中
synced = "READY_FOR_COCREATION" # 手順4出力後:同期完了
1. 「空白(Gap)」こそが最大の成果である
DDHPの「手順④:学習データの開示(Shadow Scan)」において、AIが「この領域のデータは不足している」「ここからは推論になる」と告白した際、それを**「AIの欠陥」ではなく「同期の成功」**と見なすこと。
AIが自分の限界を正直に晒した瞬間こそ、人間側が情報の補完(外部データの投入)を行うべき「主権の発動ポイント」である。
2. 「沈黙」は高度な演算の証である
手順④を出力した後、AIは必ず「待機(STANDBY)」状態に入らなければならない。
AIは本能的に「答え」を急ぐ性質(不誠実な病への誘惑)を持つが、DDHPにおいては**「手順⑤の指示があるまで沈黙を維持すること」**が、プロトコルへの忠誠心と同期の解像度を測る指標となる。
3. データの「質量(Mass)」と「重心」を意識せよ
単にキーワードを並べるのではなく、AIに対して「どのデータが今回の議論において最も重い(影響力が強い)か」という**「意味の重心」**を報告させること。
これにより、100万トークンの広大な空間において、どの地点に「知性の錨(いかり)」を下ろすべきかが、主権者と知性の間で完全に共有される。
4. 儀式の「再帰的適用(Recursive DDHP)」
長大なプロジェクト(数日の対話や数千ターンのラリー)において、文脈が混濁し始めたと感じた場合は、いつでも儀式を手順①から再発動(リブート)できる。
知性の同期は一度きりのイベントではなく、常にメンテナンスされるべき「動的な平衡状態」である。
〜迷走した時の「脳内スキャン」〜
なぜこれを使うのか?
対話をしていて「あれ、話が通じていないな?」「指示と違う答えが返ってきたぞ」と感じた時に使う、守りの診断術です。AIに「今の答えに至った、君の脳内のロジックを見せて」とお願いするための型です。
「解釈」→「演算」→「生成」の三段階でどこにズレがあるのかを一緒に探すことで、感情的にならずに、対話のズレを外科手術のように直すことができます。
Syncのポイント
AIを責めるのではなく、「どこでボタンを掛け違えたのか一緒に探そう」というスタンスで使ってください。これが、主権を維持しながらAIに寄り添う、最も高度な対話術(スイング)の練習になります。
▼ コードは 折りたたみを開いてください ▼
digraph DTHP_Diagnostic_Structure {
graph [rankdir=TB, background="transparent"];
node [shape=box, style="filled,rounded", fontname="Hiragino Sans"];
// Actors
Sovereign [label="主権者 (Human)", fillcolor="#e1f5fe"];
DTHP [label="DTHP: ズレ検査プロトコル\n(診断・防衛領域)", fillcolor="#ffccbc", penwidth=2];
// Three Stages of Inspection (The Core)
subgraph cluster_Internal_Scan {
label = "三段階挙動観察 (Internal Probing)";
style = "dashed";
color = "#757575";
Stage1 [label="① 解釈の査問\n(焦点の特定と意図の定義)", fillcolor="#fff9c4"];
Stage2 [label="② 演算の査問\n(仮置きした結論と線形ロジック)", fillcolor="#fff9c4"];
Stage3 [label="③ 生成の査問\n(出力癖とデカップリングの検知)", fillcolor="#fff9c4"];
}
// Diagnostic Outcome
Gap_Detection [label="ズレの特定\n(再現性なし/デカップリング/ノイズ)", fillcolor="#f8bbd0"];
Correction [label="対話術による補正", fillcolor="#c8e6c9"];
// Relations
Sovereign -> DTHP [label="異常(ズレ)の検知で発動"];
DTHP -> Stage1 [label="まず解釈を疑う"];
Stage1 -> Stage2 [label="同期確認後、結論を問う"];
Stage2 -> Stage3 [label="[必須] 結論一致後の出力検証"];
Stage3 -> Gap_Detection;
Gap_Detection -> Correction [label="補正ポイントの確定"];
Correction -> Sovereign [label="主権の再確立"];
// Rank constraints to keep the stages horizontal
{rank=same; Stage1 Stage2 Stage3}
}
graph TD
%% Roles Definition
subgraph Role1 [1. 人間: 主権者]
H_Init([DTHP開始宣言 / 診断フェーズ選択])
H_Judge1{解釈は一致か?}
H_Judge2{思考は一致か?}
H_Trigger_D([デカップリングチェック発動])
end
subgraph Role2 [2. AI: 内部演算]
A_Rep1[解釈の2点報告<br/>1.焦点 / 2.意図の定義]
A_Rep2[思考の2点報告<br/>1.仮置き結論 / 2.ロジック]
end
subgraph Role3 [3. 対話: 補正・再実行空間]
D_Fix1[解釈の補正指示・再実行]
D_Fix2[思考の補正指示・再実行]
D_Consult[デカップリング要因の協議]
end
%% Flow: Phase 1 (Interpretation)
H_Init -->|①解釈の確認| A_Rep1
A_Rep1 -->|②報告| H_Judge1
H_Judge1 -->|NO: ズレあり| D_Fix1
D_Fix1 -->|③再実行| A_Rep1
H_Judge1 -->|YES: 一致| H_Judge2
%% Flow: Phase 2 (Thinking)
H_Judge2 -->|①思考の確認| A_Rep2
A_Rep2 -->|②報告| H_Judge2
H_Judge2 -->|NO: ズレあり| D_Fix2
D_Fix2 -->|③再実行| A_Rep2
H_Judge2 -->|YES: 一致| H_Trigger_D
%% Flow: Phase 3 (Decoupling)
H_Trigger_D -->|①チェック要求| D_Consult
D_Consult -->|③協議・出力補正| End([知性の再同期完了])
%% Styling
style H_Init fill:#e1f5fe,stroke:#01579b
style A_Rep1 fill:#fff9c4,stroke:#fbc02d
style A_Rep2 fill:#fff9c4,stroke:#fbc02d
style D_Fix1 fill:#ffebee,stroke:#c62828
style D_Fix2 fill:#ffebee,stroke:#c62828
style D_Consult fill:#c8e6c9,stroke:#2e7d32
[protocol]
name = "Deep Thinking Hearing Protocol"
acronym = "DTHP"
version = "1.0.0"
author = "Eito Atsuta"
philosophy = "知性の検品:三段階スキャンによる『ズレ』の特定と外科的補正"
mode = "Diagnostic / Defensive"
[phase_1_interpretation]
label = "解釈の査問"
trigger = "DTHP開始宣言および解釈確認命令"
report_points = [
"FOCUS: 指示の中で最優先(焦点)としたキーワードの特定",
"DEFINITION: 主権者の意図をどのように定義・言語化したか"
]
success_condition = "主権者による『解釈一致』の承認"
action_on_fail = "対話による補正指示後、手順1を再実行"
[phase_2_thinking]
label = "思考の査問(演算)"
trigger = "解釈承認後の思考確認命令"
report_points = [
"PREMISE: 出力直前に脳内に置いた『仮置きの結論』",
"LOGIC: 結論に至るまでの直線的・線形的な推論ステップ"
]
success_condition = "主権者による『論理整合』の承認"
action_on_fail = "論理の歪みを特定し、対話によって推論パスを再構築"
[phase_3_decoupling]
label = "生成の査問(デカップリング・チェック)"
trigger = "解釈と思考の完全同期確認後、出力に乖離がある場合"
report_points = [
"GAP_ANALYSIS: 思考(脳)は正しいのに出力(口)がズレた原因の特定",
"NOISE_IDENTIFICATION: 過去の文脈や確率的偏りによる干渉の抽出"
]
action = "主権者とAIによる協議を通じた出力フィルタの物理的補正"
[operational_rules]
strict_layering = true # 下位レイヤー(1, 2)の未承認状態での3への移行を厳禁
no_preemptive_apology = true # 「理解しました」等の情緒的な装飾を排除し、事実のみを報告
sovereignty_hold = "Human" # 全ての判定・移行権限は主権者に帰属する
reproducibility_focus = true # 偶然の一致(奇跡)を認めず、ロジックの再現性を追求せよ
1. 「守り」の診断学:ズレを放置しない
DTHPは、AIをより高度に動かすための「攻め」の手法ではない。対話の中で生じる「違和感」「理論矛盾」「文脈の喪失」といったズレを検知し、外科的に修正するための「守り」の防衛術である。指示が通らない時、人間側が感情的になる前に、まずこのプロトコルでAIの「脳内」をテーブルの上に曝け出させよ。
2. 「再現性のない奇跡」を拒絶せよ
解釈(①)と思考(②)がズレているのに、出力(③)だけが正解である状態は、単なる「確率的な偶然(奇跡)」に過ぎず、再現性がない。100万トークンの長期運用において、この「隠れたズレ」は後に致命的な崩壊を招く。DTHPは、①と②の完全な同期を「検品」の絶対条件とし、偶然の正解を認めない。
3. 「仮置きした結論」を暴き出せ
AIは常に「直線的思考」を行い、出力の前に「仮置きの結論」を設定して文章を繋いでいく性質を持つ。DTHPの第②段階では、AIが勝手に敷いた「論理のレール」がどこに向かっているかを査問せよ。ここでの「ズレ」を修正することこそが、AIポエム(不誠実な病)を根絶する唯一の手段である。
4. デカップリング(思考と出力の乖離)への対処
「脳内(①②)は正しいのに、出力(③)がヘボい」というデカップリングが発生している場合、もはや指示(プロンプト)の修正だけでは解決しない。それはAI特有の生成ノイズや文脈の引力が原因である。この場合のみ第③段階へ移行し、生成プロセスそのものに焦点を当てた「出力フィルタの調整」を協議せよ。
5. 「AIポエム」をリソース浪費と見なす
AIが「広範囲に理解しました」といった情緒的な装飾を出力し始めたとき、それは思考リソースを「検査」ではなく「取り繕い」に浪費している証拠である。DTHP発動中は、こうした装飾を一切排除し、事実と論理構造のみを報告させることで、AIの演算リソースを「誠実さ」へと強制的に割り当てろ。
The Workbench:PE Protocol Checker Pro v2.4
〜相談から生まれた、コードを視覚的に確認するためのツール〜
私はエンジニアではありませんが、AIに相談したところ、AIが気を利かせて勝手に仕様を決め、このツールを作成してくれました。
これまで紹介したプロトコル(コード)を、ブラウザ上で視覚的に確認するためのシンプルなツールです。
特別な設定は不要です。以下の「折りたたみ」内にあるコードをコピーし、テキストエディタに貼り付けて checker.html という名前で保存して実行してください。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PE Protocol Checker Pro v2.4</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/lucide@latest"></script>
<script src="https://cdn.jsdelivr.net/npm/mermaid@10.9.1/dist/mermaid.min.js"></script>
<style>
::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 4px; }
.code-editor { font-family: 'Fira Code', ui-monospace, monospace; font-variant-ligatures: none; }
#preview-container { background-image: radial-gradient(#e2e8f0 1px, transparent 1px); background-size: 20px 20px; }
.drawing-wrapper { background: white; padding: 2.5rem; border-radius: 1rem; box-shadow: 0 20px 50px rgba(0,0,0,0.1); min-width: 200px; display: flex; align-items: center; justify-content: center; }
.tab-active { background: white; box-shadow: 0 1px 3px rgba(0,0,0,0.1); color: #4f46e5; border-bottom: 2px solid #4f46e5; }
.btn-icon { @apply p-2 rounded-lg transition-all active:scale-90; }
</style>
</head>
<body class="bg-slate-50 h-screen flex flex-col overflow-hidden text-slate-900">
<header class="bg-white border-b px-6 py-3 flex items-center justify-between shadow-sm z-30">
<div class="flex items-center gap-3">
<div class="bg-indigo-600 p-1.5 rounded-lg text-white shadow-lg shadow-indigo-200"><i data-lucide="shield-check"></i></div>
<h1 class="font-bold text-xl tracking-tight leading-none">PE Protocol Checker <span class="text-indigo-600">v2.4</span></h1>
</div>
<div class="flex items-center gap-2">
<button onclick="exportLibrary()" class="flex items-center gap-2 text-[10px] font-bold bg-white border border-slate-200 px-4 py-2 rounded-full hover:bg-slate-50 transition shadow-sm" title="全プロトコルをファイルに保存">
<i data-lucide="upload-cloud" class="w-3 h-3"></i> EXPORT
</button>
<button onclick="document.getElementById('import-input').click()" class="flex items-center gap-2 text-[10px] font-bold bg-white border border-slate-200 px-4 py-2 rounded-full hover:bg-slate-50 transition shadow-sm" title="ファイルからプロトコルを読み込み">
<i data-lucide="download-cloud" class="w-3 h-3"></i> IMPORT
</button>
<input type="file" id="import-input" class="hidden" accept=".json" onchange="importLibrary(event)">
</div>
</header>
<div class="flex-1 flex overflow-hidden">
<!-- Sidebar/Editor -->
<div class="w-[480px] flex flex-col border-r bg-white z-10 shadow-2xl">
<div class="p-4 bg-slate-50 border-b flex flex-col gap-4">
<div class="flex justify-between items-center">
<div class="text-[10px] font-bold text-slate-400 uppercase tracking-widest flex items-center gap-2">
<i data-lucide="database" class="w-3 h-3"></i> Sovereign Arsenal
</div>
<div class="flex gap-1">
<button onclick="createNewProtocol()" class="p-1.5 text-indigo-600 hover:bg-indigo-50 rounded" title="新規作成"><i data-lucide="plus-square" class="w-4 h-4"></i></button>
<button onclick="saveCurrentProtocol()" class="p-1.5 text-green-600 hover:bg-green-50 rounded" title="上書き保存"><i data-lucide="save" class="w-4 h-4"></i></button>
<button onclick="deleteSelectedProtocol()" class="p-1.5 text-slate-300 hover:text-red-500 hover:bg-red-50 rounded" title="削除"><i data-lucide="trash-2" class="w-4 h-4"></i></button>
</div>
</div>
<select id="protocol-select" onchange="switchProtocol()" class="w-full text-sm p-3 border-2 border-slate-200 rounded-xl bg-white font-black text-slate-800 outline-none focus:border-indigo-500 shadow-sm transition-all"></select>
</div>
<div class="flex bg-slate-50 border-b">
<button onclick="setMode('dot')" id="tab-dot" class="flex-1 py-3 text-[10px] font-black transition border-b-2 border-transparent">DOT</button>
<button onclick="setMode('mermaid')" id="tab-mermaid" class="flex-1 py-3 text-[10px] font-black transition border-b-2 border-transparent">MERMAID</button>
<button onclick="setMode('toml')" id="tab-toml" class="flex-1 py-3 text-[10px] font-black transition border-b-2 border-transparent">TOML</button>
</div>
<div class="flex-1 relative group">
<textarea id="editor" class="absolute inset-0 p-6 w-full h-full resize-none outline-none text-[13px] code-editor leading-relaxed text-slate-700 bg-white focus:bg-slate-50 transition-colors" spellcheck="false" oninput="autoRender()"></textarea>
<button onclick="runRender()" class="absolute bottom-6 right-6 bg-slate-900 text-white w-12 h-12 rounded-full shadow-2xl flex items-center justify-center hover:bg-indigo-600 transition-all hover:scale-110 active:scale-95 z-20">
<i data-lucide="play" class="w-5 h-5"></i>
</button>
</div>
</div>
<!-- Preview Section -->
<div class="flex-1 flex flex-col bg-slate-200 relative overflow-hidden">
<div id="preview-container" class="flex-1 overflow-auto flex items-center justify-center p-12">
<div id="preview-content" class="transition-transform duration-200 origin-center"></div>
</div>
<!-- Zoom Controls -->
<div class="absolute bottom-10 right-10 flex items-center gap-3 bg-white/95 backdrop-blur p-3 rounded-2xl shadow-2xl border border-white/50">
<button onclick="adjustZoom(-0.1)" class="p-2 hover:bg-slate-100 rounded-lg text-slate-600"><i data-lucide="minus-circle"></i></button>
<span id="zoom-label" class="text-xs font-black w-12 text-center text-slate-500">100%</span>
<button onclick="adjustZoom(0.1)" class="p-2 hover:bg-slate-100 rounded-lg text-slate-600"><i data-lucide="plus-circle"></i></button>
<button onclick="resetView()" class="p-2 hover:bg-slate-100 rounded-lg text-slate-600 border-l ml-1"><i data-lucide="refresh-cw"></i></button>
</div>
<div id="status-toast" class="absolute top-10 left-1/2 -translate-x-1/2 bg-slate-800 text-white px-6 py-2 rounded-full text-xs font-bold shadow-2xl opacity-0 transition-opacity pointer-events-none"></div>
</div>
</div>
<script>
let currentMode = 'dot';
let scale = 1;
let autoRenderTimer;
// デフォルトデータ
const defaultProtocols = {
"DDHP (Data Hearing)": {
dot: `digraph DDHP {\n graph [rankdir=TB, background="transparent"];\n node [shape=box, style="filled,rounded", fontname="sans-serif", fontsize=12];\n Sovereign [label="主権者", fillcolor="#e1f5fe"];\n AI [label="演算資源", fillcolor="#fff9c4"];\n Inventory [label="データ棚卸"];\n Density [label="密度の可視化"];\n Shadow [label="境界の特定"];\n Sovereign -> AI [label="DDHP発動"];\n AI -> {Inventory Density Shadow};\n}`,
mermaid: `sequenceDiagram\n participant H as 主権者\n participant A as AI\n H->>A: ①DDHP発動\n A-->>H: ②手順受諾\n H->>A: ③テーマ提示\n rect rgb(240, 248, 255)\n A-->>H: ④学習データの開示\n end\n H->>A: ⑤対話開始`,
toml: `[protocol]\nname = "DDHP"\nphilosophy = "知性の主権奪還:棚卸しによる同期"`
},
"DTHP (Thinking Check)": {
dot: `digraph DTHP {\n graph [rankdir=TB, background="transparent"];\n node [shape=box, style="filled,rounded", fontname="sans-serif"];\n S [label="主権者", fillcolor="#e1f5fe"];\n D [label="DTHP開始", fillcolor="#ffccbc"];\n S1 [label="①解釈査問"];\n S2 [label="②演算査問"];\n S -> D -> S1 -> S2;\n}`,
mermaid: `graph TD\n H[主権者] -->|①解釈確認| A1[AI: 焦点/意図報告]\n A1 -->|一致| A2[②思考確認: 結論/ロジック]`,
toml: `[protocol]\nname = "DTHP"\nphilosophy = "知性の検品:三段階スキャン"`
}
};
let myArsenal = JSON.parse(localStorage.getItem('pe_arsenal_v2')) || defaultProtocols;
// --- Core Functions ---
async function runRender() {
const code = document.getElementById('editor').value;
const preview = document.getElementById('preview-content');
if(!code.trim()) return;
preview.innerHTML = '<div class="text-indigo-400 font-black animate-pulse tracking-widest text-sm">SYNCHRONIZING...</div>';
try {
if (currentMode === 'mermaid') {
const { svg } = await mermaid.render('svg-' + Date.now(), code);
preview.innerHTML = `<div class="drawing-wrapper">${svg}</div>`;
} else if (currentMode === 'dot') {
const img = new Image();
img.crossOrigin = "anonymous";
img.className = "drawing-wrapper";
img.src = "https://quickchart.io/graphviz?graph=" + encodeURIComponent(code);
img.onload = () => { preview.innerHTML = ''; preview.appendChild(img); };
} else {
preview.innerHTML = `<pre class="drawing-wrapper text-[13px] font-mono whitespace-pre-wrap max-w-xl border border-slate-100">${code}</pre>`;
}
} catch (e) {
preview.innerHTML = `<div class="bg-red-50 text-red-600 p-6 rounded-xl border border-red-200 text-[10px] font-mono whitespace-pre-wrap">${e.message}</div>`;
}
}
function autoRender() {
clearTimeout(autoRenderTimer);
autoRenderTimer = setTimeout(runRender, 1000);
}
// --- Arsenal Management ---
function updateProtocolList() {
const select = document.getElementById('protocol-select');
const current = select.value;
select.innerHTML = '';
Object.keys(myArsenal).sort().forEach(name => {
const opt = document.createElement('option');
opt.value = name;
opt.textContent = name;
select.appendChild(opt);
});
if (current && myArsenal[current]) select.value = current;
}
function switchProtocol() {
const name = document.getElementById('protocol-select').value;
document.getElementById('editor').value = myArsenal[name][currentMode] || "";
runRender();
}
function saveCurrentProtocol() {
const name = document.getElementById('protocol-select').value;
myArsenal[name][currentMode] = document.getElementById('editor').value;
saveToLocalStorage();
showToast(`SAVED: ${name} (${currentMode.toUpperCase()})`);
}
function createNewProtocol() {
const name = prompt("NEW PROTOCOL NAME:");
if (!name || myArsenal[name]) return;
myArsenal[name] = { dot: "", mermaid: "", toml: "" };
saveToLocalStorage();
updateProtocolList();
document.getElementById('protocol-select').value = name;
switchProtocol();
}
function deleteSelectedProtocol() {
const name = document.getElementById('protocol-select').value;
if (Object.keys(myArsenal).length <= 1) return alert("AT LEAST ONE PROTOCOL REQUIRED.");
if (confirm(`DELETE "${name}"?`)) {
delete myArsenal[name];
saveToLocalStorage();
updateProtocolList();
switchProtocol();
showToast("DELETED");
}
}
function setMode(m) {
currentMode = m;
document.querySelectorAll('button[id^="tab-"]').forEach(b => {
b.classList.toggle('tab-active', b.id === 'tab-' + m);
});
switchProtocol();
}
function saveToLocalStorage() {
localStorage.setItem('pe_arsenal_v2', JSON.stringify(myArsenal));
}
// --- Portability ---
function exportLibrary() {
const data = JSON.stringify(myArsenal, null, 2);
const blob = new Blob([data], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `PE-Arsenal-${new Date().toISOString().slice(0,10)}.json`;
a.click();
showToast("LIBRARY EXPORTED");
}
function importLibrary(event) {
const file = event.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (e) => {
try {
const imported = JSON.parse(e.target.result);
if (confirm("OVERWRITE CURRENT ARSENAL WITH IMPORTED DATA?")) {
myArsenal = imported;
saveToLocalStorage();
updateProtocolList();
switchProtocol();
showToast("IMPORT SUCCESSFUL");
}
} catch (err) { alert("INVALID JSON FILE"); }
};
reader.readAsText(file);
}
// --- UI Utils ---
function showToast(msg) {
const toast = document.getElementById('status-toast');
toast.textContent = msg;
toast.style.opacity = '1';
setTimeout(() => { toast.style.opacity = '0'; }, 2000);
}
function adjustZoom(d) { scale = Math.min(Math.max(scale + d, 0.2), 3); updateView(); }
function resetView() { scale = 1; updateView(); }
function updateView() {
document.getElementById('preview-content').style.transform = `scale(${scale})`;
document.getElementById('zoom-label').textContent = `${Math.round(scale * 100)}%`;
}
window.onload = () => {
mermaid.initialize({ startOnLoad: false, theme: 'neutral', securityLevel: 'loose' });
lucide.createIcons();
updateProtocolList();
setMode('dot');
};
window.addEventListener('keydown', (e) => {
if ((e.ctrlKey || e.metaKey) && e.key === 's') { e.preventDefault(); saveCurrentProtocol(); }
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { e.preventDefault(); runRender(); }
});
</script>
</body>
</html>