ssml-builder-js 2.8.0 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -48,7 +48,7 @@ Azure TTS クライアントも `ssml-builder-js` から利用できます。
48
48
 
49
49
  ### リポジトリを開発する場合
50
50
 
51
- Node.js 24 以降を用意し、リポジトリのルートで依存関係をインストールします。
51
+ Node.js 22.6 以降をサポートします。Node.js 24 LTS 以降を推奨します(Node.js 22.6 未満は、現在の TypeScript 実行設定に必要な型消去機能を満たしません)。
52
52
 
53
53
  ```sh
54
54
  npm ci
@@ -83,6 +83,31 @@ Playground の表示言語が日本語以外の場合、埋め込みエディタ
83
83
  SSML 全体または選択部分を Azure Speech で合成してブラウザーで再生でき、音声には
84
84
  SSML 本文のキャプションが付きます。生成された SSML は画面下部に表示されます。
85
85
 
86
+ ### Next.js で Monaco と選択範囲試聴を使う
87
+
88
+ Pages Router と App Router のどちらでも、Monaco を `next/dynamic` の `ssr: false` でクライアントへ分離します。`onPreviewSelection` から `/api/synthesize` Route Handler(Pages Router では同じ検証を `pages/api/synthesize.ts` に実装)へ部分 SSML を送り、Azure のキーはサーバー環境変数だけに置きます。リポジトリ内の `apps/playground/app/components/NextSsmlEditor.tsx` がこの構成の実装例です。
89
+
90
+ ```tsx
91
+ "use client";
92
+ import dynamic from "next/dynamic";
93
+ const SsmlEditor = dynamic(
94
+ () => import("@ssml-builder-js/ssml-editor-react").then(({ SsmlEditor }) => SsmlEditor),
95
+ { ssr: false },
96
+ );
97
+
98
+ export function ClientEditor(props) {
99
+ return <SsmlEditor {...props} onPreviewSelection={(ssml) =>
100
+ fetch("/api/synthesize", {
101
+ method: "POST",
102
+ headers: { "Content-Type": "application/json" },
103
+ body: JSON.stringify({ ssml }),
104
+ })
105
+ } />;
106
+ }
107
+ ```
108
+
109
+ Route Handler/API Route では `validateSsml` と `validateAzureSsml` を実行してから Azure TTS を呼び出し、キーをクライアントへ渡さないでください。
110
+
86
111
  ## `ssml-core` の利用方法
87
112
 
88
113
  `SsmlDocument` は `version`、`lang`、`children` を持つオブジェクトです。`children` には文字列、テキストノード、SSML 要素を入れられます。`children` を使う形式が推奨され、旧形式の `content` プロパティも `buildSsml` の入力として利用できます。
@@ -136,7 +161,11 @@ const parsed = parseSsml(ssml);
136
161
  | `validateSsml(xml)` | SSML の構文エラーを `{ message, position }` または `null` で返す |
137
162
  | `extractSsmlText(xml)` | タグを除いた全テキストノードを文書順に抽出 |
138
163
  | `mapSsmlTextNodes(xml, transform, options?)` | タグ構造を維持したままテキストノードだけを同期・非同期変換。スキップタグとコンテキストフィルターに対応 |
164
+ | `extractSsmlTranslatableText(xml, options?)` | `phoneme`、`say-as`、`sub` などを除外した翻訳対象本文を文書順に抽出 |
165
+ | `fromPlainTextToSsml(text, options?)` | プレーンテキストを `<speak>`、段落 `<p>`、文 `<s>` を含む SSML に変換 |
166
+ | `validateSsmlStructureIntegrity(original, translated)` | 翻訳前後のタグ階層・タグ名・属性の一致を検証 |
139
167
  | `validateAzureSsml(xml, options?)` | Azure Speech 向けの意味検証結果を Diagnostic 配列で返す(各診断に `source: "ssml-static-validator"` を付与) |
168
+ | `getAzureVoiceCatalogMetadata()` / `getBuiltInVoiceCatalogMetadata()` | 組み込み音声カタログの生成日時、API バージョン、リージョン、収録数を返す |
140
169
 
141
170
  ### 3段階の検証モデル
142
171
 
@@ -148,14 +177,18 @@ SSML の検証は、構文、Azure 固有の静的な意味、実サービスの
148
177
  | 静的意味 | `validateAzureSsml` | Azure SSML の必須要素、属性値、音声と `xml:lang` の整合性、音声スタイル、文字数、`audio` URL/オリジン | Azure 側の最新音声一覧、キー・リージョン権限、サービス障害、実際の音声生成結果 |
149
178
  | ランタイム | `AzureTtsClient.synthesize` / Azure Speech API | アカウント、リージョン、キー、最新の音声・スタイル提供状況、サービス側の SSML 制約、通信状態 | 静的検証の代替ではないため、入力検証や SSRF 対策を自動で補完しない |
150
179
 
151
- `voice`、`prosody`、`break`、`express-as`、`say-as`、`phoneme`、`audio`、`lang`、`mark` などの要素を型付きで表現できます。`type: "custom"` と `name` を指定すれば、未定義の XML 要素や追加属性も扱えます。`mstts:` 要素を含むドキュメントを生成すると、必要な Azure Speech 名前空間が自動的に追加されます。
180
+ `voice`、`prosody`、`break`、`express-as`、`say-as`、`phoneme`、`audio`、`lang`、`mark` に加えて、`mstts:dialog`、`mstts:turn`、`mstts:backgroundaudio`、`mstts:ttsembedding`、`mstts:embedding`、`mstts:voiceconversion` を型付きで表現できます。`type: "custom"` と `name` を指定すれば、未定義の XML 要素や追加属性も扱えます。`mstts:` 要素を含むドキュメントを生成すると、必要な Azure Speech 名前空間が自動的に追加されます。
152
181
 
153
182
  `validateAzureSsml` は Azure Speech へ送信する前に実行する事前静的チェックです。返却される診断の `source` はパッケージ側の静的解析結果であることを示し、Azure Speech サービス側でのランタイム生成結果や実際の合成可否を表すものではありません。
154
183
 
155
184
  翻訳などで本文だけを置き換える場合は、`mapSsmlTextNodes` に変換関数を渡します。変換関数には直近の親タグと祖先タグの `path` が渡され、戻り値は `string` または `Promise<string>` を指定できます。`validateAzureSsml` は音声、属性値、音声スタイル、文字数、`audio` URL/オリジンを検証します。
156
185
 
186
+ `mstts:audioduration` は `<mstts:audioduration value="10s"/>` の形式で扱えます。`value` には正の `ms`/`s` 値、または `hh:mm:ss`(ミリ秒を含む場合は `hh:mm:ss.fff`)を指定できます。
187
+
157
188
  `mapSsmlTextNodes` の第 3 引数で、翻訳対象外タグとコンテキストフィルターを指定できます。デフォルトでは `phoneme`、`say-as`、`sub` の本文を変換しません。`filter` には `parentTag`、`parentAttributes`、`ancestorTags`、`path` が渡されます。
158
189
 
190
+ 移行処理では `extractSsmlTranslatableText` で翻訳対象だけを抽出し、`validateSsmlStructureIntegrity` で翻訳後にタグ階層と属性が変わっていないことを確認できます。新しい原稿は `fromPlainTextToSsml` で `<p>` と `<s>` を含む初期文書へ変換できます。
191
+
159
192
  ```ts
160
193
  const translated = await mapSsmlTextNodes(ssml, translate, {
161
194
  skipTags: ["phoneme", "say-as", "sub", "custom-no-translate"],
@@ -173,7 +206,9 @@ const diagnostics = validateAzureSsml(ssml, {
173
206
  });
174
207
  ```
175
208
 
176
- 音声カタログは `AzureVoiceDefinition` の `name`、`locale`、`secondaryLocales`、`styles` で表現できます。`voiceDefinitions`(または `voiceCatalog`)を渡すと、組み込み台帳を外部定義で補完・上書きできます。`customVoiceStyleMap` は既存利用者向けに引き続き利用でき、指定した音声のスタイルを上書きします。組み込み台帳にない音声は `azure-unknown-voice`(`unknownVoicePolicy` に従う)、登録済み音声への非対応スタイルは `azure-unsupported-style`、ロケール不一致は `azure-locale-mismatch` として区別されます。
209
+ 音声カタログは `AzureVoiceDefinition` の `name`、`locale`、`secondaryLocales`、`styles`、`supportedTags`、`unsupportedTags`、`models` で表現できます。`voiceDefinitions`(または `voiceCatalog`)を渡すと、組み込み台帳を外部定義で補完・上書きできます。`supportedTags`/`unsupportedTags` による音声別制約違反は `azure-unsupported-tag-for-voice` としてエラーになります。組み込み台帳にない音声は `azure-unknown-voice`(`unknownVoicePolicy` に従う)、登録済み音声への非対応スタイルは `azure-unsupported-style`、ロケール不一致は `azure-locale-mismatch` として区別されます。
210
+
211
+ `npm run sync:voices -- --regions eastus,japaneast` は Azure List Voices API をリージョンごとに取得し、重複を除いた TypeScript 音声定義と `azureVoiceCatalog.json`(生成日時、API バージョン、リージョン、収録数)を更新します。認証情報は `AZURE_SPEECH_KEY` と `AZURE_SPEECH_REGION(S)`、または CLI オプションで指定します。
177
212
 
178
213
  ```ts
179
214
  const diagnostics = validateAzureSsml(ssml, {
@@ -192,6 +227,8 @@ const diagnostics = validateAzureSsml(ssml, {
192
227
 
193
228
  Azure の音声・スタイル一覧はサービス更新やリージョン差分があるため、組み込み一覧は固定の完全な台帳ではありません。新しい音声を完全に静的検証したい場合は、音声名だけでなくロケールとスタイルを `voiceDefinitions` に定義してください。`validateNestedVoices` のデフォルトは `true` です。`audio` の外部 URL はデフォルトで拒否されるため、利用する場合は `allowedAudioOrigins` に許可するオリジンを列挙するか、構成を理解した上で `allowExternalAudio: true` を指定してください。
194
229
 
230
+ Azure の音声カタログを更新する場合は、`AZURE_SPEECH_REGION` と `AZURE_SPEECH_KEY` を設定して `npm run sync:voices` を実行します。`packages/ssml-core` の音声定義とエディタのスタイル補完候補が同時に更新されます。
231
+
195
232
  ```ts
196
233
  const diagnostics = validateAzureSsml(ssml, {
197
234
  customVoiceStyleMap: { "my-custom-voice": ["narration"] },
@@ -204,7 +241,9 @@ Azure Speech は `<audio>` の URL を取得するため、任意の URL をそ
204
241
 
205
242
  ## `ssml-editor-react` の利用方法
206
243
 
207
- `SsmlEditor` は `SsmlDocument` を受け取り、ツールバーと本文の表示エリアだけを表示するシンプルなコンポーネントです。ツールバーから選択範囲の速度、音量、ピッチなどの設定、元に戻す・やり直す操作ができます。音声の選択と表示はアプリ側で行います。本文の編集には Monaco Editor を使用し、変更時に SSML の構文を検証します。構文エラーはエディター上のマーカーとエラーメッセージで表示されます。XML のタグ名やパラメータへホバーすると SSML の説明を確認できます。テキストを選択すると、選択文字数と試聴を行うフローティングアクションが表示されます。`enableCodeLens`(デフォルトは `true`)が有効な場合、`prosody` と `break` タグの上に属性編集やタグ操作の CodeLens が表示されます。`showDecorations` が有効な場合、`break` と `prosody` のタグに間やピッチ変化を示すインラインバッジが表示され、Monaco のインライン装飾も有効になります。生成された SSML は `onSsmlChange` で受け取り、アプリ側で自由に表示できます。`SsmlEditorRef` を `ref` に渡すと、全体、選択範囲、または現在行の SSML を取得できます。画面表示は日本語(デフォルト)と英語に対応しています。
244
+ `SsmlEditor` は `SsmlDocument` を受け取り、ツールバーと本文の表示エリアだけを表示するシンプルなコンポーネントです。ツールバーから選択範囲の速度、音量、ピッチなどの設定、元に戻す・やり直す操作ができます。音声の選択と表示はアプリ側で行います。本文の編集には Monaco Editor を使用し、変更時に SSML の構文を検証します。構文エラーはエディター上のマーカーとエラーメッセージで表示されます。XML のタグ名やパラメータへホバーすると SSML の説明を確認できます。テキストを選択すると、選択文字数と試聴を行うフローティングアクションが表示されます。`enableCodeLens`(デフォルトは `true`)が有効な場合、`prosody`、`break`、`mstts:audioduration` タグの上に属性編集やタグ操作の CodeLens が表示されます。`showDecorations` が有効な場合、`break` と `prosody` のタグに間やピッチ変化を示すインラインバッジが表示され、Monaco のインライン装飾も有効になります。生成された SSML は `onSsmlChange` で受け取り、アプリ側で自由に表示できます。`SsmlEditorRef` を `ref` に渡すと、全体、選択範囲、または現在行の SSML を取得できます。画面表示は日本語(デフォルト)と英語に対応しています。
245
+
246
+ `editMode="visual"` を指定するかツールバーの **Visual** を選ぶと、XML を直接見ずに構造ツリーとパンくずから親要素を選択し、フォームで本文の変更、rate、pitch、emotion、pause、pronunciation の適用ができます。Azure の配置違反や属性エラーはビジュアル領域にも表示されます。`editMode="code"` で Monaco に戻ります。
208
247
 
209
248
  `<mstts:express-as>` の `style` 属性補完と標準の感情メニューは、カーソルまたは選択範囲を囲む最内の `<voice name="...">` が対応するスタイルだけを表示します。音声名が未指定の場合は全候補を表示し、登録済みでスタイル非対応の音声や未登録の音声では候補がないことを表示します。`emotionStyles` を指定した場合、感情メニューではその値と登録済み音声の対応スタイルの共通部分を使用します。
210
249
 
@@ -243,14 +282,15 @@ export function App() {
243
282
  - `ref`: `SsmlEditorRef` の `getFullSsml()` で全体の SSML、`getSelectedSsml()` で選択範囲(未選択時は内容が空でないカーソル行)の SSML、`getCurrentLineSsml()` で現在行の SSML を取得。選択範囲やカーソル行が空の場合は `null` を返します
244
283
  - `onSelectionChange`: 選択テキスト、文字数、選択状態を受け取るコールバック
245
284
  - `onPreviewSelection`: フローティングアクションの試聴ボタン押下時に、選択部分の SSML を受け取るコールバック。省略時は試聴ボタンが無効になります。Azure などの音声 API はこのコールバックから呼び出してください
285
+ - `editMode`: 初期編集モード(`"code"` または `"visual"`)。Visual モードは構造ツリーとフォームによる構造化編集を提供します
246
286
  - `locale`: 画面表示の言語(`"ja"` または `"en"`)。省略時は `"ja"`。ホバーヘルプを含む UI の翻訳にも使用されます
247
287
  - `language`: `locale` の旧名称。既存コードとの互換性のため利用できますが、新しいコードでは `locale` を使用してください
248
288
  - `showToolbar`: ツールバー領域を表示するかどうか(デフォルトは `true`)
249
289
  - `showToolbarIcons`: ツールバーのアイコン表示(デフォルトは `true`)
250
290
  - `showToolbarLabels`: ツールバーの文字による説明表示(デフォルトは `false`)。省略時はアイコンにホバーすると説明が表示されます
251
291
  - `showDecorations`: 本文中のインライン装飾(バッジや Inlay Hints)の表示(デフォルトは `false`)。ツールバーの「装飾」スイッチで表示・非表示を切り替えられます
252
- - `enableCodeLens`: `prosody` と `break` タグの CodeLens クイックコントローラーを表示するか(デフォルトは `true`)
253
- - `buttonVisibility`: ツールバーボタンごとの表示設定。`help`、`break`、`emphasis`、`rate`、`pitch`、`volume`、`emotion`、`say-as`、`lang`、`mstts:silence`、`undo`、`redo`、`clearAll`、`format`、`decorations`、カスタム挿入 ID を指定でき、未指定のボタンは表示されます
292
+ - `enableCodeLens`: `prosody`、`break`、`mstts:audioduration` タグの CodeLens クイックコントローラーを表示するか(デフォルトは `true`)
293
+ - `buttonVisibility`: ツールバーボタンごとの表示設定。`help`、`break`、`emphasis`、`rate`、`pitch`、`volume`、`emotion`、`say-as`、`lang`、`mstts:silence`、`mstts:audioduration`、`undo`、`redo`、`clearAll`、`format`、`decorations`、カスタム挿入 ID を指定でき、未指定のボタンは表示されます
254
294
  - `editorOptions` / `settings`: Monaco の設定。`height`、`minHeight`、`readOnly`、`theme`(`system` / `light` / `dark`)、`fontSize`、`wordWrap`、`lineNumbers`、`minimap`、`automaticLayout` を指定できます。これらは同名のトップレベル props でも指定できます
255
295
  - `loadingFallback`: Monaco の読み込み中に表示する React ノード
256
296
  - `toolbarOrder`: ツールバー全体のボタン ID の表示順。指定されていないボタンは後ろに続きます
@@ -263,16 +303,16 @@ export function App() {
263
303
  - `toolbarClassName` / `toolbarStyle`: ツールバーのクラス名とインラインスタイル
264
304
  - `displayClassName` / `displayStyle`: 本文表示エリアのクラス名とインラインスタイル
265
305
  - ツールバーの「フォーマット」ボタンで本文の XML を整形できます
266
- - 挿入専用の要素(`break`、`mstts:silence`、カスタム挿入の `mode: "insert"`)は、本文内で独立した行になるよう自動的に改行されます。選択範囲を囲む要素はインラインのまま挿入されます
306
+ - 挿入専用の要素(`break`、`mstts:silence`、`mstts:audioduration`、カスタム挿入の `mode: "insert"`)は、本文内で独立した行になるよう自動的に改行されます。選択範囲を囲む要素はインラインのまま挿入されます
267
307
  - 本文を変更すると SSML 構文を検証し、エラー箇所をエディター上に表示します
268
308
 
269
- 標準の挿入メニューには `break`、`emphasis`、`rate`、`pitch`、`volume`、`emotion`、`say-as`、`lang`、`mstts:silence` が含まれます。これらの定義は `SSML_INSERTIONS` から参照できます。カスタム挿入定義は配列または ID をキーにしたオブジェクトで指定でき、`createSsmlEditorInsertionDefinition` でタグ名と任意の 1 属性を持つ定義を作成できます。任意の属性や複数属性が必要な場合は `SsmlEditorInsertionDefinition` の `createTemplate` を実装してください。
309
+ 標準の挿入メニューには `break`、`emphasis`、`rate`、`pitch`、`volume`、`emotion`、`say-as`、`lang`、`mstts:silence`、`mstts:audioduration` が含まれます。これらの定義は `SSML_INSERTIONS` から参照できます。カスタム挿入定義は配列または ID をキーにしたオブジェクトで指定でき、`createSsmlEditorInsertionDefinition` でタグ名と任意の 1 属性を持つ定義を作成できます。任意の属性や複数属性が必要な場合は `SsmlEditorInsertionDefinition` の `createTemplate` を実装してください。
270
310
 
271
311
  「説明」ボタンを押すと、各コントロール、ボタン、設定の説明を表示できます。ボタンの設定はアコーディオンで表示され、デフォルトでは閉じています。アコーディオンのタイトルにはボタンの説明と生成される XML のタグ名が表示され、各設定の意味を確認できます。「全てクリア」ボタンは `voice` 要素を保持したまま、それ以外の XML 要素を削除して本文を残します。ドキュメントの `version`、`lang`、その他の属性も保持されます。
272
312
 
273
313
  ## `ssml-editor-elements` の利用方法
274
314
 
275
- `ssml-builder-js/elements` は、React などに依存しない `<ssml-editor>` Web Component を登録します。`monaco-editor` を別途インストールし、`value`、`theme`、`readonly` 属性または同名プロパティを使用できます。React エディターと同じ SSML ツールバーと説明表示も利用でき、`locale`、`show-toolbar`、`show-toolbar-labels`、`show-decorations` で表示を調整できます。編集時には `{ value }` を `detail` に持つ `change` イベントが発生します。
315
+ `ssml-builder-js/elements` は、React などに依存しない `<ssml-editor>` Web Component を登録します。`monaco-editor` を別途インストールし、`value`、`theme`、`readonly` 属性または同名プロパティを使用できます。React エディターと同じ SSML ツールバーと説明表示も利用でき、`locale`、`show-toolbar`、`show-toolbar-labels`、`show-decorations` で表示を調整できます。`edit-mode="visual"` または `editor.editMode = "visual"` で構造ツリーとフォームのビジュアル編集に切り替えられます。編集時には `{ value }` を `detail` に持つ `change` イベントが発生し、ビジュアルモードの試聴は `{ ssml }` を持つ `preview-selection` イベントで受け取れます。
276
316
 
277
317
  ```ts
278
318
  import "ssml-builder-js/elements";
@@ -456,7 +496,7 @@ The Azure TTS client is also available from `ssml-builder-js`.
456
496
 
457
497
  ### Developing this repository
458
498
 
459
- Install Node.js 24 or later, then install dependencies from the repository root:
499
+ Node.js 22.6 or later is supported; Node.js 24 LTS or later is recommended. Versions below Node.js 22.6 do not provide the type-stripping capability required by the current TypeScript execution configuration:
460
500
 
461
501
  ```sh
462
502
  npm ci
@@ -491,6 +531,31 @@ manually, and the manual choice is stored in the browser. You can synthesize the
491
531
  SSML document or the selected text and play it in the browser with an SSML caption track;
492
532
  the generated SSML is shown below the editor.
493
533
 
534
+ ### Using Monaco and selection preview with Next.js
535
+
536
+ For both the Pages Router and App Router, keep Monaco in a client-only module with `next/dynamic` and `{ ssr: false }`. Send the partial SSML from `onPreviewSelection` to the `/api/synthesize` Route Handler (or the equivalent `pages/api/synthesize.ts` API Route), and keep the Azure key in server-only environment variables. The repository example is `apps/playground/app/components/NextSsmlEditor.tsx`.
537
+
538
+ ```tsx
539
+ "use client";
540
+ import dynamic from "next/dynamic";
541
+ const SsmlEditor = dynamic(
542
+ () => import("@ssml-builder-js/ssml-editor-react").then(({ SsmlEditor }) => SsmlEditor),
543
+ { ssr: false },
544
+ );
545
+
546
+ export function ClientEditor(props) {
547
+ return <SsmlEditor {...props} onPreviewSelection={(ssml) =>
548
+ fetch("/api/synthesize", {
549
+ method: "POST",
550
+ headers: { "Content-Type": "application/json" },
551
+ body: JSON.stringify({ ssml }),
552
+ })
553
+ } />;
554
+ }
555
+ ```
556
+
557
+ The Route Handler/API Route must run `validateSsml` and `validateAzureSsml` before calling Azure TTS. Never send the subscription key to the browser.
558
+
494
559
  ## Using `ssml-core`
495
560
 
496
561
  `SsmlDocument` is an object with `version`, `lang`, and `children` properties. `children` can contain strings, text nodes, and SSML elements. The `children` form is recommended; the legacy `content` property is also accepted as input by `buildSsml`.
@@ -544,7 +609,11 @@ The main `buildSsml` and `parseSsml` signatures are:
544
609
  | `validateSsml(xml)` | Returns `{ message, position }` for a syntax error, or `null` |
545
610
  | `extractSsmlText(xml)` | Extracts all text nodes in document order |
546
611
  | `mapSsmlTextNodes(xml, transform, options?)` | Transforms only text nodes while preserving the XML structure; supports sync/async transforms, skipped tags, and context filters |
612
+ | `extractSsmlTranslatableText(xml, options?)` | Extracts translation-target text in document order while skipping `phoneme`, `say-as`, `sub`, and other configured tags |
613
+ | `fromPlainTextToSsml(text, options?)` | Converts plain text into an initial `<speak>` document containing paragraphs (`<p>`) and sentences (`<s>`) |
614
+ | `validateSsmlStructureIntegrity(original, translated)` | Checks that tag hierarchy, element names, and attributes are unchanged after translation |
547
615
  | `validateAzureSsml(xml, options?)` | Returns Azure Speech semantic-validation diagnostics (each diagnostic has `source: "ssml-static-validator"`) |
616
+ | `getAzureVoiceCatalogMetadata()` / `getBuiltInVoiceCatalogMetadata()` | Returns generation time, API version, regions, and voice count for the bundled voice catalog |
548
617
 
549
618
  ### Three-stage validation model
550
619
 
@@ -556,12 +625,14 @@ SSML validation separates XML syntax, Azure-specific static semantics, and runti
556
625
  | Static semantics | `validateAzureSsml` | Required Azure SSML elements, attribute values, voice/`xml:lang` alignment, voice styles, character limits, and `audio` URL/origin policy | Azure's latest voice catalog, key/region permissions, service incidents, and the actual generated audio |
557
626
  | Runtime | `AzureTtsClient.synthesize` / Azure Speech API | Account, region, key, current voice/style availability, service-side SSML constraints, and network state | It does not replace input validation or automatically provide SSRF protection |
558
627
 
559
- Typed representations are available for elements such as `voice`, `prosody`, `break`, `express-as`, `say-as`, `phoneme`, `audio`, `lang`, and `mark`. Use `type: "custom"` and `name` to handle undefined XML elements or additional attributes. When a document contains `mstts:` elements, the required Azure Speech namespace is added automatically.
628
+ Typed representations are available for elements such as `voice`, `prosody`, `break`, `express-as`, `say-as`, `phoneme`, `audio`, `lang`, and `mark`, plus `mstts:dialog`, `mstts:turn`, `mstts:backgroundaudio`, `mstts:ttsembedding`, `mstts:embedding`, and `mstts:voiceconversion`. Use `type: "custom"` and `name` to handle undefined XML elements or additional attributes. When a document contains `mstts:` elements, the required Azure Speech namespace is added automatically.
560
629
 
561
630
  `validateAzureSsml` is a preflight static check performed by this package before sending SSML to Azure Speech. The `source` on each returned diagnostic identifies package-side static analysis; it is independent of Azure Speech's runtime generation result and does not guarantee that synthesis will succeed.
562
631
 
563
632
  Use `mapSsmlTextNodes` to replace translatable content without changing tags, attributes, or nesting. The transform receives the immediate parent tag and ancestor `path`, and may return a `string` or a `Promise<string>`. The third argument supports `skipTags` and a `filter` callback; `phoneme`, `say-as`, and `sub` are skipped by default. The callback receives `parentTag`, decoded `parentAttributes`, `ancestorTags`, and `path`.
564
633
 
634
+ For migration workflows, use `extractSsmlTranslatableText` to produce only translation-target text, `validateSsmlStructureIntegrity` to detect tag or attribute changes after translation, and `fromPlainTextToSsml` to create a starter document with `<p>` and `<s>` elements.
635
+
565
636
  ```ts
566
637
  const translated = await mapSsmlTextNodes(ssml, translate, {
567
638
  skipTags: ["phoneme", "say-as", "sub", "custom-no-translate"],
@@ -579,7 +650,9 @@ const diagnostics = validateAzureSsml(ssml, {
579
650
  });
580
651
  ```
581
652
 
582
- The catalog is represented by `AzureVoiceDefinition` with `name`, `locale`, optional `secondaryLocales`, and optional `styles`. Pass `voiceDefinitions` (or `voiceCatalog`) to supplement or override the built-in catalog with an external definition. `customVoiceStyleMap` remains supported for backward compatibility and overrides styles for the named voice. Diagnostics distinguish an unregistered voice (`azure-unknown-voice`, controlled by `unknownVoicePolicy`), an unsupported style on a registered voice (`azure-unsupported-style`), and a locale mismatch (`azure-locale-mismatch`).
653
+ The catalog is represented by `AzureVoiceDefinition` with `name`, `locale`, optional `secondaryLocales`, `styles`, `supportedTags`, `unsupportedTags`, and `models`. Pass `voiceDefinitions` (or `voiceCatalog`) to supplement or override the built-in catalog with an external definition. A tag that violates `supportedTags` or `unsupportedTags` produces `azure-unsupported-tag-for-voice` with error severity. `customVoiceStyleMap` remains supported for backward compatibility and overrides styles for the named voice. Diagnostics distinguish an unregistered voice (`azure-unknown-voice`, controlled by `unknownVoicePolicy`), an unsupported style on a registered voice (`azure-unsupported-style`), and a locale mismatch (`azure-locale-mismatch`). The `<mstts:audioduration value="10s"/>` element accepts positive `ms` or `s` values and `hh:mm:ss[.fff]` clock values.
654
+
655
+ `npm run sync:voices -- --regions eastus,japaneast` fetches Azure's List Voices API for each region, deduplicates the results, and updates the generated TypeScript definitions plus `azureVoiceCatalog.json` with generation time, API version, regions, and voice count. Provide credentials through `AZURE_SPEECH_KEY` and `AZURE_SPEECH_REGION(S)`, or CLI options.
583
656
 
584
657
  ```ts
585
658
  const diagnostics = validateAzureSsml(ssml, {
@@ -598,6 +671,8 @@ const diagnostics = validateAzureSsml(ssml, {
598
671
 
599
672
  Azure's voice and style catalog changes over time and can differ by region, so the built-in catalog is a fixed snapshot rather than a complete live catalog. For complete static validation of a new voice, define its name, locale, and supported styles in `voiceDefinitions`. `validateNestedVoices` defaults to `true`. External `<audio>` URLs are blocked by default; provide `allowedAudioOrigins` or explicitly set `allowExternalAudio: true` only when the deployment is configured to control those requests.
600
673
 
674
+ To refresh the built-in voice catalog, set `AZURE_SPEECH_REGION` and `AZURE_SPEECH_KEY`, then run `npm run sync:voices`. The command updates the core voice definitions and editor style-completion map together.
675
+
601
676
  ```ts
602
677
  const diagnostics = validateAzureSsml(ssml, {
603
678
  customVoiceStyleMap: { "my-custom-voice": ["narration"] },
@@ -612,6 +687,8 @@ Azure Speech fetches `<audio>` URLs, so a server that accepts arbitrary user-pro
612
687
 
613
688
  `SsmlEditor` accepts an `SsmlDocument` and renders only a toolbar and text display area. The toolbar applies rate, volume, and pitch settings to the selection and provides undo and redo actions. The application is responsible for selecting and displaying the voice. Monaco Editor is used for text editing, and SSML syntax is validated whenever the text changes. Syntax errors are shown with editor markers and an error message. Hovering over XML tag names or parameters shows SSML descriptions. Selecting text displays a floating action bar with the character count and preview action. When `enableCodeLens` is enabled (the default), CodeLens quick controls for editing and unwrapping `prosody` tags and editing or deleting `break` tags are shown above those tags. When `showDecorations` is enabled, inline badges for pause and pitch changes are rendered next to `break` and `prosody` tags, and Monaco inline decorations are enabled. Generated SSML is provided through `onSsmlChange` so the application can display it wherever it needs. Pass an `SsmlEditorRef` through `ref` to retrieve full, selected, or current-line SSML, and use `onSelectionChange` to observe selection text and state. The UI supports Japanese (the default) and English.
614
689
 
690
+ Set `editMode="visual"`, or choose **Visual** in the toolbar, to edit without viewing XML source. The structured editor provides a structure tree, parent breadcrumb selection, text editing, and form actions for rate, pitch, emotion, pause, and pronunciation. Azure placement and attribute diagnostics are shown in the visual area. Choose `editMode="code"` to return to Monaco.
691
+
615
692
  Completion for the `<mstts:express-as>` `style` attribute and the built-in Emotion menu only show styles supported by the innermost `<voice name="...">` around the cursor or selection. When no voice name is available, all candidates are shown; a registered voice without supported styles or an explicitly unregistered voice displays an empty-state message. When `emotionStyles` is supplied, the Emotion menu uses its intersection with the registered voice's supported styles.
616
693
 
617
694
  ```tsx
@@ -649,14 +726,15 @@ export function App() {
649
726
  - `ref`: An `SsmlEditorRef`; `getFullSsml()` returns the full SSML, `getSelectedSsml()` returns the selected text (or the cursor line when it contains content), and `getCurrentLineSsml()` returns the current cursor line. The selection and line methods return `null` when their target is empty
650
727
  - `onSelectionChange`: A callback that receives selected text, its character count, and whether a selection exists
651
728
  - `onPreviewSelection`: A callback that receives the selected partial SSML when the floating preview action is pressed. The preview action is disabled when this callback is omitted; call an audio API such as Azure from the callback
729
+ - `editMode`: Initial editor mode (`"code"` or `"visual"`). Visual mode provides structured tree and form editing
652
730
  - `locale`: The UI language (`"ja"` or `"en"`); defaults to `"ja"` and also controls hover-help translations
653
731
  - `language`: Legacy name for `locale`, retained for compatibility; use `locale` in new code
654
732
  - `showToolbar`: Whether to display the toolbar (defaults to `true`)
655
733
  - `showToolbarIcons`: Whether to show toolbar icons (defaults to `true`)
656
734
  - `showToolbarLabels`: Whether to show text labels on the toolbar (defaults to `false`); when omitted, hover over an icon to see its description
657
735
  - `showDecorations`: Whether inline decorations such as badges and inlay hints are shown in the text (defaults to `false`); use the **Decorations** toolbar switch to toggle them at runtime
658
- - `enableCodeLens`: Whether CodeLens quick controls for `prosody` and `break` tags are shown (defaults to `true`)
659
- - `buttonVisibility`: Per-toolbar-button visibility settings for `help`, `break`, `emphasis`, `rate`, `pitch`, `volume`, `emotion`, `say-as`, `lang`, `mstts:silence`, `undo`, `redo`, `clearAll`, `format`, `decorations`, and custom insertion IDs; unspecified buttons are shown
736
+ - `enableCodeLens`: Whether CodeLens quick controls for `prosody`, `break`, and `mstts:audioduration` tags are shown (defaults to `true`)
737
+ - `buttonVisibility`: Per-toolbar-button visibility settings for `help`, `break`, `emphasis`, `rate`, `pitch`, `volume`, `emotion`, `say-as`, `lang`, `mstts:silence`, `mstts:audioduration`, `undo`, `redo`, `clearAll`, `format`, `decorations`, and custom insertion IDs; unspecified buttons are shown
660
738
  - `editorOptions` / `settings`: Monaco settings for `height`, `minHeight`, `readOnly`, `theme` (`system` / `light` / `dark`), `fontSize`, `wordWrap`, `lineNumbers`, `minimap`, and `automaticLayout`. The same settings can also be supplied as top-level props
661
739
  - `loadingFallback`: A React node displayed while Monaco is loading
662
740
  - `toolbarOrder`: Display order for all toolbar button IDs; unlisted buttons follow
@@ -669,16 +747,16 @@ export function App() {
669
747
  - `toolbarClassName` / `toolbarStyle`: A class name and inline styles for the toolbar
670
748
  - `displayClassName` / `displayStyle`: A class name and inline styles for the text display area
671
749
  - Use the **Format** button to format the XML in the text display area
672
- - Standalone elements (`break`, `mstts:silence`, and custom insertions with `mode: "insert"`) are automatically placed on separate lines; elements that wrap a selection remain inline
750
+ - Standalone elements (`break`, `mstts:silence`, `mstts:audioduration`, and custom insertions with `mode: "insert"`) are automatically placed on separate lines; elements that wrap a selection remain inline
673
751
  - Changing the text validates SSML syntax and displays errors in the editor
674
752
 
675
- The built-in insertion menus are `break`, `emphasis`, `rate`, `pitch`, `volume`, `emotion`, `say-as`, `lang`, and `mstts:silence`. Their definitions are available through `SSML_INSERTIONS`. Custom insertion definitions can be supplied as an array or an object keyed by ID. Use `createSsmlEditorInsertionDefinition` to create a definition from a tag and one optional attribute; for arbitrary or multiple attributes, implement `createTemplate` on `SsmlEditorInsertionDefinition`.
753
+ The built-in insertion menus are `break`, `emphasis`, `rate`, `pitch`, `volume`, `emotion`, `say-as`, `lang`, `mstts:silence`, and `mstts:audioduration`. Their definitions are available through `SSML_INSERTIONS`. Custom insertion definitions can be supplied as an array or an object keyed by ID. Use `createSsmlEditorInsertionDefinition` to create a definition from a tag and one optional attribute; for arbitrary or multiple attributes, implement `createTemplate` on `SsmlEditorInsertionDefinition`.
676
754
 
677
755
  Click the **Description** button to see descriptions of each control, button, and setting. Button settings are shown in accordions that are closed by default, with the button description and generated XML tag name as the accordion title and the meaning of each setting inside. The **Clear all** button preserves `voice` elements, removes the other XML elements, and leaves the text in place. The document's `version`, `lang`, and other attributes are also preserved.
678
756
 
679
757
  ## Using `ssml-editor-elements`
680
758
 
681
- `ssml-builder-js/elements` registers a framework-independent `<ssml-editor>` Web Component without React. Install `monaco-editor` separately, then use the `value`, `theme`, and `readonly` attributes or properties. The component includes the same SSML toolbar and help display as the React editor; use `locale`, `show-toolbar`, `show-toolbar-labels`, and `show-decorations` to customize it. Editing dispatches a `change` event whose `detail` is `{ value: string }`.
759
+ `ssml-builder-js/elements` registers a framework-independent `<ssml-editor>` Web Component without React. Install `monaco-editor` separately, then use the `value`, `theme`, and `readonly` attributes or properties. The component includes the same SSML toolbar and help display as the React editor; use `locale`, `show-toolbar`, `show-toolbar-labels`, and `show-decorations` to customize it. Set `edit-mode="visual"` or `editor.editMode = "visual"` to use the structured tree and form editor. Editing dispatches a `change` event whose `detail` is `{ value: string }`; visual preview dispatches a `preview-selection` event whose detail is `{ ssml: string }`.
682
760
 
683
761
  ```ts
684
762
  import "ssml-builder-js/elements";