# VOICEVOX エディタのコードの歩き方 VOICEVOX のエディタは Electron・TypeScript・Vue・Vuex などが活用されており、全体構成がわかりにくくなっています。 ここではどういう構成になっているのかを紹介します。コードを読む上で参考になれば幸いです。 ## コードが実行される順番 とにかくコードを読みたい方向けに、コードが実行される順番を簡潔に説明します。わかりやすさを優先したため正確な表現でない箇所もあることにご注意ください。 1. electron がアプリケーションを起動します。このとき実行されるコードが `src/background.ts` です。 2. `src/background.ts`から UI 表示のためのウィンドウを起動します。このとき実行されるコードが `src/main.ts` です。 3. `main.ts`が Vue を使って UI がレンダリングされます。UI のルートコンポーネントが `src/App.vue` です。 4. `src/App.vue`が様々な UI を呼び出します。`src/views`や`src/components`にそれぞれの UI があります。 5. UI のレンダリングと同時に、アプリの状態やロジックを管理する Vuex のストアが初期化されます。`src/store`ディレクトリに Vuex 用のコードがあります。 6. これらのコードは全て TypeScript で書かれています。Vue や TypeScript のコードは実行・ビルド時に自動的に JavaScript へトランスパイルされます。 また electron は、最初に起動するプロセス(メインプロセス)と UI ウィンドウ用のプロセス(レンダラープロセス)で通信するために、プリロードスクリプト`src/electron/preload.ts`を両方から読み込みます。 ```mermaid flowchart TB subgraph Electron direction TB background.ts -.- preload.ts end background.ts --> main.ts main.ts -.- preload.ts main.ts --> Vue & Vuex subgraph Vuex direction TB store/ end subgraph Vue direction TB App.vue --> views/ & components/ end ``` ## 設計 ここでは Electron 周り・Vuex 周り・UI 周りの設計方針を簡単に紹介します。 ### Electron 周り(`src/electron/`) TODO ### Vuex 周り(`src/store/`) VOICEVOX では動的に変更される状態と、状態を変更する関数を、Vuex の`Store`を介して管理しています。 `Store`は音声・エンジン・UI などそれぞれの目的に応じて分割し、`src/store/index.ts`で統合しています。 Vuex の仕様を少し説明します。 状態は`State`に保存し、状態の変更は`Mutation`で行います。 `Mutation`は同期的に実行される必要がありますが、非同期処理を行いたい場合は`Action`を使います。 VOICEVOX 内では、UI から実行するものは`Action`を介するようにして、`Action`内から`Mutation`を実行するようにしています。 これは後述する Undo/Redo 機構のための設計です。 関数は全て文字列を渡す形になっていますが、これも Vuex の仕様です。コードを追う際は文字列で全体検索を行うと便利です。 Vuex は型サポートが不十分なので、VOICEVOX では独自に型周りの機構を作っています(`src/store/type.ts`)。 関数を定義したい場合は、まず`type.ts`に型を宣言し、対応する`Store`内に実装します。 UI から Electron やエンジンにアクセスしたい場合は、必ず Vuex を介するようにしています。 これは Vuex の元の思想である Flux の考え方を引き継いでいて、外部にアクセスすることで生じる予想外な状態の変更などをなるべく一元管理するためです。 VOICEVOX では Vuex の`Mutation`をラップする形で Undo/Redo 機構を実現しています。 状態 A→B の変更を行った時、自動的にその逆方向 B→A の変更が Undo スタックに積まれます。 詳細は[Undo/Redo を実装したい #116](https://github.com/VOICEVOX/voicevox/issues/116)をご参照ください。 ### UI 周り(`src/views/`・`src/components/`) TODO ## ソースコードのディレクトリ構成 - src - background.ts ・・・ Electron 版で最初に実行されるコード。ウィンドウを表示したり、エンジンを起動したりする。Electron のメインプロセス。 - main.ts ・・・ ウィンドウを表示するために最初に実行されるコード。ここで Vue や Vuex を組み込む。 - App.vue ・・・ Vue のルートになるコンポーネント。他の全てのコンポーネントの親。 - views ディレクトリ ・・・ 画面全体を覆うような Vue コンポーネントのディレクトリ。 - components ディレクトリ ・・・ UI のパーツになる Vue コンポーネントディレクトリ。 - store ディレクトリ ・・・ Vuex のストアのディレクトリ。アプリのロジックの大半はここに書かれる。 - electron ディレクトリ ・・・ Electron の ipc 通信などのコードが置かれるディレクトリ。 - browser ディレクトリ ・・・ WIP - type ディレクトリ ・・・ TypeScript 用の型定義などが入るディレクトリ。 - styles ディレクトリ ・・・ CSS や SCSS などのディレクトリ。 - infrastructures ディレクトリ ・・・ UI 用のコードと UI 以外のコードを跨ぐときに一枚かませたいときのためのコードのディレクトリ。 - openapi ディレクトリ ・・・ エンジンの API を叩くためのコードのディレクトリ。OpenAPI で自動生成される。 - router ディレクトリ ・・・ Vue Router 用のディレクトリ。 - helpers ディレクトリ ・・・ 便利な関数を置くディレクトリ。 - public - アプリのリソースファイルが置かれるディレクトリ。 - build - ビルドに必要なファイルが置かれるディレクトリ。 ## シーケンス図 - [起動シーケンス図](res/起動シーケンス図.md) - [終了シーケンス図](res/終了シーケンス図.md) - [エンジン再起動シーケンス図](res/エンジン再起動シーケンス図.md) [Mermaid Flow](https://www.mermaidflow.app/)を用いて書いています。mermaid のコードをサイトに貼り付ければ再編集も可能です。記述ルールは以下参照。 ```mermaid flowchart LR 817736>"シーケンスの開始"] --> 584587["関数"] style 817736 fill:#ffbbbb,stroke:#ff0000 584587 --> 965553(["状態変数の変更"]) 965553 --> 945949{{"分岐"}} 945949 -->|"条件1"| 366994["能動的な処理は単線\n(関数の実行など)"] 945949 -->|"条件2"| 871294["受動的な処理は点線\n(イベント発生など)"] 366994 --> 617606["大きい関数(subgraph)"] 871294 -.->|"発生条件"| 617606 572391[["外に定義した関数"]] --> 314405>"なんらかの状態"] 572391 --> 230853>"シーケンスの終了"] style 230853 fill:#bbbbff,stroke:#0000ff 617606 --> 572391 572391 ~~~|"透明な線で結んでもよし"| 513532["外に定義した関数"] subgraph 617606["大きい関数(subgraph)"] 776462["処理開始"] --> 992786["処理終了"] end subgraph 513532["外に定義した関数"] 327013["処理"] end ``` 処理の prefix 一覧 - back: background.ts の関数 - app: app のイベント - win: win のイベント - engine: EngineManager のメソッド - store: electron-store のメソッド - Vuex: Vuex の関数