@davmash/bygone 0.8.5 → 0.8.7

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.
@@ -481,6 +481,9 @@ function parseChangeTourManifest(value) {
481
481
  }
482
482
  requireString(value.title, "title");
483
483
  requireString(value.generatedAt, "generatedAt");
484
+ if (value.windowTitle !== void 0) {
485
+ requireString(value.windowTitle, "windowTitle");
486
+ }
484
487
  if (value.sourceUrl !== void 0) {
485
488
  requireString(value.sourceUrl, "sourceUrl");
486
489
  }
@@ -744,8 +747,9 @@ function parseChangeTourSource(value) {
744
747
  if (!isRecord2(value) || value.version !== CHANGE_TOUR_SOURCE_VERSION) {
745
748
  throw new Error("Unsupported or missing change-tour source version.");
746
749
  }
747
- requireOnlyKeys(value, ["version", "title", "sourceUrl", "range", "anchors", "connections", "chapters", "coverage"], "source");
750
+ requireOnlyKeys(value, ["version", "title", "windowTitle", "sourceUrl", "range", "anchors", "connections", "chapters", "coverage"], "source");
748
751
  optionalString(value.title, "title");
752
+ optionalString(value.windowTitle, "windowTitle");
749
753
  optionalString(value.sourceUrl, "sourceUrl");
750
754
  if (value.range !== void 0) {
751
755
  if (!isRecord2(value.range)) throw new Error("range must be an object.");
@@ -1162,7 +1166,7 @@ var CHAPTERS = {
1162
1166
  packaging: { id: "packaging", title: "Packaging and dependencies", priority: 4 }
1163
1167
  };
1164
1168
  function buildChangeTourManifest(startPath, options = {}) {
1165
- var _a, _b, _c, _d;
1169
+ var _a, _b, _c, _d, _e;
1166
1170
  const range = resolveBranchReviewRange(startPath, options.headRef, options.baseRef);
1167
1171
  const maxFileBytes = options.maxFileBytes ?? DEFAULT_MAX_TOUR_FILE_BYTES;
1168
1172
  const maxLineBytes = options.maxLineBytes ?? DEFAULT_MAX_TOUR_LINE_BYTES;
@@ -1234,7 +1238,8 @@ function buildChangeTourManifest(startPath, options = {}) {
1234
1238
  const manifest = {
1235
1239
  version: CHANGE_TOUR_MANIFEST_VERSION,
1236
1240
  title: ((_a = options.source) == null ? void 0 : _a.title) || ((_b = options.story) == null ? void 0 : _b.title) || options.title || `${range.headRef} against ${range.baseRef}`,
1237
- sourceUrl: ((_c = options.source) == null ? void 0 : _c.sourceUrl) || ((_d = options.story) == null ? void 0 : _d.sourceUrl) || options.sourceUrl,
1241
+ windowTitle: (_c = options.source) == null ? void 0 : _c.windowTitle,
1242
+ sourceUrl: ((_d = options.source) == null ? void 0 : _d.sourceUrl) || ((_e = options.story) == null ? void 0 : _e.sourceUrl) || options.sourceUrl,
1238
1243
  generatedAt: options.generatedAt || (/* @__PURE__ */ new Date()).toISOString(),
1239
1244
  range: {
1240
1245
  baseRef: range.baseRef,
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/windowTitle.ts
21
+ var windowTitle_exports = {};
22
+ __export(windowTitle_exports, {
23
+ MAX_WINDOW_TITLE_LENGTH: () => MAX_WINDOW_TITLE_LENGTH,
24
+ buildHistoryTitle: () => buildHistoryTitle,
25
+ buildHistoryTitleFromViewState: () => buildHistoryTitleFromViewState,
26
+ buildMultiPanelTitle: () => buildMultiPanelTitle,
27
+ buildStandaloneSessionTitle: () => buildStandaloneSessionTitle,
28
+ buildTourWindowTitle: () => buildTourWindowTitle,
29
+ buildTwoWayComparisonTitle: () => buildTwoWayComparisonTitle,
30
+ formatComparisonPair: () => formatComparisonPair,
31
+ truncateTitle: () => truncateTitle
32
+ });
33
+ module.exports = __toCommonJS(windowTitle_exports);
34
+ var MAX_WINDOW_TITLE_LENGTH = 120;
35
+ function truncateTitle(text, max = MAX_WINDOW_TITLE_LENGTH) {
36
+ if (text.length <= max) {
37
+ return text;
38
+ }
39
+ return `${text.slice(0, max - 1)}\u2026`;
40
+ }
41
+ function formatComparisonPair(left, right) {
42
+ return `${left} \u2194 ${right}`;
43
+ }
44
+ function buildMultiPanelTitle(input) {
45
+ const { panels, activePanelId, activePairIndex, panelIds } = input;
46
+ if (panels.length === 0) {
47
+ return "Multi-Panel Compare";
48
+ }
49
+ if (panels.length === 1) {
50
+ return truncateTitle(panels[0].label);
51
+ }
52
+ if (panels.length === 2) {
53
+ return truncateTitle(formatComparisonPair(panels[0].label, panels[1].label));
54
+ }
55
+ const ids = panelIds ?? panels.map((_panel, index) => String(index));
56
+ const activeIndex = activePanelId ? ids.findIndex((id) => id === activePanelId) : -1;
57
+ if (activePairIndex !== null && activePairIndex !== void 0 && activePairIndex >= 0 && activePairIndex < panels.length - 1) {
58
+ return truncateTitle(formatComparisonPair(
59
+ panels[activePairIndex].label,
60
+ panels[activePairIndex + 1].label
61
+ ));
62
+ }
63
+ if (activeIndex >= 0) {
64
+ const derivedPairIndex = activeIndex < panels.length - 1 ? activeIndex : Math.max(0, activeIndex - 1);
65
+ return truncateTitle(formatComparisonPair(
66
+ panels[derivedPairIndex].label,
67
+ panels[derivedPairIndex + 1].label
68
+ ));
69
+ }
70
+ return truncateTitle(formatComparisonPair(panels[0].label, panels[panels.length - 1].label));
71
+ }
72
+ function buildHistoryTitle(fileName, options) {
73
+ const parts = [`${fileName} History`];
74
+ if (options == null ? void 0 : options.shortCommit) {
75
+ parts.push(options.shortCommit);
76
+ }
77
+ if (options == null ? void 0 : options.positionLabel) {
78
+ parts.push(`(${options.positionLabel})`);
79
+ }
80
+ return truncateTitle(parts.join(" \u2014 "));
81
+ }
82
+ function buildHistoryTitleFromViewState(fileName, history) {
83
+ const shortCommit = history.rightCommitLabel.split(/\s+/)[0] || void 0;
84
+ return buildHistoryTitle(fileName, {
85
+ shortCommit,
86
+ positionLabel: history.positionLabel
87
+ });
88
+ }
89
+ function buildTwoWayComparisonTitle(left, right) {
90
+ return truncateTitle(formatComparisonPair(left, right));
91
+ }
92
+ function buildTourWindowTitle(manifest, appName = "Bygone") {
93
+ var _a, _b;
94
+ const label = ((_a = manifest == null ? void 0 : manifest.windowTitle) == null ? void 0 : _a.trim()) || ((_b = manifest == null ? void 0 : manifest.title) == null ? void 0 : _b.trim());
95
+ return label ? truncateTitle(label) : `${appName} Tour`;
96
+ }
97
+ function buildStandaloneSessionTitle(session) {
98
+ var _a, _b, _c, _d;
99
+ if (session.mode === "multi-diff" && session.multi) {
100
+ return buildMultiPanelTitle({
101
+ panels: session.multi.files,
102
+ activePanelId: session.multi.activePanelId,
103
+ activePairIndex: session.multi.activePairIndex,
104
+ panelIds: session.multi.files.map((file) => file.id)
105
+ });
106
+ }
107
+ if (session.mode === "history" && ((_a = session.history) == null ? void 0 : _a.filePath)) {
108
+ const entry = (_b = session.history.entries) == null ? void 0 : _b[session.history.index ?? 0];
109
+ return buildHistoryTitle(
110
+ session.history.filePath.split("/").pop() || "File",
111
+ { shortCommit: entry == null ? void 0 : entry.shortCommit }
112
+ );
113
+ }
114
+ if (session.mode === "directory-history" && session.dirHistory) {
115
+ const name = session.dirHistory.displayName || session.dirHistory.viewRelativePath || "Directory";
116
+ return truncateTitle(`${name} Directory History`);
117
+ }
118
+ if (session.mode === "directory" && session.directory) {
119
+ if ((_c = session.directory.review) == null ? void 0 : _c.headRef) {
120
+ return truncateTitle(`${session.directory.review.headRef} Branch Change`);
121
+ }
122
+ if ((_d = session.directory.labels) == null ? void 0 : _d.length) {
123
+ return truncateTitle(session.directory.labels.join(" \u2194 "));
124
+ }
125
+ }
126
+ if (session.left && session.right) {
127
+ return buildTwoWayComparisonTitle(session.left.label, session.right.label);
128
+ }
129
+ return "Compare";
130
+ }
131
+ // Annotate the CommonJS export names for ESM import in node:
132
+ 0 && (module.exports = {
133
+ MAX_WINDOW_TITLE_LENGTH,
134
+ buildHistoryTitle,
135
+ buildHistoryTitleFromViewState,
136
+ buildMultiPanelTitle,
137
+ buildStandaloneSessionTitle,
138
+ buildTourWindowTitle,
139
+ buildTwoWayComparisonTitle,
140
+ formatComparisonPair,
141
+ truncateTitle
142
+ });
143
+ //# sourceMappingURL=windowTitle.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@davmash/bygone",
3
- "version": "0.8.5",
3
+ "version": "0.8.7",
4
4
  "description": "See how change happened. Visual diff and file history.",
5
5
  "author": "David Mashburn",
6
6
  "license": "MIT",
@@ -9,6 +9,10 @@
9
9
  "properties": {
10
10
  "version": { "const": 1 },
11
11
  "title": { "$ref": "#/$defs/nonEmptyString" },
12
+ "windowTitle": {
13
+ "$ref": "#/$defs/nonEmptyString",
14
+ "description": "Optional native window title for the tour presenter. Use a short label such as a pull request number when multiple tours may be open."
15
+ },
12
16
  "sourceUrl": { "type": "string", "minLength": 1 },
13
17
  "range": {
14
18
  "type": "object",
@@ -171,6 +171,10 @@ context, and keep the file within the corresponding repository so Bygone can
171
171
  resolve those objects. Use a compiled `.tour.json` only when a portable
172
172
  snapshot is required.
173
173
 
174
+ Set optional `windowTitle` when the tour should appear in the native window
175
+ title — for example a pull request number (`PR-1234`) so multiple open tours
176
+ stay distinguishable. When omitted, the presenter falls back to `title`.
177
+
174
178
  For every step:
175
179
 
176
180
  - make one concise explanatory claim;
@@ -30,13 +30,13 @@
30
30
  <div id="change-toolbar" class="change-toolbar header-align-diff" hidden>
31
31
  <div class="change-toolbar-main">
32
32
  <div class="change-toolbar-nav">
33
- <button id="previous-file" class="change-button icon-button" type="button" title="Open previous file" aria-label="Previous file">
33
+ <button id="previous-file" class="change-button icon-button" type="button" title="Open previous file" data-tooltip="Open previous file" aria-label="Previous file">
34
34
  <svg viewBox="0 0 24 24" aria-hidden="true">
35
35
  <path d="M17 11l-5-5-5 5"></path>
36
36
  <path d="M17 18l-5-5-5 5" opacity="0.7"></path>
37
37
  </svg>
38
38
  </button>
39
- <button id="next-file" class="change-button change-button-primary icon-button" type="button" title="Open next file" aria-label="Next file">
39
+ <button id="next-file" class="change-button change-button-primary icon-button" type="button" title="Open next file" data-tooltip="Open next file" aria-label="Next file">
40
40
  <svg viewBox="0 0 24 24" aria-hidden="true">
41
41
  <path d="M7 13l5 5 5-5"></path>
42
42
  <path d="M7 6l5 5 5-5" opacity="0.7"></path>
@@ -72,8 +72,15 @@
72
72
  </svg>
73
73
  </button>
74
74
  </div>
75
+ <div class="change-toolbar-actions">
75
76
  <button id="toggle-word-wrap" class="change-button icon-button" type="button" title="Enable line wrapping (Alt+Z)" aria-label="Enable line wrapping" aria-pressed="false" hidden>
76
- <svg viewBox="0 0 24 24" aria-hidden="true">
77
+ <svg class="word-wrap-icon-off" viewBox="0 0 24 24" aria-hidden="true">
78
+ <path d="M4 6h13a3 3 0 0 1 0 6H9"></path>
79
+ <path d="M12 9l-3 3 3 3"></path>
80
+ <path d="M4 18h8"></path>
81
+ <path d="M5 4l14 16"></path>
82
+ </svg>
83
+ <svg class="word-wrap-icon-on" viewBox="0 0 24 24" aria-hidden="true">
77
84
  <path d="M4 6h13a3 3 0 0 1 0 6H9"></path>
78
85
  <path d="M12 9l-3 3 3 3"></path>
79
86
  <path d="M4 18h8"></path>
@@ -85,6 +92,7 @@
85
92
  <path d="M20 5v6h-6"></path>
86
93
  </svg>
87
94
  </button>
95
+ </div>
88
96
  </div>
89
97
  <div class="change-hint">F7 / Shift+F7 · Cmd/Ctrl+Shift+Up/Down to jump.</div>
90
98
  </div>
@@ -94,14 +102,14 @@
94
102
  <div id="history-left-time" class="history-time"></div>
95
103
  </div>
96
104
  <div class="history-nav">
97
- <button id="history-back" class="history-button icon-button" type="button" title="Open older commit (Alt+Left)" aria-label="Older commit">
105
+ <button id="history-back" class="history-button icon-button" type="button" title="Open older commit (Alt+Left)" data-tooltip="Open older commit" aria-label="Older commit">
98
106
  <svg viewBox="0 0 24 24" aria-hidden="true">
99
107
  <path d="M15 18l-6-6 6-6"></path>
100
108
  <path d="M10 18l-6-6 6-6" opacity="0.7"></path>
101
109
  </svg>
102
110
  </button>
103
111
  <div id="history-position" class="history-position" role="status" aria-live="polite" aria-atomic="true"></div>
104
- <button id="history-forward" class="history-button icon-button" type="button" title="Open newer commit (Alt+Right)" aria-label="Newer commit">
112
+ <button id="history-forward" class="history-button icon-button" type="button" title="Open newer commit (Alt+Right)" data-tooltip="Open newer commit" aria-label="Newer commit">
105
113
  <svg viewBox="0 0 24 24" aria-hidden="true">
106
114
  <path d="M9 18l6-6-6-6"></path>
107
115
  <path d="M14 18l6-6-6-6" opacity="0.7"></path>
package/web/index.html CHANGED
@@ -60,6 +60,34 @@
60
60
  <span id="tour-breadcrumb" class="tour-breadcrumb"></span>
61
61
  <button id="tour-next" type="button" title="Next tour item (Right or Page Down)">Next →</button>
62
62
  </nav>
63
+ <div id="tour-narration-controls" class="tour-narration-controls" role="group" aria-label="Tour narration">
64
+ <div class="tour-narration-transport" role="group" aria-label="Narration playback">
65
+ <button id="tour-narration-skip-back" type="button" title="Previous sentence" aria-label="Previous sentence" disabled>
66
+ <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M3 12a9 9 0 1 0 3-6.7L3 8"/><path d="M3 3v5h5"/></svg>
67
+ </button>
68
+ <button id="tour-listen" type="button" title="Listen from the current tour item" aria-label="Listen from the current tour item">
69
+ <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path class="tour-icon-fill tour-play-pause-icon" d="m8 5 11 7-11 7Z"/></svg>
70
+ </button>
71
+ <button id="tour-stop" type="button" title="Stop narration" aria-label="Stop narration" disabled>
72
+ <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path class="tour-icon-fill" d="M6 6h12v12H6z"/></svg>
73
+ </button>
74
+ <button id="tour-narration-skip-ahead" type="button" title="Next sentence" aria-label="Next sentence" disabled>
75
+ <svg viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M21 12a9 9 0 1 1-3-6.7L21 8"/><path d="M21 3v5h-5"/></svg>
76
+ </button>
77
+ </div>
78
+ <div class="tour-narration-settings">
79
+ <select id="tour-narration-voice" aria-label="Narration voice" title="Narration voice" disabled>
80
+ <option value="">System default voice</option>
81
+ </select>
82
+ <select id="tour-narration-rate" aria-label="Narration speed" title="Narration speed">
83
+ <option value="0.75">0.75×</option>
84
+ <option value="1">1×</option>
85
+ <option value="1.25">1.25×</option>
86
+ <option value="1.5">1.5×</option>
87
+ </select>
88
+ </div>
89
+ <span id="tour-narration-status" class="tour-narration-status" role="status" aria-live="polite" aria-atomic="true">Device voice ready</span>
90
+ </div>
63
91
  <div class="tour-narrative-layout">
64
92
  <div class="tour-narrative-prose">
65
93
  <div id="tour-narrative-chapter" class="tour-narrative-chapter"></div>
@@ -113,13 +141,13 @@
113
141
  <div id="change-toolbar" class="change-toolbar header-align-diff" hidden>
114
142
  <div class="change-toolbar-main">
115
143
  <div class="change-toolbar-nav">
116
- <button id="previous-file" class="change-button icon-button" type="button" title="Open previous file" aria-label="Previous file">
144
+ <button id="previous-file" class="change-button icon-button" type="button" title="Open previous file" data-tooltip="Open previous file" aria-label="Previous file">
117
145
  <svg viewBox="0 0 24 24" aria-hidden="true">
118
146
  <path d="M17 11l-5-5-5 5"></path>
119
147
  <path d="M17 18l-5-5-5 5"></path>
120
148
  </svg>
121
149
  </button>
122
- <button id="next-file" class="change-button icon-button" type="button" title="Open next file" aria-label="Next file">
150
+ <button id="next-file" class="change-button icon-button" type="button" title="Open next file" data-tooltip="Open next file" aria-label="Next file">
123
151
  <svg viewBox="0 0 24 24" aria-hidden="true">
124
152
  <path d="M7 13l5 5 5-5"></path>
125
153
  <path d="M7 6l5 5 5-5"></path>
@@ -155,8 +183,15 @@
155
183
  </svg>
156
184
  </button>
157
185
  </div>
186
+ <div class="change-toolbar-actions">
158
187
  <button id="toggle-word-wrap" class="change-button icon-button" type="button" title="Enable line wrapping (Alt+Z)" aria-label="Enable line wrapping" aria-pressed="false" hidden>
159
- <svg viewBox="0 0 24 24" aria-hidden="true">
188
+ <svg class="word-wrap-icon-off" viewBox="0 0 24 24" aria-hidden="true">
189
+ <path d="M4 6h13a3 3 0 0 1 0 6H9"></path>
190
+ <path d="M12 9l-3 3 3 3"></path>
191
+ <path d="M4 18h8"></path>
192
+ <path d="M5 4l14 16"></path>
193
+ </svg>
194
+ <svg class="word-wrap-icon-on" viewBox="0 0 24 24" aria-hidden="true">
160
195
  <path d="M4 6h13a3 3 0 0 1 0 6H9"></path>
161
196
  <path d="M12 9l-3 3 3 3"></path>
162
197
  <path d="M4 18h8"></path>
@@ -168,6 +203,7 @@
168
203
  <path d="M20 5v6h-6"></path>
169
204
  </svg>
170
205
  </button>
206
+ </div>
171
207
  </div>
172
208
  <div class="change-hint">F7 / Shift+F7 · Cmd/Ctrl+Shift+Up/Down to jump.</div>
173
209
  </div>
@@ -177,14 +213,14 @@
177
213
  <div id="history-left-time" class="history-time"></div>
178
214
  </div>
179
215
  <div class="history-nav">
180
- <button id="history-back" class="history-button icon-button" type="button" title="Open older commit" aria-label="Older commit">
216
+ <button id="history-back" class="history-button icon-button" type="button" title="Open older commit" data-tooltip="Open older commit" aria-label="Older commit">
181
217
  <svg viewBox="0 0 24 24" aria-hidden="true">
182
218
  <path d="M15 18l-6-6 6-6"></path>
183
219
  <path d="M10 18l-6-6 6-6" opacity="0.7"></path>
184
220
  </svg>
185
221
  </button>
186
222
  <div id="history-position" class="history-position" role="status" aria-live="polite" aria-atomic="true"></div>
187
- <button id="history-forward" class="history-button icon-button" type="button" title="Open newer commit" aria-label="Newer commit">
223
+ <button id="history-forward" class="history-button icon-button" type="button" title="Open newer commit" data-tooltip="Open newer commit" aria-label="Newer commit">
188
224
  <svg viewBox="0 0 24 24" aria-hidden="true">
189
225
  <path d="M9 18l6-6-6-6"></path>
190
226
  <path d="M14 18l6-6-6-6" opacity="0.7"></path>
package/web/presenter.css CHANGED
@@ -319,6 +319,121 @@ body.tour-discussion .tour-narrative-resizer {
319
319
  cursor: default;
320
320
  }
321
321
 
322
+ .tour-narration-controls {
323
+ display: flex;
324
+ flex-wrap: wrap;
325
+ gap: 6px;
326
+ align-items: center;
327
+ margin: -4px 0 12px;
328
+ padding: 5px;
329
+ border: 1px solid var(--tour-border);
330
+ border-radius: 8px;
331
+ background: rgba(255, 255, 255, 0.025);
332
+ }
333
+
334
+ .tour-narration-controls select {
335
+ height: 28px;
336
+ padding: 3px 7px;
337
+ color: var(--tour-muted);
338
+ border: 1px solid var(--tour-border);
339
+ border-radius: 5px;
340
+ background: rgba(255, 255, 255, 0.035);
341
+ font: inherit;
342
+ }
343
+
344
+ .tour-narration-transport,
345
+ .tour-narration-settings {
346
+ display: inline-flex;
347
+ align-items: center;
348
+ }
349
+
350
+ .tour-narration-transport {
351
+ overflow: hidden;
352
+ border: 1px solid var(--tour-border);
353
+ border-radius: 6px;
354
+ }
355
+
356
+ .tour-narration-transport button {
357
+ display: grid;
358
+ width: 30px;
359
+ height: 28px;
360
+ padding: 0;
361
+ place-items: center;
362
+ color: var(--tour-muted);
363
+ border: 0;
364
+ border-right: 1px solid var(--tour-border);
365
+ background: rgba(255, 255, 255, 0.035);
366
+ cursor: pointer;
367
+ }
368
+
369
+ .tour-narration-transport button:last-child {
370
+ border-right: 0;
371
+ }
372
+
373
+ .tour-narration-transport button svg {
374
+ width: 16px;
375
+ height: 16px;
376
+ fill: none;
377
+ stroke: currentColor;
378
+ stroke-width: 1.8;
379
+ stroke-linecap: round;
380
+ stroke-linejoin: round;
381
+ }
382
+
383
+ .tour-narration-transport button svg .tour-icon-fill {
384
+ fill: currentColor;
385
+ stroke: none;
386
+ }
387
+
388
+ .tour-narration-transport #tour-listen {
389
+ color: var(--tour-accent-bright);
390
+ }
391
+
392
+ .tour-narration-transport button:hover:not(:disabled),
393
+ .tour-narration-transport button:focus-visible,
394
+ .tour-narration-controls select:focus-visible {
395
+ color: #fff;
396
+ outline: none;
397
+ background: rgba(169, 159, 255, 0.13);
398
+ box-shadow: inset 0 0 0 1px rgba(169, 159, 255, 0.55);
399
+ }
400
+
401
+ .tour-narration-transport button:disabled,
402
+ .tour-narration-controls select:disabled {
403
+ opacity: 0.36;
404
+ }
405
+
406
+ .tour-narration-settings {
407
+ gap: 5px;
408
+ }
409
+
410
+ .tour-narration-controls select {
411
+ max-width: min(210px, 29vw);
412
+ }
413
+
414
+ .tour-narration-status {
415
+ margin-left: auto;
416
+ color: var(--tour-faint);
417
+ text-align: right;
418
+ }
419
+
420
+ .tour-narration-segment {
421
+ border-radius: 3px;
422
+ box-decoration-break: clone;
423
+ -webkit-box-decoration-break: clone;
424
+ transition: background-color 100ms ease, box-shadow 100ms ease;
425
+ }
426
+
427
+ .tour-narration-segment.is-speaking {
428
+ background: rgba(255, 211, 92, 0.18);
429
+ box-shadow: inset 0 -2px 0 rgba(255, 211, 92, 0.78);
430
+ }
431
+
432
+ .tour-narration-segment.is-speaking.is-paused {
433
+ background: rgba(169, 159, 255, 0.16);
434
+ box-shadow: inset 0 -2px 0 rgba(169, 159, 255, 0.72);
435
+ }
436
+
322
437
  .tour-breadcrumb {
323
438
  min-width: 0;
324
439
  color: var(--tour-muted);
@@ -674,6 +789,11 @@ body.tour-discussion .tour-narrative-nav {
674
789
  margin-bottom: clamp(22px, 4vh, 36px);
675
790
  }
676
791
 
792
+ body.tour-discussion .tour-narration-controls {
793
+ max-width: 1000px;
794
+ margin-bottom: clamp(18px, 3vh, 28px);
795
+ }
796
+
677
797
  body.tour-discussion .tour-narrative-layout {
678
798
  display: block;
679
799
  }
@@ -811,4 +931,20 @@ body.tour-discussion .tour-narrative-takeaway {
811
931
  min-width: 64px;
812
932
  padding-inline: 7px;
813
933
  }
934
+
935
+ .tour-narration-controls select {
936
+ max-width: 150px;
937
+ }
938
+
939
+ .tour-narration-status {
940
+ width: 100%;
941
+ margin-left: 0;
942
+ text-align: left;
943
+ }
944
+ }
945
+
946
+ @media (prefers-reduced-motion: reduce) {
947
+ .tour-narration-segment {
948
+ transition: none;
949
+ }
814
950
  }