@deskwork/studio 0.14.1 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/dist/components/scrapbook-item.d.ts +20 -0
  2. package/dist/components/scrapbook-item.d.ts.map +1 -1
  3. package/dist/components/scrapbook-item.js +21 -2
  4. package/dist/components/scrapbook-item.js.map +1 -1
  5. package/dist/pages/dashboard/affordances.d.ts.map +1 -1
  6. package/dist/pages/dashboard/affordances.js +13 -1
  7. package/dist/pages/dashboard/affordances.js.map +1 -1
  8. package/dist/pages/dashboard/affordances.ts +13 -1
  9. package/dist/pages/dashboard/press-queue.d.ts +16 -0
  10. package/dist/pages/dashboard/press-queue.d.ts.map +1 -0
  11. package/dist/pages/dashboard/press-queue.js +91 -0
  12. package/dist/pages/dashboard/press-queue.js.map +1 -0
  13. package/dist/pages/dashboard/press-queue.ts +112 -0
  14. package/dist/pages/dashboard.d.ts.map +1 -1
  15. package/dist/pages/dashboard.js +2 -0
  16. package/dist/pages/dashboard.js.map +1 -1
  17. package/dist/pages/dashboard.ts +2 -0
  18. package/dist/pages/entry-review/decision-strip.d.ts.map +1 -1
  19. package/dist/pages/entry-review/decision-strip.js +10 -2
  20. package/dist/pages/entry-review/decision-strip.js.map +1 -1
  21. package/dist/pages/entry-review/index.d.ts.map +1 -1
  22. package/dist/pages/entry-review/index.js +7 -4
  23. package/dist/pages/entry-review/index.js.map +1 -1
  24. package/dist/pages/help.js +11 -11
  25. package/dist/pages/help.js.map +1 -1
  26. package/dist/pages/help.ts +11 -11
  27. package/dist/pages/review-scrapbook-drawer.d.ts.map +1 -1
  28. package/dist/pages/review-scrapbook-drawer.js +10 -1
  29. package/dist/pages/review-scrapbook-drawer.js.map +1 -1
  30. package/dist/pages/review-scrapbook-drawer.ts +11 -1
  31. package/dist/pages/scrapbook/dispatch.d.ts +50 -0
  32. package/dist/pages/scrapbook/dispatch.d.ts.map +1 -0
  33. package/dist/pages/scrapbook/dispatch.js +104 -0
  34. package/dist/pages/scrapbook/dispatch.js.map +1 -0
  35. package/dist/pages/scrapbook/image-readers.d.ts +24 -0
  36. package/dist/pages/scrapbook/image-readers.d.ts.map +1 -0
  37. package/dist/pages/scrapbook/image-readers.js +135 -0
  38. package/dist/pages/scrapbook/image-readers.js.map +1 -0
  39. package/dist/pages/scrapbook/index.d.ts +21 -0
  40. package/dist/pages/scrapbook/index.d.ts.map +1 -0
  41. package/dist/pages/scrapbook/index.js +96 -0
  42. package/dist/pages/scrapbook/index.js.map +1 -0
  43. package/dist/pages/scrapbook/render.d.ts +68 -0
  44. package/dist/pages/scrapbook/render.d.ts.map +1 -0
  45. package/dist/pages/scrapbook/render.js +315 -0
  46. package/dist/pages/scrapbook/render.js.map +1 -0
  47. package/dist/pages/scrapbook/text-helpers.d.ts +40 -0
  48. package/dist/pages/scrapbook/text-helpers.d.ts.map +1 -0
  49. package/dist/pages/scrapbook/text-helpers.js +92 -0
  50. package/dist/pages/scrapbook/text-helpers.js.map +1 -0
  51. package/dist/pages/scrapbook/types.d.ts +26 -0
  52. package/dist/pages/scrapbook/types.d.ts.map +1 -0
  53. package/dist/pages/scrapbook/types.js +12 -0
  54. package/dist/pages/scrapbook/types.js.map +1 -0
  55. package/dist/pages/scrapbook.d.ts +8 -8
  56. package/dist/pages/scrapbook.d.ts.map +1 -1
  57. package/dist/pages/scrapbook.js +8 -600
  58. package/dist/pages/scrapbook.js.map +1 -1
  59. package/dist/pages/scrapbook.ts +11 -660
  60. package/dist/routes/api.d.ts.map +1 -1
  61. package/dist/routes/api.js +102 -140
  62. package/dist/routes/api.js.map +1 -1
  63. package/dist/routes/entry-annotation-body.d.ts +29 -0
  64. package/dist/routes/entry-annotation-body.d.ts.map +1 -1
  65. package/dist/routes/entry-annotation-body.js +80 -0
  66. package/dist/routes/entry-annotation-body.js.map +1 -1
  67. package/dist/routes/scrapbook-file.d.ts +17 -9
  68. package/dist/routes/scrapbook-file.d.ts.map +1 -1
  69. package/dist/routes/scrapbook-file.js +48 -15
  70. package/dist/routes/scrapbook-file.js.map +1 -1
  71. package/dist/routes/scrapbook-mutation-dispatch.d.ts +29 -0
  72. package/dist/routes/scrapbook-mutation-dispatch.d.ts.map +1 -0
  73. package/dist/routes/scrapbook-mutation-dispatch.js +63 -0
  74. package/dist/routes/scrapbook-mutation-dispatch.js.map +1 -0
  75. package/dist/routes/scrapbook-mutation-envelope.d.ts +93 -0
  76. package/dist/routes/scrapbook-mutation-envelope.d.ts.map +1 -0
  77. package/dist/routes/scrapbook-mutation-envelope.js +147 -0
  78. package/dist/routes/scrapbook-mutation-envelope.js.map +1 -0
  79. package/dist/routes/scrapbook-mutations.d.ts +25 -7
  80. package/dist/routes/scrapbook-mutations.d.ts.map +1 -1
  81. package/dist/routes/scrapbook-mutations.js +67 -92
  82. package/dist/routes/scrapbook-mutations.js.map +1 -1
  83. package/dist/server.d.ts.map +1 -1
  84. package/dist/server.js +16 -2
  85. package/dist/server.js.map +1 -1
  86. package/package.json +2 -2
@@ -232,7 +232,7 @@ function renderStudioSection() {
232
232
  <h4>Calendar panels</h4>
233
233
  <p>Six on-pipeline columns: <em>Ideas · Planned · Outlining · Drafting · Final · Published</em>, plus parking-lot panels for <em>Blocked</em> and <em>Cancelled</em>. Each row shows slug, title, the per-stage iteration counter (<code>iterationByStage</code>), a file-present dot for the current stage's primary artifact, and any review state.</p>
234
234
  <h4>Next-move column</h4>
235
- <p>Per row, the studio surfaces either a copy-to-clipboard command (for cognitive work that lives in Claude Code) or a one-click button (for mechanical transitions). <code>approve →</code> calls <code>/deskwork:approve</code>. <code>publish →</code> calls <code>/deskwork:publish</code>. <code>block</code> / <code>cancel</code> / <code>induct</code> appear contextually.</p>
235
+ <p>Per row, the studio surfaces a button that copies the corresponding skill command to your clipboard paste into a Claude Code chat to run. <code>approve →</code> copies <code>/deskwork:approve &lt;slug&gt;</code>. <code>publish →</code> copies <code>/deskwork:publish &lt;slug&gt;</code>. <code>block</code> / <code>cancel</code> / <code>induct</code> appear contextually and copy their own commands. The state-machine work happens in the skill, not in the studio (see <code>THESIS.md</code> Consequence 2).</p>
236
236
  <h4>Shortform coverage matrix</h4>
237
237
  <p>For each <em>Published</em> entry, a row of platform cells (reddit, linkedin, youtube, instagram). Shaded cells are covered by a DistributionRecord; empty cells surface the exact <code>/deskwork:shortform-start</code> command to copy.</p>
238
238
  <h4>Voice-drift signal</h4>
@@ -241,13 +241,13 @@ function renderStudioSection() {
241
241
  <div class="eh-panel">
242
242
  <p class="eh-panel-head">Secondary surfaces</p>
243
243
  <h4>Entry review</h4>
244
- <p><code>/dev/editorial-review/entry/&lt;uuid&gt;</code>. The current-stage artifact (idea.md, plan.md, outline.md, or index.md) renders inside the review surface. Select text for a margin note; double-click anywhere to edit the markdown in place; approve, iterate, block, or cancel from the fixed strip. The review is keyed by entry uuid, not workflow id.</p>
244
+ <p><code>/dev/editorial-review/entry/&lt;uuid&gt;</code>. The current-stage artifact (idea.md, plan.md, outline.md, or index.md) renders inside the review surface. Select text for a margin note; double-click anywhere to edit the markdown in place. The fixed strip's Approve / Iterate / (Reject — disabled, see <a href="https://github.com/audiocontrol-org/deskwork/issues/173">#173</a>) buttons COPY the corresponding skill command (<code>/deskwork:approve &lt;slug&gt;</code>, etc.) to your clipboard — paste into a Claude Code chat to run. The skill reads marginalia, applies editorial judgment, edits the file (when iterating), advances state. The review is keyed by entry uuid, not workflow id.</p>
245
245
  <h4>Shortform review</h4>
246
246
  <p><code>/dev/editorial-review-shortform</code>. Cards grouped by platform. Each card has a version header, an editable textarea, and save · approve · iterate · reject controls.</p>
247
247
  <h4>Keyboard</h4>
248
- <p>In the studio: <kbd>1</kbd>–<kbd>6</kbd> jump to on-pipeline columns. In an entry review: <kbd>e</kbd> / double-click toggles edit mode; <kbd>a</kbd> approves (graduates by one stage); <kbd>i</kbd> iterates; <kbd>b</kbd> blocks; <kbd>j</kbd>/<kbd>k</kbd> step through margin notes; <kbd>?</kbd> shows a full shortcuts overlay.</p>
248
+ <p>In the studio: <kbd>1</kbd>–<kbd>6</kbd> jump to on-pipeline columns. In an entry review: <kbd>e</kbd> / double-click toggles edit mode; <kbd>a</kbd> <kbd>a</kbd> copies the approve command to clipboard; <kbd>i</kbd> <kbd>i</kbd> copies the iterate command; <kbd>j</kbd>/<kbd>k</kbd> step through margin notes; <kbd>?</kbd> shows a full shortcuts overlay.</p>
249
249
  <h4>Polling</h4>
250
- <p>Both routes poll every 8–10 seconds when idle. If the agent runs <code>/deskwork:iterate</code> in Claude Code, a new version of the current-stage artifact shows up in the browser without a reload.</p>
250
+ <p>Both routes poll every 8–10 seconds when idle. After you paste the iterate command into Claude Code and the skill writes a new version, the new artifact shows up in the browser without a manual reload.</p>
251
251
  </div>
252
252
  </div>
253
253
  </section>`);
@@ -261,22 +261,22 @@ const RUNTHROUGH_STEPS = [
261
261
  {
262
262
  title: 'Iterate the idea',
263
263
  op: 'browser then terminal',
264
- body: unsafe('<p>Open <code>/dev/editorial-review/entry/&lt;uuid&gt;</code>. Leave margin notes on <code>idea.md</code>. Click <em>Iterate</em>; back in Claude Code run <code>/deskwork:iterate &lt;uuid&gt;</code>. The agent revises using the site voice, writes the next version, bumps <code>iterationByStage.Ideas</code>.</p>'),
264
+ body: unsafe('<p>Open <code>/dev/editorial-review/entry/&lt;uuid&gt;</code>. Leave margin notes on <code>idea.md</code>. Click <em>Iterate</em>; the studio copies <code>/deskwork:iterate &lt;slug&gt;</code> to your clipboard — paste into a Claude Code chat to run. The skill reads your marginalia, revises <code>idea.md</code> in the site voice, writes the next version, bumps <code>iterationByStage.Ideas</code>.</p>'),
265
265
  },
266
266
  {
267
267
  title: 'Approve into Planned',
268
268
  op: 'browser or terminal',
269
- body: unsafe('<p>Click <em>Approve</em> on the review surface or run <code>/deskwork:approve &lt;uuid&gt;</code>. Stage graduates to <em>Planned</em>. <code>plan.md</code> is seeded from <code>idea.md</code>; <code>iterationByStage.Planned</code> starts at 0.</p>'),
269
+ body: unsafe('<p>Click <em>Approve</em> on the review surface the studio copies <code>/deskwork:approve &lt;slug&gt;</code> to your clipboard. Paste into a Claude Code chat. The skill graduates the stage to <em>Planned</em>, seeds <code>plan.md</code> from <code>idea.md</code>, and starts <code>iterationByStage.Planned</code> at 0.</p>'),
270
270
  },
271
271
  {
272
272
  title: 'Plan, then outline, then draft',
273
273
  op: 'browser then terminal',
274
- body: unsafe('<p>Same loop at each stage. <code>/deskwork:iterate</code> revises the current-stage artifact (<code>plan.md</code>, then <code>outline.md</code>, then <code>index.md</code>). <code>/deskwork:approve</code> graduates by exactly one stage. There is no “approve but stay.”</p>'),
274
+ body: unsafe('<p>Same loop at each stage. Click <em>Iterate</em> in the studio to copy the iterate command; the agent runs the skill, which revises the current-stage artifact (<code>plan.md</code>, then <code>outline.md</code>, then <code>index.md</code>). Click <em>Approve</em> to copy the approve command; the skill graduates by exactly one stage. There is no “approve but stay.”</p>'),
275
275
  },
276
276
  {
277
277
  title: 'Approve into Final',
278
278
  op: 'browser or terminal',
279
- body: unsafe('<p>When the Drafting <code>index.md</code> is good, approve once more. Stage becomes <em>Final</em>. The same <code>index.md</code> is the artifact at Final — Final is the “ready to publish” stage, not a separate file.</p>'),
279
+ body: unsafe('<p>When the Drafting <code>index.md</code> is good, click <em>Approve</em> once more (copies the command; paste into Claude Code). Stage becomes <em>Final</em>. The same <code>index.md</code> is the artifact at Final — Final is the “ready to publish” stage, not a separate file.</p>'),
280
280
  },
281
281
  {
282
282
  title: 'Publish',
@@ -342,8 +342,8 @@ function renderReferenceSection() {
342
342
  <dl>
343
343
  <dt><kbd>e</kbd> / dbl-click</dt><dd>toggle edit mode</dd>
344
344
  <dt>select text</dt><dd>leave a margin note</dd>
345
- <dt><kbd>a</kbd></dt><dd>approve graduates by one stage</dd>
346
- <dt><kbd>i</kbd></dt><dd>iterate (hand off to Claude Code)</dd>
345
+ <dt><kbd>a</kbd> <kbd>a</kbd></dt><dd>copy <code>/deskwork:approve &lt;slug&gt;</code> to clipboard</dd>
346
+ <dt><kbd>i</kbd> <kbd>i</kbd></dt><dd>copy <code>/deskwork:iterate &lt;slug&gt;</code> to clipboard</dd>
347
347
  <dt><kbd>b</kbd></dt><dd>block — out of pipeline, resumable</dd>
348
348
  <dt><kbd>j</kbd> / <kbd>k</kbd></dt><dd>step through margin notes</dd>
349
349
  <dt><kbd>?</kbd></dt><dd>shortcuts overlay</dd>
@@ -401,7 +401,7 @@ function renderReferenceSection() {
401
401
  <dl>
402
402
  <dt>404 on /dev/*</dt><dd>the dev routes only run when <code>deskwork-studio</code> is up. Start it with the documented launch command.</dd>
403
403
  <dt>nothing to review</dt><dd>capture an entry first: <code>/deskwork:add "Title"</code>, then visit <code>/dev/editorial-review/entry/&lt;uuid&gt;</code>.</dd>
404
- <dt>iterate doesn't trigger</dt><dd>the agent has to run <code>/deskwork:iterate</code>. The browser button just marks the workflow; Claude does the writing.</dd>
404
+ <dt>iterate doesn't trigger</dt><dd>the studio's <em>Iterate</em> button copies <code>/deskwork:iterate &lt;slug&gt;</code> to your clipboard paste into a Claude Code chat to run. The skill does the writing; the studio doesn't mutate state on its own.</dd>
405
405
  <dt>calendar.md drift</dt><dd>run <code>/deskwork:doctor</code>. The sidecar is truth — calendar.md is regenerated from it.</dd>
406
406
  </dl>
407
407
  </div>
@@ -1 +1 @@
1
- {"version":3,"file":"help.js","sourceRoot":"","sources":["../../src/pages/help.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"help.js","sourceRoot":"","sources":["../../src/pages/help.ts"],"names":[],"mappings":"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"}
@@ -250,7 +250,7 @@ function renderStudioSection(): RawHtml {
250
250
  <h4>Calendar panels</h4>
251
251
  <p>Six on-pipeline columns: <em>Ideas · Planned · Outlining · Drafting · Final · Published</em>, plus parking-lot panels for <em>Blocked</em> and <em>Cancelled</em>. Each row shows slug, title, the per-stage iteration counter (<code>iterationByStage</code>), a file-present dot for the current stage's primary artifact, and any review state.</p>
252
252
  <h4>Next-move column</h4>
253
- <p>Per row, the studio surfaces either a copy-to-clipboard command (for cognitive work that lives in Claude Code) or a one-click button (for mechanical transitions). <code>approve →</code> calls <code>/deskwork:approve</code>. <code>publish →</code> calls <code>/deskwork:publish</code>. <code>block</code> / <code>cancel</code> / <code>induct</code> appear contextually.</p>
253
+ <p>Per row, the studio surfaces a button that copies the corresponding skill command to your clipboard paste into a Claude Code chat to run. <code>approve →</code> copies <code>/deskwork:approve &lt;slug&gt;</code>. <code>publish →</code> copies <code>/deskwork:publish &lt;slug&gt;</code>. <code>block</code> / <code>cancel</code> / <code>induct</code> appear contextually and copy their own commands. The state-machine work happens in the skill, not in the studio (see <code>THESIS.md</code> Consequence 2).</p>
254
254
  <h4>Shortform coverage matrix</h4>
255
255
  <p>For each <em>Published</em> entry, a row of platform cells (reddit, linkedin, youtube, instagram). Shaded cells are covered by a DistributionRecord; empty cells surface the exact <code>/deskwork:shortform-start</code> command to copy.</p>
256
256
  <h4>Voice-drift signal</h4>
@@ -259,13 +259,13 @@ function renderStudioSection(): RawHtml {
259
259
  <div class="eh-panel">
260
260
  <p class="eh-panel-head">Secondary surfaces</p>
261
261
  <h4>Entry review</h4>
262
- <p><code>/dev/editorial-review/entry/&lt;uuid&gt;</code>. The current-stage artifact (idea.md, plan.md, outline.md, or index.md) renders inside the review surface. Select text for a margin note; double-click anywhere to edit the markdown in place; approve, iterate, block, or cancel from the fixed strip. The review is keyed by entry uuid, not workflow id.</p>
262
+ <p><code>/dev/editorial-review/entry/&lt;uuid&gt;</code>. The current-stage artifact (idea.md, plan.md, outline.md, or index.md) renders inside the review surface. Select text for a margin note; double-click anywhere to edit the markdown in place. The fixed strip's Approve / Iterate / (Reject — disabled, see <a href="https://github.com/audiocontrol-org/deskwork/issues/173">#173</a>) buttons COPY the corresponding skill command (<code>/deskwork:approve &lt;slug&gt;</code>, etc.) to your clipboard — paste into a Claude Code chat to run. The skill reads marginalia, applies editorial judgment, edits the file (when iterating), advances state. The review is keyed by entry uuid, not workflow id.</p>
263
263
  <h4>Shortform review</h4>
264
264
  <p><code>/dev/editorial-review-shortform</code>. Cards grouped by platform. Each card has a version header, an editable textarea, and save · approve · iterate · reject controls.</p>
265
265
  <h4>Keyboard</h4>
266
- <p>In the studio: <kbd>1</kbd>–<kbd>6</kbd> jump to on-pipeline columns. In an entry review: <kbd>e</kbd> / double-click toggles edit mode; <kbd>a</kbd> approves (graduates by one stage); <kbd>i</kbd> iterates; <kbd>b</kbd> blocks; <kbd>j</kbd>/<kbd>k</kbd> step through margin notes; <kbd>?</kbd> shows a full shortcuts overlay.</p>
266
+ <p>In the studio: <kbd>1</kbd>–<kbd>6</kbd> jump to on-pipeline columns. In an entry review: <kbd>e</kbd> / double-click toggles edit mode; <kbd>a</kbd> <kbd>a</kbd> copies the approve command to clipboard; <kbd>i</kbd> <kbd>i</kbd> copies the iterate command; <kbd>j</kbd>/<kbd>k</kbd> step through margin notes; <kbd>?</kbd> shows a full shortcuts overlay.</p>
267
267
  <h4>Polling</h4>
268
- <p>Both routes poll every 8–10 seconds when idle. If the agent runs <code>/deskwork:iterate</code> in Claude Code, a new version of the current-stage artifact shows up in the browser without a reload.</p>
268
+ <p>Both routes poll every 8–10 seconds when idle. After you paste the iterate command into Claude Code and the skill writes a new version, the new artifact shows up in the browser without a manual reload.</p>
269
269
  </div>
270
270
  </div>
271
271
  </section>`);
@@ -280,22 +280,22 @@ const RUNTHROUGH_STEPS: ReadonlyArray<{ title: string; op: string; body: RawHtml
280
280
  {
281
281
  title: 'Iterate the idea',
282
282
  op: 'browser then terminal',
283
- body: unsafe('<p>Open <code>/dev/editorial-review/entry/&lt;uuid&gt;</code>. Leave margin notes on <code>idea.md</code>. Click <em>Iterate</em>; back in Claude Code run <code>/deskwork:iterate &lt;uuid&gt;</code>. The agent revises using the site voice, writes the next version, bumps <code>iterationByStage.Ideas</code>.</p>'),
283
+ body: unsafe('<p>Open <code>/dev/editorial-review/entry/&lt;uuid&gt;</code>. Leave margin notes on <code>idea.md</code>. Click <em>Iterate</em>; the studio copies <code>/deskwork:iterate &lt;slug&gt;</code> to your clipboard — paste into a Claude Code chat to run. The skill reads your marginalia, revises <code>idea.md</code> in the site voice, writes the next version, bumps <code>iterationByStage.Ideas</code>.</p>'),
284
284
  },
285
285
  {
286
286
  title: 'Approve into Planned',
287
287
  op: 'browser or terminal',
288
- body: unsafe('<p>Click <em>Approve</em> on the review surface or run <code>/deskwork:approve &lt;uuid&gt;</code>. Stage graduates to <em>Planned</em>. <code>plan.md</code> is seeded from <code>idea.md</code>; <code>iterationByStage.Planned</code> starts at 0.</p>'),
288
+ body: unsafe('<p>Click <em>Approve</em> on the review surface the studio copies <code>/deskwork:approve &lt;slug&gt;</code> to your clipboard. Paste into a Claude Code chat. The skill graduates the stage to <em>Planned</em>, seeds <code>plan.md</code> from <code>idea.md</code>, and starts <code>iterationByStage.Planned</code> at 0.</p>'),
289
289
  },
290
290
  {
291
291
  title: 'Plan, then outline, then draft',
292
292
  op: 'browser then terminal',
293
- body: unsafe('<p>Same loop at each stage. <code>/deskwork:iterate</code> revises the current-stage artifact (<code>plan.md</code>, then <code>outline.md</code>, then <code>index.md</code>). <code>/deskwork:approve</code> graduates by exactly one stage. There is no “approve but stay.”</p>'),
293
+ body: unsafe('<p>Same loop at each stage. Click <em>Iterate</em> in the studio to copy the iterate command; the agent runs the skill, which revises the current-stage artifact (<code>plan.md</code>, then <code>outline.md</code>, then <code>index.md</code>). Click <em>Approve</em> to copy the approve command; the skill graduates by exactly one stage. There is no “approve but stay.”</p>'),
294
294
  },
295
295
  {
296
296
  title: 'Approve into Final',
297
297
  op: 'browser or terminal',
298
- body: unsafe('<p>When the Drafting <code>index.md</code> is good, approve once more. Stage becomes <em>Final</em>. The same <code>index.md</code> is the artifact at Final — Final is the “ready to publish” stage, not a separate file.</p>'),
298
+ body: unsafe('<p>When the Drafting <code>index.md</code> is good, click <em>Approve</em> once more (copies the command; paste into Claude Code). Stage becomes <em>Final</em>. The same <code>index.md</code> is the artifact at Final — Final is the “ready to publish” stage, not a separate file.</p>'),
299
299
  },
300
300
  {
301
301
  title: 'Publish',
@@ -365,8 +365,8 @@ function renderReferenceSection(): RawHtml {
365
365
  <dl>
366
366
  <dt><kbd>e</kbd> / dbl-click</dt><dd>toggle edit mode</dd>
367
367
  <dt>select text</dt><dd>leave a margin note</dd>
368
- <dt><kbd>a</kbd></dt><dd>approve graduates by one stage</dd>
369
- <dt><kbd>i</kbd></dt><dd>iterate (hand off to Claude Code)</dd>
368
+ <dt><kbd>a</kbd> <kbd>a</kbd></dt><dd>copy <code>/deskwork:approve &lt;slug&gt;</code> to clipboard</dd>
369
+ <dt><kbd>i</kbd> <kbd>i</kbd></dt><dd>copy <code>/deskwork:iterate &lt;slug&gt;</code> to clipboard</dd>
370
370
  <dt><kbd>b</kbd></dt><dd>block — out of pipeline, resumable</dd>
371
371
  <dt><kbd>j</kbd> / <kbd>k</kbd></dt><dd>step through margin notes</dd>
372
372
  <dt><kbd>?</kbd></dt><dd>shortcuts overlay</dd>
@@ -424,7 +424,7 @@ function renderReferenceSection(): RawHtml {
424
424
  <dl>
425
425
  <dt>404 on /dev/*</dt><dd>the dev routes only run when <code>deskwork-studio</code> is up. Start it with the documented launch command.</dd>
426
426
  <dt>nothing to review</dt><dd>capture an entry first: <code>/deskwork:add "Title"</code>, then visit <code>/dev/editorial-review/entry/&lt;uuid&gt;</code>.</dd>
427
- <dt>iterate doesn't trigger</dt><dd>the agent has to run <code>/deskwork:iterate</code>. The browser button just marks the workflow; Claude does the writing.</dd>
427
+ <dt>iterate doesn't trigger</dt><dd>the studio's <em>Iterate</em> button copies <code>/deskwork:iterate &lt;slug&gt;</code> to your clipboard paste into a Claude Code chat to run. The skill does the writing; the studio doesn't mutate state on its own.</dd>
428
428
  <dt>calendar.md drift</dt><dd>run <code>/deskwork:doctor</code>. The sidecar is truth — calendar.md is regenerated from it.</dd>
429
429
  </dl>
430
430
  </div>
@@ -1 +1 @@
1
- {"version":3,"file":"review-scrapbook-drawer.d.ts","sourceRoot":"","sources":["../../src/pages/review-scrapbook-drawer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAYH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAOtD,OAAO,EAA4B,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAqFnE,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,aAAa,GAAG,IAAI,EAC3B,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,YAAY,GACnB,OAAO,CA6DT"}
1
+ {"version":3,"file":"review-scrapbook-drawer.d.ts","sourceRoot":"","sources":["../../src/pages/review-scrapbook-drawer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAYH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAOtD,OAAO,EAA4B,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAqFnE,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,aAAa,GAAG,IAAI,EAC3B,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,YAAY,GACnB,OAAO,CAuET"}
@@ -101,6 +101,15 @@ export function renderScrapbookDrawer(ctx, site, entry, slug, index) {
101
101
  const secretItems = summary?.secretItems ?? [];
102
102
  const total = items.length + secretItems.length;
103
103
  const loader = makeInlineTextLoader(ctx, site, entry, slug, index);
104
+ // #205: prefer entry-aware addressing for the "open viewer ↗" link
105
+ // when this drawer renders for a tracked calendar entry. The standalone
106
+ // viewer's server route resolves the listing via `scrapbookDirForEntry`
107
+ // when `entryId` is present, matching the entry-aware mutation API.
108
+ // Falls back to slug-template addressing when the entry has no id
109
+ // binding (pre-doctor entries, legacy / organizational paths).
110
+ const viewerAddress = entry !== null && entry.id !== undefined && entry.id !== ''
111
+ ? { site, path: slug, entryId: entry.id }
112
+ : { site, path: slug };
104
113
  return unsafe(html `
105
114
  <aside class="er-scrapbook-drawer" data-scrapbook-drawer aria-label="Scrapbook for this entry">
106
115
  <header class="er-scrapbook-drawer-handle" data-drawer-toggle role="button" tabindex="0"
@@ -110,7 +119,7 @@ export function renderScrapbookDrawer(ctx, site, entry, slug, index) {
110
119
  <span class="er-scrapbook-drawer-peek" aria-hidden="true">
111
120
  ${unsafe(renderPeek(items, secretItems))}
112
121
  </span>
113
- <a class="er-scrapbook-drawer-open" href="${scrapbookViewerUrl({ site, path: slug })}"
122
+ <a class="er-scrapbook-drawer-open" href="${scrapbookViewerUrl(viewerAddress)}"
114
123
  title="Open the standalone scrapbook viewer"
115
124
  onclick="event.stopPropagation()">open viewer ↗</a>
116
125
  <button class="er-scrapbook-drawer-toggle" type="button" data-drawer-toggle
@@ -1 +1 @@
1
- {"version":3,"file":"review-scrapbook-drawer.js","sourceRoot":"","sources":["../../src/pages/review-scrapbook-drawer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"review-scrapbook-drawer.js","sourceRoot":"","sources":["../../src/pages/review-scrapbook-drawer.ts"],"names":[],"mappings":"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"}
@@ -156,6 +156,16 @@ export function renderScrapbookDrawer(
156
156
  const secretItems = summary?.secretItems ?? [];
157
157
  const total = items.length + secretItems.length;
158
158
  const loader = makeInlineTextLoader(ctx, site, entry, slug, index);
159
+ // #205: prefer entry-aware addressing for the "open viewer ↗" link
160
+ // when this drawer renders for a tracked calendar entry. The standalone
161
+ // viewer's server route resolves the listing via `scrapbookDirForEntry`
162
+ // when `entryId` is present, matching the entry-aware mutation API.
163
+ // Falls back to slug-template addressing when the entry has no id
164
+ // binding (pre-doctor entries, legacy / organizational paths).
165
+ const viewerAddress: { site: string; path: string; entryId?: string } =
166
+ entry !== null && entry.id !== undefined && entry.id !== ''
167
+ ? { site, path: slug, entryId: entry.id }
168
+ : { site, path: slug };
159
169
 
160
170
  return unsafe(html`
161
171
  <aside class="er-scrapbook-drawer" data-scrapbook-drawer aria-label="Scrapbook for this entry">
@@ -166,7 +176,7 @@ export function renderScrapbookDrawer(
166
176
  <span class="er-scrapbook-drawer-peek" aria-hidden="true">
167
177
  ${unsafe(renderPeek(items, secretItems))}
168
178
  </span>
169
- <a class="er-scrapbook-drawer-open" href="${scrapbookViewerUrl({ site, path: slug })}"
179
+ <a class="er-scrapbook-drawer-open" href="${scrapbookViewerUrl(viewerAddress)}"
170
180
  title="Open the standalone scrapbook viewer"
171
181
  onclick="event.stopPropagation()">open viewer ↗</a>
172
182
  <button class="er-scrapbook-drawer-toggle" type="button" data-drawer-toggle
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Scrapbook page listing dispatch — entry-aware vs slug-template
3
+ * resolution. Mirrors the post-#191 envelope/dispatch split for
4
+ * mutations: dispatch chooses the addressing mode, render stays free
5
+ * of the resolution branch.
6
+ */
7
+ import { type ScrapbookSummary } from '@deskwork/core/scrapbook';
8
+ import type { StudioContext } from '../../routes/api.ts';
9
+ /**
10
+ * Error class surfaced by the page route to translate into HTTP status
11
+ * codes (`server.ts` maps these onto 400/404 responses).
12
+ */
13
+ export declare class ScrapbookPageError extends Error {
14
+ readonly status: 400 | 404;
15
+ constructor(message: string, status: 400 | 404);
16
+ }
17
+ /**
18
+ * #168 Phase 34 ship-pass — when the scrapbook path matches a tracked
19
+ * calendar entry with a stamped UUID, return the entry's id so the aside
20
+ * can render a "← back to review" link AND the client can address
21
+ * mutations via `entryId` (#191 fix). Returns null when no entry matches
22
+ * (organizational subdirs, ad-hoc paths, or pre-doctor entries lacking
23
+ * an id) — the link is then omitted and mutations fall back to slug-
24
+ * template addressing.
25
+ *
26
+ * Failures (calendar absent, parse error) fall through to null so a
27
+ * transient calendar issue never blocks the scrapbook render.
28
+ */
29
+ export declare function lookupEntryId(ctx: StudioContext, site: string, path: string): string | null;
30
+ /**
31
+ * #205 — listing + dir-resolution dispatch. Two addressing modes:
32
+ *
33
+ * - Entry-id mode: `entryId` is validated as a UUID, the sidecar is
34
+ * read, and the listing resolves via `listScrapbookForEntry` so
35
+ * non-kebab-case entries (feature-doc layouts, dotted version
36
+ * segments, etc.) read items from the entry's artifact-parent
37
+ * `scrapbook/` directory. Symmetric to the entry-aware mutation API.
38
+ * - Slug mode: legacy / organizational paths under the site's content
39
+ * dir. Resolves via `scrapbookDirAtPath` + `listScrapbook` (the
40
+ * pre-#205 behavior).
41
+ *
42
+ * Returns the resolved `scrapbookDir` (absolute) plus the listing
43
+ * summary so the caller threads both through to render helpers.
44
+ */
45
+ export declare function resolveListing(ctx: StudioContext, site: string, path: string, entryId: string | null): Promise<{
46
+ scrapbookDir: string;
47
+ result: ScrapbookSummary;
48
+ resolvedEntryId: string | null;
49
+ }>;
50
+ //# sourceMappingURL=dispatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../../src/pages/scrapbook/dispatch.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,EAKL,KAAK,gBAAgB,EACtB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD;;;GAGG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,MAAM,EAAE,GAAG,GAAG,GAAG,CAAC;gBACf,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,GAAG;CAK/C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,IAAI,CAYf;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GAAG,IAAI,GACrB,OAAO,CAAC;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAC;IAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CA0C7F"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Scrapbook page listing dispatch — entry-aware vs slug-template
3
+ * resolution. Mirrors the post-#191 envelope/dispatch split for
4
+ * mutations: dispatch chooses the addressing mode, render stays free
5
+ * of the resolution branch.
6
+ */
7
+ import { existsSync } from 'node:fs';
8
+ import { readCalendar } from '@deskwork/core/calendar';
9
+ import { findEntry } from '@deskwork/core/calendar-mutations';
10
+ import { resolveCalendarPath } from '@deskwork/core/paths';
11
+ import { listScrapbook, listScrapbookForEntry, scrapbookDirAtPath, scrapbookDirForEntry, } from '@deskwork/core/scrapbook';
12
+ import { readSidecar } from '@deskwork/core/sidecar';
13
+ import { UUID_RE } from "../../routes/scrapbook-mutation-envelope.js";
14
+ /**
15
+ * Error class surfaced by the page route to translate into HTTP status
16
+ * codes (`server.ts` maps these onto 400/404 responses).
17
+ */
18
+ export class ScrapbookPageError extends Error {
19
+ status;
20
+ constructor(message, status) {
21
+ super(message);
22
+ this.status = status;
23
+ this.name = 'ScrapbookPageError';
24
+ }
25
+ }
26
+ /**
27
+ * #168 Phase 34 ship-pass — when the scrapbook path matches a tracked
28
+ * calendar entry with a stamped UUID, return the entry's id so the aside
29
+ * can render a "← back to review" link AND the client can address
30
+ * mutations via `entryId` (#191 fix). Returns null when no entry matches
31
+ * (organizational subdirs, ad-hoc paths, or pre-doctor entries lacking
32
+ * an id) — the link is then omitted and mutations fall back to slug-
33
+ * template addressing.
34
+ *
35
+ * Failures (calendar absent, parse error) fall through to null so a
36
+ * transient calendar issue never blocks the scrapbook render.
37
+ */
38
+ export function lookupEntryId(ctx, site, path) {
39
+ if (!(site in ctx.config.sites))
40
+ return null;
41
+ try {
42
+ const calendarPath = resolveCalendarPath(ctx.projectRoot, ctx.config, site);
43
+ if (!existsSync(calendarPath))
44
+ return null;
45
+ const cal = readCalendar(calendarPath);
46
+ const entry = findEntry(cal, path);
47
+ if (!entry || !entry.id)
48
+ return null;
49
+ return entry.id;
50
+ }
51
+ catch {
52
+ return null;
53
+ }
54
+ }
55
+ /**
56
+ * #205 — listing + dir-resolution dispatch. Two addressing modes:
57
+ *
58
+ * - Entry-id mode: `entryId` is validated as a UUID, the sidecar is
59
+ * read, and the listing resolves via `listScrapbookForEntry` so
60
+ * non-kebab-case entries (feature-doc layouts, dotted version
61
+ * segments, etc.) read items from the entry's artifact-parent
62
+ * `scrapbook/` directory. Symmetric to the entry-aware mutation API.
63
+ * - Slug mode: legacy / organizational paths under the site's content
64
+ * dir. Resolves via `scrapbookDirAtPath` + `listScrapbook` (the
65
+ * pre-#205 behavior).
66
+ *
67
+ * Returns the resolved `scrapbookDir` (absolute) plus the listing
68
+ * summary so the caller threads both through to render helpers.
69
+ */
70
+ export async function resolveListing(ctx, site, path, entryId) {
71
+ if (entryId !== null) {
72
+ if (!UUID_RE.test(entryId)) {
73
+ throw new ScrapbookPageError(`invalid entryId`, 400);
74
+ }
75
+ let entry;
76
+ try {
77
+ entry = await readSidecar(ctx.projectRoot, entryId);
78
+ }
79
+ catch (e) {
80
+ // readSidecar throws "sidecar not found" with ENOENT; map to 404.
81
+ const reason = e instanceof Error ? e.message : String(e);
82
+ if (/sidecar not found/i.test(reason)) {
83
+ throw new ScrapbookPageError(reason, 404);
84
+ }
85
+ throw e;
86
+ }
87
+ const scrapbookDir = scrapbookDirForEntry(ctx.projectRoot, ctx.config, site, { id: entry.uuid, slug: entry.slug });
88
+ const result = listScrapbookForEntry(ctx.projectRoot, ctx.config, site, { id: entry.uuid, slug: entry.slug });
89
+ return { scrapbookDir, result, resolvedEntryId: entry.uuid };
90
+ }
91
+ // Slug-mode: legacy back-compat. listScrapbook returns
92
+ // { exists: false, items: [] } for missing dirs, so an empty
93
+ // scrapbook is not an error path. Real errors (slug validation,
94
+ // scrapbookDir resolution failures, FS permission issues)
95
+ // propagate to the studio's error handler.
96
+ const scrapbookDir = scrapbookDirAtPath(ctx.projectRoot, ctx.config, site, path);
97
+ const result = listScrapbook(ctx.projectRoot, ctx.config, site, path);
98
+ // Best-effort lookup so the back-link to the review surface still
99
+ // renders for slug-template entries that happen to match a calendar
100
+ // entry by slug.
101
+ const resolvedEntryId = lookupEntryId(ctx, site, path);
102
+ return { scrapbookDir, result, resolvedEntryId };
103
+ }
104
+ //# sourceMappingURL=dispatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch.js","sourceRoot":"","sources":["../../../src/pages/scrapbook/dispatch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Scrapbook image-format readers — pure functions on Buffer headers.
3
+ *
4
+ * Recognizes PNG, JPEG, WebP, and GIF; returns null for unrecognized
5
+ * signatures or truncated/malformed buffers. Used by the scrapbook
6
+ * card meta to render `{W} × {H}` next to the image kind chip.
7
+ *
8
+ * Each format is parsed from its file-header structure (no external
9
+ * dependency); each branch returns null on any unexpected byte rather
10
+ * than throwing, so a corrupt image still renders with empty meta.
11
+ */
12
+ export interface ImageDimensions {
13
+ readonly width: number;
14
+ readonly height: number;
15
+ }
16
+ /**
17
+ * Read width × height from a buffer. Recognizes PNG, JPEG, WebP, and
18
+ * GIF; returns null for unrecognized signatures or truncated/malformed
19
+ * buffers.
20
+ */
21
+ export declare function readImageDimensions(buf: Buffer): ImageDimensions | null;
22
+ export declare function readJpegDimensions(buf: Buffer): ImageDimensions | null;
23
+ export declare function readWebpDimensions(buf: Buffer): ImageDimensions | null;
24
+ //# sourceMappingURL=image-readers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-readers.d.ts","sourceRoot":"","sources":["../../../src/pages/scrapbook/image-readers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAiCvE;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAoCtE;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAyCtE"}
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Scrapbook image-format readers — pure functions on Buffer headers.
3
+ *
4
+ * Recognizes PNG, JPEG, WebP, and GIF; returns null for unrecognized
5
+ * signatures or truncated/malformed buffers. Used by the scrapbook
6
+ * card meta to render `{W} × {H}` next to the image kind chip.
7
+ *
8
+ * Each format is parsed from its file-header structure (no external
9
+ * dependency); each branch returns null on any unexpected byte rather
10
+ * than throwing, so a corrupt image still renders with empty meta.
11
+ */
12
+ /**
13
+ * Read width × height from a buffer. Recognizes PNG, JPEG, WebP, and
14
+ * GIF; returns null for unrecognized signatures or truncated/malformed
15
+ * buffers.
16
+ */
17
+ export function readImageDimensions(buf) {
18
+ if (buf.length < 12)
19
+ return null;
20
+ // PNG: 89 50 4E 47 0D 0A 1A 0A then IHDR (chunk-length 4, "IHDR" 4,
21
+ // width 4, height 4 — width@16, height@20).
22
+ if (buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4e && buf[3] === 0x47) {
23
+ if (buf.length < 24)
24
+ return null;
25
+ return { width: buf.readUInt32BE(16), height: buf.readUInt32BE(20) };
26
+ }
27
+ // GIF: "GIF87a" or "GIF89a" + logical screen descriptor (width LE @6,
28
+ // height LE @8).
29
+ if (buf[0] === 0x47 && buf[1] === 0x49 && buf[2] === 0x46 &&
30
+ buf[3] === 0x38 && (buf[4] === 0x37 || buf[4] === 0x39) && buf[5] === 0x61) {
31
+ if (buf.length < 10)
32
+ return null;
33
+ return { width: buf.readUInt16LE(6), height: buf.readUInt16LE(8) };
34
+ }
35
+ // JPEG: starts FF D8. Width/height live in a Start-Of-Frame marker
36
+ // (FF C0–CF, excluding DHT C4 / JPG C8 / DAC CC). Walk markers
37
+ // skipping their payloads until the SOF is found.
38
+ if (buf[0] === 0xff && buf[1] === 0xd8) {
39
+ return readJpegDimensions(buf);
40
+ }
41
+ // WebP: "RIFF" {size} "WEBP" then a chunk (VP8 / VP8L / VP8X).
42
+ if (buf[0] === 0x52 && buf[1] === 0x49 && buf[2] === 0x46 && buf[3] === 0x46 &&
43
+ buf[8] === 0x57 && buf[9] === 0x45 && buf[10] === 0x42 && buf[11] === 0x50) {
44
+ return readWebpDimensions(buf);
45
+ }
46
+ return null;
47
+ }
48
+ export function readJpegDimensions(buf) {
49
+ // Marker walk: skip FF D8 (SOI), then each marker is FF Xn followed
50
+ // by a 2-byte big-endian segment length (which includes its own 2
51
+ // bytes). The SOF segment's payload is: 1 byte precision, 2 bytes
52
+ // height, 2 bytes width (the rest is component info).
53
+ let i = 2;
54
+ while (i + 4 <= buf.length) {
55
+ if (buf[i] !== 0xff)
56
+ return null;
57
+ let marker = buf[i + 1] ?? 0;
58
+ // Skip fill bytes (0xff padding before the actual marker byte).
59
+ while (marker === 0xff && i + 2 < buf.length) {
60
+ i++;
61
+ marker = buf[i + 1] ?? 0;
62
+ }
63
+ i += 2;
64
+ // Standalone markers (no length): RST0–7 (D0–D7) and SOI/EOI/TEM.
65
+ if (marker === 0xd9 || marker === 0xd8 || marker === 0x01)
66
+ return null;
67
+ if (marker >= 0xd0 && marker <= 0xd7)
68
+ continue;
69
+ if (i + 2 > buf.length)
70
+ return null;
71
+ const segLen = buf.readUInt16BE(i);
72
+ // SOF markers carry the dimensions. Exclusions per JPEG spec:
73
+ // C4 (DHT — Huffman tables), C8 (JPG reserved), CC (DAC — arithmetic
74
+ // coding conditioning).
75
+ const isSof = marker >= 0xc0 && marker <= 0xcf &&
76
+ marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc;
77
+ if (isSof) {
78
+ if (i + 7 > buf.length)
79
+ return null;
80
+ return {
81
+ width: buf.readUInt16BE(i + 5),
82
+ height: buf.readUInt16BE(i + 3),
83
+ };
84
+ }
85
+ i += segLen;
86
+ }
87
+ return null;
88
+ }
89
+ export function readWebpDimensions(buf) {
90
+ // Three sub-formats (per RFC 6386 / VP8L spec / WebP container spec):
91
+ // - "VP8 " (lossy): header @20-22: signature 9D 01 2A, then
92
+ // 14-bit width LE @23, 14-bit height LE @25
93
+ // (each masked to 14 bits — top 2 bits are
94
+ // horizontal/vertical scale).
95
+ // - "VP8L" (lossless): header @20: signature 0x2F, then 32 bits
96
+ // packed: width-1 (14 bits) + height-1 (14
97
+ // bits) + alpha-flag + version (4 bits).
98
+ // - "VP8X" (extended): flags @20, reserved @21-23, width-1 (24
99
+ // bits LE) @24, height-1 (24 bits LE) @27.
100
+ // Need at least the RIFF header + chunk fourcc; per-variant length
101
+ // checks below cover the variant-specific payload sizes.
102
+ if (buf.length < 16)
103
+ return null;
104
+ const fourcc = buf.subarray(12, 16).toString('ascii');
105
+ if (fourcc === 'VP8 ') {
106
+ if (buf.length < 30)
107
+ return null;
108
+ if (buf[23] !== 0x9d || buf[24] !== 0x01 || buf[25] !== 0x2a)
109
+ return null;
110
+ const w = buf.readUInt16LE(26) & 0x3fff;
111
+ const h = buf.readUInt16LE(28) & 0x3fff;
112
+ return { width: w, height: h };
113
+ }
114
+ if (fourcc === 'VP8L') {
115
+ if (buf.length < 25)
116
+ return null;
117
+ if (buf[20] !== 0x2f)
118
+ return null;
119
+ // Read 4 bytes LE at offset 21, then unpack.
120
+ const packed = buf.readUInt32LE(21);
121
+ const widthMinus1 = packed & 0x3fff;
122
+ const heightMinus1 = (packed >>> 14) & 0x3fff;
123
+ return { width: widthMinus1 + 1, height: heightMinus1 + 1 };
124
+ }
125
+ if (fourcc === 'VP8X') {
126
+ if (buf.length < 30)
127
+ return null;
128
+ // 24-bit little-endian: low byte + (mid << 8) + (high << 16).
129
+ const w = ((buf[24] ?? 0) | ((buf[25] ?? 0) << 8) | ((buf[26] ?? 0) << 16)) + 1;
130
+ const h = ((buf[27] ?? 0) | ((buf[28] ?? 0) << 8) | ((buf[29] ?? 0) << 16)) + 1;
131
+ return { width: w, height: h };
132
+ }
133
+ return null;
134
+ }
135
+ //# sourceMappingURL=image-readers.js.map