@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.
- package/README.md +2 -2
- package/bin/fit-outpost.js +3 -2
- package/package.json +4 -4
- package/src/agent-path.js +10 -6
- package/src/agent-runner.js +28 -24
- package/src/index.js +2 -3
- package/src/kb-manager.js +21 -19
- package/src/outpost.js +37 -33
- package/src/posture.js +3 -3
- package/src/privilege.js +11 -11
- package/src/scheduler.js +14 -12
- package/src/socket-server.js +19 -18
- package/src/spawn-env.js +7 -6
- package/src/state-manager.js +2 -2
- package/templates/.claude/agents/chief-of-staff.md +22 -22
- package/templates/.claude/agents/concierge.md +19 -17
- package/templates/.claude/agents/head-hunter.md +27 -26
- package/templates/.claude/agents/librarian.md +17 -16
- package/templates/.claude/agents/postman.md +19 -18
- package/templates/.claude/agents/recruiter.md +31 -27
- package/templates/.claude/skills/anarlog-follow/SKILL.md +50 -49
- package/templates/.claude/skills/anarlog-follow/references/coaching.md +22 -21
- package/templates/.claude/skills/anarlog-follow/scripts/follow.mjs +4 -4
- package/templates/.claude/skills/anarlog-process/SKILL.md +35 -32
- package/templates/.claude/skills/anarlog-process/references/extraction.md +26 -22
- package/templates/.claude/skills/anarlog-process/references/sessions.md +10 -10
- package/templates/.claude/skills/anarlog-process/scripts/scan.mjs +10 -9
- package/templates/.claude/skills/anarlog-trim/SKILL.md +43 -40
- package/templates/.claude/skills/candidate-report/SKILL.md +30 -28
- package/templates/.claude/skills/candidate-report/references/report-template.html +10 -10
- package/templates/.claude/skills/candidate-report/references/report.css +4 -4
- package/templates/.claude/skills/candidate-report/references/rubric.md +15 -14
- package/templates/.claude/skills/candidate-report/scripts/render-pdf.mjs +2 -2
- package/templates/.claude/skills/changelog/SKILL.md +55 -50
- package/templates/.claude/skills/deck-create/SKILL.md +30 -30
- package/templates/.claude/skills/deck-create/references/slide.css +4 -4
- package/templates/.claude/skills/deck-create/scripts/convert-to-pdf.mjs +6 -6
- package/templates/.claude/skills/deck-review/SKILL.md +59 -57
- package/templates/.claude/skills/deck-review/assets/slide-annotator.js +58 -49
- package/templates/.claude/skills/deck-summarize/SKILL.md +49 -43
- package/templates/.claude/skills/deck-summarize/references/brief-template.md +4 -3
- package/templates/.claude/skills/deck-summarize/scripts/extract-pptx.mjs +8 -7
- package/templates/.claude/skills/doc-collab/SKILL.md +25 -25
- package/templates/.claude/skills/doc-create/SKILL.md +21 -20
- package/templates/.claude/skills/doc-create/scripts/convert-to-pdf.mjs +6 -6
- package/templates/.claude/skills/draft-emails/SKILL.md +37 -33
- package/templates/.claude/skills/draft-emails/references/template.md +2 -2
- package/templates/.claude/skills/draft-emails/scripts/scan-emails.mjs +5 -4
- package/templates/.claude/skills/draft-emails/scripts/send-email.mjs +11 -9
- package/templates/.claude/skills/extract-entities/SKILL.md +65 -65
- package/templates/.claude/skills/extract-entities/references/TEMPLATES.md +5 -5
- package/templates/.claude/skills/extract-entities/references/conditions.md +14 -13
- package/templates/.claude/skills/extract-entities/references/content.md +10 -9
- package/templates/.claude/skills/extract-entities/references/links.md +5 -5
- package/templates/.claude/skills/extract-entities/references/recruitment.md +19 -18
- package/templates/.claude/skills/extract-entities/references/resolution.md +13 -13
- package/templates/.claude/skills/extract-entities/references/sources.md +8 -8
- package/templates/.claude/skills/extract-entities/references/templates-conditions.md +2 -2
- package/templates/.claude/skills/extract-entities/references/templates-priorities.md +2 -2
- package/templates/.claude/skills/extract-entities/scripts/state.mjs +9 -8
- package/templates/.claude/skills/meeting-prep/SKILL.md +25 -24
- package/templates/.claude/skills/organize-files/SKILL.md +24 -22
- package/templates/.claude/skills/organize-files/scripts/organize-by-type.mjs +5 -4
- package/templates/.claude/skills/organize-files/scripts/summarize.mjs +4 -4
- package/templates/.claude/skills/person-identify/SKILL.md +24 -22
- package/templates/.claude/skills/person-identify/scripts/identify.sh +17 -17
- package/templates/.claude/skills/person-lookup/SKILL.md +28 -29
- package/templates/.claude/skills/person-lookup/scripts/lookup.sh +19 -16
- package/templates/.claude/skills/req-assess/SKILL.md +27 -27
- package/templates/.claude/skills/req-assess/references/interview-template.md +1 -1
- package/templates/.claude/skills/req-assess/references/panel-template.md +3 -2
- package/templates/.claude/skills/req-assess/references/rubric.md +13 -13
- package/templates/.claude/skills/req-decide/SKILL.md +28 -25
- package/templates/.claude/skills/req-decide/references/rubric.md +11 -10
- package/templates/.claude/skills/req-forget/SKILL.md +28 -26
- package/templates/.claude/skills/req-forget/references/classify.md +13 -13
- package/templates/.claude/skills/req-forget/references/locations.md +2 -2
- package/templates/.claude/skills/req-forget/references/report-template.md +1 -1
- package/templates/.claude/skills/req-scan/SKILL.md +21 -22
- package/templates/.claude/skills/req-scan/references/fallbacks.md +9 -9
- package/templates/.claude/skills/req-scan/references/filters.md +13 -13
- package/templates/.claude/skills/req-scan/references/sources.md +7 -7
- package/templates/.claude/skills/req-scan/references/state.md +1 -1
- package/templates/.claude/skills/req-scan/scripts/state.mjs +3 -2
- package/templates/.claude/skills/req-screen/SKILL.md +31 -29
- package/templates/.claude/skills/req-screen/references/rubric.md +18 -18
- package/templates/.claude/skills/req-screen/references/template.md +4 -4
- package/templates/.claude/skills/req-track/SKILL.md +40 -40
- package/templates/.claude/skills/req-track/references/fields.md +19 -18
- package/templates/.claude/skills/req-track/references/signals.md +12 -12
- package/templates/.claude/skills/req-track/references/statuses.md +3 -2
- package/templates/.claude/skills/req-track/references/templates.md +4 -4
- package/templates/.claude/skills/req-workday/SKILL.md +24 -24
- package/templates/.claude/skills/req-workday/references/status-mapping.md +5 -5
- package/templates/.claude/skills/req-workday/references/templates.md +13 -13
- package/templates/.claude/skills/req-workday/references/xlsx-format.md +7 -7
- package/templates/.claude/skills/req-workday/scripts/parse-workday.mjs +19 -17
- package/templates/.claude/skills/send-chat/SKILL.md +47 -43
- package/templates/.claude/skills/sync-apple-calendar/SKILL.md +20 -19
- package/templates/.claude/skills/sync-apple-calendar/references/SCHEMA.md +7 -7
- package/templates/.claude/skills/sync-apple-calendar/scripts/query.mjs +6 -6
- package/templates/.claude/skills/sync-apple-calendar/scripts/sync.mjs +9 -8
- package/templates/.claude/skills/sync-apple-mail/SKILL.md +17 -14
- package/templates/.claude/skills/sync-apple-mail/references/SCHEMA.md +10 -10
- package/templates/.claude/skills/sync-apple-mail/scripts/parse-emlx.mjs +9 -9
- package/templates/.claude/skills/sync-apple-mail/scripts/sync-helpers.mjs +16 -13
- package/templates/.claude/skills/sync-apple-mail/scripts/sync.mjs +3 -3
- package/templates/.claude/skills/sync-teams/SKILL.md +43 -43
- package/templates/.claude/skills/sync-teams/scripts/idb-reader.mjs +22 -21
- package/templates/.claude/skills/sync-teams/scripts/leveldb-reader.mjs +4 -4
- package/templates/.claude/skills/sync-teams/scripts/sync.mjs +4 -4
- package/templates/.claude/skills/upstream-instructions/SKILL.md +39 -40
- package/templates/.claude/skills/upstream-instructions/references/examples.md +2 -2
- 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
|
|
4
|
-
compatibility: Requires Node.js
|
|
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.
|
|
10
|
-
|
|
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
|
-
-
|
|
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.
|
|
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
|
|
59
|
+
**These rules prevent problems when the PDF renders:**
|
|
60
60
|
|
|
61
|
-
1. **No layered elements** — Style content elements directly
|
|
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
|
|
65
|
-
4. **Content must fit** — Slides are 1280x720px with 60px padding.
|
|
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
|
-
|
|
73
|
-
navigable in the browser)
|
|
74
|
-
deliberately minimal. Rich event
|
|
75
|
-
|
|
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
|
|
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
|
|
92
|
-
|
|
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
|
|
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
|
|
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
|
|
106
|
-
HTML deck, you can offer to run `deck-review` to make it reviewable
|
|
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
|
|
114
|
-
- Use
|
|
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
|
|
116
|
+
(arrow-keys-only, no click-to-advance or spacebar)
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
/* Required CSS for HTML slide decks
|
|
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
|
|
4
|
+
* See scripts/convert-to-pdf.mjs for the script that renders them.
|
|
5
5
|
*
|
|
6
|
-
* PDF
|
|
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
|
|
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
|
|
3
|
+
* Convert HTML slides to PDF with Playwright.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* PDF document. Each slide
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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
|
|
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
|
|
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
|
|
10
|
-
user can **highlight text on a slide and save the feedback as a sidecar
|
|
11
|
-
Each annotation carries a robust anchor (exact text +
|
|
12
|
-
slide)
|
|
13
|
-
**source line, column and context lines
|
|
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
|
|
17
|
-
|
|
18
|
-
overlay
|
|
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
|
|
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
|
|
31
|
-
source of truth
|
|
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
|
|
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
|
|
52
|
-
|
|
53
|
-
to a hidden slide precisely.
|
|
54
|
-
-
|
|
55
|
-
labels in the panel
|
|
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
|
|
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
|
|
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 *
|
|
76
|
-
other dependency or framework
|
|
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
|
|
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
|
|
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
|
-
|
|
91
|
-
guard
|
|
92
|
-
gracefully
|
|
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
|
-
##
|
|
96
|
+
## Use the overlay (tell the user)
|
|
96
97
|
|
|
97
|
-
1. Open the deck in Chrome
|
|
98
|
-
2. **Select text** on a slide
|
|
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
|
|
103
|
-
|
|
104
|
-
**source line / column / context** for each highlight.
|
|
105
|
-
folder access on `file://`, **Save** downloads the JSON instead
|
|
106
|
-
|
|
107
|
-
4. The connected folder
|
|
108
|
-
|
|
109
|
-
silently if the browser still grants access
|
|
110
|
-
**Reconnect folder
|
|
111
|
-
|
|
112
|
-
one click
|
|
113
|
-
5.
|
|
114
|
-
keystrokes never leak to the deck
|
|
115
|
-
|
|
116
|
-
##
|
|
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
|
|
125
|
-
matches the source even across inline tags / entities.
|
|
126
|
-
3. Make the edit
|
|
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
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
|
162
|
-
on any static HTML page
|
|
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
|
|
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)
|
|
11
|
-
*
|
|
12
|
-
* “Connect folder” once
|
|
13
|
-
*
|
|
14
|
-
*
|
|
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
|
|
17
|
-
*
|
|
18
|
-
* grants permission
|
|
19
|
-
* click re-grants access without
|
|
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
|
|
22
|
-
* “Go” buttons
|
|
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
|
-
//
|
|
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
|
|
164
|
-
// Space
|
|
165
|
-
// button. We stop propagation in the BUBBLE
|
|
166
|
-
// Esc handler
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
//
|
|
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
|
|
214
|
-
// host with click-to-advance
|
|
215
|
-
// clicks (with no active selection) pass through
|
|
216
|
-
// and buttons keep working.
|
|
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)
|
|
256
|
-
//
|
|
257
|
-
// longer match the source or the slide's
|
|
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
|
|
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
|
|
446
|
-
* structured-cloneable, so they live in IndexedDB (localStorage
|
|
447
|
-
* only).
|
|
448
|
-
* gesture
|
|
449
|
-
* "Connect folder" button
|
|
450
|
-
* cfg.target so distinct decks in a folder
|
|
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
|
|
488
|
-
// sidecar, and remember
|
|
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 →
|
|
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 + "'
|
|
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.
|
|
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
|
|
511
|
-
// a single click (this click IS the required user gesture)
|
|
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
|
|
528
|
-
// no gesture
|
|
529
|
-
// we keep the handle
|
|
530
|
-
// grant dialog (no picker).
|
|
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
|
|
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 + "
|
|
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
|
|
681
|
+
// for automation and tests:
|
|
673
682
|
_capture: captureSelection,
|
|
674
683
|
_confirm: function (note) { if (pending) { ui.note.value = note || ""; confirmPending(); } }
|
|
675
684
|
};
|