@forwardimpact/outpost 3.11.0 → 3.12.1

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.
Files changed (114) hide show
  1. package/README.md +2 -2
  2. package/bin/fit-outpost.js +3 -2
  3. package/package.json +4 -4
  4. package/src/agent-path.js +10 -6
  5. package/src/agent-runner.js +28 -24
  6. package/src/index.js +2 -3
  7. package/src/kb-manager.js +21 -19
  8. package/src/outpost.js +37 -33
  9. package/src/posture.js +3 -3
  10. package/src/privilege.js +11 -11
  11. package/src/scheduler.js +14 -12
  12. package/src/socket-server.js +19 -18
  13. package/src/spawn-env.js +7 -6
  14. package/src/state-manager.js +2 -2
  15. package/templates/.claude/agents/chief-of-staff.md +22 -22
  16. package/templates/.claude/agents/concierge.md +19 -17
  17. package/templates/.claude/agents/head-hunter.md +27 -26
  18. package/templates/.claude/agents/librarian.md +17 -16
  19. package/templates/.claude/agents/postman.md +19 -18
  20. package/templates/.claude/agents/recruiter.md +31 -27
  21. package/templates/.claude/skills/anarlog-follow/SKILL.md +50 -49
  22. package/templates/.claude/skills/anarlog-follow/references/coaching.md +22 -21
  23. package/templates/.claude/skills/anarlog-follow/scripts/follow.mjs +4 -4
  24. package/templates/.claude/skills/anarlog-process/SKILL.md +35 -32
  25. package/templates/.claude/skills/anarlog-process/references/extraction.md +26 -22
  26. package/templates/.claude/skills/anarlog-process/references/sessions.md +10 -10
  27. package/templates/.claude/skills/anarlog-process/scripts/scan.mjs +10 -9
  28. package/templates/.claude/skills/anarlog-trim/SKILL.md +43 -40
  29. package/templates/.claude/skills/candidate-report/SKILL.md +30 -28
  30. package/templates/.claude/skills/candidate-report/references/report-template.html +10 -10
  31. package/templates/.claude/skills/candidate-report/references/report.css +4 -4
  32. package/templates/.claude/skills/candidate-report/references/rubric.md +15 -14
  33. package/templates/.claude/skills/candidate-report/scripts/render-pdf.mjs +2 -2
  34. package/templates/.claude/skills/changelog/SKILL.md +55 -50
  35. package/templates/.claude/skills/deck-create/SKILL.md +30 -30
  36. package/templates/.claude/skills/deck-create/references/slide.css +4 -4
  37. package/templates/.claude/skills/deck-create/scripts/convert-to-pdf.mjs +6 -6
  38. package/templates/.claude/skills/deck-review/SKILL.md +59 -57
  39. package/templates/.claude/skills/deck-review/assets/slide-annotator.js +58 -49
  40. package/templates/.claude/skills/deck-summarize/SKILL.md +49 -43
  41. package/templates/.claude/skills/deck-summarize/references/brief-template.md +4 -3
  42. package/templates/.claude/skills/deck-summarize/scripts/extract-pptx.mjs +8 -7
  43. package/templates/.claude/skills/doc-collab/SKILL.md +25 -25
  44. package/templates/.claude/skills/doc-create/SKILL.md +21 -20
  45. package/templates/.claude/skills/doc-create/scripts/convert-to-pdf.mjs +6 -6
  46. package/templates/.claude/skills/draft-emails/SKILL.md +37 -33
  47. package/templates/.claude/skills/draft-emails/references/template.md +2 -2
  48. package/templates/.claude/skills/draft-emails/scripts/scan-emails.mjs +5 -4
  49. package/templates/.claude/skills/draft-emails/scripts/send-email.mjs +11 -9
  50. package/templates/.claude/skills/extract-entities/SKILL.md +65 -65
  51. package/templates/.claude/skills/extract-entities/references/TEMPLATES.md +5 -5
  52. package/templates/.claude/skills/extract-entities/references/conditions.md +14 -13
  53. package/templates/.claude/skills/extract-entities/references/content.md +10 -9
  54. package/templates/.claude/skills/extract-entities/references/links.md +5 -5
  55. package/templates/.claude/skills/extract-entities/references/recruitment.md +19 -18
  56. package/templates/.claude/skills/extract-entities/references/resolution.md +13 -13
  57. package/templates/.claude/skills/extract-entities/references/sources.md +8 -8
  58. package/templates/.claude/skills/extract-entities/references/templates-conditions.md +2 -2
  59. package/templates/.claude/skills/extract-entities/references/templates-priorities.md +2 -2
  60. package/templates/.claude/skills/extract-entities/scripts/state.mjs +9 -8
  61. package/templates/.claude/skills/meeting-prep/SKILL.md +25 -24
  62. package/templates/.claude/skills/organize-files/SKILL.md +24 -22
  63. package/templates/.claude/skills/organize-files/scripts/organize-by-type.mjs +5 -4
  64. package/templates/.claude/skills/organize-files/scripts/summarize.mjs +4 -4
  65. package/templates/.claude/skills/person-identify/SKILL.md +24 -22
  66. package/templates/.claude/skills/person-identify/scripts/identify.sh +17 -17
  67. package/templates/.claude/skills/person-lookup/SKILL.md +28 -29
  68. package/templates/.claude/skills/person-lookup/scripts/lookup.sh +19 -16
  69. package/templates/.claude/skills/req-assess/SKILL.md +27 -27
  70. package/templates/.claude/skills/req-assess/references/interview-template.md +1 -1
  71. package/templates/.claude/skills/req-assess/references/panel-template.md +3 -2
  72. package/templates/.claude/skills/req-assess/references/rubric.md +13 -13
  73. package/templates/.claude/skills/req-decide/SKILL.md +28 -25
  74. package/templates/.claude/skills/req-decide/references/rubric.md +11 -10
  75. package/templates/.claude/skills/req-forget/SKILL.md +28 -26
  76. package/templates/.claude/skills/req-forget/references/classify.md +13 -13
  77. package/templates/.claude/skills/req-forget/references/locations.md +2 -2
  78. package/templates/.claude/skills/req-forget/references/report-template.md +1 -1
  79. package/templates/.claude/skills/req-scan/SKILL.md +21 -22
  80. package/templates/.claude/skills/req-scan/references/fallbacks.md +9 -9
  81. package/templates/.claude/skills/req-scan/references/filters.md +13 -13
  82. package/templates/.claude/skills/req-scan/references/sources.md +7 -7
  83. package/templates/.claude/skills/req-scan/references/state.md +1 -1
  84. package/templates/.claude/skills/req-scan/scripts/state.mjs +3 -2
  85. package/templates/.claude/skills/req-screen/SKILL.md +31 -29
  86. package/templates/.claude/skills/req-screen/references/rubric.md +18 -18
  87. package/templates/.claude/skills/req-screen/references/template.md +4 -4
  88. package/templates/.claude/skills/req-track/SKILL.md +40 -40
  89. package/templates/.claude/skills/req-track/references/fields.md +19 -18
  90. package/templates/.claude/skills/req-track/references/signals.md +12 -12
  91. package/templates/.claude/skills/req-track/references/statuses.md +3 -2
  92. package/templates/.claude/skills/req-track/references/templates.md +4 -4
  93. package/templates/.claude/skills/req-workday/SKILL.md +24 -24
  94. package/templates/.claude/skills/req-workday/references/status-mapping.md +5 -5
  95. package/templates/.claude/skills/req-workday/references/templates.md +13 -13
  96. package/templates/.claude/skills/req-workday/references/xlsx-format.md +7 -7
  97. package/templates/.claude/skills/req-workday/scripts/parse-workday.mjs +19 -17
  98. package/templates/.claude/skills/send-chat/SKILL.md +47 -43
  99. package/templates/.claude/skills/sync-apple-calendar/SKILL.md +20 -19
  100. package/templates/.claude/skills/sync-apple-calendar/references/SCHEMA.md +7 -7
  101. package/templates/.claude/skills/sync-apple-calendar/scripts/query.mjs +6 -6
  102. package/templates/.claude/skills/sync-apple-calendar/scripts/sync.mjs +9 -8
  103. package/templates/.claude/skills/sync-apple-mail/SKILL.md +17 -14
  104. package/templates/.claude/skills/sync-apple-mail/references/SCHEMA.md +10 -10
  105. package/templates/.claude/skills/sync-apple-mail/scripts/parse-emlx.mjs +9 -9
  106. package/templates/.claude/skills/sync-apple-mail/scripts/sync-helpers.mjs +16 -13
  107. package/templates/.claude/skills/sync-apple-mail/scripts/sync.mjs +3 -3
  108. package/templates/.claude/skills/sync-teams/SKILL.md +43 -43
  109. package/templates/.claude/skills/sync-teams/scripts/idb-reader.mjs +22 -21
  110. package/templates/.claude/skills/sync-teams/scripts/leveldb-reader.mjs +4 -4
  111. package/templates/.claude/skills/sync-teams/scripts/sync.mjs +4 -4
  112. package/templates/.claude/skills/upstream-instructions/SKILL.md +39 -40
  113. package/templates/.claude/skills/upstream-instructions/references/examples.md +2 -2
  114. package/templates/CLAUDE.md +40 -37
@@ -1,13 +1,13 @@
1
1
  ---
2
2
  name: deck-create
3
- description: Generate PDF slide decks from user requests using Playwright to render HTML slides to PDF. Use when the user asks to create a presentation, slide deck, or pitch deck. Pulls context from the knowledge base for company info, project details, and people.
4
- compatibility: Requires Node.js installed. Playwright is installed on first use.
3
+ description: Generate PDF slide decks from user requests. Playwright renders the HTML slides to PDF. Use when the user asks to create a presentation, slide deck, or pitch deck. Pulls context from the knowledge base for company info, project details, and people.
4
+ compatibility: Requires Node.js. The skill installs Playwright on first use.
5
5
  ---
6
6
 
7
7
  # Create Presentations
8
8
 
9
- Generate PDF slide decks from user requests. Uses Playwright to render HTML
10
- slides to PDF. Can pull context from the knowledge base for company info,
9
+ Generate PDF slide decks from user requests. Playwright renders the HTML slides
10
+ to PDF. This skill can pull context from the knowledge base for company info,
11
11
  project details, and people.
12
12
 
13
13
  ## Trigger
@@ -17,7 +17,7 @@ Run when the user asks to create a presentation, slide deck, or pitch deck.
17
17
  ## Prerequisites
18
18
 
19
19
  - Node.js installed
20
- - Playwright will be installed on first use
20
+ - The skill installs Playwright on first use
21
21
 
22
22
  ## Inputs
23
23
 
@@ -34,7 +34,7 @@ Run when the user asks to create a presentation, slide deck, or pitch deck.
34
34
 
35
35
  1. Check `Knowledge/` for relevant context about the company, product, team,
36
36
  etc.
37
- 2. Ensure Playwright is installed:
37
+ 2. Make sure Playwright is installed:
38
38
  `bun install playwright && bunx playwright install chromium`
39
39
  3. Create an HTML file at `/tmp/outpost-presentation.html` with slides
40
40
  (1280x720px each)
@@ -56,24 +56,24 @@ Defaults: input = `/tmp/outpost-presentation.html`, output =
56
56
 
57
57
  ## PDF Rendering Rules
58
58
 
59
- **These prevent rendering issues in PDF:**
59
+ **These rules prevent problems when the PDF renders:**
60
60
 
61
- 1. **No layered elements** — Style content elements directly, no separate
62
- background elements
61
+ 1. **No layered elements** — Style content elements directly. Do not add
62
+ separate background elements
63
63
  2. **No box-shadow** — Use borders instead: `border: 1px solid #e5e7eb`
64
- 3. **Bullets via CSS only** — Use `li::before` pseudo-elements
65
- 4. **Content must fit** — Slides are 1280x720px with 60px padding. Safe area is
66
- 1160x600px. Use `overflow: hidden`
64
+ 3. **Bullets with CSS only** — Use `li::before` pseudo-elements
65
+ 4. **Content must fit** — Slides are 1280x720px with 60px padding. The safe area
66
+ is 1160x600px. Use `overflow: hidden`
67
67
  5. **No footers or headers** — No fixed/absolute positioned footer/header
68
68
  elements
69
69
 
70
70
  ## Interactive HTML Decks — Navigation & Event Standards
71
71
 
72
- When the deck is delivered as a **standalone interactive HTML file** (animated /
73
- navigable in the browser) rather than a static PDF, keep input handling
74
- deliberately minimal. Rich event handling fights with two things the user needs:
75
- selecting/copying text on a slide, and typing into overlay tools (e.g. the
76
- `slide-annotator.js` review overlay).
72
+ You can deliver the deck as a **standalone interactive HTML file**
73
+ (animated / navigable in the browser) instead of a static PDF. Then keep the
74
+ input handlers deliberately minimal. Rich event handlers fight with two things
75
+ the user needs: selection and copy of text on a slide, and text entry into
76
+ overlay tools (e.g. the `slide-annotator.js` review overlay).
77
77
 
78
78
  **Required:**
79
79
 
@@ -83,13 +83,13 @@ selecting/copying text on a slide, and typing into overlay tools (e.g. the
83
83
  navigate (e.g. "click left/right third"). They fire on the mouse-up that ends
84
84
  a text-selection drag and jump the slide unexpectedly.
85
85
  3. **No spacebar, PageUp/PageDown, or other global key bindings.** Space
86
- conflicts with typing in overlay inputs; the rest are redundant and
87
- surprising.
86
+ conflicts when the user types into an overlay input. The rest are redundant
87
+ and surprising.
88
88
  4. **A progress indicator may be clickable**, but it must live in the
89
89
  footer/chrome and never overlap slide content.
90
90
  5. **Expose `window.deckGoto(index)`** (0-based) right after the slide-show
91
- function, so review/overlay tools can jump to a slide without simulating
92
- clicks or keys:
91
+ function, so review/overlay tools can jump to a slide without a simulated
92
+ click or key:
93
93
 
94
94
  function go(n) { /* ...show slide n... */ }
95
95
  window.deckGoto = go;
@@ -97,20 +97,20 @@ selecting/copying text on a slide, and typing into overlay tools (e.g. the
97
97
  6. **Keep the hint honest** — the on-screen nav hint should read
98
98
  `← → to navigate` (don't advertise click/space).
99
99
  7. **Use stable structural hooks.** Make each slide one element with class
100
- `.slide`, and put the slide-number label (if any) in a `.slide-num` element.
100
+ `.slide`. Put the slide-number label (if any) in a `.slide-num` element.
101
101
  The review overlay defaults to these selectors to detect and index slides.
102
102
 
103
- These rules keep decks compatible with the **`deck-review`** skill, which
103
+ These rules keep decks compatible with the **`deck-review`** skill. That skill
104
104
  installs the `slide-annotator.js` review overlay (highlight text on a slide →
105
- sidecar JSON of feedback that an agent acts on). After producing an interactive
106
- HTML deck, you can offer to run `deck-review` to make it reviewable; see that
107
- skill for the install steps and the sidecar JSON schema.
105
+ sidecar JSON of feedback that an agent acts on). After you produce an
106
+ interactive HTML deck, you can offer to run `deck-review` to make it reviewable.
107
+ See that skill for the install steps and the sidecar JSON schema.
108
108
 
109
109
  ## Constraints
110
110
 
111
111
  - Always use the knowledge base for context when available
112
- - Output to `~/Desktop/presentation.pdf` unless user specifies otherwise
113
- - Keep slides clean and readable max 5-6 bullet points per slide
114
- - Use consistent styling throughout
112
+ - Output to `~/Desktop/presentation.pdf` unless the user specifies otherwise
113
+ - Keep slides clean and readable (max 5-6 bullet points per slide)
114
+ - Use the same styles throughout
115
115
  - For interactive HTML decks, follow the navigation & event standards above
116
- (arrow-keys-only; no click-to-advance or spacebar)
116
+ (arrow-keys-only, no click-to-advance or spacebar)
@@ -1,12 +1,12 @@
1
- /* Required CSS for HTML slide decks rendered to PDF via Playwright.
1
+ /* Required CSS for HTML slide decks that Playwright renders to PDF.
2
2
  *
3
3
  * Include this in the <style> block of /tmp/outpost-presentation.html.
4
- * See scripts/convert-to-pdf.mjs for the rendering script.
4
+ * See scripts/convert-to-pdf.mjs for the script that renders them.
5
5
  *
6
- * PDF rendering rules:
6
+ * Rules for PDF output:
7
7
  * - No layered elements — style content directly, no separate backgrounds
8
8
  * - No box-shadow — use borders: border: 1px solid #e5e7eb
9
- * - Bullets via CSS only — use li::before pseudo-elements
9
+ * - Bullets with CSS only — use li::before pseudo-elements
10
10
  * - Content must fit — safe area is 1160x600px (1280x720 minus 60px padding)
11
11
  * - No footers or headers — no fixed/absolute positioned elements
12
12
  */
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env bun
2
2
  /**
3
- * Convert HTML slides to PDF using Playwright.
3
+ * Convert HTML slides to PDF with Playwright.
4
4
  *
5
- * Renders an HTML file containing slide markup (1280x720px per slide) into a
6
- * PDF document. Each slide is sized to exactly 1280x720 pixels with background
7
- * colours and images preserved. Defaults to reading from /tmp and writing to
8
- * ~/Desktop when no arguments are given.
5
+ * This script renders an HTML file that contains slide markup (1280x720px per
6
+ * slide) into a PDF document. Each slide measures exactly 1280x720 pixels. The
7
+ * PDF keeps the background colours and the images. Without arguments, the
8
+ * script reads from /tmp and writes to ~/Desktop.
9
9
  *
10
10
  * Requires: bun install playwright && bunx playwright install chromium
11
11
  */
@@ -14,7 +14,7 @@ import { join } from "node:path";
14
14
  import { resolve } from "node:path";
15
15
  import { homedir } from "node:os";
16
16
 
17
- const HELP = `convert-to-pdf — render HTML slides to PDF via Playwright
17
+ const HELP = `convert-to-pdf — render HTML slides to PDF with Playwright
18
18
 
19
19
  Usage: bun scripts/convert-to-pdf.mjs [input.html] [output.pdf] [-h|--help]
20
20
 
@@ -1,34 +1,34 @@
1
1
  ---
2
2
  name: deck-review
3
- description: Add a lightweight text-highlight review overlay to an HTML deck. Lets you highlight text on slides and capture feedback as a sidecar JSON (with source line/column + context) that an agent can act on in small iterations. Use when the user asks to add review/annotation/highlight/comment capability to a deck, make a deck "reviewable", or wants to mark up slides for revision. Pairs with the deck-create skill.
3
+ description: Add a lightweight text-highlight review overlay to an HTML deck. Lets you highlight text on slides and capture feedback as a sidecar JSON. The JSON carries the source line, column, and context, so an agent can act on it in small iterations. Use when the user asks to add review/annotation/highlight/comment capability to a deck, make a deck "reviewable", or wants to mark up slides for revision. Pairs with the deck-create skill.
4
4
  compatibility: Standalone HTML deck opened in a Chromium-based browser (Chrome/Edge). No build step, no server, no dependencies.
5
5
  ---
6
6
 
7
7
  # Add a Review Overlay to a Deck
8
8
 
9
- Install the self-contained `slide-annotator.js` overlay onto an HTML deck so the
10
- user can **highlight text on a slide and save the feedback as a sidecar JSON**.
11
- Each annotation carries a robust anchor (exact text + surrounding context +
12
- slide) and, once the folder is connected, the resolved
13
- **source line, column and context lines** so an agent can locate and edit the
14
- exact text in small iterations.
9
+ Install the self-contained `slide-annotator.js` overlay onto an HTML deck. The
10
+ user can then **highlight text on a slide and save the feedback as a sidecar
11
+ JSON**. Each annotation carries a robust anchor (exact text + the context around
12
+ it + slide). After you connect the folder, the annotation also carries the
13
+ resolved **source line, column and context lines**. An agent can then locate and
14
+ edit the exact text in small iterations.
15
15
 
16
- This is the companion to **`deck-create`**: decks produced by `deck-create`
17
- already follow the navigation/structure standards this overlay needs, and this
18
- overlay is designed to drop onto them with one script tag.
16
+ This is the companion to **`deck-create`**. Decks from `deck-create` already
17
+ follow the navigation and structure standards this overlay needs. You drop the
18
+ overlay onto them with one script tag.
19
19
 
20
20
  ## Trigger
21
21
 
22
22
  Run when the user asks to add review / annotation / highlight / comment / markup
23
- capability to a deck, "make this deck reviewable", or to set up a feedback loop
24
- on slides.
23
+ capability to a deck. Run it also when the user says "make this deck
24
+ reviewable" or asks to set up a feedback loop on slides.
25
25
 
26
26
  ## Inputs
27
27
 
28
28
  - Path to the target deck `.html` file (ask, or default to the most recently
29
29
  edited `*.html` in `Drafts/`).
30
- - The bundled tool at `assets/slide-annotator.js` (this skill's own copy is the
31
- source of truth edit it here, then re-install to update decks).
30
+ - The bundled tool at `assets/slide-annotator.js`. This skill's own copy is the
31
+ source of truth. Edit it here, then re-install it to update decks.
32
32
 
33
33
  ## Outputs
34
34
 
@@ -40,7 +40,8 @@ on slides.
40
40
 
41
41
  ## Install steps
42
42
 
43
- 1. **Resolve the deck path** (absolute). Confirm it is an HTML deck, not a PDF.
43
+ 1. **Resolve the deck path** (absolute). Confirm it is an HTML deck. Confirm it
44
+ is not a PDF.
44
45
 
45
46
  2. **Check compatibility** (see *Compatibility contract* below). The two things
46
47
  that matter:
@@ -48,18 +49,18 @@ on slides.
48
49
  (default `.slide`). If the deck uses a different class, note it for step 4.
49
50
  - **Navigation hook** — the deck exposes `window.deckGoto(index)` (0-based).
50
51
  If it has a slideshow function (e.g. `go(n)`) but no hook, add one line
51
- right after it: `window.deckGoto = go;`. Without it the overlay still works
52
- (the panel's *Go* button falls back to `scrollIntoView`), but it can't jump
53
- to a hidden slide precisely.
54
- - Optionally a slide-number label element (default `.slide-num`) for nicer
55
- labels in the panel purely cosmetic.
52
+ right after it: `window.deckGoto = go;`. Without it the overlay still
53
+ works. The panel's *Go* button falls back to `scrollIntoView`. But the
54
+ overlay can't jump to a hidden slide precisely.
55
+ - A slide-number label element (default `.slide-num`) is optional. It gives
56
+ nicer labels in the panel. It is purely cosmetic.
56
57
 
57
58
  3. **Install the tool**: copy this skill's `assets/slide-annotator.js` into the
58
- **same directory as the deck**. Resolve `~` to `$HOME`; pass the Write/copy a
59
+ **same directory as the deck**. Resolve `~` to `$HOME`. Pass the Write/copy a
59
60
  full path.
60
61
 
61
- 4. **Inject the script tag** immediately before `</body>` (idempotent skip if
62
- a `slide-annotator` script tag is already present):
62
+ 4. **Inject the script tag** immediately before `</body>`. The step is
63
+ idempotent. Skip it if a `slide-annotator` script tag is already present:
63
64
 
64
65
  ```html
65
66
  <!-- Review overlay: highlight text on a slide → sidecar JSON. Self-contained, optional. -->
@@ -72,8 +73,8 @@ on slides.
72
73
  differs from the defaults. If there are no slide elements at all, the tool
73
74
  treats the whole `<body>` as one container.
74
75
 
75
- 5. **Tell the user how to use it** (see *Using the overlay*). Do **not** add any
76
- other dependency or framework the tool is plain JS and must stay that way.
76
+ 5. **Tell the user how to use it** (see *Use the overlay*). Do **not** add any
77
+ other dependency or framework. The tool is plain JS and must stay that way.
77
78
 
78
79
  ## Compatibility contract (must match `deck-create`)
79
80
 
@@ -82,38 +83,38 @@ follow:
82
83
 
83
84
  | Convention | Default | Why the overlay needs it |
84
85
  |---|---|---|
85
- | One element per slide with a stable class | `.slide` | locate which slide a highlight is on; index slides |
86
+ | One element per slide with a stable class | `.slide` | locate which slide a highlight is on, and index the slides |
86
87
  | Slide-number label element (optional) | `.slide-num` | human-friendly panel labels |
87
88
  | Navigation hook | `window.deckGoto(index)` (0-based) | panel "Go" jumps to the right slide |
88
- | Arrow-keys-only navigation, **no** click-to-advance / spacebar | — | text selection + typing in the overlay must not move slides |
89
+ | Arrow-keys-only navigation, **no** click-to-advance / spacebar | — | text selection and typed input in the overlay must not move slides |
89
90
 
90
- If a deck violates the last row (has click-to-advance), the overlay's click
91
- guard only suppresses the click that ends a text-selection drag, so it degrades
92
- gracefully but the correct fix is to make the deck arrow-keys-only per
91
+ A deck can violate the last row and have click-to-advance. The overlay's click
92
+ guard then suppresses only the click that ends a text-selection drag, so it
93
+ degrades gracefully. The correct fix is to make the deck arrow-keys-only, per
93
94
  `deck-create`'s *Navigation & Event Standards*.
94
95
 
95
- ## Using the overlay (tell the user)
96
+ ## Use the overlay (tell the user)
96
97
 
97
- 1. Open the deck in Chrome and click **✎ Review** (bottom-left).
98
- 2. **Select text** on a slide a popover lets you add an optional note
98
+ 1. Open the deck in Chrome. Click **✎ Review** (bottom-left).
99
+ 2. **Select text** on a slide. A popover lets you add an optional note. Click
99
100
  **Add**. The highlight appears and **autosaves to `localStorage`**
100
101
  immediately.
101
102
  3. Click **Connect folder** once and pick the deck's folder. From then on
102
- **Save** writes a real `‹deck›.annotations.json` next to the deck, and the
103
- tool reads the deck's own source to fill in
104
- **source line / column / context** for each highlight. (If the browser blocks
105
- folder access on `file://`, **Save** downloads the JSON instead move it
106
- next to the deck.)
107
- 4. The connected folder is **remembered across reloads** (the directory handle
108
- is stored in IndexedDB, keyed per deck). After a reload the tool reconnects
109
- silently if the browser still grants access; otherwise the button reads
110
- **Reconnect folder** and a single click re-grants permission without
111
- re-picking the folder. (Browsers require a user gesture to re-grant, so the
112
- one click can't be avoided; clearing site data forgets the folder.)
113
- 5. Navigation while reviewing is the deck's normal **← / →** (the overlay's own
114
- keystrokes never leak to the deck).
115
-
116
- ## Acting on the feedback (the review loop)
103
+ **Save** writes a real `‹deck›.annotations.json` next to the deck. The tool
104
+ also reads the deck's own source to fill in
105
+ **source line / column / context** for each highlight. If the browser blocks
106
+ folder access on `file://`, **Save** downloads the JSON instead. Move it next
107
+ to the deck.
108
+ 4. The tool **remembers the connected folder across reloads**. It stores the
109
+ directory handle in IndexedDB, keyed per deck. After a reload the tool
110
+ reconnects silently if the browser still grants access. If not, the button
111
+ reads **Reconnect folder**, and a single click re-grants permission without a
112
+ new folder pick. Browsers require a user gesture to re-grant, so you can't
113
+ avoid the one click. If you clear site data, the tool forgets the folder.
114
+ 5. During a review you navigate with the deck's normal **← / →**. The overlay's
115
+ own keystrokes never leak to the deck.
116
+
117
+ ## Act on the feedback (the review loop)
117
118
 
118
119
  When the user says "work the annotations":
119
120
 
@@ -121,12 +122,13 @@ When the user says "work the annotations":
121
122
  2. For each `status: "open"` annotation, locate the text in the deck source:
122
123
  - Prefer `source.line` / `source.column` when present.
123
124
  - Otherwise search the source for `quote` (disambiguate with `prefix` /
124
- `suffix`, scoped to `slideId`). The quote is the underlying DOM text, so it
125
- matches the source even across inline tags / entities.
126
- 3. Make the edit, honoring the user's `note`.
125
+ `suffix`, scoped to `slideId`). The quote is the underlying DOM text, so
126
+ it matches the source even across inline tags / entities.
127
+ 3. Make the edit. Honor the user's `note`.
127
128
  4. Optionally set the annotation's `status` to `"done"` in the JSON so the panel
128
129
  shows it resolved.
129
- 5. Re-render / re-screenshot to verify, then report what changed per annotation.
130
+ 5. Re-render or re-screenshot to verify. Then report what changed for each
131
+ annotation.
130
132
 
131
133
  ### Sidecar JSON schema
132
134
 
@@ -150,17 +152,17 @@ When the user says "work the annotations":
150
152
  }
151
153
  ```
152
154
 
153
- ## Removing the overlay (for final delivery)
155
+ ## Remove the overlay (for final delivery)
154
156
 
155
157
  To hand off a clean presentation, delete the injected
156
158
  `<script src="slide-annotator.js" …>` line and the `slide-annotator.js` file.
157
- Leaving the `window.deckGoto = go;` line in the deck is harmless.
159
+ The `window.deckGoto = go;` line can stay in the deck. It is harmless.
158
160
 
159
161
  ## Constraints
160
162
 
161
- - Keep `slide-annotator.js` **dependency-free and host-agnostic** it must work
162
- on any static HTML page, not just `deck-create` output.
163
+ - Keep `slide-annotator.js` **dependency-free and host-agnostic**. It must work
164
+ on any static HTML page. `deck-create` output is not the only target.
163
165
  - Edit the tool **here** (`assets/slide-annotator.js`) as the source of truth,
164
166
  then re-install onto decks. Don't fork per-deck copies with divergent
165
167
  behavior.
166
- - Never auto-send or upload annotations anywhere the sidecar JSON stays local.
168
+ - Never auto-send or upload annotations anywhere. The sidecar JSON stays local.
@@ -7,20 +7,21 @@
7
7
  * data-slide-selector=".slide"
8
8
  * data-label-selector=".slide-num"></script>
9
9
  *
10
- * Click the “✎ Review” button (bottom-left), select text on a slide, optionally
11
- * add a note, and Add. Annotations autosave to localStorage immediately. Click
12
- * “Connect folder” once to (a) write a real sidecar JSON next to the page and
13
- * (b) resolve each highlight to its SOURCE line/column + context lines by
14
- * reading the page’s own source file. No build step, no server, no deps.
10
+ * Click the “✎ Review” button (bottom-left). Select text on a slide. Add an
11
+ * optional note. Then click Add. Annotations autosave to localStorage
12
+ * immediately. Click “Connect folder” once. The tool then writes a real
13
+ * sidecar JSON next to the page. It also reads the page’s own source file, so
14
+ * it resolves each highlight to its SOURCE line/column + context lines. The
15
+ * tool needs no build step, no server, and no dependencies.
15
16
  *
16
- * The connected folder is remembered across reloads (the directory handle is
17
- * stored in IndexedDB). On reload it reconnects silently if the browser still
18
- * grants permission; otherwise the button reads “Reconnect folder” and one
19
- * click re-grants access without re-picking the folder.
17
+ * The tool remembers the connected folder across reloads. It stores the
18
+ * directory handle in IndexedDB. On reload it reconnects silently if the
19
+ * browser still grants permission. If not, the button reads “Reconnect
20
+ * folder”. One click then re-grants access without a new folder pick.
20
21
  *
21
- * Optional host hook: if the page defines `window.deckGoto(index)` the panel’s
22
- * “Go” buttons will navigate to the right slide. Without it, the tool still
23
- * works (it falls back to scrollIntoView).
22
+ * Optional host hook. If the page defines `window.deckGoto(index)`, the
23
+ * panel’s “Go” buttons navigate to the right slide. Without it, the tool
24
+ * still works (it falls back to scrollIntoView).
24
25
  *
25
26
  * SIDECAR JSON SCHEMA (what an agent reads to act on the feedback):
26
27
  * {
@@ -120,7 +121,8 @@
120
121
  ::highlight(sa-all){background:rgba(255,221,0,.45);text-decoration:underline;text-decoration-color:#caا}\
121
122
  ::highlight(sa-active){background:rgba(255,0,110,.35)}\
122
123
  @media print{.sa-ui{display:none!important}}";
123
- // (one stray glyph above is harmless inside a CSS comment-free value; replaced below)
124
+ // The stray glyph above is harmless in a comment-free CSS value.
125
+ // The next line replaces it.
124
126
  css = css.replace("#caا", "#b38f00");
125
127
  var styleEl = document.createElement("style");
126
128
  styleEl.textContent = css;
@@ -160,13 +162,14 @@
160
162
  ui.note = ui.pop.querySelector("textarea");
161
163
  var onlyThisSlide = false;
162
164
 
163
- // Keep the tool's own keystrokes from leaking to the host page (e.g. a deck's
164
- // Space / arrow-key slide navigation) while typing a note or using a panel
165
- // button. We stop propagation in the BUBBLE phase, so: (a) our capture-phase
166
- // Esc handler has already run, and (b) the default action — typing the
167
- // character, moving the caret — still happens. Scoped to the tool's own UI,
168
- // so arrow/Space navigation still works when focus is on the page itself.
169
- // Independent of any host: we never touch the host's handlers, just our own.
165
+ // Keep the tool's own keystrokes away from the host page. One example is a
166
+ // deck's Space and arrow-key slide navigation. This applies while the user
167
+ // types a note or uses a panel button. We stop propagation in the BUBBLE
168
+ // phase. So our capture-phase Esc handler already ran. The default action
169
+ // also still happens. The character appears and the caret moves. We scope
170
+ // this to the tool's own UI. Arrow and Space navigation still works when
171
+ // focus is on the page itself. This works with any host. We never touch
172
+ // the host's handlers, only our own.
170
173
  [ui.fab, ui.banner, ui.panel, ui.pop].forEach(function (node) {
171
174
  ["keydown", "keyup", "keypress"].forEach(function (type) {
172
175
  node.addEventListener(type, function (e) { e.stopPropagation(); });
@@ -210,10 +213,11 @@
210
213
  else if (review) { toggle(false); e.stopPropagation(); }
211
214
  }, true);
212
215
 
213
- // In review mode, swallow ONLY the click that ends a text-selection drag, so a
214
- // host with click-to-advance can't navigate out from under the highlight. Plain
215
- // clicks (with no active selection) pass through untouched — footer dots, links
216
- // and buttons keep working. Non-invasive and host-agnostic.
216
+ // In review mode, swallow ONLY the click that ends a text-selection drag.
217
+ // A host with click-to-advance then cannot navigate out from under the
218
+ // highlight. Plain clicks (with no active selection) pass through
219
+ // untouched. Footer dots, links and buttons keep working. The tool does
220
+ // not intrude, and it works with any host.
217
221
  function clickSuppressor(e) {
218
222
  if (e.target.closest(".sa-ui")) return;
219
223
  var sel = window.getSelection();
@@ -252,9 +256,10 @@
252
256
  var off = offsets(slide, range);
253
257
  var text = slide.textContent;
254
258
  var slides = allSlides();
255
- // Anchor on the underlying DOM text (text.slice), NOT Selection.toString():
256
- // the latter applies text-transform (e.g. uppercase headings) and would no
257
- // longer match the source or the slide's textContent.
259
+ // Anchor on the underlying DOM text (text.slice). Do NOT use
260
+ // Selection.toString(). It applies text-transform (e.g. uppercase
261
+ // headings). The result would no longer match the source or the slide's
262
+ // textContent.
258
263
  var storedQuote = text.slice(off.start, off.end) || quote;
259
264
 
260
265
  pending = {
@@ -394,7 +399,7 @@
394
399
  if (!list.length) {
395
400
  ui.list.innerHTML = '<div class="sa-empty">' +
396
401
  (onlyThisSlide ? "No annotations on this slide." :
397
- "No annotations yet.<br><br>Select text on a slide to highlight it. Highlights autosave locally; click <b>Connect folder</b> to write the sidecar JSON and capture source line numbers.") +
402
+ "No annotations yet.<br><br>Select text on a slide to highlight it. Highlights autosave locally. Click <b>Connect folder</b> to write the sidecar JSON and capture source line numbers.") +
398
403
  '</div>';
399
404
  return;
400
405
  }
@@ -417,7 +422,7 @@
417
422
  function currentSlideIndex() {
418
423
  var slides = allSlides();
419
424
  for (var i = 0; i < slides.length; i++) if (slides[i].classList.contains("active")) return i;
420
- // else the slide most centered in viewport
425
+ // else the slide most centered in the viewport
421
426
  var best = 0, bestD = Infinity, cx = window.innerWidth / 2, cy = window.innerHeight / 2;
422
427
  slides.forEach(function (s, i) {
423
428
  var r = s.getBoundingClientRect();
@@ -442,12 +447,13 @@
442
447
  if (ui.count) ui.count.textContent = state.annotations.length;
443
448
  }
444
449
 
445
- /* Persist the connected directory handle across reloads. FileSystemHandles are
446
- * structured-cloneable, so they live in IndexedDB (localStorage is strings
447
- * only). Re-granting read/write permission after a reload needs a user
448
- * gesture, so a remembered folder reconnects with one click via the same
449
- * "Connect folder" button rather than the full directory picker. Keyed by
450
- * cfg.target so distinct decks in a folder don't clobber each other. */
450
+ /* Persist the connected directory handle across reloads. FileSystemHandles
451
+ * are structured-cloneable, so they live in IndexedDB (localStorage holds
452
+ * strings only). A re-grant of read/write permission after a reload needs
453
+ * a user gesture. So a remembered folder reconnects with one click through
454
+ * the same "Connect folder" button. It does not open the full directory
455
+ * picker. The key is cfg.target, so distinct decks in a folder do not
456
+ * clobber each other. */
451
457
  var IDB_NAME = "slide-annotator", IDB_STORE = "handles";
452
458
  function idbOpen() {
453
459
  return new Promise(function (resolve, reject) {
@@ -484,16 +490,17 @@
484
490
  : rememberedHandle ? "Reconnect folder" : "Connect folder";
485
491
  }
486
492
 
487
- // Adopt a (freshly picked or re-granted) handle: link source, load any
488
- // sidecar, and remember it for next time. Shared by connect and restore.
493
+ // Adopt a freshly picked or re-granted handle. Link the source, load any
494
+ // sidecar, and remember the handle for next time. Both connect and restore
495
+ // call this.
489
496
  async function adoptFolder(handle) {
490
497
  dirHandle = handle;
491
- // read page source → enables source line/column resolution
498
+ // read the page source → the tool can then resolve source line/column
492
499
  try {
493
500
  var fh = await dirHandle.getFileHandle(cfg.target);
494
501
  sourceText = await (await fh.getFile()).text();
495
502
  state.annotations.forEach(function (a) { a.source = locateInSource(a); });
496
- } catch (e) { sourceText = null; setStatus("Connected, but '" + cfg.target + "' not found in folder — line numbers unavailable."); }
503
+ } catch (e) { sourceText = null; setStatus("Connected, but the folder has no '" + cfg.target + "'. Line numbers are unavailable."); }
497
504
  // load an existing sidecar if present
498
505
  try {
499
506
  var sh = await dirHandle.getFileHandle(SIDECAR);
@@ -503,12 +510,13 @@
503
510
  rememberedHandle = null;
504
511
  updateConnectLabel();
505
512
  persistLocal(); renderPanel(); renderHighlights();
506
- setStatus("Connected. Saving to " + SIDECAR + (sourceText ? " · source linked" : ""));
513
+ setStatus("Connected. Saves to " + SIDECAR + (sourceText ? " · source linked" : ""));
507
514
  }
508
515
 
509
516
  async function connectFolder() {
510
- // Prefer a folder remembered from a prior session: re-granting permission is
511
- // a single click (this click IS the required user gesture), no picker.
517
+ // Prefer a folder remembered from a prior session. A permission re-grant
518
+ // takes a single click (this click IS the required user gesture). The
519
+ // picker does not open.
512
520
  if (rememberedHandle) {
513
521
  try {
514
522
  var perm = await rememberedHandle.requestPermission({ mode: "readwrite" });
@@ -524,10 +532,11 @@
524
532
  }
525
533
  }
526
534
 
527
- // On load, try to restore a previously connected folder. queryPermission needs
528
- // no gesture; if it's still "granted" we reconnect silently. If it's "prompt",
529
- // we keep the handle so the next "Connect folder" click reconnects with one
530
- // grant dialog (no picker). Handles cleared/denied storage are ignored.
535
+ // On load, try to restore a previously connected folder. queryPermission
536
+ // needs no gesture. If it is still "granted", we reconnect silently. If it
537
+ // is "prompt", we keep the handle. The next "Connect folder" click then
538
+ // reconnects with one grant dialog (no picker). We ignore a handle from
539
+ // cleared or denied storage.
531
540
  function restoreFolder() {
532
541
  idbGet(cfg.target).then(function (handle) {
533
542
  if (!handle || typeof handle.queryPermission !== "function") return;
@@ -535,7 +544,7 @@
535
544
  if (perm === "granted") { adoptFolder(handle); }
536
545
  else {
537
546
  rememberedHandle = handle; updateConnectLabel();
538
- if (review) setStatus("Folder '" + cfg.target + "' remembered click Reconnect folder.");
547
+ if (review) setStatus("Folder '" + cfg.target + "' remembered. Click Reconnect folder.");
539
548
  }
540
549
  }).catch(function () {});
541
550
  });
@@ -553,7 +562,7 @@
553
562
  } catch (e) { setStatus("Disk write failed (" + e.name + "). Downloaded instead."); }
554
563
  }
555
564
  download(SIDECAR, json);
556
- setStatus("Downloaded " + SIDECAR + " move it next to the deck.");
565
+ setStatus("Downloaded " + SIDECAR + ". Move it next to the deck.");
557
566
  }
558
567
 
559
568
  async function loadDisk() {
@@ -669,7 +678,7 @@
669
678
  // Supply page source to resolve source line/column without the folder picker.
670
679
  setSource: function (text) { sourceText = text; state.annotations.forEach(function (a) { a.source = locateInSource(a); }); persistLocal(); renderPanel(); },
671
680
  get state() { return state; },
672
- // for automation/testing:
681
+ // for automation and tests:
673
682
  _capture: captureSelection,
674
683
  _confirm: function (note) { if (pending) { ui.note.value = note || ""; confirmPending(); } }
675
684
  };