diff --git a/.agent/rules/documents.md b/.agent/rules/documents.md index 238c6ad..24d7115 100644 --- a/.agent/rules/documents.md +++ b/.agent/rules/documents.md @@ -1,7 +1,7 @@ --- trigger: always_on glob: -description: 設計参照・計測・ジャーナル・コミット・ログ確認。他ルールは配布ビルド前→distribution-build.md、Issue→issue_management.md。 +description: 設計参照・計測・ジャーナル・コミット・ログ確認。調べる場面では search_text を積極的に使う。他ルールは配布ビルド前→distribution-build.md、Issue→issue_management.md。 --- 0. **着手前**: タスク着手前に、タスク種別に応じて関連ルールを確認してから作業を開始すること。推測や慣習に頼らず、ルールを読んでから着手する。 @@ -12,7 +12,7 @@ 1. toolsは.gitignoreだがアクセスは許可されています。 2. 詳細な設計がdocumentsにあるので、改造を計画する際に参照すること。 -2.1 **調べるときは search_text も使う**: 「どこでやってるか」「どういう仕様か」「他プロジェクトのやり方」を調べる文脈では、リポジトリの grep/read に加えて MCP の `search_text` でインデックスを検索すること。インデックスにモニタや add_item_text で取り込んだドキュメントがあれば、横断的にヒットする。自然な質問(例: 「PDF のソースはどこ」「アーカイブの手順」)で検索するとよい。 +2.1 **search_text を積極的に使う**: 事実・仕様・「どこでやってるか」「他プロジェクトのやり方」を調べるときは、grep/read に加えて(あるいは先に)MCP の `search_text` でインデックスを検索すること。ユーザーが「〜はある?」「〜の方法は?」と聞いた場合も、search_text の利用を検討する。自然な質問(例: 「PDF のソースはどこ」「アーカイブの手順」)で検索するとよい。 3. ソースコードの編集が完了したら、`tools/count_loc.cjs` と `tools/nesting_depth.cjs` を使用して計測を行うこと。 4. 計測の結果、ソースコードが600行以上、またはネストが7階層以上のコードについては、リファクタリングを検討すること。 diff --git a/RELEASE_v0.3.3_Community.md b/RELEASE_v0.3.3_Community.md new file mode 100644 index 0000000..6192eb3 --- /dev/null +++ b/RELEASE_v0.3.3_Community.md @@ -0,0 +1,93 @@ +# TelosDB Community 版 v0.3.3 — 商品紹介 + +## そのまま使える、ローカル完結の意味検索 + +**TelosDB Community 版**は、追加のモデルや設定なしで、すぐに意味検索を始められるデスクトップアプリです。データはすべてお使いの PC 内に留まり、クラウドへ一切送信しません。 + +--- + +## こんな方におすすめ + +- まずは手軽に意味検索を試したい +- モデルのダウンロードや GPU 環境を用意したくない +- 個人のメモやドキュメントを「意味で」探したい +- Cursor や Claude Desktop など AI ツールと MCP で連携したい + +--- + +## Community 版の特長 + +### モデル不要で動く + +LSA(潜在意味解析)により、**50 次元のベクトル**で文書を表現します。大きな AI モデルは不要。インストール後、そのまま検索できます。 + +### キーワードも自然な文も、そのまま検索 + +「春の歌」でも「桜の季節に聴きたい曲」のような文でも検索可能。結果は**文書単位**でまとめて返すため、どのドキュメントがヒットしたかが分かりやすくなっています。 + +### MCP で AI とつながる + +Model Context Protocol(MCP)に対応。Cursor や Claude Desktop から「検索」「追加」「RE-INDEX」などのツールとして TelosDB を呼び出せます。AI エージェントの「記憶」として利用するのに最適です。 + +### 自動でインデックスを整える + +ドキュメントの増減に応じて、バックグラウンドで LSA を再学習。画面上では「LSA学習中…」「ベクトル同期中…」の表示で進行状況を確認できます。必要に応じて RE-INDEX で手動再構築も可能です。 + +### ログイン時に自動起動(オプション) + +設定で「OSにログインしたときに自動で起動する」をオンにすると、Windows にサインインしたタイミングで TelosDB が起動し、トレイで待機。いつでも MCP 経由で検索できます。 + +### フォルダ監視で自動取り込み(New) + +指定したフォルダを常時監視し、ファイルの追加・更新・削除を自動検知してインデックスに反映します。テキストやマークダウンを保存するだけで、検索対象に自動登録。フォルダごとにカテゴリ名を付けられるため、検索時の分類にも活用できます。 + +--- + +## v0.3.3 の主な変更点 + +### 新機能 + +- **フォルダ監視**: 指定フォルダ内のファイル追加・更新・削除を自動検知し、インデックスに反映。設定パネルからフォルダの追加・削除・カテゴリ指定が可能。取込対象の拡張子もカスタマイズ可能(デフォルト: txt, md, json, html, css, js, mjs, ts, rs)。 +- **接続状態表示**: MCP サーバーへの接続状態を UI に表示。 + +### 改善 + +- **自動起動の安定化**: 設定ファイル読み込み失敗時にレジストリエントリが消える問題を修正。`isEnabled()` で OS の実状態と比較し、不一致時のみ登録を更新するように改善。失敗時のエラー表示を追加。 +- **バックエンドリファクタリング**: MCP ツールの登録を `registry.rs` に集約し、検索ロジックを `search.rs` に整理。DB アクセス関数を `db/mod.rs` に追加。 +- **DB マイグレーション**: ドキュメントパスの正規化マイグレーションを追加(テーブル未存在時はスキップ)。 +- **バージョン表示の安定化**: フッターのバージョン取得でサーバー未応答時のビジーウェイトとフォールバック未設定の問題を修正。 +- **E2E テスト**: 自動起動・フォルダ監視・パネル操作・文書編集のテストを追加。 + +--- + +## 含まれる機能(v0.3.3) + +- 意味検索(LSA 50 次元)+ 全文検索(FTS5)のハイブリッド検索 +- 検索結果の文書単位表示(`group_by_document`) +- **フォルダ監視**: 指定フォルダの自動取り込み・カテゴリ指定・拡張子フィルタ +- MCP ツール: `search_text` / `add_item_text` / `update_item` / `delete_item` / `get_item_by_id` / `get_document_count` / `lsa_retrain`(RE-INDEX) +- ドキュメント件数の表示(ヘッダー「X docs」・`get_document_count`) +- インデックス状態の表示(LSA学習中・ベクトル同期中) +- ログイン時の自動起動(オプション) +- トレイ常駐・バージョン表示 + +--- + +## ご利用方法 + +**インストーラ**: `TelosDB-Community_0.3.3_x64-setup.exe` を実行してインストールしてください。 + +**ソースからビルドする場合**: + +```bash +npm install +npm run build:community +``` + +- 出力: `src/backend/target/release/bundle/nsis/TelosDB-Community_0.3.3_x64-setup.exe` +- 初回起動時に vec0.dll がアプリデータフォルダへコピーされます。不具合時は `%APPDATA%\com.telosdb.app\logs\telos.log` をご確認ください。 + +--- + +より高精度な意味検索をご希望の方は **Pro 版**をご検討ください。 +→ [TelosDB Pro 版 商品紹介](RELEASE_v0.3.3_Pro.md) diff --git a/RELEASE_v0.3.3_Pro.md b/RELEASE_v0.3.3_Pro.md new file mode 100644 index 0000000..686a66a --- /dev/null +++ b/RELEASE_v0.3.3_Pro.md @@ -0,0 +1,107 @@ +# TelosDB Pro 版 v0.3.3 — 商品紹介 + +## 日本語に強い、高精度な意味検索 + +**TelosDB Pro 版**は、日本語向けの埋め込みモデル(768 次元)を**インストーラに同梱**したエディションです。インストールするだけで、言い回しの違いに強い高精度な意味検索が使えます。ビジネス文書や学術メモの検索に適し、データはすべてローカルで完結。クラウドへ送信しません。 + +--- + +## こんな方におすすめ + +- 検索精度をより高めたい +- 「似た意味の文」でしっかりヒットさせたい +- 報告書・論文・仕様書などフォーマルな日本語を扱う +- Cursor や Claude Desktop と MCP で連携し、高品質な検索結果を AI に渡したい + +--- + +## Pro 版の特長 + +### 日本語特化の埋め込みで高精度 + +**sentence-BERT 系の日本語モデル**(768 次元)で文書をベクトル化。キーワードの一致だけでなく、「言い換え」や「関連する表現」にも強く、自然な文で検索できます。結果は**文書単位**で返るため、どのドキュメントが該当したかが一目で分かります。 + +### 量子化モデルで軽く・速く(インストーラに同梱) + +推論用に **INT8 量子化した ONNX モデル**を使用。GPU は不要で、一般的な PC の CPU だけで動作します。**モデルはインストーラに含まれており、別途ダウンロードは不要**。インストール後すぐに高精度検索をご利用いただけます。 + +### MCP で AI とつながる + +Model Context Protocol(MCP)に対応。Cursor や Claude Desktop から「検索」「追加」「RE-INDEX」などのツールとして TelosDB を呼び出せます。高精度な検索結果を AI のコンテキストとしてそのまま利用できます。 + +### インデックス状態が分かる + +起動時や RE-INDEX 時に、HNSW の構築状況などが画面上で確認できます。「Pro」バッジでエディションが識別でき、モデルの読み込み状態も把握しやすくなっています。 + +### ログイン時に自動起動(オプション) + +設定で「OSにログインしたときに自動で起動する」をオンにすると、Windows にサインインしたタイミングで TelosDB が起動。トレイで待機し、いつでも MCP 経由で高精度検索を利用できます。 + +### フォルダ監視で自動取り込み(New) + +指定したフォルダを常時監視し、ファイルの追加・更新・削除を自動検知してインデックスに反映します。テキストやマークダウンを保存するだけで、高精度な意味検索の対象に自動登録。フォルダごとにカテゴリ名を付けられるため、検索時の分類にも活用できます。 + +--- + +## v0.3.3 の主な変更点 + +### 新機能 + +- **フォルダ監視**: 指定フォルダ内のファイル追加・更新・削除を自動検知し、インデックスに反映。設定パネルからフォルダの追加・削除・カテゴリ指定が可能。取込対象の拡張子もカスタマイズ可能(デフォルト: txt, md, json, html, css, js, mjs, ts, rs)。 +- **接続状態表示**: MCP サーバーへの接続状態を UI に表示。 + +### 改善 + +- **自動起動の安定化**: 設定ファイル読み込み失敗時にレジストリエントリが消える問題を修正。`isEnabled()` で OS の実状態と比較し、不一致時のみ登録を更新するように改善。失敗時のエラー表示を追加。 +- **バックエンドリファクタリング**: MCP ツールの登録を `registry.rs` に集約し、検索ロジックを `search.rs` に整理。DB アクセス関数を `db/mod.rs` に追加。 +- **DB マイグレーション**: ドキュメントパスの正規化マイグレーションを追加(テーブル未存在時はスキップ)。 +- **バージョン表示の安定化**: フッターのバージョン取得でサーバー未応答時のビジーウェイトとフォールバック未設定の問題を修正。 +- **E2E テスト**: 自動起動・フォルダ監視・パネル操作・文書編集のテストを追加。 + +--- + +## インストーラにモデル同梱 + +Pro 版のインストーラ(`TelosDB-Pro_0.3.3_x64-setup.exe`)には、**埋め込み用の量子化 ONNX モデル**が同梱されています。 + +- **利用者**: 別途モデルのダウンロードは不要。インストールするだけで、インストール先のリソースから自動的にモデルが参照され、高精度な意味検索が利用できます。 +- **同梱内容**: `model_quantized.onnx`(日本語 sentence-BERT 量子化モデル)・`vocab.txt`(語彙)。 +- モデルが読み込めない場合でも、全文検索(FTS)のみで検索は可能です。 + +--- + +## 含まれる機能(v0.3.3) + +- **埋め込みモデルのインストーラ同梱**(別途ダウンロード不要。インストール先から自動参照) +- 意味検索(埋め込み 768 次元)+ 全文検索(FTS5)のハイブリッド検索 +- 検索結果の文書単位表示(`group_by_document`) +- **フォルダ監視**: 指定フォルダの自動取り込み・カテゴリ指定・拡張子フィルタ +- MCP ツール: `search_text` / `add_item_text` / `update_item` / `delete_item` / `get_item_by_id` / `get_document_count` / `lsa_retrain`(RE-INDEX) +- ドキュメント件数の表示(ヘッダー「X docs」・`get_document_count`) +- インデックス状態の表示(ベクトル同期中・HNSW 構築状況) +- ログイン時の自動起動(オプション) +- トレイ常駐・バージョン表示 + +--- + +## ご利用方法 + +**インストーラ**: `TelosDB-Pro_0.3.3_x64-setup.exe` を実行してインストールしてください。埋め込みモデルはインストーラに含まれており、追加のダウンロードは不要です。 + +**ソースからビルドする場合**(配布用インストーラを作る開発者向け): + +1. [sentence-bert-base-ja-mean-tokens-v2-int8](https://gitbucket.tmworks.club/dtmoyaji/sentence-bert-base-ja-mean-tokens-v2-int8) から `target/` 一式(`model_quantized.onnx`・`vocab.txt`)を取得し、プロジェクトの **`embedding_model/`** に配置します。 +2. `npm run build:pro` を実行すると、それらがインストーラに同梱された Pro 版がビルドされます。 + +```bash +npm install +npm run build:pro +``` + +- 出力: `src/backend/target/release/bundle/nsis/TelosDB-Pro_0.3.3_x64-setup.exe`(モデル同梱) +- 初回起動時に vec0.dll がアプリデータフォルダへコピーされます。不具合時は `%APPDATA%\com.telosdb.app\logs\telos.log` をご確認ください。 + +--- + +モデル不要で手軽に始めたい方は **Community 版**をご利用ください。 +→ [TelosDB Community 版 商品紹介](RELEASE_v0.3.3_Community.md) diff --git a/docs/README.md b/docs/README.md index d22c708..692d859 100644 --- a/docs/README.md +++ b/docs/README.md @@ -7,7 +7,7 @@ | ディレクトリ | 内容 | |-------------|------| | **specification/** | システム仕様・設計。アーキテクチャ、DB、MCP API、UI、開発ガイド、埋め込み・エディション、モノリシック化、KPI・検証、UI テスト、OpenAPI / MCP スキーマ。 | -| **plans/** | 今後の実装計画。計画ごとにサブフォルダを切る(例: `plans/folder_monitor/`)。 | +| **plans/** | 今後の実装計画。機能単位でファイルまたはサブフォルダを置く。 | | **issues/** | Issue 同期用の Markdown(Git 追跡外。`node tools/scripts/sync_issues.mjs` で同期)。 | | **references/** | 調査メモ・外部資料の参照。ベクトル化手法、BM25、Elasticsearch 等。 | @@ -27,19 +27,14 @@ - **10_monolithic_dev.md** — モノリシック化(開発時アーキテクチャ) - **11_pro_vectorization_and_ann.md** — Pro ベクトル化・ANN 動作確認 - **12_ui_testing_options.md** — UI をテストに組み込む方法(E2E / Playwright 等) +- **13_autostart.md** — 自動起動仕様(ログイン時起動・設定連携・UI・テスト) +- **14_folder_monitor.md** — フォルダ監視仕様(notify・DB 連携・設定 UI) - **mcp.json**, **openapi.yaml** — MCP / API スキーマ -**plans/** は計画ごとにサブフォルダを置く。 +**plans/** は計画ごとにファイルまたはサブフォルダを置く。 -- **plans/auto_start/** — ユーザーログイン時に自動起動する仕組み。検討事項別に分割(スコープ・技術方針・UI 改造・実装ステップ・注意事項・参照)。 -- **plans/folder_monitor/** — 指定フォルダ監視(追加・削除・更新の検出→DB取り込み・ベクトル化) - - **folder_monitor.md** — 計画トップ(目的・目次) - - **folder_monitor_01_scope.md** — 01 スコープ - - **folder_monitor_02_tech.md** — 02 技術方針(notify・デバウンス・ネットワーク・Config 等) - - **folder_monitor_03_os_protocol.md** — 03 OS・ファイルプロトコル別実装方針 - - **folder_monitor_04_phases.md** — 04 実装ステップ(Phase 1〜5) - - **folder_monitor_05_considerations.md** — 05 注意事項・未決定 - - **folder_monitor_06_references.md** — 06 参照・調査元 -- **plans/lda/** — LDA(潜在的ディリクレ配分)の扱い。**v0.3.3 Community 版**で実装。LSA と LDA を切り替え、規定 128 次元・ユーザー指定で再構成。検討事項別に分割(スコープ・技術・**UI 改造**・実装ステップ・注意事項・参照)。 +- **plans/cross_platform.md** — クロスプラットフォーム検証(autostart・folder_monitor の macOS / Linux 動作確認・アンインストール時のエントリ残留) +- **plans/folder_monitor_enhancements.md** — フォルダ監視の拡張(PollWatcher フォールバック・初期スキャン改善・権限エラー UI・パス正規化) +- **plans/lda/** — LDA(潜在的ディリクレ配分)の扱い。検討事項別に分割(スコープ・技術・UI 改造・実装ステップ・注意事項・参照)。 運用ルール(Issue 管理・Git 運用など)は `.agent/rules/` にあります。 diff --git a/docs/plans/auto_start/auto_start.md b/docs/plans/auto_start/auto_start.md deleted file mode 100644 index 76e8e81..0000000 --- a/docs/plans/auto_start/auto_start.md +++ /dev/null @@ -1,29 +0,0 @@ -# 計画: ユーザーログイン時に自動起動する仕組み - -## 1. 目的 - -OS にユーザーがログインしたタイミングで TelosDB を自動的に起動し、システムトレイで待機させる。ユーザーが手動で起動しなくても MCP サーバーや検索機能を利用可能にする。 - -```mermaid -flowchart LR - subgraph ユーザー操作なし - A[OS ログイン] --> B[TelosDB 自動起動] - B --> C[トレイで待機] - C --> D[MCP / 検索 利用可能] - end -``` - ---- - -## 2. 検討事項別ドキュメント - -各検討事項は別ファイルに分割している。 - -| No. | 項目 | ファイル | 内容 | -|-----|------|----------|------| -| 01 | **スコープ** | [auto_start_01_scope.md](auto_start_01_scope.md) | 対応プラットフォーム、トリガー、起動後挙動、設定保存。 | -| 02 | **技術方針** | [auto_start_02_tech.md](auto_start_02_tech.md) | tauri-plugin-autostart、API、設定との連携。 | -| 03 | **UI 改造** | [auto_start_03_ui.md](auto_start_03_ui.md) | 設定パネルにトグル追加、配置・ラベル、仕様書更新。 | -| 04 | **実装ステップ** | [auto_start_04_phases.md](auto_start_04_phases.md) | Phase 1〜4 の実装順序。**テスト・検証**(単体・結合・UI/E2E・手動・リリース前)を含む。 | -| 05 | **注意事項・未決定** | [auto_start_05_considerations.md](auto_start_05_considerations.md) | トレイ起動、権限、競合。 | -| 06 | **参照** | [auto_start_06_references.md](auto_start_06_references.md) | プラグイン・Tauri 公式・06 仕様。 | diff --git a/docs/plans/auto_start/auto_start_01_scope.md b/docs/plans/auto_start/auto_start_01_scope.md deleted file mode 100644 index cd6d42d..0000000 --- a/docs/plans/auto_start/auto_start_01_scope.md +++ /dev/null @@ -1,16 +0,0 @@ -# 自動起動計画: 01 スコープ - -[計画トップ](auto_start.md) - ---- - -## スコープ - -| 項目 | 内容 | -|------|------| -| **対応プラットフォーム** | **Windows**(現行)、**macOS**、**Linux**。Tauri 2 の対応 OS に合わせて自動起動も各 OS で有効にする。 | -| **トリガー** | ユーザーが OS に**ログイン(サインイン)**したとき。ロック画面からの解除やスリープ復帰では発火しない。 | -| **デフォルト** | 設定キーが無い場合(初回・既存ユーザーのアップデート後)は**自動起動はオフ**とする。 | -| **起動後の挙動** | 通常起動と同様(ウィンドウまたはトレイで待機)。設定で「自動起動」のオン・オフを切り替え可能とする。 | -| **設定の保存** | 自動起動の有無はアプリ設定に永続化し、次回起動時にも反映する。 | -| **対象エディション** | **Community 版・Pro 版の両方**で利用可能とする。エディションによる差異は設けず、同一の設定項目・プラグイン API で扱う。 | diff --git a/docs/plans/auto_start/auto_start_02_tech.md b/docs/plans/auto_start/auto_start_02_tech.md deleted file mode 100644 index c5e5f00..0000000 --- a/docs/plans/auto_start/auto_start_02_tech.md +++ /dev/null @@ -1,64 +0,0 @@ -# 自動起動計画: 02 技術方針 - -[計画トップ](auto_start.md) - ---- - -## 3.1 採用: tauri-plugin-autostart - -- **プラグイン**: [tauri-plugin-autostart](https://github.com/tauri-apps/tauri-plugin-autostart)([crates.io](https://crates.io/crates/tauri-plugin-autostart))を採用する。 -- **対応 OS**: Windows / macOS / Linux でログイン時起動をサポート。Android / iOS は非対応(Tauri のデスクトップ用途と一致)。 -- **実装の裏側**(プラグインが担当): - - **Windows**: レジストリ `HKCU\Software\Microsoft\Windows\CurrentVersion\Run` にアプリパスを登録。 - - **macOS**: Launch Agent(`~/Library/LaunchAgents/` に plist を配置)。 - - **Linux**: XDG Autostart(`~/.config/autostart/*.desktop` に .desktop ファイルを配置)。 - -- **起動引数(任意)**: プラグインの `Builder` で `.args([...])` を指定できる。自動起動時に「トレイのみで起動」など振る舞いを変えたい場合は、ここで引数(例: `--minimized`)を渡し、アプリ起動時に解釈する。 - -```mermaid -flowchart TB - subgraph プラットフォーム別の実体 - W[Windows: レジストリ Run] - M[macOS: LaunchAgents] - L[Linux: autostart .desktop] - end - P[tauri-plugin-autostart] --> W - P --> M - P --> L - U[ユーザー: enable/disable] --> P -``` - -## 3.2 導入の手順(Phase 1 で行うこと) - -- **Cargo.toml**: `tauri-plugin-autostart` を依存に追加。 -- **Rust**: `tauri::Builder` に `.plugin(tauri_plugin_autostart::init(...))` を登録。必要なら `.args([...])` で起動引数を指定。 -- **Tauri 設定**: プラグインが `tauri.conf.json` の `plugins` や capability を要求する場合は、ドキュメントに従い permission を追加。 -- **フロント**: `@tauri-apps/plugin-autostart` の `enable` / `disable` / `isEnabled` を呼ぶ。Tauri コマンド経由でも可。 - -## 3.3 API(フロント/バック) - -- **enable()**: 自動起動を有効にする。 -- **disable()**: 自動起動を無効にする。 -- **isEnabled()**: 現在自動起動が有効かどうかを返す。 - -設定 UI のトグルや起動時の読み込みで上記を呼び出す。設定の永続化(localStorage や Tauri の app 設定)と連携し、「自動起動オン」のときだけプラグインの `enable()` を呼ぶ。 - -## 3.4 設定との連携 - -- 設定項目「ログイン時に自動起動する」を追加する(現状は [06 UI 仕様](../../specification/06_ui_design_spec.md) 上で非表示のため、本機能実装時に設定パネルに表示する)。 -- オンにした場合: 設定を保存し、`tauri-plugin-autostart` の `enable()` を実行。 -- オフにした場合: 設定を保存し、`disable()` を実行。 -- アプリ起動時: 保存済み設定を読み、`isEnabled()` と一致していなければ `enable()` / `disable()` で同期する(他ツールでレジストリ等をいじった場合のずれを吸収)。 -- **アップデート後**: 再インストールで実行ファイルのパスが変わると、既存の Run キー等は無効になる。次回起動時の「設定読み込み+isEnabled() との同期」で、設定がオンの場合は `enable()` を呼び直し、新しいパスで登録し直す。 - -```mermaid -stateDiagram-v2 - [*] --> 設定読み込み - 設定読み込み --> 同期: 設定値と isEnabled() が不一致 - 設定読み込み --> 待機: 一致 - 同期 --> enableまたはdisable - enableまたはdisable --> 待機 - 待機 --> ユーザーがトグル変更 - ユーザーがトグル変更 --> 保存してenable/disable - 保存してenable/disable --> 待機 -``` diff --git a/docs/plans/auto_start/auto_start_03_ui.md b/docs/plans/auto_start/auto_start_03_ui.md deleted file mode 100644 index 31a9680..0000000 --- a/docs/plans/auto_start/auto_start_03_ui.md +++ /dev/null @@ -1,43 +0,0 @@ -# 自動起動計画: 03 UI 改造 - -[計画トップ](auto_start.md) - ---- - -本機能を有効にするには、**設定 UI の変更**が必要である。現状 [06 UI 仕様](../../specification/06_ui_design_spec.md) では「ログイン時起動・フォルダモニタリングのパネルは UI に含めない」とされているため、自動起動用の項目を表示する改造を計画に含める。 - -## 4.1 設定パネルに追加する項目 - -| 項目 | 内容 | -|------|------| -| **表示するコントロール** | 「ログイン時に自動起動する」の **トグル(スイッチ)** 1 つ。 | -| **配置** | 設定パネル内。検索まわり(スコア足切り・取得件数)の上または下に、明確に区切って配置する。 | -| **ラベル** | 短く分かりやすい文言(例: 「ログイン時に自動起動する」)。必要なら補足テキスト(「OS にサインインしたときに TelosDB を起動します」)を小さく表示。 | -| **見た目** | 既存の [06 コンセプト](../../specification/06_ui_design_spec.md)(ハイコントラスト・ミニマリズム)に合わせる。装飾を抑え、トグルは既存の設定項目とスタイルを統一する。 | -| **トグルの表示値** | 設定パネルを**開いたとき**に、保存済み設定だけでなく **`isEnabled()` の結果**も参照し、実状態に合わせてトグルを表示する。起動時同期のあとなら実状態と保存値は一致している想定だが、他ツールで登録を外した場合などに備える。 | -| **デフォルト** | 設定が無い場合は**オフ**表示([01 スコープ](auto_start_01_scope.md))。 | - -## 4.2 画面フロー - -```mermaid -flowchart LR - subgraph 設定パネル - A[設定ボタン] --> B[パネル開く] - B --> C[検索設定] - B --> D[ログイン時自動起動 トグル] - D --> E[ON: enable] - D --> F[OFF: disable] - end - E --> G[設定保存] - F --> G -``` - -## 4.3 仕様書の更新 - -- **06_ui_design_spec.md**: 「非表示: ログイン時起動・フォルダモニタリングのパネル」を、**自動起動については「表示する」** に変更する。フォルダモニタリングは別計画のため、当面は非表示のままでもよい。 -- **設定の永続化**: 自動起動のオン・オフは、既存の `telosdb_settings`(localStorage)に含めるか、別キーで保存するかを実装時に決める。いずれにしても「設定パネルを開いたとき・起動時に読み込み、トグルとプラグイン状態を一致させる」。 - -## 4.4 考慮事項 - -- **初回表示**: これまで非表示だった項目を出すため、設定パネルの高さやレイアウトが変わる。小さいウィンドウでもはみ出さないよう、スクロールや折りたたみを検討する。 -- **他計画との関係**: フォルダモニタリングもいずれ設定パネルに出す場合、同じ「起動・動作オプション」として自動起動と並べて配置する構成が分かりやすい。 diff --git a/docs/plans/auto_start/auto_start_04_phases.md b/docs/plans/auto_start/auto_start_04_phases.md deleted file mode 100644 index 411e39f..0000000 --- a/docs/plans/auto_start/auto_start_04_phases.md +++ /dev/null @@ -1,28 +0,0 @@ -# 自動起動計画: 04 実装ステップ - -[計画トップ](auto_start.md) - ---- - -## 実装ステップ(案) - -| 段階 | 内容 | -|------|------| -| **Phase 1** | `tauri-plugin-autostart` を導入。[02 技術方針の「導入の手順」](auto_start_02_tech.md#32-導入の手順phase-1-で行うこと)(Cargo.toml、プラグイン登録、capability 必要なら追加)を実施。フロントまたは Tauri コマンドから `enable` / `disable` / `isEnabled` を呼べるようにする。**動作確認**: 有効化後に OS をログアウト→ログイン(または再起動)し、TelosDB が自動起動することを手動で確認する。 | -| **Phase 2** | 設定の永続化と連携。「ログイン時自動起動」のオン・オフを保存し、起動時に設定を読み込んでプラグインの状態と同期する。 | -| **Phase 3** | **UI 改造**([03 UI 改造](auto_start_03_ui.md))。設定パネルに「ログイン時に自動起動する」トグルを追加し、表示・トグル操作で enable/disable と設定保存を行う。06 仕様の「非表示」を自動起動分だけ「表示」に更新する。 | -| **Phase 4(任意)** | macOS / Linux ビルドで自動起動の動作確認。各 OS でもログアウト→ログイン(または再起動)で自動起動することを手動確認する。 | - -**動作確認の注意**: 自動起動の検証は**手動**で行う(ログアウト→ログインまたは再起動)。CI での自動テストは難しいため、リリース前チェックリストに「自動起動オンでログインし直し、アプリが起動することを確認」を入れる。 - ---- - -## テスト・検証 - -| 種別 | 内容 | -|------|------| -| **単体** | Tauri コマンドまたはフロントから `enable()` / `disable()` / `isEnabled()` を呼び、戻り値やレジストリ・LaunchAgents の書き換えが期待どおりか検証する。プラグインをモックに差し替えて「設定保存と API 呼び出しの対応」をテストするのも可。 | -| **結合** | 起動時に保存済み設定を読み、`isEnabled()` と不一致なら `enable`/`disable` で同期する流れをテスト。設定をオフ→オン→オフと変更したときに毎回正しく API が呼ばれるか。 | -| **UI / E2E** | 既存の [E2E(WebdriverIO)](https://github.com/tauri-apps/webdriver-example) や `tests/e2e/` を使い、設定パネルを開いて「ログイン時自動起動」トグルを操作し、表示が変わること・保存されることを検証する([12 UI テスト方針](../../specification/12_ui_testing_options.md) 参照)。 | -| **手動** | 自動起動をオンにし、OS をログアウト→ログイン(または再起動)して TelosDB が起動することを確認。オフにした場合は起動しないことを確認。インストーラでインストールしたバイナリでも同様に確認する。 | -| **リリース前** | 上記に加え、アンインストール後に Run キー(または LaunchAgents / autostart)にエントリが残っていないことを確認するチェックを入れる。 | diff --git a/docs/plans/auto_start/auto_start_05_considerations.md b/docs/plans/auto_start/auto_start_05_considerations.md deleted file mode 100644 index 8f94459..0000000 --- a/docs/plans/auto_start/auto_start_05_considerations.md +++ /dev/null @@ -1,16 +0,0 @@ -# 自動起動計画: 05 注意事項・未決定 - -[計画トップ](auto_start.md) - ---- - -## 注意事項・未決定 - -- **トレイ起動**: 自動起動時にウィンドウを表示しない「トレイのみ」で起動するか、通常どおりウィンドウを出すか。仕様で決める。 -- **権限・ユーザー操作**: Windows では Run キーはユーザー単位。macOS の Launch Agent もユーザー単位。管理者権限は不要の想定。 -- **競合**: 既に別の方法(タスクスケジューラ等)で同じアプリをログイン時に起動している場合、二重起動にならないか確認する。プラグインは一般的な 1 エントリ追加なので、他で同じ実行ファイルを登録していなければ通常は 1 つだけ起動する。 -- **enable/disable の失敗**: 権限不足やプラグイン未初期化などで `enable()` / `disable()` が失敗した場合、トグル操作時にエラーメッセージを表示し、設定と実状態の不整合を防ぐ。 -- **アンインストール時**: アプリ削除時にレジストリ Run や LaunchAgents のエントリが残らないか確認する。NSIS 等のアンインストーラで Run キーを削除する処理が必要な場合、インストーラ設定に含める。 -- **リリース目標**: 本機能をどのバージョン(例: 0.3.3)に含めるか、他計画(フォルダ監視・LDA)との優先度と合わせて決める。 -- **同一マシンで Community と Pro の両方**: 両方インストールし、両方で「自動起動オン」にした場合は、ログイン時に**両方**起動する。通常は片方のみ使う想定だが、挙動として明記しておく。 -- **動作確認**: 自動起動の有無はログアウト→ログインまたは再起動でしか確認できない。テスト手順に「手動でログインし直して起動を確認」を含める([04 実装ステップ](auto_start_04_phases.md))。 diff --git a/docs/plans/auto_start/auto_start_06_references.md b/docs/plans/auto_start/auto_start_06_references.md deleted file mode 100644 index 237ca5e..0000000 --- a/docs/plans/auto_start/auto_start_06_references.md +++ /dev/null @@ -1,12 +0,0 @@ -# 自動起動計画: 06 参照 - -[計画トップ](auto_start.md) - ---- - -## 参照 - -- [tauri-plugin-autostart](https://github.com/tauri-apps/tauri-plugin-autostart) — 公式プラグイン(Builder の args / app_name、各 OS の登録先) -- [Tauri v2 Autostart プラグイン](https://v2.tauri.app/plugin/autostart/) -- Tauri 2 の **capability / permission**: プラグイン利用に必要な権限を `capabilities` に記載する必要がある場合は、プラグインのドキュメントを確認する。 -- 既存仕様: `docs/specification/06_ui_design_spec.md`(設定パネル・ログイン時起動の「非表示」記載) diff --git a/docs/plans/cross_platform.md b/docs/plans/cross_platform.md new file mode 100644 index 0000000..278db24 --- /dev/null +++ b/docs/plans/cross_platform.md @@ -0,0 +1,44 @@ +# 計画: クロスプラットフォーム検証 + +## 目的 + +Windows で実装済みの自動起動(autostart)とフォルダ監視(folder_monitor)を macOS / Linux で検証し、プラットフォーム固有の問題を解消する。 + +## 対象機能 + +| 機能 | 仕様書 | Windows 実装 | +|------|--------|-------------| +| 自動起動 | [13_autostart.md](../specification/13_autostart.md) | ✅ v0.3.3 | +| フォルダ監視 | [14_folder_monitor.md](../specification/14_folder_monitor.md) | ✅ v0.3.3 | + +## 検証項目 + +### 自動起動 + +| OS | 検証内容 | 状態 | +|----|---------|------| +| macOS | Launch Agent(`~/Library/LaunchAgents/`)で起動するか。ログアウト→ログインで確認。 | ⬜ | +| Linux | XDG Autostart(`~/.config/autostart/*.desktop`)で起動するか。ログアウト→ログインで確認。 | ⬜ | +| macOS | `--minimized` 引数でトレイのみ起動が動作するか。 | ⬜ | +| Linux | 同上。 | ⬜ | + +### フォルダ監視 + +| OS | 検証内容 | 状態 | +|----|---------|------| +| macOS | FSEvents でファイル追加・更新・削除イベントが検知されるか。 | ⬜ | +| Linux | inotify でファイル追加・更新・削除イベントが検知されるか。 | ⬜ | +| macOS | サンドボックス環境での監視パス権限。entitlement が必要か確認。 | ⬜ | +| Linux | `max_user_watches` が小さい環境で大量ファイルを監視した場合のエラーハンドリング。 | ⬜ | + +### アンインストール + +| OS | 検証内容 | 状態 | +|----|---------|------| +| Windows | NSIS アンインストーラが Run キーを自動削除するか。残留した場合の影響。 | ⬜ | +| macOS | アンインストール時に LaunchAgents の plist が残留しないか。 | ⬜ | +| Linux | アンインストール時に `.desktop` ファイルが残留しないか。 | ⬜ | + +## 優先度 + +中。Windows で動作確認済みのため、macOS / Linux ビルドを出す際に実施する。 diff --git a/docs/plans/folder_monitor/folder_monitor.md b/docs/plans/folder_monitor/folder_monitor.md deleted file mode 100644 index 2a47b71..0000000 --- a/docs/plans/folder_monitor/folder_monitor.md +++ /dev/null @@ -1,28 +0,0 @@ -# 計画: 指定フォルダ内のファイル追加・削除・更新を監視する仕組み - -## 1. 目的 - -ユーザーが指定したフォルダを監視し、以下のイベントを検知する仕組みを用意する。 - -- **ファイル追加**: フォルダ内に新規ファイルが作成されたとき -- **ファイル削除**: フォルダ内のファイルが削除されたとき -- **ファイル更新**: 既存ファイルの内容が変更されたとき - -検知した結果を TelosDB の文書取り込み(ドキュメント・チャンクの登録・更新・削除)に連携し、指定フォルダと DB の内容を同期させることを想定する。 - -**リリース目標**: **Windows 版の実装を v0.3.3 に含める**。Phase 1〜3(単一フォルダ監視・DB 連携・設定 UI・永続化)を Windows で実装し、0.3.3 で出荷する。macOS / Linux およびネットワークフォルダの検証・ポーリングフォールバックは Phase 5 として以降のバージョンに回す。 - ---- - -## 2. 検討事項別ドキュメント - -各検討事項は別ファイルに分割している。 - -| No. | 項目 | ファイル | 内容 | -|-----|------|----------|------| -| 01 | **スコープ** | [folder_monitor_01_scope.md](folder_monitor_01_scope.md) | 対応プラットフォーム、監視対象の種類・パス・深さ、対象ファイル、動作タイミング。 | -| 02 | **技術方針** | [folder_monitor_02_tech.md](folder_monitor_02_tech.md) | notify、デバウンス、ネットワーク・フォールバック、プラットフォーム別注意点、Config、既存機能連携、設定保存。 | -| 03 | **実装方針(OS・プロトコル別)** | [folder_monitor_03_os_protocol.md](folder_monitor_03_os_protocol.md) | OS × ファイルプロトコル別の Watcher 選択、判定、共通処理。 | -| 04 | **実装ステップ** | [folder_monitor_04_phases.md](folder_monitor_04_phases.md) | Phase 1〜5 の実装順序。**テスト・検証**(単体・結合・手動・UI/E2E・Phase 別)を含む。 | -| 05 | **注意事項・未決定** | [folder_monitor_05_considerations.md](folder_monitor_05_considerations.md) | 権限、パス正規化、ネットワーク制約、大容量フォルダ、UI。 | -| 06 | **参照・調査元** | [folder_monitor_06_references.md](folder_monitor_06_references.md) | notify / debouncer / inotify / macOS 等の公式ドキュメント・リンク。 | diff --git a/docs/plans/folder_monitor/folder_monitor_01_scope.md b/docs/plans/folder_monitor/folder_monitor_01_scope.md deleted file mode 100644 index 17c5d7e..0000000 --- a/docs/plans/folder_monitor/folder_monitor_01_scope.md +++ /dev/null @@ -1,17 +0,0 @@ -# フォルダ監視: 01 スコープ - -[計画トップ](folder_monitor.md) - ---- - -## スコープ - -| 項目 | 内容 | -|------|------| -| **対応プラットフォーム** | **Windows**(現行)、**macOS**、**Linux**。Tauri 2 の対応 OS に合わせて監視機能も各 OS で動作させる。 | -| **監視対象の種類** | **ローカルフォルダ**(各 OS の通常のパス)に加え、**ネットワークフォルダ**(SMB/CIFS・NFS 等でマウントされたパス)も対象とする。ネットワークパスは OS がマウント済みであれば、ローカルと同様に指定可能とする。 | -| **監視対象** | ユーザーが設定で指定する 1 つ以上のディレクトリパス(例: Windows `C:\Users\...\Documents\TelosDB-watch`、macOS `/Users/.../Documents/TelosDB-watch`、Linux `/home/.../Documents/TelosDB-watch`、ネットワーク `//server/share/watch` や `/mnt/nfs/watch` など)。 | -| **監視の深さ** | 指定フォルダ直下のみ / サブフォルダ再帰のいずれかを設定で選択可能とする(案)。 | -| **対象ファイル** | 拡張子でフィルタ(例: `.txt`, `.md`, `.json` など)。MIME 対応済みの形式を優先。 | -| **動作タイミング** | アプリ起動中の常時監視。設定で監視のオン・オフを切り替え可能とする。 | -| **MCP・API との関係** | **能動的なプッシュは不要**。監視で検出した変化は DB 取り込み・ベクトル化まで内部で完結する。MCP クライアント(Cursor 等)や HTTP API は従来どおり「検索」「件数取得」などで**現在の DB 状態を参照する(プル)**だけ。 | diff --git a/docs/plans/folder_monitor/folder_monitor_02_tech.md b/docs/plans/folder_monitor/folder_monitor_02_tech.md deleted file mode 100644 index 3dc591b..0000000 --- a/docs/plans/folder_monitor/folder_monitor_02_tech.md +++ /dev/null @@ -1,80 +0,0 @@ -# フォルダ監視: 02 技術方針 - -[計画トップ](folder_monitor.md) - ---- - -## 3.0 設計: 監視プロトコルとドライバ - -**方針**: 監視の「やり方」を共通プロトコル(トレイト/インターフェース)で定義し、OS・ファイルシステム・ネットワーク種別ごとに**ドライバ**を用意する。 - -- **プロトコル(共通インターフェース)** - - 監視の開始・停止、イベントの購読など、呼び出し側が依存する操作だけを定義する。 - - 例: `watch(path, recursive)` → ハンドル、`EventStream` またはコールバックで `Create` / `Remove` / `Modify` を受け取る、`unwatch` / `stop`。 -- **ドライバ** - - 上記プロトコルを実装する。中身は [03 OS・プロトコル別](folder_monitor_03_os_protocol.md) のマトリクスに沿い、**ネイティブ監視**(RecommendedWatcher)か**ポーリング**(PollWatcher)のどちらか+設定で表現する。 - - 実装の切り口は「OS×ネットワーク」の全組み合わせを個別クラスにする必要はなく、**バックエンド 2 種**で足りる: - - **ネイティブドライバ**: `notify::recommended_watcher()` + デバウンス。中で OS 別 API(ReadDirectoryChangesW / FSEvents / inotify)は notify が担当。 - - **ポーリングドライバ**: `PollWatcher` + `Config`(poll_interval, 必要なら compare_contents)。ネットワーク FS・WSL・擬似 FS 等で使用。 - - **ドライバ選択**(セレクタ): パスと実行環境(OS・マウント情報など)から、上記どちらのドライバ(およびオプション)を使うかを決める。選択ロジックは [03](folder_monitor_03_os_protocol.md) の「プロトコル・ストレージ種別の判定」にまとめる。 -- **効果** - - 上位(DB 連携・設定 UI)はプロトコルだけに依存し、OS やネットワークの違いを意識しない。 - - 新たな環境(別 OS や新しいネットワーク FS)への対応は、セレクタの判定と必要なら新ドライバ(または既存ドライバの設定)の追加で済む。 - ---- - -## 3.1 監視ライブラリ: notify - -- **採用**: Rust の **`notify`** クレート(現行安定版 8.x、[crates.io](https://crates.io/crates/notify)、[docs.rs](https://docs.rs/notify/latest/notify/))を採用する。クロスプラットフォームで同一 API が使え、各 OS のネイティブ API を利用する。 -- **バックエンド(プラットフォーム別)**: - - **Windows**: `ReadDirectoryChangesW` - - **macOS**: FSEvents(デフォルト)。feature `macos_kqueue` で kqueue に切り替え可能。 - - **Linux / Android**: inotify - - **BSD 系**: kqueue -- **推奨 Watcher**: `notify::recommended_watcher()` で、そのプラットフォームに最適な実装(`RecommendedWatcher`)を取得する。イベント駆動で CPU 負荷が低い。 -- **イベント種別**: `EventKind` の `Create`, `Remove`, `Modify` 等を区別して扱う。`RecursiveMode::Recursive` でサブディレクトリも監視可能。 - -## 3.2 デバウンス - -- **目的**: 同一ファイルへの短時間の連続変更(エディタの自動保存・一括書き換え等)を、1 回の「更新」にまとめる。 -- **手段**: **`notify-debouncer-mini`**([docs.rs](https://docs.rs/notify-debouncer-mini/latest/notify_debouncer_mini/))を利用する。`new_debouncer(Duration::from_secs(2), callback)` のように時間幅を指定し、その間のイベントを集約してコールバックに渡す。 -- **代替**: より高度な処理(Rename の追跡・重複 Create の除去等)が必要なら `notify-debouncer-full` を検討する。TelosDB では「追加・削除・更新」の 3 種で十分な場合は mini で足りる。 -- **注意**: debouncer-mini は内部でスレッドを使いブロッキング受信するため、Tauri の async ループからは `spawn_blocking` や専用スレッドでラップし、結果をチャンネルで async 側に渡す構成が無難。 - -## 3.3 ネットワークフォルダ・フォールバック - -- **公式の既知問題**([notify Known Problems](https://docs.rs/notify/latest/notify/)): - - **ネットワークマウント(NFS 等)**: 多くのネットワーク FS ではネイティブの変更通知が発火しない。WSL 上で Windows パスを監視する場合も同様(issue #254)。 - - **対処**: **`PollWatcher`** バックエンドを使う。`notify::Config::default().with_poll_interval(Duration::from_secs(30))` でポーリング間隔を指定し、`notify::WatcherKind::PollWatcher` で明示的に PollWatcher を生成する。デフォルトのポーリング間隔は 30 秒。大容量ツリーでは負荷が高いため、間隔の調整や「ネットワークパス用」の別ワッチャーとして扱う設計がよい。 -- **その他で PollWatcher が推奨されるケース**: Docker on macOS M1(Function not implemented)、`/proc` や `/sys` などの擬似 FS、macOS で「自分が所有していないファイル」を FSEvents で追う場合([FileSystemEventSecurity](https://developer.apple.com/library/archive/documentation/Darwin/Conceptual/FSEvents_ProgGuide/FileSystemEventSecurity/FileSystemEventSecurity.html))。 -- **実装方針**: まずは `RecommendedWatcher` で監視を開始し、ネイティブ監視が失敗するか、ネットワークパスであることを検出した場合に、同じ設定オブジェクトで `PollWatcher` に切り替えるフォールバックを用意する。ネットワークパスかどうかは、パスがマウントポイントか、または `watch` 登録時のエラー種別で判断する。 - -## 3.4 プラットフォーム別の注意点 - -- **Linux: inotify の上限** - - `max_user_watches`(デフォルト 8192、カーネルによっては 1048576)を超えると「No space left on device」や「upper limit on inotify watches reached」が出る。再帰監視では「監視対象ディレクトリ内のファイル・フォルダ数」がそのまま watch 数に効く。 - - 対処: ユーザーに `sysctl fs.inotify.max_user_watches=524288` 等の増設を案内するか、監視対象が大きい場合は初めから **PollWatcher** を使う選択肢を設ける。PollWatcher は inotify の上限に縛られない。 -- **macOS: サンドボックス** - - サンドボックス環境では、監視できるパスが権限付与された範囲に限られる。[Accessing files from the macOS App Sandbox](https://developer.apple.com/documentation/security/accessing_files_from_the_macos_app_sandbox) に従い、必要なら `com.apple.security.temporary-exception.files.home-relative-path.read-only` 等の entitlement を検討する。Tauri アプリがサンドボックスを有効にする場合、ユーザーが「監視フォルダ」を選択したパスがその例外に含まれるようにする必要がある。 -- **エディタの保存挙動** - - 編集ツールによっては「上書き保存」で truncate して書き直すため、Notify では Remove + Create や複数回の Modify に見えることがある。デバウンスと「同一パスは最後のイベントで 1 回だけ再取り込み」とする方針で吸収する。 - -## 3.5 Config とオプション(notify) - -- **`Config::with_poll_interval(dur)`**: PollWatcher 用。再スキャン間隔(デフォルト 30 秒)。ツリーが大きい場合は 60 秒などに延ばす検討。 -- **`Config::with_compare_contents(true)`**: PollWatcher 用。変更検知を mtime ではなくファイル内容のハッシュで行う。`/proc` 等で有効。通常のローカル/ネットワークではオフでよい(パフォーマンス影響大)。 -- **`Config::with_follow_symlinks(bool)`**: シンボリックリンクをたどって監視するか。デフォルトは true。同一実体の二重登録を防ぐなら false にする選択肢がある。 - -## 3.6 既存機能との連携 - -- **方向**: 監視 → DB の**一方向**。検出イベントを内部の取り込みパイプラインに渡すだけでよく、MCP や HTTP API へ「新規追加しました」などを能動的に通知(プッシュ)する仕様は設けない。クライアントは必要時に `search_text` や `get_document_count` 等で現在状態を取得すれば足りる([01 スコープ](folder_monitor_01_scope.md#スコープ))。 -- **追加**: 新規ファイル検知 → 既存の文書取り込みパイプライン(チャンク分割・ベクトル化・`documents` / `items` / `vec_items` / FTS 登録)を呼び出す。 -- **更新**: 変更検知 → 当該パスに紐づく既存ドキュメントを更新(再取り込み)する。 -- **削除**: 削除検知 → 当該パスに紐づくドキュメントを `documents` から削除(関連 `items` / `vec_items` / FTS も連動削除)。 -- **親フォルダ削除**: notify の仕様上、`/a/b/` 以下の削除イベントを受け取るには親 `/a` を watch する必要がある。再帰監視では通常その前提が満たされるが、直下のみ監視する場合は注意する([notify ドキュメント](https://docs.rs/notify/latest/notify/))。 - -## 3.7 設定の保存 - -- 監視対象フォルダパス・再帰の有無・対象拡張子・監視オン/オフは、アプリの設定(Windows は `%APPDATA%\com.telosdb.app\`、macOS/Linux は各 OS のアプリ設定ディレクトリ、または既存の localStorage と連携する API)に永続化する。 -- 設定変更時に、監視ワッチャーを再起動(購読パスの付け替え)する。 -- **パス表記**: プラットフォームごとのパス形式(Windows の `C:\`、macOS/Linux の `/`、ネットワークの `//` や `/mnt/` 等)をそのまま保存し、実行環境に応じて正しく解釈する。 diff --git a/docs/plans/folder_monitor/folder_monitor_03_os_protocol.md b/docs/plans/folder_monitor/folder_monitor_03_os_protocol.md deleted file mode 100644 index c4127ea..0000000 --- a/docs/plans/folder_monitor/folder_monitor_03_os_protocol.md +++ /dev/null @@ -1,58 +0,0 @@ -# フォルダ監視: 03 実装方針(OS タイプ・ファイルプロトコル別) - -[計画トップ](folder_monitor.md) - ---- - -監視対象を **OS** と **ファイルプロトコル(ストレージ種別)** の組み合わせで分類し、それぞれの実装方針を定める。共通の[監視プロトコルとドライバ設計](folder_monitor_02_tech.md#30-設計-監視プロトコルとドライバ)は [02 技術方針](folder_monitor_02_tech.md) に記載。本ドキュメントは「どの組み合わせでどのドライバ(ネイティブ or ポーリング)を使うか」のマトリクスと判定方針。 - -## 4.1 マトリクス概要 - -| OS | ローカル FS | SMB/CIFS(ネットワーク共有) | NFS | その他(擬似 FS・WSL 等) | -|----|-------------|------------------------------|-----|---------------------------| -| **Windows** | RecommendedWatcher(ReadDirectoryChangesW) | 要検証。失敗時は PollWatcher。 | 同上。 | WSL で Windows パス監視は PollWatcher。 | -| **macOS** | RecommendedWatcher(FSEvents)。非所有ファイルは PollWatcher。 | PollWatcher 推奨。 | PollWatcher 推奨。 | Docker M1 等は PollWatcher。 | -| **Linux** | RecommendedWatcher(inotify)。watch 数上限に注意。 | PollWatcher 推奨。 | PollWatcher 推奨。 | /proc, /sys は PollWatcher(compare_contents 検討)。 | - -## 4.2 OS タイプ別 - -### Windows - -| プロトコル・種別 | 実装方針 | 備考 | -|------------------|----------|------| -| **ローカル** | `RecommendedWatcher`(ReadDirectoryChangesW)。デバウンスは `notify-debouncer-mini`。 | ネイティブで安定。パスは `C:\` 等。 | -| **SMB/CIFS** | まず `RecommendedWatcher` で `watch`。失敗またはイベントが来ない場合は **PollWatcher** に切り替え。`with_poll_interval(Duration::from_secs(30))` 等。 | `\\server\share\...` や マッピンドライブ(`Z:\`)はネットワーク経路のため、多くの環境でネイティブ通知が発火しない。 | -| **NFS** | 同上。ネイティブが効かない前提で **PollWatcher** を優先してもよい。 | マウント済み NFS パスを指定された場合。 | -| **WSL 内で Windows パス** | **PollWatcher** を最初から使用。 | notify の既知問題(issue #254)。 | - -### macOS - -| プロトコル・種別 | 実装方針 | 備考 | -|------------------|----------|------| -| **ローカル** | `RecommendedWatcher`(FSEvents)。「自分が所有していないファイル」のみ監視する場合は **PollWatcher** にフォールバック。 | サンドボックス利用時は entitlement で監視パスを許可。 | -| **SMB/CIFS(マウント済み)** | **PollWatcher** を推奨。ネイティブは多くの場合発火しない。 | `/Volumes/ShareName` 等。 | -| **NFS** | **PollWatcher** を推奨。 | 同上。 | -| **Docker on M1 等** | **PollWatcher**。ネイティブは "Function not implemented" になる。 | コンテナ内で監視する場合。 | - -### Linux - -| プロトコル・種別 | 実装方針 | 備考 | -|------------------|----------|------| -| **ローカル** | `RecommendedWatcher`(inotify)。再帰監視時は **watch 数** が `max_user_watches` を超えないよう注意。超過時は PollWatcher に切り替えまたはユーザーに sysctl 案内。 | デフォルト 8192。大容量ツリーは PollWatcher の選択肢を用意。 | -| **SMB/CIFS(cifs-utils 等でマウント)** | **PollWatcher** を推奨。inotify は多くのネットワーク FS で効かない。 | `/mnt/smb/...` 等。 | -| **NFS** | **PollWatcher** を推奨。 | `/mnt/nfs/...` 等。 | -| **擬似 FS(/proc, /sys)** | **PollWatcher**。必要なら `with_compare_contents(true)` を検討(mtime が信用できないため)。 | 通常の文書監視では対象にしなくてよい。 | - -## 4.3 プロトコル・ストレージ種別の判定 - -- **ローカルかネットワークか**の判定は、実装時に次のいずれかで行う想定とする。 - - **パス prefix で推定**: Windows の `\\\\`(UNC)、Linux の `/mnt/` や `/media/` の下、macOS の `/Volumes/` の一部など。 - - **watch 登録結果**: `RecommendedWatcher` で `watch()` した直後や、一定時間イベントが来ない場合に「ネイティブが使えない」とみなし、PollWatcher に切り替える。 - - **OS のマウント情報**: 各 OS の API(例: Linux の `/proc/mounts`、macOS の `getfsstat`)でマウントポイント一覧を取得し、監視パスがネットワーク FS 上かどうかを判定する(任意の拡張)。 -- 判定結果に応じて **Watcher 種別**(RecommendedWatcher または PollWatcher)と **Config**(poll_interval の有無など)を決め、同一の「監視エンジン」インターフェースで扱う。 - -## 4.4 共通処理 - -- 上記どの組み合わせでも、**デバウンス**は `notify-debouncer-mini` で統一する(PollWatcher は元から間隔が空くため、デバウンス時間は 2〜5 秒程度でよい)。 -- **イベントの解釈**(Create → 追加、Modify → 更新、Remove → 削除)と **DB 連携**は、OS・プロトコルに依存せず共通ロジックとする。 -- **設定の保存**・**パス表記**は [02 技術方針 3.7](folder_monitor_02_tech.md#37-設定の保存) のとおり。プロトコル別の特別な設定項目(例: 「ネットワークパス用ポーリング間隔」)は必要に応じて追加する。 diff --git a/docs/plans/folder_monitor/folder_monitor_04_phases.md b/docs/plans/folder_monitor/folder_monitor_04_phases.md deleted file mode 100644 index 338acb8..0000000 --- a/docs/plans/folder_monitor/folder_monitor_04_phases.md +++ /dev/null @@ -1,29 +0,0 @@ -# フォルダ監視: 04 実装ステップ - -[計画トップ](folder_monitor.md) - ---- - -## 実装ステップ(案) - -| 段階 | 内容 | v0.3.3(Windows) | -|------|------|-------------------| -| **Phase 1** | 単一フォルダの監視のみ。`notify` で Create/Remove/Modify を検知し、ログ出力またはイベント送信まで。DB 連携は行わない。 | ✅ 含める | -| **Phase 2** | 検知イベントと既存の文書取り込み(MCP 経由または内部 API)を接続。追加・更新・削除それぞれのハンドラを実装。 | ✅ 含める | -| **Phase 3** | 設定 UI(監視フォルダパス・オン/オフ・対象拡張子)の追加と、設定の永続化・ワッチャー再起動。 | ✅ 含める | -| **Phase 4** | 複数フォルダ対応・再帰監視オプション・デバウンス時間の調整など、運用面の拡張。 | 以降 | -| **Phase 5(任意)** | macOS / Linux ビルドでの動作確認と、ネットワークフォルダ監視の検証。ネイティブ通知が使えない場合のポーリングフォールバック実装。[03 OS・プロトコル別方針](folder_monitor_03_os_protocol.md)のマトリクスに沿った Watcher 切り替えの実装。 | 以降 | - -**v0.3.3 の範囲**: Windows 上で Phase 1〜3 までを実装・出荷する。ローカルフォルダを RecommendedWatcher(ReadDirectoryChangesW)で監視し、設定 UI と DB 連携まで含める。 - ---- - -## テスト・検証 - -| 種別 | 内容 | -|------|------| -| **単体** | `notify` の `watch` / `unwatch` およびデバウンス(`notify-debouncer-mini`)が、指定ディレクトリ配下の Create/Modify/Remove を期待どおり集約してコールバックに渡すかを、一時ディレクトリとテスト用ファイルで検証する。 | -| **結合** | イベント受信後に DB 連携(追加・更新・削除のハンドラ)が呼ばれることを、テスト用 DB またはインメモリ DB で検証。既存の文書取り込み API をモックにしてもよい。 | -| **手動** | 監視対象フォルダにファイルを追加・編集・削除し、ログまたは UI でイベントが検知されること、および DB に文書が反映される(または削除される)ことを確認する。デバウンス時間内の連続変更が 1 回にまとまることも確認。 | -| **UI / E2E** | 設定パネルで監視フォルダパスを指定し、オン/オフを切り替えて保存できることを、既存 [E2E](https://v2.tauri.app/develop/tests/webdriver/) や [12 UI テスト方針](../../specification/12_ui_testing_options.md) に沿って検証する(実装後にスペック追加)。 | -| **Phase 別** | Phase 1: イベント検知の単体テスト。Phase 2: イベント→DB 連携の結合テスト。Phase 3: 設定 UI の表示・永続化・ワッチャー再起動のテスト。 | diff --git a/docs/plans/folder_monitor/folder_monitor_05_considerations.md b/docs/plans/folder_monitor/folder_monitor_05_considerations.md deleted file mode 100644 index b664578..0000000 --- a/docs/plans/folder_monitor/folder_monitor_05_considerations.md +++ /dev/null @@ -1,13 +0,0 @@ -# フォルダ監視: 05 注意事項・未決定 - -[計画トップ](folder_monitor.md) - ---- - -## 注意事項・未決定 - -- **権限**: 監視対象フォルダへの読み取り権限が無い場合のエラー表示と、設定画面での再指定を促す動線。ネットワークフォルダでは、マウント時やログオン時の認証・再接続が必要な場合がある。 -- **パス正規化**: Windows の短いパス・ジャンクション・シンボリックリンク、macOS/Linux のシンボリックリンク・バインドマウントの扱いをどうするか(同一ファイルの二重登録を防ぐ)。ネットワークパスは OS ごとのマウントポイントの違い(例: Windows `\\server\share`、Linux `/mnt/smb/share`)を考慮する。 -- **ネットワークフォルダの制約**: 一部のネットワーク FS は inotify/FSEvents/ReadDirectoryChangesW をサポートしない。その場合はポーリング間隔の設定や「ネットワークパスでは監視が限定的」である旨の UI 表示を検討する。 -- **大容量フォルダ**: 監視開始時に既存ファイルを一括スキャンするか、監視開始後の変更のみ扱うか。既存ファイルの初回同期は別機能(「フォルダをインポート」)に任せる案もある。 -- **UI の扱い**: 現状、設定画面の「モニター先フォルダ」は非表示。本機能を有効にする際に、当該パネルを再度表示するか、別タブ・別画面にするか。 diff --git a/docs/plans/folder_monitor/folder_monitor_06_references.md b/docs/plans/folder_monitor/folder_monitor_06_references.md deleted file mode 100644 index afa1b84..0000000 --- a/docs/plans/folder_monitor/folder_monitor_06_references.md +++ /dev/null @@ -1,16 +0,0 @@ -# フォルダ監視: 06 参照・調査元 - -[計画トップ](folder_monitor.md) - ---- - -## 参照・調査元 - -- **notify**: [notify 8.2.0 - docs.rs](https://docs.rs/notify/latest/notify/)、[Known Problems](https://docs.rs/notify/latest/notify/#known-problems)(ネットワーク FS、Docker M1、macOS 非所有ファイル、Linux 上限、エディタ挙動、親フォルダ削除)。 -- **notify Config**: [Config - docs.rs](https://docs.rs/notify/latest/notify/struct.Config.html)(poll_interval, compare_contents, follow_symlinks)。 -- **notify-debouncer-mini**: [notify-debouncer-mini - docs.rs](https://docs.rs/notify-debouncer-mini/latest/notify_debouncer_mini/)。 -- **Linux inotify**: [Watchexec - inotify limits](https://watchexec.github.io/docs/inotify-limits.html)、`max_user_watches` / `max_user_instances` の意味と増やし方。 -- **macOS**: [File System Event Security](https://developer.apple.com/library/archive/documentation/Darwin/Conceptual/FSEvents_ProgGuide/FileSystemEventSecurity/FileSystemEventSecurity.html)、[Accessing files from the macOS App Sandbox](https://developer.apple.com/documentation/security/accessing_files_from_the_macos_app_sandbox)。 -- 既存の文書取り込み: MCP ツール `add_item_text`、ドキュメント登録のフロー(`documents` / `items` の作成)。 -- データベース仕様: `docs/specification/03_database_specification.md`。 -- 設定の永続化: 現状の検索設定(localStorage)との棲み分け。 diff --git a/docs/plans/folder_monitor_enhancements.md b/docs/plans/folder_monitor_enhancements.md new file mode 100644 index 0000000..6e2bbfb --- /dev/null +++ b/docs/plans/folder_monitor_enhancements.md @@ -0,0 +1,91 @@ +# 計画: フォルダ監視の拡張 + +## 目的 + +現在の `RecommendedWatcher`(ネイティブ監視)一本の実装を拡張し、ネットワーク FS 対応・パフォーマンス改善・エラーハンドリング強化を行う。 + +関連仕様: [14_folder_monitor.md](../specification/14_folder_monitor.md) + +--- + +## 1. PollWatcher フォールバック(ネットワーク FS 対応) + +### 背景 + +多くのネットワーク FS(SMB/CIFS・NFS 等)では `RecommendedWatcher` のネイティブ通知が発火しない([notify Known Problems](https://docs.rs/notify/latest/notify/#known-problems))。WSL 上で Windows パスを監視する場合も同様。 + +### 方針 + +`RecommendedWatcher` が使えない環境では **`PollWatcher`** にフォールバックする。 + +| 条件 | Watcher | +|------|---------| +| ローカル FS | `RecommendedWatcher`(ネイティブ) | +| SMB/CIFS・NFS 等 | `PollWatcher`(`poll_interval: 30s`) | +| WSL 上の Windows パス | `PollWatcher` | + +### ネットワークパスの判定 + +以下のいずれかで判定する: + +- **パス prefix**: Windows の `\\`(UNC)、Linux の `/mnt/`・`/media/`、macOS の `/Volumes/` +- **watch 登録結果**: `RecommendedWatcher` の `watch()` 失敗時、または一定時間イベントが来ない場合にフォールバック +- **OS マウント情報**: `/proc/mounts`(Linux)、`getfsstat`(macOS)等で判定(任意の拡張) + +### notify Config + +| オプション | 用途 | 想定値 | +|-----------|------|--------| +| `with_poll_interval(dur)` | 再スキャン間隔 | 30 秒(大容量ツリーは 60 秒) | +| `with_compare_contents(bool)` | mtime が信用できない FS 向け(`/proc` 等) | 通常 false | + +--- + +## 2. 大容量フォルダの初期スキャン + +### 問題 + +モニター先追加時の初期スキャンは再帰走査 + 逐次取込のため、大量ファイルがあると長時間かかる。 + +### 対策案 + +- プログレス表示(取込済み / 全体のカウントを UI に送信) +- バッチ制御(一定数ごとに `yield` して他の処理をブロックしない) +- 初期スキャンをバックグラウンドタスク化し、監視は先に開始する + +--- + +## 3. 権限エラー時の UI フィードバック + +### 問題 + +監視対象フォルダへの読み取り権限がない場合、`watch()` や `read_dir()` がエラーを返すが、ユーザーへの通知が `log::warn` のみ。 + +### 対策案 + +- 設定保存時にパスの存在・読み取り権限をチェックし、UI にエラーを表示 +- 監視開始時のエラーをフロントに通知し、設定パネルで該当行をハイライト + +--- + +## 4. パス正規化 + +### 問題 + +シンボリックリンク・ジャンクション・バインドマウントを経由した場合、同一ファイルが異なるパスで検知され、二重登録になる可能性がある。 + +### 対策案 + +- `std::fs::canonicalize()` でパスを正規化してから DB パスと照合 +- `Config::with_follow_symlinks(false)` でリンクをたどらない選択肢 + +--- + +## 優先度 + +| 項目 | 優先度 | +|------|--------| +| PollWatcher フォールバック | 中 | +| 大容量フォルダ初期スキャン | 低 | +| 権限エラー UI | 低 | +| パス正規化 | 低 | diff --git a/docs/specification/06_ui_design_spec.md b/docs/specification/06_ui_design_spec.md index e8f704e..dbafa7b 100644 --- a/docs/specification/06_ui_design_spec.md +++ b/docs/specification/06_ui_design_spec.md @@ -16,7 +16,7 @@ ## 3. 設定パネル -- **表示する項目**: 検索まわり(スコア足切り 0〜1、取得件数)、**ログイン時自動起動**(計画 auto_start)、**モニター先フォルダ**(計画 folder_monitor)。保存はアプリ設定(settings.json)および localStorage(キー `telosdb_settings`)。 +- **表示する項目**: 検索まわり(スコア足切り 0〜1、取得件数)、**ログイン時自動起動**([13_autostart.md](13_autostart.md))、**モニター先フォルダ**([14_folder_monitor.md](14_folder_monitor.md))。保存はアプリ設定(settings.json)および localStorage(キー `telosdb_settings`)。 ## 4. ステータス・アクティビティ diff --git a/docs/specification/13_autostart.md b/docs/specification/13_autostart.md new file mode 100644 index 0000000..23aca64 --- /dev/null +++ b/docs/specification/13_autostart.md @@ -0,0 +1,239 @@ +# 自動起動仕様 (Autostart Specification) + +## 1. 概要 + +OS にユーザーがログインしたタイミングで TelosDB を自動的に起動し、システムトレイで待機させる。ユーザーが手動で起動しなくても MCP サーバーや検索機能を利用可能にする。 + +```mermaid +flowchart LR + subgraph ユーザー操作なし + A[OS ログイン] --> B[TelosDB 自動起動] + B --> C[トレイで待機] + C --> D[MCP / 検索 利用可能] + end +``` + +## 2. スコープ + +| 項目 | 仕様 | +|------|------| +| **対応プラットフォーム** | Windows / macOS / Linux。Tauri 2 の対応 OS に準拠する。 | +| **トリガー** | ユーザーが OS に**ログイン(サインイン)**したとき。ロック画面からの解除やスリープ復帰では発火しない。 | +| **デフォルト** | **オフ**(`run_on_login: false`)。設定キーが存在しない場合(初回・アップデート後)もオフとして扱う。 | +| **起動後の挙動** | 自動起動時: `--minimized` 引数によりウィンドウを非表示にし、トレイのみで待機。手動起動時: 通常どおりウィンドウを表示。いずれもウィンドウを閉じるとトレイに常駐する。 | +| **設定の保存** | `settings.json`(Tauri AppData ディレクトリ)に永続化。フロント側は `localStorage`(キー `telosdb_settings`)にもキャッシュする。 | +| **対象エディション** | Community 版・Pro 版の両方で利用可能。エディション間に差異はない。 | + +## 3. 技術構成 + +### 3.1 プラグイン + +[tauri-plugin-autostart](https://github.com/tauri-apps/tauri-plugin-autostart) を使用する。 + +| 依存 | パッケージ | バージョン | +|------|-----------|-----------| +| Rust | `tauri-plugin-autostart` | `2.5.1` | +| JS | `@tauri-apps/plugin-autostart` | `^2.5.1` | + +> Rust 側と JS 側のバージョンは揃えること。 + +### 3.2 プラットフォーム別の登録先 + +プラグインが各 OS のネイティブ機構を使って自動起動を登録する。 + +| OS | 登録先 | +|----|--------| +| Windows | レジストリ `HKCU\Software\Microsoft\Windows\CurrentVersion\Run` | +| macOS | Launch Agent(`~/Library/LaunchAgents/` に plist を配置) | +| Linux | XDG Autostart(`~/.config/autostart/*.desktop`) | + +```mermaid +flowchart TB + subgraph プラットフォーム別の実体 + W[Windows: レジストリ Run] + M[macOS: LaunchAgents] + L[Linux: autostart .desktop] + end + P[tauri-plugin-autostart] --> W + P --> M + P --> L + U[ユーザー: enable/disable] --> P +``` + +### 3.3 Rust 側プラグイン登録 + +`tauri::Builder` に以下を登録する: + +```rust +.plugin(tauri_plugin_autostart::init(MacosLauncher::LaunchAgent, None)) +``` + +### 3.4 Tauri capability + +`capabilities/default.json` に以下の権限を追加する: + +- `autostart:allow-enable` +- `autostart:allow-disable` +- `autostart:allow-is-enabled` + +### 3.5 起動引数 + +プラグインの `Builder` で `.args([...])` を指定可能。自動起動時に `--minimized` を渡すことで、トレイのみ起動を実現する。 + +## 4. API + +フロントエンドから利用する API は以下の 3 つ。 + +| 関数 | 説明 | +|------|------| +| `enable()` | 自動起動を有効にする(OS にエントリを登録)。 | +| `disable()` | 自動起動を無効にする(OS からエントリを削除)。 | +| `isEnabled()` | 現在 OS 上で自動起動が有効かどうかを返す。 | + +## 5. 設定との連携 + +### 5.1 起動時の同期 + +アプリ起動時に保存済み設定と OS の実状態を同期する。 + +```mermaid +stateDiagram-v2 + [*] --> 設定ファイル読み込み + 設定ファイル読み込み --> スキップ: 読み込み失敗 (null) + 設定ファイル読み込み --> isEnabledチェック: 読み込み成功 + isEnabledチェック --> 同期: 設定値と isEnabled() が不一致 + isEnabledチェック --> 待機: 一致 + 同期 --> enableまたはdisable + enableまたはdisable --> 待機 + 待機 --> ユーザーがトグル変更 + ユーザーがトグル変更 --> 保存してenable/disable + 保存してenable/disable --> 待機 +``` + +| 条件 | 処理 | +|------|------| +| `loadSettingsFromFile()` が `null` を返した場合 | autostart 同期を**スキップ**する。デフォルト値 `run_on_login: false` で `disable()` を呼ぶと正常登録済みエントリを削除してしまうため。 | +| 設定値と `isEnabled()` が一致 | 何もしない。 | +| 設定値と `isEnabled()` が不一致 | 設定値に合わせて `enable()` または `disable()` を呼ぶ。 | + +### 5.2 アップデート時のパス変更 + +再インストールで実行ファイルのパスが変わると、既存の Run キー等は無効になる。次回起動時の同期処理で設定がオンの場合は `enable()` を呼び直し、新しいパスで再登録する。 + +### 5.3 エラーハンドリング + +- `enable()` / `disable()` が失敗した場合: `console.error` に出力し、UI にエラーメッセージを表示する。 +- autostart 登録失敗時の保存メッセージ: 「保存しました(自動起動の登録に失敗しました。次回起動時に再試行します)」を 5 秒間表示する。 + +## 6. UI 仕様 + +### 6.1 設定パネルの表示 + +設定パネルに「ログイン時自動起動」fieldset を表示する。 + +| 項目 | 仕様 | +|------|------| +| **コントロール** | チェックボックス(``)。既存の設定項目と統一。 | +| **配置** | 検索 fieldset の直下に独立した `
` として配置。 | +| **legend** | 「ログイン時自動起動」 | +| **ラベル** | 「有効にする」 | +| **補足テキスト** | 「OS にサインインしたときに TelosDB を自動で起動します」 | +| **デフォルト** | オフ(`run_on_login: false`) | + +### 6.2 HTML 構造 + +```html +
+ ログイン時自動起動 +
+ + + + OS にサインインしたときに TelosDB を自動で起動します + +
+
+``` + +### 6.3 操作フロー + +設定の反映は**保存ボタンのクリック時**に行う。チェックボックスの操作だけでは `enable()` / `disable()` は呼ばれない。 + +```mermaid +flowchart LR + subgraph 設定パネル + A[設定ボタン] --> B[パネル開く] + B --> C["loadSettingsFromFile()"] + C --> D[フォームに反映] + D --> E[ユーザーがチェックボックスを操作] + E --> F[保存ボタンをクリック] + end + F --> G{"run_on_login?"} + G -->|true| H["autostart.enable()"] + G -->|false| I["autostart.disable()"] + H --> J[settings.json に保存] + I --> J + J --> K[UI フィードバック表示] +``` + +### 6.4 表示値の更新タイミング + +| タイミング | 挙動 | +|------------|------| +| **起動時** | `loadSettingsFromFile()` から `run_on_login` を読み、チェックボックスに反映。 | +| **設定パネルを開いたとき** | `loadSettingsFromFile()` を再取得してフォームに反映。`isEnabled()` で OS 実状態と照合し、不一致時は OS 側に合わせて表示を補正。 | +| **保存後** | 保存した値をフォームに再セットし、`localStorage` にもキャッシュ。 | + +## 7. 注意事項 + +| 項目 | 内容 | +|------|------| +| **権限** | Windows の Run キーはユーザー単位。macOS の Launch Agent もユーザー単位。管理者権限は不要。 | +| **二重起動** | 別の方法(タスクスケジューラ等)で同じアプリを登録している場合、二重起動の可能性がある。プラグインは 1 エントリのみ登録する。 | +| **Community / Pro 共存** | 同一マシンに両方インストールし、両方で自動起動をオンにした場合はログイン時に両方起動する。 | +| **アンインストール時** | NSIS アンインストーラが Run キー / LaunchAgents / autostart エントリを削除することを確認する必要がある。 | +| **ヘッドレス起動** | `TELOS_HEADLESS=1` 環境変数でも `--minimized` と同様にトレイのみ起動が可能。 | + +## 8. テスト + +| 種別 | 内容 | +|------|------| +| **単体テスト** | Rust 側: `settings_default_has_run_on_login_false()` で初期値が `false` であることを検証(`src/backend/src/mcp/handlers.rs`)。 | +| **結合テスト** | 起動時の `loadSettingsFromFile()` → `isEnabled()` 比較 → 不一致時のみ同期、の一連フローを検証。 | +| **E2E テスト** | `tests/e2e/specs/settings-autostart.spec.js` で以下を検証: (1) チェックボックスの表示、(2) オンで保存→成功メッセージ表示、(3) パネル切替後もオン/オフが永続化。実行: `npx wdio run wdio.conf.js --spec tests/e2e/specs/settings-autostart.spec.js` | +| **手動テスト** | 自動起動をオンにし、OS ログアウト→ログイン(または再起動)で TelosDB が起動することを確認。オフの場合は起動しないことを確認。NSIS インストーラ版でも同様に確認する。 | + +## 9. 実装ファイル一覧 + +| ファイル | 役割 | +|----------|------| +| `src/backend/Cargo.toml` | Rust 依存: `tauri-plugin-autostart = "2.5.1"` | +| `package.json` | JS 依存: `@tauri-apps/plugin-autostart: ^2.5.1` | +| `src/backend/src/lib.rs` | Rust 側プラグイン登録、`get_app_settings` / `set_app_settings` コマンド | +| `src/backend/capabilities/default.json` | Tauri capability 権限設定 | +| `src/frontend/components/main-panel.js` | UI チェックボックス、起動時 `isEnabled()` 同期、保存時 `enable()` / `disable()` | +| `src/backend/src/mcp/handlers.rs` | MCP 経由の設定読み書き、`run_on_login` のデフォルト値 | +| `tests/e2e/specs/settings-autostart.spec.js` | E2E テスト | + +## 10. 外部リファレンス + +- [tauri-plugin-autostart](https://github.com/tauri-apps/tauri-plugin-autostart) — 公式プラグイン +- [Tauri v2 Autostart プラグイン](https://v2.tauri.app/plugin/autostart/) + +## 11. 関連計画 + +- [cross_platform.md](../plans/cross_platform.md) — macOS / Linux での動作検証、アンインストール時のエントリ残留確認 + +## 12. 修正履歴 + +### v0.3.3 + +初期実装のバグにより「自動起動がレジストリに登録されない(またはすぐ消える)」問題が発生し、以下を修正。 + +| 問題 | 修正 | +|------|------| +| `loadSettingsFromFile()` 失敗時にデフォルト値で `disable()` が走り、レジストリエントリが毎起動時に削除されていた | 読み込み失敗時は autostart 同期をスキップ | +| `isEnabled()` チェックなしに毎回 `enable()` / `disable()` を呼んでいた | `isEnabled()` で比較し不一致時のみ呼び出し | +| `enable()` / `disable()` のエラーが `catch(_){}` で握りつぶされていた | `console.error` 出力 + UI エラーメッセージ表示 | +| `Cargo.toml` のバージョンが `"2.0.0"` のまま(JS 側は `^2.5.1`) | `"2.5.1"` に更新 | diff --git a/docs/specification/14_folder_monitor.md b/docs/specification/14_folder_monitor.md new file mode 100644 index 0000000..a817877 --- /dev/null +++ b/docs/specification/14_folder_monitor.md @@ -0,0 +1,170 @@ +# フォルダ監視仕様 (Folder Monitor Specification) + +## 1. 概要 + +ユーザーが指定したフォルダを監視し、ファイルの追加・削除・更新を検知して TelosDB のインデックスに自動的に反映する。監視は一方向(フォルダ → DB)で完結し、MCP クライアントは従来どおり検索や件数取得で現在の DB 状態を参照する。 + +```mermaid +flowchart LR + A[監視フォルダ] -->|Create / Modify / Remove| B[notify + debouncer] + B --> C{イベント種別} + C -->|ファイル存在| D[取込パイプライン] + C -->|ファイル不在| E[インデックス削除] + D --> F[(TelosDB)] + E --> F +``` + +## 2. スコープ + +| 項目 | 仕様 | +|------|------| +| **対応プラットフォーム** | Windows / macOS / Linux。Tauri 2 の対応 OS に準拠する。 | +| **監視対象** | ユーザーが設定で指定する 1 つ以上のディレクトリパス。 | +| **監視の深さ** | 再帰監視(`RecursiveMode::Recursive`)。サブディレクトリも含む。 | +| **対象ファイル** | 拡張子でフィルタ。デフォルト: `txt`, `md`, `json`, `html`, `css`, `js`, `mjs`, `ts`, `rs`。設定で変更可能。 | +| **動作タイミング** | アプリ起動中の常時監視。設定でモニター先を空にすることで実質オフにできる。 | +| **デフォルト** | モニター先フォルダなし(`monitor_paths: []`)。 | +| **MCP との関係** | 能動的なプッシュ通知はしない。クライアントは `search_text` や `get_document_count` 等で現在状態をプルする。 | +| **対象エディション** | Community 版・Pro 版の両方で利用可能。 | + +## 3. 技術構成 + +### 3.1 監視ライブラリ + +Rust の **`notify`** クレートと **`notify-debouncer-mini`** を使用する。 + +| 依存 | 用途 | +|------|------| +| `notify` | クロスプラットフォームのファイル監視。各 OS のネイティブ API を利用する。 | +| `notify-debouncer-mini` | 短時間の連続変更を 1 イベントに集約(デバウンス)。 | + +### 3.2 プラットフォーム別バックエンド + +`notify::recommended_watcher()` で OS に最適な実装を取得する。 + +| OS | ネイティブ API | +|----|---------------| +| Windows | ReadDirectoryChangesW | +| macOS | FSEvents | +| Linux | inotify | + +### 3.3 デバウンス + +`notify-debouncer-mini` で **2 秒間**のデバウンスを行う。エディタの自動保存や一括書き換えによる連続イベントを 1 回の処理にまとめる。 + +debouncer-mini は内部でスレッドを使いブロッキング受信するため、専用の std スレッドで動作させ、Tokio ランタイムの `Handle` を通じて async 側(DB 連携)と接続する。 + +### 3.4 アーキテクチャ + +```mermaid +flowchart TB + subgraph "std スレッド" + W[notify Watcher] --> D[debouncer-mini] + D --> CB[コールバック] + end + CB -->|"Handle.block_on()"| I[ingest_file_path / delete_document_by_path] + I --> DB[(SQLite)] + CFG[WatcherConfig via mpsc] --> W +``` + +- **`spawn_folder_watcher`**: 専用 std スレッドで監視ループを起動。`mpsc::Receiver` から設定を受け取り、設定変更時にワッチャーを再起動する。 +- **イベント処理**: debouncer-mini は `Any` / `AnyContinuous` のみ区別するため、パスがファイルとして存在すれば取り込み(Create/Modify)、存在しなければ削除(Remove)として処理する。 + +## 4. イベント処理 + +### 4.1 ファイル追加・更新 + +対象拡張子にマッチするファイルが検知された場合: + +1. ファイルパスからカテゴリを解決(`category_map` でモニター先ディレクトリごとにカテゴリを紐付け) +2. `ingest_file_path()` でファイルを読み取り、チャンク分割・ベクトル化・`documents` / `items` / `vec_items` / FTS に登録 +3. 既にインデックス済みの場合は再取り込み(更新) + +### 4.2 ファイル削除 + +ファイルが存在しなくなった場合: + +1. `delete_document_by_path()` で該当パスに紐づくドキュメントを `documents` から削除(関連 `items` / `vec_items` / FTS も連動削除) + +### 4.3 拡張子フィルタ + +`is_watched_file()` で対象拡張子にマッチするか判定する。大文字・小文字は区別しない。拡張子リストが空の場合はすべてのファイルを無視する。 + +### 4.4 初期スキャン + +モニター先が追加された際、既存ファイルを再帰走査し、未インデックスのもの(`get_document_id_by_path()` が `None` を返すもの)のみ取り込む。 + +## 5. 設定仕様 + +### 5.1 設定項目 + +| 項目 | キー | 型 | デフォルト | +|------|------|-----|----------| +| モニター先フォルダ | `monitor_paths` | `[{path: string, category: string}]` | `[]` | +| 取込対象拡張子 | `watch_extensions` | `string[]` | `["txt", "md", "json", "html", "css", "js", "mjs", "ts", "rs"]` | + +`monitor_paths` はオブジェクト配列(`{path, category}`)形式。旧形式の文字列配列(`["path"]`)にも後方互換で対応する。 + +### 5.2 永続化 + +`settings.json`(Tauri AppData ディレクトリ)に永続化する。フロント側は `localStorage`(キー `telosdb_settings`)にもキャッシュする。 + +### 5.3 設定変更時の挙動 + +設定保存時に `WatcherConfig` を `mpsc` チャンネル経由でワッチャースレッドに送信し、ワッチャーを再起動する(監視パスの付け替え)。 + +## 6. UI 仕様 + +### 6.1 設定パネルの表示 + +設定パネルに「モニター先フォルダ」fieldset を表示する。 + +| 項目 | 仕様 | +|------|------| +| **配置** | 「ログイン時自動起動」fieldset の直下。 | +| **legend** | 「モニター先フォルダ」 | +| **フォルダ行** | パス入力欄 + カテゴリ名入力欄 + 削除ボタンの行を動的に追加・削除。 | +| **追加ボタン** | 「追加」ボタンクリックで Tauri のフォルダ選択ダイアログを表示し、選択パスを新規行に追加。 | +| **拡張子入力** | カンマ区切りのテキスト入力。空の場合はデフォルト拡張子を使用。 | + +### 6.2 操作フロー + +設定の反映は**保存ボタンのクリック時**に行う。保存時に `WatcherConfig` がワッチャースレッドに送信され、即座に監視が再起動する。 + +### 6.3 取込ステータス + +ファイル取込中は `watch_ingestion_status`(`RwLock`)に「取込中: ファイル名」を格納し、UI から参照可能にする。 + +## 7. テスト + +| 種別 | 内容 | +|------|------| +| **単体テスト** | `is_watched_file()` の拡張子マッチ(大文字小文字、空リスト)。`watch.rs` 内の `#[cfg(test)]` で検証済み。 | +| **結合テスト** | イベント受信後に DB 連携(追加・更新・削除のハンドラ)が呼ばれることを検証。 | +| **手動テスト** | 監視対象フォルダにファイルを追加・編集・削除し、DB に反映されることを確認。デバウンス時間内の連続変更が 1 回にまとまることも確認。 | +| **UI / E2E** | 設定パネルで監視フォルダパスを指定し、保存できることを検証。 | + +## 8. 制限事項 + +- ネットワーク FS(SMB/CIFS・NFS 等)ではネイティブ通知が発火しない場合がある。現在は `RecommendedWatcher` のみ使用しており、PollWatcher フォールバックは未実装。 +- エディタによっては保存時に Remove + Create や複数 Modify が発生するが、デバウンスで吸収する。 + +## 9. 実装ファイル一覧 + +| ファイル | 役割 | +|----------|------| +| `src/backend/src/mcp/watch.rs` | フォルダ監視コア。`spawn_folder_watcher`、イベント処理、初期スキャン。 | +| `src/backend/src/mcp/types.rs` | `WatcherConfig` / `WatcherRestartSender` 型定義。 | +| `src/backend/src/mcp/mod.rs` | 起動時の `WatcherConfig` 構築と `spawn_folder_watcher` 呼び出し。 | +| `src/backend/src/mcp/handlers.rs` | 設定保存時の `WatcherConfig` 送信。 | +| `src/frontend/components/main-panel.js` | モニター先フォルダ UI(パス追加・カテゴリ・拡張子・保存)。 | + +## 10. 外部リファレンス + +- [notify](https://docs.rs/notify/latest/notify/) — Rust ファイル監視クレート +- [notify-debouncer-mini](https://docs.rs/notify-debouncer-mini/latest/notify_debouncer_mini/) — デバウンサー + +## 11. 関連計画 + +- [cross_platform.md](../plans/cross_platform.md) — macOS / Linux での動作検証 +- [folder_monitor_enhancements.md](../plans/folder_monitor_enhancements.md) — PollWatcher フォールバック、初期スキャン改善、権限エラー UI、パス正規化 diff --git "a/journals/202603-017-\350\207\252\345\213\225\350\265\267\345\213\225\346\224\271\345\226\204\343\201\250\343\203\225\343\202\251\343\203\253\343\203\200\343\203\242\343\203\213\343\202\277\343\202\253\343\203\206\343\202\264\343\203\252\346\251\237\350\203\275.md" "b/journals/202603-017-\350\207\252\345\213\225\350\265\267\345\213\225\346\224\271\345\226\204\343\201\250\343\203\225\343\202\251\343\203\253\343\203\200\343\203\242\343\203\213\343\202\277\343\202\253\343\203\206\343\202\264\343\203\252\346\251\237\350\203\275.md" new file mode 100644 index 0000000..5dd0dfc --- /dev/null +++ "b/journals/202603-017-\350\207\252\345\213\225\350\265\267\345\213\225\346\224\271\345\226\204\343\201\250\343\203\225\343\202\251\343\203\253\343\203\200\343\203\242\343\203\213\343\202\277\343\202\253\343\203\206\343\202\264\343\203\252\346\251\237\350\203\275.md" @@ -0,0 +1,61 @@ +# 2026-03-09 自動起動改善・フォルダ選択ダイアログ・モニターカテゴリ機能 + +## 実施内容 + +### 1. 自動起動時のウィンドウ一瞬表示を修正 + +自動起動(`--minimized`)時にメインウィンドウが一瞬表示されてから隠れる問題を修正した。 + +- **原因**: `tauri.conf.json` でウィンドウが `visible: true`(デフォルト)で作成され、`setup()` の重い初期化完了後に `hide()` していた。 +- **修正**: ウィンドウを `visible: false` で作成し、通常起動時のみ初期化完了後に `show()` する方式に反転した。自動起動時はウィンドウが一度も表示されない。 + +### 2. モニター先フォルダ追加時にフォルダ選択ダイアログを表示 + +手入力のみだったモニター先フォルダのパス指定に、OS ネイティブのフォルダ選択ダイアログを導入した。 + +- `tauri-plugin-dialog` v2.6.0 を導入(Cargo.toml, package.json, capabilities, lib.rs) +- 「追加」ボタンクリックでフォルダ選択ダイアログが開き、選択パスが自動入力される。重複チェック付き。 + +### 3. モニター先フォルダにカテゴリ名を付与(検索フィルタ対応) + +モニター先フォルダごとにカテゴリ名を付けられるようにし、検索時にカテゴリで絞り込めるようにした。 + +- **DB**: `documents` テーブルに `category TEXT DEFAULT ''` カラムを追加(ALTER TABLE マイグレーション)。 +- **settings.json**: `monitor_paths` を文字列配列からオブジェクト配列 `[{path, category}]` に変更。旧形式(文字列配列)との後方互換を維持する `parse_monitor_paths` ヘルパーを `types.rs` に追加。 +- **WatcherConfig**: `category_map: HashMap` を追加。ファイル取込時にモニターディレクトリからカテゴリを解決して `documents.category` に保存。 +- **検索 API**: `search_text` ツールに `category` フィルタパラメータを追加。レスポンスの各アイテムに `category` フィールドを含む。 +- **MCP ツール**: `add_item_text` ツールにもオプションの `category` パラメータを追加。 +- **設定画面 UI**: 各モニターパス行にカテゴリ名入力欄を追加。 +- **検索画面 UI**: カテゴリ選択ドロップダウンを追加。結果カードにカテゴリバッジを表示。 + +## 主な変更ファイル + +### バックエンド (Rust) + +| ファイル | 変更内容 | +|---|---| +| src/backend/tauri.conf.json | ウィンドウ `visible: false` | +| src/backend/Cargo.toml | `tauri-plugin-dialog` 追加 | +| src/backend/capabilities/default.json | `dialog:allow-open` 追加 | +| src/backend/src/lib.rs | dialog プラグイン登録、起動時 show/hide 反転 | +| src/backend/src/db/migration.rs | `migrate_add_documents_category` 追加 | +| src/backend/src/db/mod.rs | `get_item_content_with_doc` に category 追加 | +| src/backend/src/mcp/types.rs | `WatcherConfig.category_map`、`parse_monitor_paths` | +| src/backend/src/mcp/handlers.rs | 設定保存時の WatcherConfig 構築で parse_monitor_paths 使用 | +| src/backend/src/mcp/mod.rs | 起動時の設定読込で parse_monitor_paths 使用 | +| src/backend/src/mcp/watch.rs | `resolve_category` 追加、ingest 呼び出しにカテゴリ伝播 | +| src/backend/src/mcp/tools/items.rs | ingest_file_path / handle_add_item_text / list_documents にカテゴリ対応 | +| src/backend/src/mcp/tools/search.rs | category フィルタ・レスポンスに category 追加 | +| src/backend/src/mcp/tools/registry.rs | search_text / add_item_text スキーマに category 追加 | + +### フロントエンド + +| ファイル | 変更内容 | +|---|---| +| src/frontend/components/main-panel.js | カテゴリ入力欄、カテゴリフィルタ dropdown、フォルダ選択ダイアログ | +| src/frontend/index.html | 検索リクエストに category、結果カードにカテゴリバッジ | +| src/frontend/styles.css | カテゴリ入力欄・フィルタ・バッジのスタイル | + +## 計測 + +`count_loc.cjs` / `nesting_depth.cjs` 実行済み。items.rs (670行, depth 7)、main-panel.js (747行, depth 10) 等の超過は既存。今回の変更で新たに閾値を超えたファイルはない。 diff --git "a/journals/202603-018-\343\202\253\343\203\206\343\202\264\343\203\252\344\270\200\346\213\254\346\233\264\346\226\260.md" "b/journals/202603-018-\343\202\253\343\203\206\343\202\264\343\203\252\344\270\200\346\213\254\346\233\264\346\226\260.md" new file mode 100644 index 0000000..f162428 --- /dev/null +++ "b/journals/202603-018-\343\202\253\343\203\206\343\202\264\343\203\252\344\270\200\346\213\254\346\233\264\346\226\260.md" @@ -0,0 +1,20 @@ +# 2026-03-09 設定保存時のカテゴリ一括更新 + +## 実施内容 + +### カテゴリ変更時に既存ドキュメントを即座に一括更新 + +モニター先フォルダのカテゴリ名を変更して設定を保存した際、既にインデックス済みのドキュメントにも +カテゴリが即座に反映されるようにした。従来はファイルが更新されて再取込されるまでカテゴリが変わらなかった。 + +- **仕組み**: `settings_post_handler` で WatcherConfig 送信後、`category_map` をもとに SQL `UPDATE` で既存ドキュメントのカテゴリを一括書き換え。 + - カテゴリが設定されたパス → 配下の全ドキュメントの `category` を新しい値に更新(変更がある場合のみ)。 + - カテゴリが空のパス → 配下のドキュメントの `category` を空文字にリセット。 +- `db::escape_like` を `pub` に変更して `handlers.rs` から利用可能にした。 + +## 主な変更ファイル + +| ファイル | 変更内容 | +|---|---| +| src/backend/src/mcp/handlers.rs | 設定保存時にカテゴリ一括 UPDATE ロジックを追加 | +| src/backend/src/db/mod.rs | `escape_like` を `pub` に変更 | diff --git a/package-lock.json b/package-lock.json index d0fac35..fc79a4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@tauri-apps/api": "^2.10.1", "@tauri-apps/cli": "^2.10.0", "@tauri-apps/plugin-autostart": "^2.5.1", + "@tauri-apps/plugin-dialog": "^2.6.0", "@toast-ui/editor": "^3.2.2", "axios": "^1.13.5", "better-sqlite3": "^12.6.2", @@ -21,6 +22,7 @@ "sqlite-vec-windows-x64": "^0.1.7-alpha.2" }, "devDependencies": { + "@mermaid-js/mermaid-cli": "^11.6.0", "@types/react": "^18.2.21", "@types/react-dom": "^18.2.7", "@wdio/cli": "^9.19.0", @@ -33,6 +35,31 @@ "vite": "^6.0.0" } }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@antfu/install-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", + "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", + "dev": true, + "dependencies": { + "package-manager-detector": "^1.3.0", + "tinyexec": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/@babel/code-frame": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", @@ -56,6 +83,51 @@ "node": ">=6.9.0" } }, + "node_modules/@braintree/sanitize-url": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.2.tgz", + "integrity": "sha512-jigsZK+sMF/cuiB7sERuo9V7N9jx+dhmHHnQyDSVdpZwVutaBu7WvNYqMDLSgFgfB30n452TP3vjDAvFC973mA==", + "dev": true + }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.1.2.tgz", + "integrity": "sha512-XTsjvDVB5nDZBQB8o0o/0ozNelQtn2KrUVteIHSlPd2VAV2utEb6JzyCJaJ8tGxACR4RiBNWy5uYUHX2eji88Q==", + "dev": true, + "dependencies": { + "@chevrotain/gast": "11.1.2", + "@chevrotain/types": "11.1.2", + "lodash-es": "4.17.23" + } + }, + "node_modules/@chevrotain/gast": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.1.2.tgz", + "integrity": "sha512-Z9zfXR5jNZb1Hlsd/p+4XWeUFugrHirq36bKzPWDSIacV+GPSVXdk+ahVWZTwjhNwofAWg/sZg58fyucKSQx5g==", + "dev": true, + "dependencies": { + "@chevrotain/types": "11.1.2", + "lodash-es": "4.17.23" + } + }, + "node_modules/@chevrotain/regexp-to-ast": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.1.2.tgz", + "integrity": "sha512-nMU3Uj8naWer7xpZTYJdxbAs6RIv/dxYzkYU8GSwgUtcAAlzjcPfX1w+RKRcYG8POlzMeayOQ/znfwxEGo5ulw==", + "dev": true + }, + "node_modules/@chevrotain/types": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.1.2.tgz", + "integrity": "sha512-U+HFai5+zmJCkK86QsaJtoITlboZHBqrVketcO2ROv865xfCMSFpELQoz1GkX5GzME8pTa+3kbKrZHQtI0gdbw==", + "dev": true + }, + "node_modules/@chevrotain/utils": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.1.2.tgz", + "integrity": "sha512-4mudFAQ6H+MqBTfqLmU7G1ZwRzCLfJEooL/fsF6rCX5eePMbGhoy5n4g+G4vlh2muDcsCTJtL+uKbOzWxs5LHA==", + "dev": true + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", @@ -472,6 +544,106 @@ "node": ">=18" } }, + "node_modules/@floating-ui/core": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", + "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", + "dev": true, + "dependencies": { + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", + "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", + "dev": true, + "dependencies": { + "@floating-ui/core": "^1.7.5", + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.27.19", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.27.19.tgz", + "integrity": "sha512-31B8h5mm8YxotlE7/AU/PhNAl8eWxAmjL/v2QOxroDNkTFLk3Uu82u63N3b6TXa4EGJeeZLVcd/9AlNlVqzeog==", + "dev": true, + "dependencies": { + "@floating-ui/react-dom": "^2.1.8", + "@floating-ui/utils": "^0.2.11", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=17.0.0", + "react-dom": ">=17.0.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.8.tgz", + "integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==", + "dev": true, + "dependencies": { + "@floating-ui/dom": "^1.7.6" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", + "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", + "dev": true + }, + "node_modules/@headlessui/react": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.2.9.tgz", + "integrity": "sha512-Mb+Un58gwBn0/yWZfyrCh0TJyurtT+dETj7YHleylHk5od3dv2XqETPGWMyQ5/7sYN7oWdyM1u9MvC0OC8UmzQ==", + "dev": true, + "dependencies": { + "@floating-ui/react": "^0.26.16", + "@react-aria/focus": "^3.20.2", + "@react-aria/interactions": "^3.25.0", + "@tanstack/react-virtual": "^3.13.9", + "use-sync-external-store": "^1.5.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" + } + }, + "node_modules/@headlessui/react/node_modules/@floating-ui/react": { + "version": "0.26.28", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.28.tgz", + "integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==", + "dev": true, + "dependencies": { + "@floating-ui/react-dom": "^2.1.2", + "@floating-ui/utils": "^0.2.8", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@headlessui/tailwindcss": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@headlessui/tailwindcss/-/tailwindcss-0.2.2.tgz", + "integrity": "sha512-xNe42KjdyA4kfUKLLPGzME9zkH7Q3rOZ5huFihWNWOQFxnItxPB3/67yBI8/qBfY8nwBRx5GHn4VprsoluVMGw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "tailwindcss": "^3.0 || ^4.0" + } + }, "node_modules/@hono/node-server": { "version": "1.19.9", "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", @@ -483,6 +655,23 @@ "hono": "^4" } }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "dev": true + }, + "node_modules/@iconify/utils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.1.0.tgz", + "integrity": "sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==", + "dev": true, + "dependencies": { + "@antfu/install-pkg": "^1.1.0", + "@iconify/types": "^2.0.0", + "mlly": "^1.8.0" + } + }, "node_modules/@inquirer/ansi": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", @@ -979,12 +1168,84 @@ "node": ">=8" } }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mermaid-js/mermaid-cli": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/@mermaid-js/mermaid-cli/-/mermaid-cli-11.12.0.tgz", + "integrity": "sha512-a0swOS6PByXKi0dZnLQQIhbtUEu7ubc6bojmIqXqvUPq7mIJukCNEvVBTv6IAbuEWqB3Ti8QntupoGdz3ej+kg==", + "dev": true, + "dependencies": { + "@mermaid-js/mermaid-zenuml": "^0.2.0", + "chalk": "^5.0.1", + "commander": "^14.0.0", + "import-meta-resolve": "^4.1.0", + "mermaid": "^11.0.2" + }, + "bin": { + "mmdc": "src/cli.js" + }, + "engines": { + "node": "^18.19 || >=20.0" + }, + "peerDependencies": { + "puppeteer": "^23" + } + }, + "node_modules/@mermaid-js/mermaid-zenuml": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@mermaid-js/mermaid-zenuml/-/mermaid-zenuml-0.2.2.tgz", + "integrity": "sha512-sUjwk4NWUpy9uaHypYSIGJDks10ZaZo5CHH9lx9xcmyqv9w7yvd4vecUmlUQxmlHStYO+aqSkYKX5/gFjDfypw==", + "dev": true, + "dependencies": { + "@zenuml/core": "^3.35.2" + }, + "peerDependencies": { + "mermaid": "^10 || ^11" + } + }, + "node_modules/@mermaid-js/parser": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-1.0.1.tgz", + "integrity": "sha512-opmV19kN1JsK0T6HhhokHpcVkqKpF+x2pPDKKM2ThHtZAB5F4PROopk0amuVYK5qMrIA4erzpNm8gmPNJgMDxQ==", + "dev": true, + "dependencies": { + "langium": "^4.0.0" + } + }, "node_modules/@modelcontextprotocol/sdk": { "version": "1.26.0", "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz", @@ -1055,6 +1316,41 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -1145,6 +1441,103 @@ "streamx": "^2.15.0" } }, + "node_modules/@react-aria/focus": { + "version": "3.21.5", + "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.21.5.tgz", + "integrity": "sha512-V18fwCyf8zqgJdpLQeDU5ZRNd9TeOfBbhLgmX77Zr5ae9XwaoJ1R3SFJG1wCJX60t34AW+aLZSEEK+saQElf3Q==", + "dev": true, + "dependencies": { + "@react-aria/interactions": "^3.27.1", + "@react-aria/utils": "^3.33.1", + "@react-types/shared": "^3.33.1", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/interactions": { + "version": "3.27.1", + "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.27.1.tgz", + "integrity": "sha512-M3wLpTTmDflI0QGNK0PJNUaBXXfeBXue8ZxLMngfc1piHNiH4G5lUvWd9W14XVbqrSCVY8i8DfGrNYpyyZu0tw==", + "dev": true, + "dependencies": { + "@react-aria/ssr": "^3.9.10", + "@react-aria/utils": "^3.33.1", + "@react-stately/flags": "^3.1.2", + "@react-types/shared": "^3.33.1", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/ssr": { + "version": "3.9.10", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.10.tgz", + "integrity": "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==", + "dev": true, + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-aria/utils": { + "version": "3.33.1", + "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.33.1.tgz", + "integrity": "sha512-kIx1Sj6bbAT0pdqCegHuPanR9zrLn5zMRiM7LN12rgRf55S19ptd9g3ncahArifYTRkfEU9VIn+q0HjfMqS9/w==", + "dev": true, + "dependencies": { + "@react-aria/ssr": "^3.9.10", + "@react-stately/flags": "^3.1.2", + "@react-stately/utils": "^3.11.0", + "@react-types/shared": "^3.33.1", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", + "react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-stately/flags": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.2.tgz", + "integrity": "sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==", + "dev": true, + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, + "node_modules/@react-stately/utils": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.11.0.tgz", + "integrity": "sha512-8LZpYowJ9eZmmYLpudbo/eclIRnbhWIJZ994ncmlKlouNzKohtM8qTC6B1w1pwUbiwGdUoyzLuQbeaIor5Dvcw==", + "dev": true, + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, + "node_modules/@react-types/shared": { + "version": "3.33.1", + "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.33.1.tgz", + "integrity": "sha512-oJHtjvLG43VjwemQDadlR5g/8VepK56B/xKO2XORPHt9zlW6IZs3tZrYlvH29BMvoqC7RtE7E5UjgbnbFtDGag==", + "dev": true, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.59.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", @@ -1494,6 +1887,42 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@swc/helpers": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.19.tgz", + "integrity": "sha512-QamiFeIK3txNjgUTNppE6MiG3p7TdninpZu0E0PbqVh1a9FNLT2FRhisaa4NcaX52XVhA5l7Pk58Ft7Sqi/2sA==", + "dev": true, + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tanstack/react-virtual": { + "version": "3.13.21", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.21.tgz", + "integrity": "sha512-SYXFrmrbPgXBvf+HsOsKhFgqSe4M6B29VHOsX9Jih9TlNkNkDWx0hWMiMLUghMEzyUz772ndzdEeCEBx+3GIZw==", + "dev": true, + "dependencies": { + "@tanstack/virtual-core": "3.13.21" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@tanstack/virtual-core": { + "version": "3.13.21", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.21.tgz", + "integrity": "sha512-ww+fmLHyCbPSf7JNbWZP3g7wl6SdNo3ah5Aiw+0e9FDErkVHLKprYUrwTm7dF646FtEkN/KkAKPYezxpmvOjxw==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@tauri-apps/api": { "version": "2.10.1", "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.10.1.tgz", @@ -1704,6 +2133,14 @@ "@tauri-apps/api": "^2.8.0" } }, + "node_modules/@tauri-apps/plugin-dialog": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-dialog/-/plugin-dialog-2.6.0.tgz", + "integrity": "sha512-q4Uq3eY87TdcYzXACiYSPhmpBA76shgmQswGkSVio4C82Sz2W4iehe9TnKYwbq7weHiL88Yw19XZm7v28+Micg==", + "dependencies": { + "@tauri-apps/api": "^2.8.0" + } + }, "node_modules/@toast-ui/editor": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/@toast-ui/editor/-/editor-3.2.2.tgz", @@ -1725,12 +2162,271 @@ "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", "dev": true }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "dev": true, + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "dev": true + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "dev": true, + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "dev": true, + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", + "dev": true + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "dev": true + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "dev": true, + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", + "dev": true + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", + "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==", + "dev": true + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "dev": true, + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", + "dev": true + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "dev": true + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "dev": true, + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", + "dev": true + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "dev": true + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "dev": true, + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", + "dev": true + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dev": true, + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "dev": true + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", + "dev": true + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "dev": true + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", + "dev": true + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "dev": true, + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "dev": true + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", + "dev": true + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "dev": true, + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "dev": true + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "dev": true + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "dev": true + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "dev": true, + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "dev": true, + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "dev": true + }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", @@ -1813,6 +2509,13 @@ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true, + "optional": true + }, "node_modules/@types/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.2.tgz", @@ -1853,6 +2556,16 @@ "@types/node": "*" } }, + "node_modules/@upsetjs/venn.js": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@upsetjs/venn.js/-/venn.js-2.0.0.tgz", + "integrity": "sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==", + "dev": true, + "optionalDependencies": { + "d3-selection": "^3.0.0", + "d3-transition": "^3.0.1" + } + }, "node_modules/@vitest/pretty-format": { "version": "2.1.9", "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", @@ -2152,6 +2865,84 @@ "node": ">=18" } }, + "node_modules/@zenuml/core": { + "version": "3.46.0", + "resolved": "https://registry.npmjs.org/@zenuml/core/-/core-3.46.0.tgz", + "integrity": "sha512-bjG37doCwXer4DhlBh1d7bHUuJbqAK/1I+kHyL7Evy0eWZnBOTEOfp6LodB5cokw3w2IMxzYuj7qyRzwjqoeiA==", + "dev": true, + "dependencies": { + "@floating-ui/react": "^0.27.16", + "@headlessui/react": "^2.2.9", + "@headlessui/tailwindcss": "^0.2.2", + "antlr4": "~4.11.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "color-string": "^2.1.4", + "dompurify": "^3.3.1", + "highlight.js": "^10.7.3", + "html-to-image": "^1.11.13", + "immer": "^10.2.0", + "jotai": "^2.16.1", + "lodash": "^4.17.21", + "marked": "^4.3.0", + "pako": "^2.1.0", + "pino": "^8.21.0", + "radash": "^12.1.1", + "ramda": "^0.28.0", + "react": "^19.2.3", + "react-dom": "^19.2.3", + "tailwind-merge": "^3.4.0", + "tailwindcss": "^3.4.19" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@zenuml/core/node_modules/dompurify": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.2.tgz", + "integrity": "sha512-6obghkliLdmKa56xdbLOpUZ43pAR6xFy1uOrxBaIDjT+yaRuuybLjGS9eVBoSR/UPU5fq3OXClEHLJNGvbxKpQ==", + "dev": true, + "engines": { + "node": ">=20" + }, + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/@zenuml/core/node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "dev": true + }, + "node_modules/@zenuml/core/node_modules/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@zenuml/core/node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "dev": true, + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/@zenuml/core/node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "dev": true + }, "node_modules/@zip.js/zip.js": { "version": "2.8.23", "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.8.23.tgz", @@ -2187,6 +2978,18 @@ "node": ">= 0.6" } }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -2262,6 +3065,21 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/antlr4": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/antlr4/-/antlr4-4.11.0.tgz", + "integrity": "sha512-GUGlpE2JUjAN+G8G5vY+nOoeyNhHsXoIJwP1XF1oRw89vifA1K46T6SEkwLwr7drihN7I/lf0DIjKc4OZvBX8w==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -2441,6 +3259,12 @@ "streamx": "^2.15.0" } }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -2488,6 +3312,15 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/axios": { "version": "1.13.5", "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz", @@ -2785,6 +3618,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -2797,6 +3640,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/chalk": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", @@ -2866,6 +3718,32 @@ "node": ">=20.18.1" } }, + "node_modules/chevrotain": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.1.2.tgz", + "integrity": "sha512-opLQzEVriiH1uUQ4Kctsd49bRoFDXGGSC4GUqj7pGyxM3RehRhvTlZJc1FL/Flew2p5uwxa1tUDWKzI4wNM8pg==", + "dev": true, + "dependencies": { + "@chevrotain/cst-dts-gen": "11.1.2", + "@chevrotain/gast": "11.1.2", + "@chevrotain/regexp-to-ast": "11.1.2", + "@chevrotain/types": "11.1.2", + "@chevrotain/utils": "11.1.2", + "lodash-es": "4.17.23" + } + }, + "node_modules/chevrotain-allstar": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz", + "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==", + "dev": true, + "dependencies": { + "lodash-es": "^4.17.21" + }, + "peerDependencies": { + "chevrotain": "^11.0.0" + } + }, "node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", @@ -2886,6 +3764,30 @@ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" }, + "node_modules/chromium-bidi": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.11.0.tgz", + "integrity": "sha512-6CJWHkNRoyZyjV9Rwv2lYONZf1Xm0IuDyNq97nwSsxxP3wf5Bwy15K5rOvVKMtJ127jJBmxFUanSAOjgFRxgrA==", + "dev": true, + "peer": true, + "dependencies": { + "mitt": "3.0.1", + "zod": "3.23.8" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, + "node_modules/chromium-bidi/node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "dev": true, + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/ci-info": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", @@ -2901,6 +3803,18 @@ "node": ">=8" } }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "dev": true, + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, "node_modules/cli-width": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", @@ -2998,6 +3912,15 @@ "node": ">=0.8" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -3016,6 +3939,27 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/color-string": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", + "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", + "dev": true, + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-string/node_modules/color-name": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", + "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", + "dev": true, + "engines": { + "node": ">=12.20" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -3110,6 +4054,12 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true + }, "node_modules/content-disposition": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", @@ -3168,6 +4118,75 @@ "url": "https://opencollective.com/express" } }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "dev": true, + "dependencies": { + "layout-base": "^1.0.0" + } + }, + "node_modules/cosmiconfig": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.1.tgz", + "integrity": "sha512-hr4ihw+DBqcvrsEDioRO31Z17x71pUYoNe/4h6Z0wB72p7MU7/9gH8Q3s12NFhHPfYBBOV3qyfUxmr/Yn3shnQ==", + "dev": true, + "peer": true, + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/cosmiconfig/node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "peer": true + }, + "node_modules/cosmiconfig/node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "peer": true + }, + "node_modules/cosmiconfig/node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/crc-32": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", @@ -3313,12 +4332,544 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "dev": true }, + "node_modules/cytoscape": { + "version": "3.33.1", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz", + "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "dev": true, + "dependencies": { + "cose-base": "^1.0.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", + "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", + "dev": true, + "dependencies": { + "cose-base": "^2.2.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/cose-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", + "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", + "dev": true, + "dependencies": { + "layout-base": "^2.0.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/layout-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", + "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==", + "dev": true + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "dev": true, + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "dev": true, + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "dev": true, + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "dev": true, + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "dev": true, + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "dev": true, + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dev": true, + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "dev": true, + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-dsv/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "dev": true, + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "dev": true, + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "dev": true, + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dev": true, + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "dev": true, + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "dev": true, + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", + "dev": true + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "dev": true, + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "dev": true + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dev": true, + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "dev": true, + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dev": true, + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dev": true, + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dev": true, + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dev": true, + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dev": true, + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre-d3-es": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.14.tgz", + "integrity": "sha512-P4rFMVq9ESWqmOgK+dlXvOtLwYg0i7u0HBGJER0LZDJT2VHIPAMZ/riPxqJceWMStH5+E61QxFra9kIS3AqdMg==", + "dev": true, + "dependencies": { + "d3": "^7.9.0", + "lodash-es": "^4.17.21" + } + }, "node_modules/data-uri-to-buffer": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", @@ -3328,6 +4879,12 @@ "node": ">= 14" } }, + "node_modules/dayjs": { + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "dev": true + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -3423,6 +4980,15 @@ "node": ">= 14" } }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "dev": true, + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -3447,6 +5013,19 @@ "node": ">=8" } }, + "node_modules/devtools-protocol": { + "version": "0.0.1367902", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1367902.tgz", + "integrity": "sha512-XxtPuC3PGakY6PD7dG66/o8KwJ/LkH2/EKe19Dcw58w53dv4/vSQEkn/SzuyhHE2q4zPgCkxQBxus3VV4ql+Pg==", + "dev": true, + "peer": true + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, "node_modules/diff": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", @@ -3456,6 +5035,12 @@ "node": ">=0.3.1" } }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", @@ -3701,6 +5286,16 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/error-ex": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", @@ -4164,6 +5759,31 @@ "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", "dev": true }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-redact": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/fast-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", @@ -4210,6 +5830,15 @@ "fxparser": "src/cli/cli.js" } }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -4632,6 +6261,12 @@ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "dev": true }, + "node_modules/hachure-fill": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", + "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==", + "dev": true + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -4686,6 +6321,15 @@ "he": "bin/he" } }, + "node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "dev": true, + "engines": { + "node": "*" + } + }, "node_modules/hono": { "version": "4.11.9", "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.9.tgz", @@ -4706,6 +6350,12 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/html-to-image": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.13.tgz", + "integrity": "sha512-cuOPoI7WApyhBElTTb9oqsawRvZ0rHhaHwghRLlTuffoD1B2aDemlCruLeZrUIIdvG7gs9xeELEPm6PhuASqrg==", + "dev": true + }, "node_modules/htmlfy": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/htmlfy/-/htmlfy-0.8.1.tgz", @@ -4837,6 +6487,33 @@ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", "dev": true }, + "node_modules/immer": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz", + "integrity": "sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "peer": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/import-meta-resolve": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", @@ -4894,6 +6571,15 @@ } } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "dev": true, + "engines": { + "node": ">=12" + } + }, "node_modules/ip-address": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", @@ -4928,6 +6614,21 @@ "node": ">=8" } }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -5330,6 +7031,35 @@ "url": "https://github.com/sponsors/panva" } }, + "node_modules/jotai": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/jotai/-/jotai-2.18.0.tgz", + "integrity": "sha512-XI38kGWAvtxAZ+cwHcTgJsd+kJOJGf3OfL4XYaXWZMZ7IIY8e53abpIHvtVn1eAgJ5dlgwlGFnP4psrZ/vZbtA==", + "dev": true, + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0", + "@babel/template": ">=7.0.0", + "@types/react": ">=17.0.0", + "react": ">=17.0.0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@babel/template": { + "optional": true + }, + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5403,6 +7133,60 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/katex": { + "version": "0.16.38", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.38.tgz", + "integrity": "sha512-cjHooZUmIAUmDsHBN+1n8LaZdpmbj03LtYeYPyuYB7OuloiaeaV6N4LcfjcnHVzGWjVQmKrxxTrpDcmSzEZQwQ==", + "dev": true, + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==", + "dev": true + }, + "node_modules/langium": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/langium/-/langium-4.2.1.tgz", + "integrity": "sha512-zu9QWmjpzJcomzdJQAHgDVhLGq5bLosVak1KVa40NzQHXfqr4eAHupvnPOVXEoLkg6Ocefvf/93d//SB7du4YQ==", + "dev": true, + "dependencies": { + "chevrotain": "~11.1.1", + "chevrotain-allstar": "~0.3.1", + "vscode-languageserver": "~9.0.1", + "vscode-languageserver-textdocument": "~1.0.11", + "vscode-uri": "~3.1.0" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==", + "dev": true + }, "node_modules/lazystream": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", @@ -5454,6 +7238,18 @@ "immediate": "~3.0.5" } }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, "node_modules/lines-and-columns": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.4.tgz", @@ -5517,6 +7313,12 @@ "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", "dev": true }, + "node_modules/lodash-es": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz", + "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==", + "dev": true + }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -5663,6 +7465,18 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -5690,6 +7504,68 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mermaid": { + "version": "11.13.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.13.0.tgz", + "integrity": "sha512-fEnci+Immw6lKMFI8sqzjlATTyjLkRa6axrEgLV2yHTfv8r+h1wjFbV6xeRtd4rUV1cS4EpR9rwp3Rci7TRWDw==", + "dev": true, + "dependencies": { + "@braintree/sanitize-url": "^7.1.1", + "@iconify/utils": "^3.0.2", + "@mermaid-js/parser": "^1.0.1", + "@types/d3": "^7.4.3", + "@upsetjs/venn.js": "^2.0.0", + "cytoscape": "^3.33.1", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.2.0", + "d3": "^7.9.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.14", + "dayjs": "^1.11.19", + "dompurify": "^3.3.1", + "katex": "^0.16.25", + "khroma": "^2.1.0", + "lodash-es": "^4.17.23", + "marked": "^16.3.0", + "roughjs": "^4.6.6", + "stylis": "^4.3.6", + "ts-dedent": "^2.2.0", + "uuid": "^11.1.0" + } + }, + "node_modules/mermaid/node_modules/dompurify": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.2.tgz", + "integrity": "sha512-6obghkliLdmKa56xdbLOpUZ43pAR6xFy1uOrxBaIDjT+yaRuuybLjGS9eVBoSR/UPU5fq3OXClEHLJNGvbxKpQ==", + "dev": true, + "engines": { + "node": ">=20" + }, + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/mermaid/node_modules/marked": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz", + "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -5792,6 +7668,24 @@ "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, + "node_modules/mlly": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.1.tgz", + "integrity": "sha512-SnL6sNutTwRWWR/vcmCYHSADjiEesp5TGQQ0pXyLhW5IoeibRlF/CbSLailbB3CNqJUk9cVJ9dUDnbD7GrcHBQ==", + "dev": true, + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "node_modules/mlly/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true + }, "node_modules/mocha": { "version": "10.8.2", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", @@ -6032,6 +7926,17 @@ "node": "^18.17.0 || >=20.5.0" } }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -6154,6 +8059,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -6165,6 +8079,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", @@ -6257,12 +8180,31 @@ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true }, + "node_modules/package-manager-detector": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.6.0.tgz", + "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==", + "dev": true + }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "dev": true }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "peer": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/parse-json": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-7.1.1.tgz", @@ -6363,6 +8305,12 @@ "node": ">= 0.8" } }, + "node_modules/path-data-parser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", + "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==", + "dev": true + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -6380,6 +8328,12 @@ "node": ">=8" } }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, "node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", @@ -6435,6 +8389,102 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pino": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.21.0.tgz", + "integrity": "sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==", + "dev": true, + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^1.2.0", + "pino-std-serializers": "^6.0.0", + "process-warning": "^3.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^3.7.0", + "thread-stream": "^2.6.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", + "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", + "dev": true, + "dependencies": { + "readable-stream": "^4.0.0", + "split2": "^4.0.0" + } + }, + "node_modules/pino-abstract-transport/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/pino-abstract-transport/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", + "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==", + "dev": true + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/pkce-challenge": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", @@ -6443,6 +8493,39 @@ "node": ">=16.20.0" } }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/pkg-types/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true + }, + "node_modules/points-on-curve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", + "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==", + "dev": true + }, + "node_modules/points-on-path": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", + "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", + "dev": true, + "dependencies": { + "path-data-parser": "0.1.0", + "points-on-curve": "0.2.0" + } + }, "node_modules/postcss": { "version": "8.5.6", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", @@ -6471,6 +8554,134 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, "node_modules/prebuild-install": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", @@ -6555,6 +8766,12 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, + "node_modules/process-warning": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==", + "dev": true + }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -6693,6 +8910,179 @@ "once": "^1.3.1" } }, + "node_modules/puppeteer": { + "version": "23.11.1", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-23.11.1.tgz", + "integrity": "sha512-53uIX3KR5en8l7Vd8n5DUv90Ae9QDQsyIthaUFVzwV6yU750RjqRznEtNMBT20VthqAdemnJN+hxVdmMHKt7Zw==", + "deprecated": "< 24.15.0 is no longer supported", + "dev": true, + "hasInstallScript": true, + "peer": true, + "dependencies": { + "@puppeteer/browsers": "2.6.1", + "chromium-bidi": "0.11.0", + "cosmiconfig": "^9.0.0", + "devtools-protocol": "0.0.1367902", + "puppeteer-core": "23.11.1", + "typed-query-selector": "^2.12.0" + }, + "bin": { + "puppeteer": "lib/cjs/puppeteer/node/cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/puppeteer-core": { + "version": "23.11.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.11.1.tgz", + "integrity": "sha512-3HZ2/7hdDKZvZQ7dhhITOUg4/wOrDRjyK2ZBllRB0ZCOi9u0cwq1ACHDjBB+nX+7+kltHjQvBRdeY7+W0T+7Gg==", + "dev": true, + "peer": true, + "dependencies": { + "@puppeteer/browsers": "2.6.1", + "chromium-bidi": "0.11.0", + "debug": "^4.4.0", + "devtools-protocol": "0.0.1367902", + "typed-query-selector": "^2.12.0", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/puppeteer-core/node_modules/@puppeteer/browsers": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.6.1.tgz", + "integrity": "sha512-aBSREisdsGH890S2rQqK82qmQYU3uFpSH8wcZWHgHzl3LfzsxAKbLNiAG9mO8v1Y0UICBeClICxPJvyr0rcuxg==", + "dev": true, + "peer": true, + "dependencies": { + "debug": "^4.4.0", + "extract-zip": "^2.0.1", + "progress": "^2.0.3", + "proxy-agent": "^6.5.0", + "semver": "^7.6.3", + "tar-fs": "^3.0.6", + "unbzip2-stream": "^1.4.3", + "yargs": "^17.7.2" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/puppeteer-core/node_modules/b4a": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.0.tgz", + "integrity": "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==", + "dev": true, + "peer": true, + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/puppeteer-core/node_modules/tar-fs": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.2.tgz", + "integrity": "sha512-QGxxTxxyleAdyM3kpFs14ymbYmNFrfY+pHj7Z8FgtbZ7w2//VAgLMac7sT6nRpIHjppXO2AwwEOg0bPFVRcmXw==", + "dev": true, + "peer": true, + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" + } + }, + "node_modules/puppeteer-core/node_modules/tar-stream": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.8.tgz", + "integrity": "sha512-U6QpVRyCGHva435KoNWy9PRoi2IFYCgtEhq9nmrPPpbRacPs9IH4aJ3gbrFC8dPcXvdSZ4XXfXT5Fshbp2MtlQ==", + "dev": true, + "peer": true, + "dependencies": { + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/puppeteer/node_modules/@puppeteer/browsers": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.6.1.tgz", + "integrity": "sha512-aBSREisdsGH890S2rQqK82qmQYU3uFpSH8wcZWHgHzl3LfzsxAKbLNiAG9mO8v1Y0UICBeClICxPJvyr0rcuxg==", + "dev": true, + "peer": true, + "dependencies": { + "debug": "^4.4.0", + "extract-zip": "^2.0.1", + "progress": "^2.0.3", + "proxy-agent": "^6.5.0", + "semver": "^7.6.3", + "tar-fs": "^3.0.6", + "unbzip2-stream": "^1.4.3", + "yargs": "^17.7.2" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/puppeteer/node_modules/b4a": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.0.tgz", + "integrity": "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==", + "dev": true, + "peer": true, + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/puppeteer/node_modules/tar-fs": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.2.tgz", + "integrity": "sha512-QGxxTxxyleAdyM3kpFs14ymbYmNFrfY+pHj7Z8FgtbZ7w2//VAgLMac7sT6nRpIHjppXO2AwwEOg0bPFVRcmXw==", + "dev": true, + "peer": true, + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" + } + }, + "node_modules/puppeteer/node_modules/tar-stream": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.8.tgz", + "integrity": "sha512-U6QpVRyCGHva435KoNWy9PRoi2IFYCgtEhq9nmrPPpbRacPs9IH4aJ3gbrFC8dPcXvdSZ4XXfXT5Fshbp2MtlQ==", + "dev": true, + "peer": true, + "dependencies": { + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, "node_modules/qs": { "version": "6.15.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", @@ -6713,6 +9103,51 @@ "integrity": "sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==", "dev": true }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "dev": true + }, + "node_modules/radash": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/radash/-/radash-12.1.1.tgz", + "integrity": "sha512-h36JMxKRqrAxVD8201FrCpyeNuUY9Y5zZwujr20fFO77tpUtGa6EZzfKw/3WaiBX95fq7+MpsuMLNdSnORAwSA==", + "dev": true, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/ramda": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.28.0.tgz", + "integrity": "sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ramda" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -6795,6 +9230,15 @@ "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, "node_modules/read-pkg": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-8.1.0.tgz", @@ -6985,6 +9429,15 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "dev": true, + "engines": { + "node": ">= 12.13.0" + } + }, "node_modules/recursive-readdir": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", @@ -7036,6 +9489,36 @@ "node": ">=0.10.0" } }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, "node_modules/resolve-pkg-maps": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", @@ -7069,12 +9552,28 @@ "node": ">=10" } }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rgb2hex": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/rgb2hex/-/rgb2hex-0.2.5.tgz", "integrity": "sha512-22MOP1Rh7sAo1BZpDG6R5RFYzR2lYEgwq7HEmyW2qcsOqR2lQKmn+O//xV3YG/0rrhMC6KVX2hU+ZXuaw9a5bw==", "dev": true }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", + "dev": true + }, "node_modules/rollup": { "version": "4.59.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", @@ -7124,6 +9623,18 @@ "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz", "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==" }, + "node_modules/roughjs": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", + "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==", + "dev": true, + "dependencies": { + "hachure-fill": "^0.5.2", + "path-data-parser": "^0.1.0", + "points-on-curve": "^0.2.0", + "points-on-path": "^0.2.1" + } + }, "node_modules/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", @@ -7148,6 +9659,35 @@ "node": ">=0.12.0" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "dev": true + }, "node_modules/rxjs": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", @@ -7204,6 +9744,15 @@ "ret": "~0.5.0" } }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -7495,6 +10044,15 @@ "node": ">= 14" } }, + "node_modules/sonic-boom": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.1.tgz", + "integrity": "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==", + "dev": true, + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -7765,6 +10323,49 @@ } ] }, + "node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "dev": true + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/sucrase/node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, "node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -7780,6 +10381,152 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tabbable": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", + "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", + "dev": true + }, + "node_modules/tailwind-merge": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.5.0.tgz", + "integrity": "sha512-I8K9wewnVDkL1NTGoqWmVEIlUcB9gFriAEkXkfCjX5ib8ezGxtR3xD7iZIxrfArjEsH7F1CHD4RFUtxefdqV/A==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "dev": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/tailwindcss/node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tailwindcss/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/tailwindcss/node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/tailwindcss/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tailwindcss/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, "node_modules/tar-fs": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", @@ -7838,6 +10585,52 @@ } } }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/thread-stream": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz", + "integrity": "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==", + "dev": true, + "dependencies": { + "real-require": "^0.2.0" + } + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "peer": true + }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "engines": { + "node": ">=18" + } + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -7883,6 +10676,21 @@ "node": ">=0.6" } }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "dev": true, + "engines": { + "node": ">=6.10" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -8401,6 +11209,13 @@ "node": ">= 0.6" } }, + "node_modules/typed-query-selector": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.1.tgz", + "integrity": "sha512-uzR+FzI8qrUEIu96oaeBJmd9E7CFEiQ3goA5qCVgc4s5llSubcfGHq9yUstZx/k4s9dXHVKsE35YWoFyvEqEHA==", + "dev": true, + "peer": true + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -8414,6 +11229,23 @@ "node": ">=14.17" } }, + "node_modules/ufo": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", + "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "dev": true + }, + "node_modules/unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "dev": true, + "peer": true, + "dependencies": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, "node_modules/undici": { "version": "6.23.0", "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz", @@ -8455,6 +11287,15 @@ "integrity": "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==", "dev": true }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "dev": true, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/userhome": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/userhome/-/userhome-1.0.1.tgz", @@ -8469,6 +11310,19 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -8561,6 +11415,55 @@ } } }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "dev": true, + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "dev": true, + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "dev": true + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "dev": true + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true + }, "node_modules/w3c-keyname": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", diff --git a/package.json b/package.json index e003f29..b54fa2d 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "@tauri-apps/api": "^2.10.1", "@tauri-apps/cli": "^2.10.0", "@tauri-apps/plugin-autostart": "^2.5.1", + "@tauri-apps/plugin-dialog": "^2.6.0", "@toast-ui/editor": "^3.2.2", "axios": "^1.13.5", "better-sqlite3": "^12.6.2", diff --git a/src/backend/Cargo.lock b/src/backend/Cargo.lock index b7c2e54..6b3b4e3 100644 --- a/src/backend/Cargo.lock +++ b/src/backend/Cargo.lock @@ -204,6 +204,7 @@ "tauri", "tauri-build", "tauri-plugin-autostart", + "tauri-plugin-dialog", "tauri-plugin-log", "tauri-plugin-shell", "tempfile", @@ -1199,6 +1200,8 @@ checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ "bitflags 2.11.0", + "block2", + "libc", "objc2", ] @@ -4713,6 +4716,30 @@ ] [[package]] +name = "rfd" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15ad77d9e70a92437d8f74c35d99b4e4691128df018833e99f90bcd36152672" +dependencies = [ + "block2", + "dispatch2", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "log", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "raw-window-handle", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.60.2", +] + +[[package]] name = "ring" version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -6187,6 +6214,46 @@ ] [[package]] +name = "tauri-plugin-dialog" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9204b425d9be8d12aa60c2a83a289cf7d1caae40f57f336ed1155b3a5c0e359b" +dependencies = [ + "log", + "raw-window-handle", + "rfd", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.18", + "url", +] + +[[package]] +name = "tauri-plugin-fs" +version = "2.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed390cc669f937afeb8b28032ce837bac8ea023d975a2e207375ec05afaf1804" +dependencies = [ + "anyhow", + "dunce", + "glob", + "percent-encoding", + "schemars 0.8.22", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "tauri-utils", + "thiserror 2.0.18", + "toml 0.9.12+spec-1.1.0", + "url", +] + +[[package]] name = "tauri-plugin-log" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/src/backend/Cargo.toml b/src/backend/Cargo.toml index 8125de7..556d8f1 100644 --- a/src/backend/Cargo.toml +++ b/src/backend/Cargo.toml @@ -47,7 +47,8 @@ env_logger = "0.11" chrono = { version = "0.4", features = ["serde"] } tauri-plugin-shell = "2.0.0" -tauri-plugin-autostart = "2.0.0" +tauri-plugin-autostart = "2.5.1" +tauri-plugin-dialog = "2.6.0" reqwest = { version = "0.12", features = ["json"] } dirs = "6.0" # Japanese NLP & LSA diff --git a/src/backend/capabilities/default.json b/src/backend/capabilities/default.json index 1734009..4c48b51 100644 --- a/src/backend/capabilities/default.json +++ b/src/backend/capabilities/default.json @@ -21,6 +21,7 @@ }, "autostart:allow-enable", "autostart:allow-disable", - "autostart:allow-is-enabled" + "autostart:allow-is-enabled", + "dialog:allow-open" ] } \ No newline at end of file diff --git a/src/backend/src/db/migration.rs b/src/backend/src/db/migration.rs index f69fb86..3cf3f7b 100644 --- a/src/backend/src/db/migration.rs +++ b/src/backend/src/db/migration.rs @@ -7,6 +7,8 @@ migrate_025_to_030(pool).await?; // path 正規化(\ → /、末尾 / 除去)。照合を一貫させるため。 migrate_normalize_document_paths(pool).await?; + // documents に category カラムを追加(モニター先フォルダのカテゴリ分類用) + migrate_add_documents_category(pool).await?; Ok(()) } @@ -125,6 +127,27 @@ Ok(()) } +/// documents に category カラムを追加(既存DBへの後方互換マイグレーション) +async fn migrate_add_documents_category(pool: &SqlitePool) -> Result<(), String> { + let cols = sqlx::query("PRAGMA table_info(documents)") + .fetch_all(pool) + .await + .map_err(|e| e.to_string())?; + let has_category = cols.iter().any(|r| { + let name: String = r.get(1); + name == "category" + }); + if has_category { + return Ok(()); + } + sqlx::query("ALTER TABLE documents ADD COLUMN category TEXT DEFAULT ''") + .execute(pool) + .await + .map_err(|e| e.to_string())?; + log::info!("Migration: added 'category' column to documents table."); + Ok(()) +} + /// documents.path を正規化(\ → /、末尾 / 除去)。既存DBの path を吸収用の一形式に揃える。 async fn migrate_normalize_document_paths(pool: &SqlitePool) -> Result<(), String> { use std::collections::HashMap; diff --git a/src/backend/src/db/mod.rs b/src/backend/src/db/mod.rs index 39fc374..c986204 100644 --- a/src/backend/src/db/mod.rs +++ b/src/backend/src/db/mod.rs @@ -183,13 +183,13 @@ .map_err(|e| e.to_string()) } -/// 指定 id の item の content, path, mime, document_id を取得。検索結果の行構築で共通利用。 +/// 指定 id の item の content, path, mime, document_id, category を取得。検索結果の行構築で共通利用。 pub async fn get_item_content_with_doc( pool: &SqlitePool, item_id: i64, -) -> Result, i64)>, String> { +) -> Result, i64, String)>, String> { let row = sqlx::query( - "SELECT i.content, d.path, d.mime, i.document_id FROM items i JOIN documents d ON i.document_id = d.id WHERE i.id = ?", + "SELECT i.content, d.path, d.mime, i.document_id, COALESCE(d.category, '') FROM items i JOIN documents d ON i.document_id = d.id WHERE i.id = ?", ) .bind(item_id) .fetch_optional(pool) @@ -201,6 +201,7 @@ r.get::(1), r.get::, _>(2), r.get::(3), + r.get::(4), ) })) } @@ -225,7 +226,7 @@ /// 指定ディレクトリ配下の document の id 一覧を返す(path が prefix と一致するか、prefix の直下・サブディレクトリにあるもの)。 /// path は normalize_document_path で正規化された形式(/ 区切り)で格納・照合する。 -fn escape_like(s: &str) -> String { +pub fn escape_like(s: &str) -> String { s.replace('\\', "\\\\").replace('%', "\\%").replace('_', "\\_") } diff --git a/src/backend/src/lib.rs b/src/backend/src/lib.rs index e893be6..7dab6f3 100644 --- a/src/backend/src/lib.rs +++ b/src/backend/src/lib.rs @@ -129,8 +129,9 @@ .plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_autostart::init( tauri_plugin_autostart::MacosLauncher::LaunchAgent, - None, + Some(vec!["--minimized".into()]), )) + .plugin(tauri_plugin_dialog::init()) .plugin({ let log_dir = if cfg!(debug_assertions) { std::env::current_dir().unwrap().join("logs") @@ -397,11 +398,16 @@ } }); - if std::env::var_os("TELOS_HEADLESS").map(|v| v == "1").unwrap_or(false) { + let minimized = std::env::args().any(|a| a == "--minimized"); + let headless = std::env::var_os("TELOS_HEADLESS").map(|v| v == "1").unwrap_or(false); + if !minimized && !headless { if let Some(w) = app.get_webview_window("main") { - let _ = w.hide(); - log::info!("[BOOT] Headless mode: main window hidden."); + let _ = w.show(); + let _ = w.set_focus(); + log::info!("[BOOT] Normal mode: main window shown."); } + } else { + log::info!("[BOOT] Minimized/headless mode: window stays hidden (minimized={}).", minimized); } log::info!("[BOOT] Setup complete ({}ms). Window ready; MCP and LSA/HNSW continue in background.", boot_start.elapsed().as_millis()); Ok(()) diff --git a/src/backend/src/mcp/handlers.rs b/src/backend/src/mcp/handlers.rs index 77d753e..4167636 100644 --- a/src/backend/src/mcp/handlers.rs +++ b/src/backend/src/mcp/handlers.rs @@ -150,14 +150,10 @@ // フォルダ監視: 保存した monitor_paths と watch_extensions をワッチャーに送り再起動(Phase 3) if let Some(ref tx) = state.watcher_restart_tx { - let monitor_paths: Vec = to_write + let (monitor_paths, category_map) = to_write .get("monitor_paths") .and_then(|a| a.as_array()) - .map(|a| { - a.iter() - .filter_map(|v| v.as_str().map(std::path::PathBuf::from)) - .collect() - }) + .map(|a| crate::mcp::types::parse_monitor_paths(a)) .unwrap_or_default(); let watch_extensions: Vec = to_write .get("watch_extensions") @@ -172,12 +168,45 @@ let config = crate::mcp::types::WatcherConfig { paths: monitor_paths.clone(), extensions: watch_extensions, + category_map: category_map.clone(), }; if tx.send(config).is_err() { log::warn!("settings_post: watcher channel closed"); } else { log::info!("settings_post: sent watcher config ({} paths) to watcher", monitor_paths.len()); } + + // カテゴリマップに基づき既存ドキュメントのカテゴリを一括更新 + for (dir, cat) in &category_map { + let prefix = crate::db::normalize_document_path(&dir.to_string_lossy()); + if prefix.is_empty() { continue; } + let like_pat = format!("{}/%", crate::db::escape_like(&prefix)); + match sqlx::query("UPDATE documents SET category = ? WHERE (path = ? OR path LIKE ? ESCAPE '\\') AND COALESCE(category, '') != ?") + .bind(cat) + .bind(&prefix) + .bind(&like_pat) + .bind(cat) + .execute(&state.db_pool) + .await + { + Ok(r) if r.rows_affected() > 0 => log::info!("settings_post: updated category '{}' for {} docs under {}", cat, r.rows_affected(), prefix), + Ok(_) => {} + Err(e) => log::warn!("settings_post: category update for {}: {}", prefix, e), + } + } + // カテゴリなしのパス → 空文字にリセット + for path in &monitor_paths { + if !category_map.contains_key(path) { + let prefix = crate::db::normalize_document_path(&path.to_string_lossy()); + if prefix.is_empty() { continue; } + let like_pat = format!("{}/%", crate::db::escape_like(&prefix)); + let _ = sqlx::query("UPDATE documents SET category = '' WHERE (path = ? OR path LIKE ? ESCAPE '\\') AND COALESCE(category, '') != ''") + .bind(&prefix) + .bind(&like_pat) + .execute(&state.db_pool) + .await; + } + } } // モニター解除時に「インデックスからも削除」を選んだパスを処理 diff --git a/src/backend/src/mcp/mod.rs b/src/backend/src/mcp/mod.rs index a5d086d..1356465 100644 --- a/src/backend/src/mcp/mod.rs +++ b/src/backend/src/mcp/mod.rs @@ -119,17 +119,13 @@ let watcher_config: crate::mcp::types::WatcherConfig = { let path = app_data_dir.join("settings.json"); - let (monitor_paths, extensions) = if path.exists() { + let (monitor_paths, extensions, category_map) = if path.exists() { if let Ok(s) = std::fs::read_to_string(&path) { if let Ok(v) = serde_json::from_str::(&s) { - let paths: Vec = v + let (paths, cat_map) = v .get("monitor_paths") .and_then(|a| a.as_array()) - .map(|a| { - a.iter() - .filter_map(|v| v.as_str().map(std::path::PathBuf::from)) - .collect() - }) + .map(|a| crate::mcp::types::parse_monitor_paths(a)) .unwrap_or_default(); let exts: Vec = v .get("watch_extensions") @@ -141,17 +137,17 @@ .map(|s| s.to_string()) .collect() }); - (paths, exts) + (paths, exts, cat_map) } else { - (vec![], watch::DEFAULT_WATCH_EXTENSIONS.iter().map(|s| s.to_string()).collect()) + (vec![], watch::DEFAULT_WATCH_EXTENSIONS.iter().map(|s| s.to_string()).collect(), Default::default()) } } else { - (vec![], watch::DEFAULT_WATCH_EXTENSIONS.iter().map(|s| s.to_string()).collect()) + (vec![], watch::DEFAULT_WATCH_EXTENSIONS.iter().map(|s| s.to_string()).collect(), Default::default()) } } else { - (vec![], watch::DEFAULT_WATCH_EXTENSIONS.iter().map(|s| s.to_string()).collect()) + (vec![], watch::DEFAULT_WATCH_EXTENSIONS.iter().map(|s| s.to_string()).collect(), Default::default()) }; - crate::mcp::types::WatcherConfig { paths: monitor_paths, extensions } + crate::mcp::types::WatcherConfig { paths: monitor_paths, extensions, category_map } }; if let Some(ref tx) = state.watcher_restart_tx { let _ = tx.send(watcher_config); diff --git a/src/backend/src/mcp/tools/items.rs b/src/backend/src/mcp/tools/items.rs index 834d8c1..9b4a85a 100644 --- a/src/backend/src/mcp/tools/items.rs +++ b/src/backend/src/mcp/tools/items.rs @@ -10,11 +10,12 @@ ) -> Option { let id = args.get("id").and_then(|v| v.as_i64()).unwrap_or(0); match crate::db::get_item_content_with_doc(&state.db_pool, id).await { - Ok(Some((content, path, mime, _document_id))) => Some(serde_json::json!({ + Ok(Some((content, path, mime, _document_id, category))) => Some(serde_json::json!({ "id": id, "content": content, "path": path, - "mime": mime + "mime": mime, + "category": category })), _ => Some(serde_json::json!({ "content": [{ "type": "text", "text": format!("Item not found: {}", id) }], @@ -31,6 +32,7 @@ let path_str = crate::db::normalize_document_path( args.get("path").and_then(|v| v.as_str()).unwrap_or("unknown"), ); + let category = args.get("category").and_then(|v| v.as_str()).unwrap_or(""); let mut mime_str = args.get("mime").and_then(|v| v.as_str()).map(|s| s.to_string()); // MIMEタイプが未指定なら拡張子から推測 @@ -78,12 +80,20 @@ .execute(&state.db_pool) .await; } + if !category.is_empty() { + let _ = sqlx::query("UPDATE documents SET category = ? WHERE id = ?") + .bind(category) + .bind(id) + .execute(&state.db_pool) + .await; + } Ok(id) }, Ok(None) => { - match sqlx::query("INSERT INTO documents (path, mime) VALUES (?, ?)") + match sqlx::query("INSERT INTO documents (path, mime, category) VALUES (?, ?, ?)") .bind(&path_str) .bind(mime_str) + .bind(category) .execute(&state.db_pool) .await { @@ -418,7 +428,8 @@ let rows = match sqlx::query( "SELECT d.id, d.path, d.mime, d.updated_at, (SELECT COUNT(*) FROM items i WHERE i.document_id = d.id) AS chunk_count, - (SELECT substr(i.content, 1, 15) FROM items i WHERE i.document_id = d.id ORDER BY i.chunk_index ASC LIMIT 1) AS chunk0_preview + (SELECT substr(i.content, 1, 15) FROM items i WHERE i.document_id = d.id ORDER BY i.chunk_index ASC LIMIT 1) AS chunk0_preview, + COALESCE(d.category, '') AS category FROM documents d ORDER BY d.path" ) .fetch_all(&state.db_pool) @@ -440,13 +451,15 @@ let updated_at: Option = row.get(3); let chunk_count: i64 = row.get(4); let chunk0_preview: Option = row.get(5); + let category: String = row.get(6); serde_json::json!({ "id": id, "path": path, "mime": mime, "updated_at": updated_at, "chunk_count": chunk_count, - "chunk0_preview": chunk0_preview + "chunk0_preview": chunk0_preview, + "category": category }) }).collect(); @@ -591,12 +604,15 @@ // --------------------------------------------------------------------------- /// 指定パスのファイルを UTF-8 テキストとして読み、add_item_text と同様に DB に取り込む。 -pub async fn ingest_file_path(state: &AppState, path: &Path) -> Result<(), String> { +pub async fn ingest_file_path(state: &AppState, path: &Path, category: &str) -> Result<(), String> { let path_str = path.to_string_lossy().to_string(); let content = std::fs::read_to_string(path).map_err(|e| format!("read file: {}", e))?; let mut args = serde_json::Map::new(); args.insert("content".to_string(), serde_json::Value::String(content)); args.insert("path".to_string(), serde_json::Value::String(path_str.clone())); + if !category.is_empty() { + args.insert("category".to_string(), serde_json::Value::String(category.to_string())); + } match handle_add_item_text(state, &args).await { Some(v) if v.get("isError").and_then(|x| x.as_bool()).unwrap_or(false) => { let msg = v diff --git a/src/backend/src/mcp/tools/registry.rs b/src/backend/src/mcp/tools/registry.rs index 79961be..911e530 100644 --- a/src/backend/src/mcp/tools/registry.rs +++ b/src/backend/src/mcp/tools/registry.rs @@ -14,13 +14,14 @@ }), serde_json::json!({ "name": "add_item_text", - "description": "Add or overweight a document path. Chunks are generated automatically.", + "description": "Add or overwrite a document by path. Chunks are generated automatically.", "inputSchema": { "type": "object", "properties": { "content": { "type": "string" }, "path": { "type": "string" }, - "mime": { "type": "string" } + "mime": { "type": "string" }, + "category": { "type": "string", "description": "Optional. Category label for the document." } }, "required": ["content", "path"] } @@ -33,7 +34,8 @@ "properties": { "content": { "type": "string" }, "limit": { "type": "integer", "default": 5 }, - "min_score": { "type": "number", "default": 0.3, "description": "Minimum similarity (0-1). Results below this are dropped. Default 0.3." } + "min_score": { "type": "number", "default": 0.3, "description": "Minimum similarity (0-1). Results below this are dropped. Default 0.3." }, + "category": { "type": "string", "description": "Optional. Filter results to a specific category." } }, "required": ["content"] } diff --git a/src/backend/src/mcp/tools/search.rs b/src/backend/src/mcp/tools/search.rs index 483936d..2d4e857 100644 --- a/src/backend/src/mcp/tools/search.rs +++ b/src/backend/src/mcp/tools/search.rs @@ -60,6 +60,10 @@ .and_then(|v| v.as_f64()) .unwrap_or(0.3) .clamp(0.0, 1.0) as f32; + let filter_category = args.get("category") + .and_then(|v| v.as_str()) + .unwrap_or("") + .to_string(); if search_content.is_empty() { log::info!("[search] query=empty -> skipped"); @@ -173,15 +177,19 @@ for (id, v_sim) in vector_hits { let f_sim = fts_results.get(&id).cloned().unwrap_or(0.0); let final_sim = v_sim.max(f_sim); - if let Ok(Some((content, path, mime, document_id))) = + if let Ok(Some((content, path, mime, document_id, category))) = crate::db::get_item_content_with_doc(&state.db_pool, id).await { + if !filter_category.is_empty() && category != filter_category { + continue; + } final_results.insert(id, serde_json::json!({ "id": id, "document_id": document_id, "content": content, "path": path, "mime": mime, + "category": category, "similarity": final_sim.clamp(0.0, 1.0) })); } @@ -191,15 +199,19 @@ // 3. Add FTS results (and for Community, remaining FTS not in vector results) for (id, f_sim) in fts_results { if !final_results.contains_key(&id) { - if let Ok(Some((content, path, mime, document_id))) = + if let Ok(Some((content, path, mime, document_id, category))) = crate::db::get_item_content_with_doc(&state.db_pool, id).await { + if !filter_category.is_empty() && category != filter_category { + continue; + } final_results.insert(id, serde_json::json!({ "id": id, "document_id": document_id, "content": content, "path": path, "mime": mime, + "category": category, "similarity": f_sim.clamp(0.0, 1.0) })); } @@ -222,24 +234,25 @@ let final_items: Vec = if group_by_document && !filtered.is_empty() { // 文書単位にまとめる: document_id ごとにチャンクを結合 use std::collections::BTreeMap; - let mut by_doc: BTreeMap, Option)> = BTreeMap::new(); + let mut by_doc: BTreeMap, Option, String)> = BTreeMap::new(); for it in &filtered { let doc_id = it.get("document_id").and_then(|v| v.as_i64()).unwrap_or(0); let sim = it.get("similarity").and_then(|v| v.as_f64()).unwrap_or(0.0); let path = it.get("path").and_then(|v| v.as_str()).map(String::from); let mime = it.get("mime").and_then(|v| v.as_str()).map(String::from); - let entry = by_doc.entry(doc_id).or_insert((0.0f64, path, mime)); + let cat = it.get("category").and_then(|v| v.as_str()).unwrap_or("").to_string(); + let entry = by_doc.entry(doc_id).or_insert((0.0f64, path, mime, cat)); if sim > entry.0 { entry.0 = sim; } } - let mut doc_list: Vec<(i64, f64, Option, Option)> = by_doc + let mut doc_list: Vec<(i64, f64, Option, Option, String)> = by_doc .into_iter() - .map(|(doc_id, (sim, path, mime))| (doc_id, sim, path, mime)) + .map(|(doc_id, (sim, path, mime, cat))| (doc_id, sim, path, mime, cat)) .collect(); doc_list.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); let mut out = Vec::with_capacity(doc_list.len().min(search_limit as usize)); - for (doc_id, best_sim, path, mime) in doc_list.into_iter().take(search_limit as usize) { + for (doc_id, best_sim, path, mime, category) in doc_list.into_iter().take(search_limit as usize) { let chunks: Vec = sqlx::query_scalar::<_, String>( "SELECT content FROM items WHERE document_id = ? ORDER BY chunk_index" ) @@ -253,6 +266,7 @@ "document_id": doc_id, "path": path, "mime": mime, + "category": category, "content": content, "similarity": best_sim })); diff --git a/src/backend/src/mcp/types.rs b/src/backend/src/mcp/types.rs index ed097bf..053a8c9 100644 --- a/src/backend/src/mcp/types.rs +++ b/src/backend/src/mcp/types.rs @@ -14,11 +14,34 @@ pub struct WatcherConfig { pub paths: Vec, pub extensions: Vec, + /// モニター先パス → カテゴリ名のマッピング + pub category_map: std::collections::HashMap, } /// 設定保存時にワッチャーを再起動する用(Phase 3) pub type WatcherRestartSender = std::sync::mpsc::Sender; +/// monitor_paths JSON 配列をパースする。オブジェクト配列 `[{path, category}]` と旧形式の文字列配列 `["path"]` の両方に対応。 +pub fn parse_monitor_paths(arr: &[serde_json::Value]) -> (Vec, std::collections::HashMap) { + let mut paths = Vec::new(); + let mut category_map = std::collections::HashMap::new(); + for v in arr { + if let Some(obj) = v.as_object() { + if let Some(p) = obj.get("path").and_then(|x| x.as_str()) { + let pb = PathBuf::from(p); + let cat = obj.get("category").and_then(|x| x.as_str()).unwrap_or("").to_string(); + if !cat.is_empty() { + category_map.insert(pb.clone(), cat); + } + paths.push(pb); + } + } else if let Some(s) = v.as_str() { + paths.push(PathBuf::from(s)); + } + } + (paths, category_map) +} + #[derive(Clone)] pub struct AppState { pub app_data_dir: PathBuf, diff --git a/src/backend/src/mcp/watch.rs b/src/backend/src/mcp/watch.rs index 4458051..664e9da 100644 --- a/src/backend/src/mcp/watch.rs +++ b/src/backend/src/mcp/watch.rs @@ -24,7 +24,17 @@ .unwrap_or(false) } -fn process_event(state: &AppState, path: &Path, handle: &Handle, extensions: &[String]) { +/// ファイルパスからカテゴリを解決する。category_map の各キー(モニター先ディレクトリ)がファイルパスの祖先であれば対応するカテゴリを返す。 +fn resolve_category(file_path: &Path, category_map: &std::collections::HashMap) -> String { + for (dir, cat) in category_map { + if file_path.starts_with(dir) { + return cat.clone(); + } + } + String::new() +} + +fn process_event(state: &AppState, path: &Path, handle: &Handle, extensions: &[String], category_map: &std::collections::HashMap) { if !is_watched_file(path, extensions) { return; } @@ -33,11 +43,12 @@ .file_name() .map(|n| n.to_string_lossy().into_owned()) .unwrap_or_else(|| path_str.clone()); + let category = resolve_category(path, category_map); if path.is_file() { if let Ok(mut guard) = state.watch_ingestion_status.write() { *guard = format!("取込中: {}", display_name); } - if let Err(e) = handle.block_on(ingest_file_path(state, path)) { + if let Err(e) = handle.block_on(ingest_file_path(state, path, &category)) { log::warn!("[watch] ingest {:?}: {}", path, e); } else { log::info!("[watch] ingested: {}", path_str); @@ -61,7 +72,7 @@ } /// 指定ディレクトリを再帰走査し、対象拡張子のファイルのうち未インデックスのものだけ取り込む(モニター追加時の初期スキャン)。 -fn initial_scan_directory(state: &AppState, dir: &Path, extensions: &[String], handle: &Handle) { +fn initial_scan_directory(state: &AppState, dir: &Path, extensions: &[String], handle: &Handle, category_map: &std::collections::HashMap) { let read_dir = match std::fs::read_dir(dir) { Ok(r) => r, Err(e) => { @@ -72,7 +83,7 @@ for entry in read_dir.flatten() { let path: PathBuf = entry.path(); if path.is_dir() { - initial_scan_directory(state, &path, extensions, handle); + initial_scan_directory(state, &path, extensions, handle, category_map); } else if path.is_file() && is_watched_file(&path, extensions) { let path_str = path.to_string_lossy().to_string(); let display_name = path @@ -83,10 +94,11 @@ match already { Ok(Some(_)) => { /* 既にインデックス済み */ } Ok(None) => { + let category = resolve_category(&path, category_map); if let Ok(mut guard) = state.watch_ingestion_status.write() { *guard = format!("取込中: {}", display_name); } - if let Err(e) = handle.block_on(ingest_file_path(state, &path)) { + if let Err(e) = handle.block_on(ingest_file_path(state, &path, &category)) { log::warn!("[watch] initial_scan ingest {:?}: {}", path, e); } else { log::info!("[watch] initial_scan ingested: {}", path_str); @@ -129,11 +141,12 @@ let state_for_debouncer = state.clone(); let handle_for_cb = handle.clone(); let extensions_for_cb = extensions.clone(); + let category_map_for_cb = config.category_map.clone(); let mut debouncer = match new_debouncer(Duration::from_secs(2), move |res: DebounceEventResult| { match res { Ok(events) => { for e in events { - process_event(state_for_debouncer.as_ref(), &e.path, &handle_for_cb, &extensions_for_cb); + process_event(state_for_debouncer.as_ref(), &e.path, &handle_for_cb, &extensions_for_cb, &category_map_for_cb); } } Err(e) => { @@ -168,7 +181,7 @@ for path in &config.paths { if path.is_dir() { log::info!("[watch] initial_scan: {}", path.display()); - initial_scan_directory(state.as_ref(), path, &extensions, &handle); + initial_scan_directory(state.as_ref(), path, &extensions, &handle, &config.category_map); } } diff --git a/src/backend/tauri.conf.json b/src/backend/tauri.conf.json index 869b83f..ecd20d7 100644 --- a/src/backend/tauri.conf.json +++ b/src/backend/tauri.conf.json @@ -9,7 +9,7 @@ "beforeDevCommand": "" }, "app": { - "windows": [{ "title": "TelosDB", "width": 800, "height": 600, "resizable": true, "fullscreen": false }], + "windows": [{ "title": "TelosDB", "width": 800, "height": 600, "resizable": true, "fullscreen": false, "visible": false }], "security": { "csp": null }, "withGlobalTauri": true }, diff --git a/src/frontend/components/main-panel.js b/src/frontend/components/main-panel.js index b2df1f2..637be51 100644 --- a/src/frontend/components/main-panel.js +++ b/src/frontend/components/main-panel.js @@ -7,6 +7,9 @@