ssml-builder-js 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 nitta-a
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,509 @@
1
+ # SSML-Builder
2
+
3
+ - [日本語](#日本語)
4
+ - [English](#english)
5
+
6
+ ## 日本語
7
+
8
+ Azure Speech Service で利用できる SSML を、TypeScript のデータ構造から生成・解析し、React の GUI で編集するための npm ワークスペースモノレポです。公開成果物は `ssml-builder-js` という単一の npm パッケージです。
9
+
10
+ SSML の XML エスケープや Azure Speech 拡張要素に対応したコアライブラリ、Monaco Editor を利用した React コンポーネント、Azure Text-to-Speech API クライアントをサブパスから提供します。
11
+
12
+ ## パッケージ構成
13
+
14
+ | サブパス | 内容 |
15
+ | --- | --- |
16
+ | `ssml-builder-js` | コア機能と Azure Text-to-Speech クライアント |
17
+ | `ssml-builder-js/core` | SSML の型定義、ドキュメントの生成(`buildSsml`)、XML からの解析(`parseSsml`)、構文検証(`validateSsml`) |
18
+ | `ssml-builder-js/react` | ツールバーと本文の表示エリアを備えた `SsmlEditor` コンポーネント |
19
+
20
+ `ssml-builder-js` と `ssml-builder-js/core` は同じコア機能を提供します。React エディタは `ssml-builder-js/react` から読み込み、Azure TTS クライアントは `ssml-builder-js` から読み込みます。
21
+
22
+ ## セットアップ
23
+
24
+ ### npm パッケージとして利用する場合
25
+
26
+ 公開済みのパッケージを利用するアプリケーションでは、パッケージをインストールします。
27
+
28
+ ```sh
29
+ npm install ssml-builder-js
30
+ ```
31
+
32
+ React エディタを使用する場合は、React と Monaco Editor のアダプターもインストールします。
33
+
34
+ ```sh
35
+ npm install ssml-builder-js @monaco-editor/react react react-dom
36
+ ```
37
+
38
+ Azure TTS クライアントも `ssml-builder-js` から利用できます。
39
+
40
+ ### リポジトリを開発する場合
41
+
42
+ Node.js 24 以降を用意し、リポジトリのルートで依存関係をインストールします。
43
+
44
+ ```sh
45
+ npm ci
46
+ ```
47
+
48
+ ### Playground で音声を生成する
49
+
50
+ `apps/playground` の「音声を生成」ボタンは、現在の SSML をサーバー側の
51
+ Next.js Route Handler に送信し、`ssml-builder-js` で Azure
52
+ Speech の音声を生成します。Azure のサブスクリプションキーをブラウザへ公開しないため、
53
+ `apps/playground/.env.local` に次の値を設定してください。
54
+
55
+ ```dotenv
56
+ AZURE_SPEECH_KEY=your-subscription-key
57
+ AZURE_SPEECH_REGION=japaneast
58
+ # AZURE_SPEECH_ENDPOINT=https://{region}.tts.speech.microsoft.com/tts/cognitiveservices/websocket/v1
59
+ ```
60
+
61
+ `apps/playground/.env.example` をコピーして使用できます。設定後、リポジトリのルートから
62
+ 次のコマンドで Playground を起動します。
63
+
64
+ ```sh
65
+ cp apps/playground/.env.example apps/playground/.env.local
66
+ npm run dev --workspace playground
67
+ ```
68
+
69
+ ## `ssml-core` の利用方法
70
+
71
+ `SsmlDocument` は `version`、`lang`、`children` を持つオブジェクトです。`children` には文字列、テキストノード、SSML 要素を入れられます。`children` を使う形式が推奨され、旧形式の `content` プロパティも `buildSsml` の入力として利用できます。
72
+
73
+ `lang` は読み上げ言語を表す BCP-47 タグで、パッケージ外部から `SsmlDocument` に設定します。`SsmlEditor` の `locale` は画面表示言語の設定であり、読み上げ言語とは別です。
74
+
75
+ ```ts
76
+ import { buildSsml, parseSsml } from "ssml-builder-js/core";
77
+ import type { SsmlDocument } from "ssml-builder-js/core";
78
+
79
+ const document: SsmlDocument = {
80
+ version: "1.0",
81
+ lang: "ja-JP",
82
+ children: [
83
+ {
84
+ type: "voice",
85
+ name: "ja-JP-NanamiNeural",
86
+ children: [
87
+ {
88
+ type: "prosody",
89
+ rate: "medium",
90
+ pitch: "+2st",
91
+ children: ["こんにちは。"],
92
+ },
93
+ { type: "break", time: "300ms" },
94
+ {
95
+ type: "express-as",
96
+ style: "cheerful",
97
+ children: ["SSML Builder です。"],
98
+ },
99
+ ],
100
+ },
101
+ ],
102
+ };
103
+
104
+ // SsmlDocument から XML 文字列を生成します。
105
+ const ssml = buildSsml(document);
106
+
107
+ // XML 文字列を SsmlDocument に戻します。
108
+ const parsed = parseSsml(ssml);
109
+ ```
110
+
111
+ `buildSsml` と `parseSsml` の主なシグネチャは次のとおりです。
112
+
113
+ | API | 用途 |
114
+ | --- | --- |
115
+ | `buildSsml(document)` | SSML ドキュメントを XML 文字列に変換 |
116
+ | `buildSsml(content, lang?)` | 本文から `SsmlDocument` を作成する簡易形式(`content` を使用する旧形式) |
117
+ | `buildPartialSsml(text, context?)` / `buildPartialSsml({ text, ...context })` | 言語、音声、プロソディなどのコンテキスト付きで部分テキストから最小の SSML を生成 |
118
+ | `parseSsml(xml)` | `<speak>` XML を `SsmlDocument` に変換 |
119
+ | `validateSsml(xml)` | SSML の構文エラーを `{ message, position }` または `null` で返す |
120
+
121
+ `voice`、`prosody`、`break`、`express-as`、`say-as`、`phoneme`、`audio`、`lang`、`mark` などの要素を型付きで表現できます。`type: "custom"` と `name` を指定すれば、未定義の XML 要素や追加属性も扱えます。`mstts:` 要素を含むドキュメントを生成すると、必要な Azure Speech 名前空間が自動的に追加されます。
122
+
123
+ ## `ssml-editor-react` の利用方法
124
+
125
+ `SsmlEditor` は `SsmlDocument` を受け取り、ツールバーと本文の表示エリアだけを表示するシンプルなコンポーネントです。ツールバーから選択範囲の速度、音量、ピッチなどの設定、元に戻す・やり直す操作ができます。音声の選択と表示はアプリ側で行います。本文の編集には Monaco Editor を使用し、変更時に SSML の構文を検証します。構文エラーはエディター上のマーカーとエラーメッセージで表示されます。XML のタグ名やパラメータへホバーすると SSML の説明を確認できます。テキストを選択すると、選択文字数と試聴を行うフローティングアクションが表示されます。`showDecorations` が有効な場合、`break` と `prosody` のタグに間やピッチ変化を示すインラインバッジが表示され、Monaco のインライン装飾も有効になります。生成された SSML は `onSsmlChange` で受け取り、アプリ側で自由に表示できます。`SsmlEditorRef` を `ref` に渡すと、全体、選択範囲、または現在行の SSML を取得できます。画面表示は日本語(デフォルト)と英語に対応しています。
126
+
127
+ ```tsx
128
+ import { useState } from "react";
129
+ import { SsmlEditor } from "ssml-builder-js/react";
130
+ import type { SsmlDocument } from "ssml-builder-js/core";
131
+
132
+ const initialDocument: SsmlDocument = {
133
+ version: "1.0",
134
+ lang: "ja-JP",
135
+ children: ["編集する本文"],
136
+ };
137
+
138
+ export function App() {
139
+ const [document, setDocument] = useState(initialDocument);
140
+ const [ssml, setSsml] = useState("");
141
+
142
+ return (
143
+ <>
144
+ <SsmlEditor
145
+ document={document}
146
+ onChange={setDocument}
147
+ onSsmlChange={setSsml}
148
+ locale="ja"
149
+ />
150
+ <pre>{ssml}</pre>
151
+ </>
152
+ );
153
+ }
154
+ ```
155
+
156
+ - `document`: 編集対象の `SsmlDocument`
157
+ - `onChange`: 編集後の `SsmlDocument` を受け取るコールバック
158
+ - `onSsmlChange`: 編集後に生成された SSML 文字列を受け取るコールバック
159
+ - `ref`: `SsmlEditorRef` の `getFullSsml()` で全体の SSML、`getSelectedSsml()` で選択範囲(未選択時はカーソル行)の SSML、`getCurrentLineSsml()` で現在行の SSML を取得
160
+ - `onSelectionChange`: 選択テキスト、文字数、選択状態を受け取るコールバック
161
+ - `onPreviewSelection`: フローティングアクションの試聴ボタン押下時に、選択部分の SSML を受け取るコールバック。省略時は試聴ボタンが無効になります。Azure などの音声 API はこのコールバックから呼び出してください
162
+ - `locale`: 画面表示の言語(`"ja"` または `"en"`)。省略時は `"ja"`。ホバーヘルプを含む UI の翻訳にも使用されます
163
+ - `language`: `locale` の旧名称。既存コードとの互換性のため利用できますが、新しいコードでは `locale` を使用してください
164
+ - `showToolbar`: ツールバー領域を表示するかどうか(デフォルトは `true`)
165
+ - `showToolbarIcons`: ツールバーのアイコン表示(デフォルトは `true`)
166
+ - `showToolbarLabels`: ツールバーの文字による説明表示(デフォルトは `false`)。省略時はアイコンにホバーすると説明が表示されます
167
+ - `showDecorations`: 本文中のインライン装飾(バッジや Inlay Hints)の表示(デフォルトは `false`)。ツールバーの「装飾」スイッチで表示・非表示を切り替えられます
168
+ - `buttonVisibility`: ツールバーボタンごとの表示設定。`help`、`break`、`emphasis`、`rate`、`pitch`、`volume`、`emotion`、`say-as`、`lang`、`mstts:silence`、`undo`、`redo`、`clearAll`、`format`、`decorations`、カスタム挿入 ID を指定でき、未指定のボタンは表示されます
169
+ - `editorOptions` / `settings`: Monaco の設定。`height`、`minHeight`、`readOnly`、`theme`(`system` / `light` / `dark`)、`fontSize`、`wordWrap`、`lineNumbers`、`minimap`、`automaticLayout` を指定できます。これらは同名のトップレベル props でも指定できます
170
+ - `loadingFallback`: Monaco の読み込み中に表示する React ノード
171
+ - `toolbarOrder`: ツールバー全体のボタン ID の表示順。指定されていないボタンは後ろに続きます
172
+ - `toolbarGroups`: ツールバー全体を縦線で区切るグループ設定。`{ id, buttonIds }` 形式で指定し、枠線付きのグループは使用しません
173
+ - `insertionOrder`: 挿入メニューの ID の表示順。`toolbarOrder` が未指定の場合の挿入メニュー順にも使用されます
174
+ - `insertionGroups`: 挿入メニューを縦線で区切るグループ設定。`toolbarGroups` を省略した場合は、この設定からツールバーの既定グループも作成されます。省略時は間・無音、声の調整、表現、読み上げに分けて表示されます
175
+ - `emotionStyles`: `emotion` メニューに表示する音声スタイル候補
176
+ - `customInsertions` / `additionalInsertions`: カスタム SSML 挿入定義。`customInsertions` は同じ ID の標準定義を置き換え、`additionalInsertions` は標準定義へ追加します
177
+ - `className` / `style`: エディター全体のクラス名とインラインスタイル
178
+ - `toolbarClassName` / `toolbarStyle`: ツールバーのクラス名とインラインスタイル
179
+ - `displayClassName` / `displayStyle`: 本文表示エリアのクラス名とインラインスタイル
180
+ - ツールバーの「フォーマット」ボタンで本文の XML を整形できます
181
+ - 挿入専用の要素(`break`、`mstts:silence`、カスタム挿入の `mode: "insert"`)は、本文内で独立した行になるよう自動的に改行されます。選択範囲を囲む要素はインラインのまま挿入されます
182
+ - 本文を変更すると SSML 構文を検証し、エラー箇所をエディター上に表示します
183
+
184
+ 標準の挿入メニューには `break`、`emphasis`、`rate`、`pitch`、`volume`、`emotion`、`say-as`、`lang`、`mstts:silence` が含まれます。これらの定義は `SSML_INSERTIONS` から参照できます。カスタム挿入定義は配列または ID をキーにしたオブジェクトで指定でき、`createSsmlEditorInsertionDefinition` でタグ名と任意の 1 属性を持つ定義を作成できます。任意の属性や複数属性が必要な場合は `SsmlEditorInsertionDefinition` の `createTemplate` を実装してください。
185
+
186
+ 「説明」ボタンを押すと、各コントロール、ボタン、設定の説明を表示できます。ボタンの設定はアコーディオンで表示され、デフォルトでは閉じています。アコーディオンのタイトルにはボタンの説明と生成される XML のタグ名が表示され、各設定の意味を確認できます。「全てクリア」ボタンは `voice` 要素を保持したまま、それ以外の XML 要素を削除して本文を残します。ドキュメントの `version`、`lang`、その他の属性も保持されます。
187
+
188
+ ## `azure-tts-client` の利用方法
189
+
190
+ `AzureTtsClient` に Azure Speech のサブスクリプションキーとリージョンを渡し、`synthesize` に SSML を渡します。戻り値は音声データの `ArrayBuffer` です。
191
+
192
+ ```ts
193
+ import { AzureTtsClient } from "ssml-builder-js";
194
+
195
+ const client = new AzureTtsClient({
196
+ subscriptionKey: process.env.AZURE_SPEECH_KEY!,
197
+ region: "japaneast",
198
+ });
199
+
200
+ const audio = await client.synthesize(ssml);
201
+ // audio は audio/mpeg の ArrayBuffer
202
+ ```
203
+
204
+ 低レベルの `synthesizeSpeech` 関数も利用できます。この関数では `TtsConfig` の
205
+ `endpoint`、`subscriptionKey`、`region` を指定します。
206
+
207
+ ```ts
208
+ import { synthesizeSpeech } from "ssml-builder-js";
209
+
210
+ const audio = await synthesizeSpeech(ssml, {
211
+ endpoint: "https://japaneast.tts.speech.microsoft.com/tts/cognitiveservices/websocket/v1",
212
+ subscriptionKey: process.env.AZURE_SPEECH_KEY!,
213
+ region: "japaneast",
214
+ });
215
+ ```
216
+
217
+ 内部では Microsoft Cognitive Services Speech SDK の `SpeechSynthesizer` を使用します。`AzureTtsClient` の `endpoint` を省略すると `https://{region}.tts.speech.microsoft.com/cognitiveservices/v1` が使用されます。上の Playground の例では `.env.example` に合わせて WebSocket エンドポイントを明示しています。独自エンドポイントに `{region}` を含めた場合は、設定したリージョンに置き換えられます。
218
+ `outputFormat` には Speech SDK がサポートする出力形式を指定できます。省略時は `audio-16khz-128kbitrate-mono-mp3` が使用されます。
219
+
220
+ Speech SDK の合成エラーでは `AzureTtsSdkError`(`AzureTtsError` のサブクラス)がスローされ、`errorDetails` または `responseBody` から SDK のエラー詳細を確認できます。SDK が HTTP ステータスやリクエスト ID を公開しないため、SDK 経由のエラーでは `status` は `0`、`statusText` は `"Speech SDK"`、`requestId` は `null` です。Playground のサーバー側ログにもこれらの情報とリージョン、SSML の文字数が出力されます。ログに出力するエラー詳細は 4,096 文字までに制限されます。サブスクリプションキーや SSML 本文自体はログに出力されません。
221
+
222
+ サブスクリプションキーはリクエストヘッダーに含まれるため、ソースコードへハードコードしたりログへ出力したりしないでください。ブラウザから直接呼び出す場合はキーが利用者へ公開されるため、通常はサーバー側で Azure TTS を呼び出す構成にします。
223
+
224
+ ## 仕様と参照元
225
+
226
+ このプロジェクトは Azure Speech における SSML の実装を主な対象としています。Azure Speech の SSML 実装は W3C の SSML Version 1.0 をベースにしていますが、対応要素や動作は W3C 標準と異なる場合があり、Azure 固有の `mstts:` 拡張も含まれます。
227
+
228
+ - [Azure Speech SSML のドキュメント構造とイベント(Microsoft Learn)](https://learn.microsoft.com/azure/ai-services/speech-service/speech-synthesis-markup-structure)
229
+ - [Azure Speech SSML リファレンス(Microsoft Learn)](https://learn.microsoft.com/azure/ai-services/speech-service/speech-synthesis-markup)
230
+ - [Speech Synthesis Markup Language (SSML) Version 1.0(W3C Recommendation)](https://www.w3.org/TR/2004/REC-speech-synthesis-20040907/)
231
+ - 参照バージョン: **W3C SSML 1.0**(2004 年 9 月 7 日勧告)。Microsoft Learn の Azure Speech ドキュメントには固定された製品バージョン番号がないため、利用時は上記リンク先の最新の仕様も確認してください。
232
+
233
+ ## 開発用コマンド
234
+
235
+ リポジトリのルートで次のコマンドを実行できます。
236
+
237
+ | コマンド | 内容 |
238
+ | --- | --- |
239
+ | `npm run format` | Biome によるフォーマットチェック |
240
+ | `npm run format:write` | Biome によるフォーマット適用 |
241
+ | `npm run lint` | Biome による静的チェック |
242
+ | `npm run check` | `format` と lint をまとめた Biome チェック |
243
+ | `npm run typecheck` | 全ワークスペースの TypeScript 型チェック |
244
+ | `npm run build` | 各パッケージのビルド |
245
+ | `npm test` | 各パッケージのテスト |
246
+
247
+ CI と同じ確認をまとめて行う場合は、次のコマンドを使用します。
248
+
249
+ ```sh
250
+ npm run format
251
+ npm run lint
252
+ npm run typecheck
253
+ npm run build
254
+ npm test
255
+ ```
256
+
257
+ `dist/` と `packages/*/dist` はビルド時に生成されるファイルのため、直接編集したりコミットしたりしないでください。
258
+
259
+ ## English
260
+
261
+ SSML-Builder is an npm workspace monorepo for generating and parsing SSML supported by Azure Speech Service from TypeScript data structures and editing it in a React GUI. Its published output is a single npm package, `ssml-builder-js`.
262
+
263
+ It provides a core library with XML escaping and Azure Speech extension support, a React component based on Monaco Editor, and an Azure Text-to-Speech API client through package subpaths.
264
+
265
+ ## Package structure
266
+
267
+ | Subpath | Description |
268
+ | --- | --- |
269
+ | `ssml-builder-js` | Core functionality and the Azure Text-to-Speech client |
270
+ | `ssml-builder-js/core` | SSML type definitions, document generation (`buildSsml`), XML parsing (`parseSsml`), and syntax validation (`validateSsml`) |
271
+ | `ssml-builder-js/react` | The `SsmlEditor` component with a toolbar and text display area |
272
+
273
+ `ssml-builder-js` and `ssml-builder-js/core` provide the same core functionality. Import the React editor from `ssml-builder-js/react`; the Azure TTS client is available from `ssml-builder-js`.
274
+
275
+ ## Setup
276
+
277
+ ### Using the npm packages
278
+
279
+ Install the package required by your application:
280
+
281
+ ```sh
282
+ npm install ssml-builder-js
283
+ ```
284
+
285
+ To use the React editor, also install React and the Monaco Editor adapter:
286
+
287
+ ```sh
288
+ npm install ssml-builder-js @monaco-editor/react react react-dom
289
+ ```
290
+
291
+ The Azure TTS client is also available from `ssml-builder-js`.
292
+
293
+ ### Developing this repository
294
+
295
+ Install Node.js 24 or later, then install dependencies from the repository root:
296
+
297
+ ```sh
298
+ npm ci
299
+ ```
300
+
301
+ ### Generating audio in the playground
302
+
303
+ The **Generate audio** button in `apps/playground` sends the current SSML to a
304
+ server-side Next.js Route Handler, which uses `ssml-builder-js` to
305
+ generate speech with Azure Speech. To keep the Azure subscription key out of the
306
+ browser, set the following values in `apps/playground/.env.local`:
307
+
308
+ ```dotenv
309
+ AZURE_SPEECH_KEY=your-subscription-key
310
+ AZURE_SPEECH_REGION=japaneast
311
+ # AZURE_SPEECH_ENDPOINT=https://{region}.tts.speech.microsoft.com/tts/cognitiveservices/websocket/v1
312
+ ```
313
+
314
+ Copy `apps/playground/.env.example` to get started. Then run the playground from the repository root:
315
+
316
+ ```sh
317
+ cp apps/playground/.env.example apps/playground/.env.local
318
+ npm run dev --workspace playground
319
+ ```
320
+
321
+ ## Using `ssml-core`
322
+
323
+ `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`.
324
+
325
+ `lang` is the BCP-47 tag for speech synthesis and is set on `SsmlDocument` by the package consumer. `SsmlEditor`'s `locale` prop controls the UI language and is separate from the speech language.
326
+
327
+ ```ts
328
+ import { buildSsml, parseSsml } from "ssml-builder-js/core";
329
+ import type { SsmlDocument } from "ssml-builder-js/core";
330
+
331
+ const document: SsmlDocument = {
332
+ version: "1.0",
333
+ lang: "en-US",
334
+ children: [
335
+ {
336
+ type: "voice",
337
+ name: "en-US-JennyNeural",
338
+ children: [
339
+ {
340
+ type: "prosody",
341
+ rate: "medium",
342
+ pitch: "+2st",
343
+ children: ["Hello."],
344
+ },
345
+ { type: "break", time: "300ms" },
346
+ {
347
+ type: "express-as",
348
+ style: "cheerful",
349
+ children: ["This is SSML Builder."],
350
+ },
351
+ ],
352
+ },
353
+ ],
354
+ };
355
+
356
+ // Generate an XML string from an SsmlDocument.
357
+ const ssml = buildSsml(document);
358
+
359
+ // Parse an XML string back into an SsmlDocument.
360
+ const parsed = parseSsml(ssml);
361
+ ```
362
+
363
+ The main `buildSsml` and `parseSsml` signatures are:
364
+
365
+ | API | Description |
366
+ | --- | --- |
367
+ | `buildSsml(document)` | Converts an SSML document into an XML string |
368
+ | `buildSsml(content, lang?)` | Convenience form that creates an `SsmlDocument` from text (legacy `content` form) |
369
+ | `buildPartialSsml(text, context?)` / `buildPartialSsml({ text, ...context })` | Builds minimal playable SSML for partial text with language, voice, and prosody context |
370
+ | `parseSsml(xml)` | Converts a `<speak>` XML document into an `SsmlDocument` |
371
+ | `validateSsml(xml)` | Returns `{ message, position }` for a syntax error, or `null` |
372
+
373
+ 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.
374
+
375
+ ## Using `ssml-editor-react`
376
+
377
+ `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 `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.
378
+
379
+ ```tsx
380
+ import { useState } from "react";
381
+ import { SsmlEditor } from "ssml-builder-js/react";
382
+ import type { SsmlDocument } from "ssml-builder-js/core";
383
+
384
+ const initialDocument: SsmlDocument = {
385
+ version: "1.0",
386
+ lang: "en-US",
387
+ children: ["Text to edit"],
388
+ };
389
+
390
+ export function App() {
391
+ const [document, setDocument] = useState(initialDocument);
392
+ const [ssml, setSsml] = useState("");
393
+
394
+ return (
395
+ <>
396
+ <SsmlEditor
397
+ document={document}
398
+ onChange={setDocument}
399
+ onSsmlChange={setSsml}
400
+ locale="en"
401
+ />
402
+ <pre>{ssml}</pre>
403
+ </>
404
+ );
405
+ }
406
+ ```
407
+
408
+ - `document`: The `SsmlDocument` being edited
409
+ - `onChange`: A callback that receives the edited `SsmlDocument`
410
+ - `onSsmlChange`: A callback that receives the generated SSML string
411
+ - `ref`: An `SsmlEditorRef`; `getFullSsml()` returns the full SSML, `getSelectedSsml()` returns the selected text (or the cursor line when no text is selected), and `getCurrentLineSsml()` returns the current cursor line
412
+ - `onSelectionChange`: A callback that receives selected text, its character count, and whether a selection exists
413
+ - `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
414
+ - `locale`: The UI language (`"ja"` or `"en"`); defaults to `"ja"` and also controls hover-help translations
415
+ - `language`: Legacy name for `locale`, retained for compatibility; use `locale` in new code
416
+ - `showToolbar`: Whether to display the toolbar (defaults to `true`)
417
+ - `showToolbarIcons`: Whether to show toolbar icons (defaults to `true`)
418
+ - `showToolbarLabels`: Whether to show text labels on the toolbar (defaults to `false`); when omitted, hover over an icon to see its description
419
+ - `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
420
+ - `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
421
+ - `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
422
+ - `loadingFallback`: A React node displayed while Monaco is loading
423
+ - `toolbarOrder`: Display order for all toolbar button IDs; unlisted buttons follow
424
+ - `toolbarGroups`: Groups all toolbar buttons with vertical separators using `{ id, buttonIds }`; groups are not rendered with borders
425
+ - `insertionOrder`: Display order for insertion menu IDs; also supplies the insertion order used when `toolbarOrder` is omitted
426
+ - `insertionGroups`: Groups insertion menus with vertical separators; when `toolbarGroups` is omitted, these groups also define the default toolbar groups. Menus are grouped into pauses, voice, expression, and pronunciation by default
427
+ - `emotionStyles`: Candidate voice styles shown by the `emotion` menu
428
+ - `customInsertions` / `additionalInsertions`: Custom SSML insertion definitions. `customInsertions` replaces a built-in definition with the same ID, while `additionalInsertions` adds definitions to the built-ins
429
+ - `className` / `style`: A class name and inline styles for the editor container
430
+ - `toolbarClassName` / `toolbarStyle`: A class name and inline styles for the toolbar
431
+ - `displayClassName` / `displayStyle`: A class name and inline styles for the text display area
432
+ - Use the **Format** button to format the XML in the text display area
433
+ - Standalone elements (`break`, `mstts:silence`, and custom insertions with `mode: "insert"`) are automatically placed on separate lines; elements that wrap a selection remain inline
434
+ - Changing the text validates SSML syntax and displays errors in the editor
435
+
436
+ 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`.
437
+
438
+ 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.
439
+
440
+ ## Using `azure-tts-client`
441
+
442
+ Pass an Azure Speech subscription key and region to `AzureTtsClient`, then pass SSML to `synthesize`. The return value is an `ArrayBuffer` containing audio data.
443
+
444
+ ```ts
445
+ import { AzureTtsClient } from "ssml-builder-js";
446
+
447
+ const client = new AzureTtsClient({
448
+ subscriptionKey: process.env.AZURE_SPEECH_KEY!,
449
+ region: "japaneast",
450
+ });
451
+
452
+ const audio = await client.synthesize(ssml);
453
+ // audio is an audio/mpeg ArrayBuffer
454
+ ```
455
+
456
+ The lower-level `synthesizeSpeech` function is also available. It requires
457
+ `endpoint`, `subscriptionKey`, and `region` in its `TtsConfig` argument.
458
+
459
+ ```ts
460
+ import { synthesizeSpeech } from "ssml-builder-js";
461
+
462
+ const audio = await synthesizeSpeech(ssml, {
463
+ endpoint: "https://japaneast.tts.speech.microsoft.com/tts/cognitiveservices/websocket/v1",
464
+ subscriptionKey: process.env.AZURE_SPEECH_KEY!,
465
+ region: "japaneast",
466
+ });
467
+ ```
468
+
469
+ Internally, the client uses the Microsoft Cognitive Services Speech SDK's `SpeechSynthesizer`. If `endpoint` is omitted from `AzureTtsClient`, `https://{region}.tts.speech.microsoft.com/cognitiveservices/v1` is used. The Playground example explicitly uses the WebSocket endpoint from `.env.example`. If a custom endpoint contains `{region}`, it is replaced with the configured region.
470
+ Set `outputFormat` to a format supported by the Speech SDK. If omitted, `audio-16khz-128kbitrate-mono-mp3` is used.
471
+
472
+ Speech SDK synthesis errors throw `AzureTtsSdkError` (a subclass of `AzureTtsError`); its `errorDetails` and `responseBody` fields contain the SDK error details. Because the SDK does not expose HTTP status or request IDs, SDK errors use `0` for `status`, `"Speech SDK"` for `statusText`, and `null` for `requestId`. The playground's server-side logs include these fields along with the region and SSML character count. Logged error details are limited to 4,096 characters. The subscription key and SSML content itself are not written to logs.
473
+
474
+ The subscription key is sent in a request header, so do not hard-code it in source code or write it to logs. Calling Azure TTS directly from a browser exposes the key to users; a server-side Azure TTS integration is normally recommended.
475
+
476
+ ## Specifications and references
477
+
478
+ This project primarily targets the SSML implementation provided by Azure Speech. Azure Speech's SSML implementation is based on W3C SSML Version 1.0, but its supported elements and behavior can differ from the W3C standard and include Azure-specific `mstts:` extensions.
479
+
480
+ - [Azure Speech SSML document structure and events (Microsoft Learn)](https://learn.microsoft.com/azure/ai-services/speech-service/speech-synthesis-markup-structure)
481
+ - [Azure Speech SSML reference (Microsoft Learn)](https://learn.microsoft.com/azure/ai-services/speech-service/speech-synthesis-markup)
482
+ - [Speech Synthesis Markup Language (SSML) Version 1.0 (W3C Recommendation)](https://www.w3.org/TR/2004/REC-speech-synthesis-20040907/)
483
+ - Reference version: **W3C SSML 1.0** (Recommendation dated September 7, 2004). Microsoft Learn's Azure Speech documentation does not expose a fixed product version, so check the linked documentation for the latest Azure behavior when using this project.
484
+
485
+ ## Development commands
486
+
487
+ Run the following commands from the repository root:
488
+
489
+ | Command | Description |
490
+ | --- | --- |
491
+ | `npm run format` | Check formatting with Biome |
492
+ | `npm run format:write` | Apply formatting with Biome |
493
+ | `npm run lint` | Run static checks with Biome |
494
+ | `npm run check` | Run Biome formatting and lint checks together |
495
+ | `npm run typecheck` | Type-check all workspaces with TypeScript |
496
+ | `npm run build` | Build each package |
497
+ | `npm test` | Run tests for each package |
498
+
499
+ To run the same checks as CI:
500
+
501
+ ```sh
502
+ npm run format
503
+ npm run lint
504
+ npm run typecheck
505
+ npm run build
506
+ npm test
507
+ ```
508
+
509
+ `dist/` and `packages/*/dist` contain generated build files; do not edit or commit them directly.
@@ -0,0 +1,20 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __typeError = (msg) => {
3
+ throw TypeError(msg);
4
+ };
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
10
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
11
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
12
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
13
+
14
+ export {
15
+ __export,
16
+ __privateGet,
17
+ __privateAdd,
18
+ __privateSet
19
+ };
20
+ //# sourceMappingURL=chunk-6S5ODO6A.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}