@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.
package/CHANGELOG.md CHANGED
@@ -2,11 +2,24 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.8.7
6
+
7
+ - Refined tour narration controls with clearer playback state, sentence-level skipping, and consolidated play/pause behavior
8
+ - Allowed narration skips to continue across adjacent tour steps and slides while preserving paused playback
9
+ - Improved tour and comparison toolbar controls, including clearer disabled word-wrap feedback
10
+
11
+ ## 0.8.6
12
+
13
+ - Added optional authored-tour `windowTitle` labels and context-aware desktop and VS Code window titles
14
+ - Fixed tour presenter windows so authored `windowTitle` values appear in the native title instead of staying on the default `Bygone Tour` label
15
+
5
16
  ## 0.8.5
6
17
 
7
18
  - Fixed Homebrew cask `bygone` CLI launch on Apple Silicon by routing through a bundle-aware launcher script inside the app
8
19
  - Documented the macOS **“app is damaged”** workaround for unsigned desktop downloads (README, releasing docs, PR Tour Guide skill)
9
20
 
21
+ **macOS install:** Desktop builds are unsigned. If Gatekeeper reports the app is damaged, run `xattr -cr /Applications/Bygone.app` then open the app.
22
+
10
23
  ## 0.8.4
11
24
 
12
25
  - Clarified PR Tour Guide skill install commands for project scope and agent-specific global installs
package/README.md CHANGED
@@ -98,6 +98,12 @@ new comparison brings the central app window forward and replaces its current
98
98
  session after any unsaved-change confirmation. Tours reuse a dedicated window
99
99
  owned by that same app process.
100
100
 
101
+ When Bygone is quit or restarted for an upgrade, the desktop app remembers the
102
+ open Explore session and tour windows. A normal no-argument launch restores
103
+ them; an explicit file, directory, review, or presentation launch takes
104
+ precedence. Windows whose source files or Git targets no longer exist are
105
+ skipped or replaced with a blank Explore window.
106
+
101
107
  Shell completion is generated from the same command specification as `bygone --help`, including contextual file arguments and local Git refs for branch review. `npm run dev:sync`, the desktop app's command-line installer, and the Homebrew packages install completions automatically. To install one manually, choose the command for your shell:
102
108
 
103
109
  ```bash
package/cli/present.js CHANGED
@@ -3,6 +3,7 @@ const { createServer } = require('http');
3
3
  const path = require('path');
4
4
  const { spawn } = require('child_process');
5
5
  const { buildChangeTourManifest, parseChangeTourStory } = require('../out/changeTour.js');
6
+ const { buildTourWindowTitle } = require('../out/windowTitle.js');
6
7
  const { tokenMatches } = require('./commandSpec.js');
7
8
  const { loadTourSource } = require('./tourFile.js');
8
9
 
@@ -39,8 +40,26 @@ async function startPresentation(args, cwd, packageRoot, options = {}) {
39
40
  process.stdout.write(`Wrote change-tour manifest to ${resolvedOutput}\n`);
40
41
  }
41
42
 
43
+ const tourWindowTitle = buildTourWindowTitle(manifest, 'Bygone');
44
+ const presenterIndexPath = path.join(packageRoot, 'web', 'index.html');
45
+ let presenterIndexTemplate;
46
+
42
47
  const server = createServer((request, response) => {
43
48
  const requestUrl = new URL(request.url || '/', 'http://127.0.0.1');
49
+ if (requestUrl.pathname === '/narration/claim') {
50
+ if (request.method !== 'POST') {
51
+ respond(response, 405, 'Method not allowed');
52
+ return;
53
+ }
54
+ if (!isSameOriginLoopbackRequest(request)) {
55
+ respond(response, 403, 'Forbidden');
56
+ return;
57
+ }
58
+ options.onNarrationClaim?.();
59
+ response.writeHead(204, { 'Cache-Control': 'no-store' });
60
+ response.end();
61
+ return;
62
+ }
44
63
  if (requestUrl.pathname === '/tour.json') {
45
64
  response.writeHead(200, {
46
65
  'Content-Type': 'application/json; charset=utf-8',
@@ -49,6 +68,21 @@ async function startPresentation(args, cwd, packageRoot, options = {}) {
49
68
  response.end(serializedManifest);
50
69
  return;
51
70
  }
71
+ if (requestUrl.pathname === '/' || requestUrl.pathname === '/index.html') {
72
+ if (!presenterIndexTemplate) {
73
+ presenterIndexTemplate = readFileSync(presenterIndexPath, 'utf8');
74
+ }
75
+ const html = presenterIndexTemplate.replace(
76
+ /<title>Bygone Tour<\/title>/,
77
+ `<title>${escapeHtml(tourWindowTitle)}</title>`
78
+ );
79
+ response.writeHead(200, {
80
+ 'Content-Type': 'text/html; charset=utf-8',
81
+ 'Cache-Control': 'no-store'
82
+ });
83
+ response.end(html);
84
+ return;
85
+ }
52
86
  const targetPath = resolveAssetPath(packageRoot, requestUrl.pathname);
53
87
  if (!targetPath) {
54
88
  respond(response, 404, 'Not found');
@@ -120,7 +154,7 @@ function parsePresentArgs(args) {
120
154
 
121
155
  function resolveAssetPath(packageRoot, requestPath) {
122
156
  if (requestPath === '/' || requestPath === '/index.html') {
123
- return path.join(packageRoot, 'web', 'index.html');
157
+ return null;
124
158
  }
125
159
  if (!requestPath.startsWith('/web/') && !requestPath.startsWith('/media/')) {
126
160
  return null;
@@ -160,7 +194,28 @@ function respond(response, statusCode, message) {
160
194
  response.end(message);
161
195
  }
162
196
 
197
+ function isSameOriginLoopbackRequest(request) {
198
+ const origin = request.headers.origin;
199
+ const host = request.headers.host;
200
+ if (typeof origin !== 'string' || typeof host !== 'string') return false;
201
+ try {
202
+ const parsed = new URL(origin);
203
+ return parsed.protocol === 'http:' && parsed.hostname === '127.0.0.1' && parsed.host === host;
204
+ } catch {
205
+ return false;
206
+ }
207
+ }
208
+
209
+ function escapeHtml(text) {
210
+ return text
211
+ .replaceAll('&', '&amp;')
212
+ .replaceAll('<', '&lt;')
213
+ .replaceAll('>', '&gt;')
214
+ .replaceAll('"', '&quot;');
215
+ }
216
+
163
217
  module.exports = {
164
218
  parsePresentArgs,
165
- startPresentation
219
+ startPresentation,
220
+ escapeHtml
166
221
  };
@@ -35,11 +35,16 @@ contents, and diffs into the portable JSON manifest served by the presenter.
35
35
  Compilation fails if an anchor has no match or has multiple matches without an
36
36
  explicit `occurrence`.
37
37
 
38
+ The optional `windowTitle` sets the native window title for the tour presenter.
39
+ Use a short label such as a pull request number when several tours may be open at
40
+ once. When omitted, the presenter falls back to `title`, then a generic tour label.
41
+
38
42
  ## Structure
39
43
 
40
44
  ```yaml
41
45
  version: 1
42
46
  title: A reviewer-facing title
47
+ windowTitle: PR-1234
43
48
  sourceUrl: https://example.test/pull/123
44
49
  range:
45
50
  base: 0123456789abcdef
package/docs/present.md CHANGED
@@ -14,6 +14,38 @@ The Files rail is independent of the narrative. Browsing another file keeps
14
14
  the active tour anchor visible and offers **Return to Tour** to restore the
15
15
  scene's intended focus.
16
16
 
17
+ ## Listen to a tour
18
+
19
+ Use **Listen** in the narrative header or **Present → Listen to Tour** in the
20
+ desktop app to read the tour aloud with a device voice. Narration works
21
+ offline and does not send tour text to a hosted speech service.
22
+
23
+ - **Pause/Resume** retains the current sentence; **Stop** clears playback. The
24
+ outer jump controls move one sentence backward or forward within the current
25
+ narration item.
26
+ - Existing Previous and Next controls move through the same scene/step order
27
+ used by continuous narration.
28
+ - Choose any device voice exposed by the browser and a speed from 0.75× to
29
+ 1.5×. Bygone stores those preferences locally and returns to the system
30
+ default if a selected voice disappears.
31
+ - The visible sentence is highlighted while it is spoken. Pausing retains a
32
+ distinct paused highlight without moving keyboard focus.
33
+ - Direct scene, file, or search navigation interrupts speech and leaves it
34
+ paused at the selected tour position. Resume continues from there.
35
+
36
+ Automatic narration reads chapter and scene framing, summaries, bullets,
37
+ step titles and bodies, connection labels, and takeaways. It does not
38
+ automatically read diff contents, code blocks, hashes, line numbers, or raw
39
+ URLs. Those remain visible and available to assistive technology normally.
40
+
41
+ Device voice names and quality depend on the operating system and browser.
42
+ If no device speech engine is exposed, Bygone disables Listen with a specific
43
+ unsupported-device message; lack of network access alone never disables it.
44
+
45
+ Future narration work may add opt-in treatment for code and raw URLs, plus
46
+ higher-quality hosted voices or routing through the agent that originated the
47
+ conversation. Those remain separate from the offline, device-first baseline.
48
+
17
49
  ## Read provenance correctly
18
50
 
19
51
  - Normal walkthrough scenes point to exact committed source evidence.
@@ -48,6 +48,7 @@ not packaged.
48
48
  | --- | --- | --- | --- | --- | --- |
49
49
  | Explain the current branch automatically | Present → Present Current Branch; `bygone present` | Committed merge-base-to-tip range | Read-only | Core | presentation launch and generated-tour tests |
50
50
  | Open an authored walkthrough | Present → Open Authored Tour; `bygone present --tour`; VS Code hand-off | Manifest range, scene, step, exact source anchor | Read-only | Core | reproducible example and tour validation tests |
51
+ | Listen to a generated or authored tour | Narrative Listen controls; Present → Listen to Tour | Existing manifest narrative segmented locally by scene/step and visible sentence | Read-only device TTS; voice/rate preferences remain local; continuous playback follows tour navigation and direct exploration pauses it | Core | narration model/controller and presenter-host tests |
51
52
  | Explain a real stack | `stacked-diff` scene | Exact real Git revisions per panel | Read-only | Advanced | `examples/stacked-diff.bygone` |
52
53
  | Explain a change in conceptual stages | `deconstructed-diff` scene | Explicitly labeled synthetic explanation stages backed by exact hunk IDs | Read-only | Advanced/experimental authoring | `examples/deconstructed-diff.bygone` |
53
54
  | Browse away from the narration | Files rail, change navigation | Browsed file versus active tour focus remain distinct | Read-only; Return to Tour restores narrative focus | Core | tour focus and persistent-anchor tests |