@deskwork/studio 0.9.5

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 (93) hide show
  1. package/dist/build-client-assets.d.ts +51 -0
  2. package/dist/build-client-assets.d.ts.map +1 -0
  3. package/dist/build-client-assets.js +341 -0
  4. package/dist/build-client-assets.js.map +1 -0
  5. package/dist/components/scrapbook-item.d.ts +108 -0
  6. package/dist/components/scrapbook-item.d.ts.map +1 -0
  7. package/dist/components/scrapbook-item.js +205 -0
  8. package/dist/components/scrapbook-item.js.map +1 -0
  9. package/dist/lib/editorial-skills-catalogue.d.ts +33 -0
  10. package/dist/lib/editorial-skills-catalogue.d.ts.map +1 -0
  11. package/dist/lib/editorial-skills-catalogue.js +211 -0
  12. package/dist/lib/editorial-skills-catalogue.js.map +1 -0
  13. package/dist/lib/override-render.d.ts +41 -0
  14. package/dist/lib/override-render.d.ts.map +1 -0
  15. package/dist/lib/override-render.js +80 -0
  16. package/dist/lib/override-render.js.map +1 -0
  17. package/dist/listen.d.ts +78 -0
  18. package/dist/listen.d.ts.map +1 -0
  19. package/dist/listen.js +155 -0
  20. package/dist/listen.js.map +1 -0
  21. package/dist/pages/chrome.d.ts +26 -0
  22. package/dist/pages/chrome.d.ts.map +1 -0
  23. package/dist/pages/chrome.js +50 -0
  24. package/dist/pages/chrome.js.map +1 -0
  25. package/dist/pages/content-detail.d.ts +14 -0
  26. package/dist/pages/content-detail.d.ts.map +1 -0
  27. package/dist/pages/content-detail.js +279 -0
  28. package/dist/pages/content-detail.js.map +1 -0
  29. package/dist/pages/content.d.ts +39 -0
  30. package/dist/pages/content.d.ts.map +1 -0
  31. package/dist/pages/content.js +414 -0
  32. package/dist/pages/content.js.map +1 -0
  33. package/dist/pages/dashboard.d.ts +32 -0
  34. package/dist/pages/dashboard.d.ts.map +1 -0
  35. package/dist/pages/dashboard.js +803 -0
  36. package/dist/pages/dashboard.js.map +1 -0
  37. package/dist/pages/help.d.ts +24 -0
  38. package/dist/pages/help.d.ts.map +1 -0
  39. package/dist/pages/help.js +433 -0
  40. package/dist/pages/help.js.map +1 -0
  41. package/dist/pages/html.d.ts +35 -0
  42. package/dist/pages/html.d.ts.map +1 -0
  43. package/dist/pages/html.js +73 -0
  44. package/dist/pages/html.js.map +1 -0
  45. package/dist/pages/index.d.ts +21 -0
  46. package/dist/pages/index.d.ts.map +1 -0
  47. package/dist/pages/index.js +174 -0
  48. package/dist/pages/index.js.map +1 -0
  49. package/dist/pages/layout.d.ts +33 -0
  50. package/dist/pages/layout.d.ts.map +1 -0
  51. package/dist/pages/layout.js +50 -0
  52. package/dist/pages/layout.js.map +1 -0
  53. package/dist/pages/review-scrapbook-drawer.d.ts +20 -0
  54. package/dist/pages/review-scrapbook-drawer.d.ts.map +1 -0
  55. package/dist/pages/review-scrapbook-drawer.js +98 -0
  56. package/dist/pages/review-scrapbook-drawer.js.map +1 -0
  57. package/dist/pages/review.d.ts +68 -0
  58. package/dist/pages/review.d.ts.map +1 -0
  59. package/dist/pages/review.js +434 -0
  60. package/dist/pages/review.js.map +1 -0
  61. package/dist/pages/scrapbook.d.ts +21 -0
  62. package/dist/pages/scrapbook.d.ts.map +1 -0
  63. package/dist/pages/scrapbook.js +250 -0
  64. package/dist/pages/scrapbook.js.map +1 -0
  65. package/dist/pages/shortform.d.ts +17 -0
  66. package/dist/pages/shortform.d.ts.map +1 -0
  67. package/dist/pages/shortform.js +142 -0
  68. package/dist/pages/shortform.js.map +1 -0
  69. package/dist/request-context.d.ts +52 -0
  70. package/dist/request-context.d.ts.map +1 -0
  71. package/dist/request-context.js +84 -0
  72. package/dist/request-context.js.map +1 -0
  73. package/dist/routes/api.d.ts +36 -0
  74. package/dist/routes/api.d.ts.map +1 -0
  75. package/dist/routes/api.js +175 -0
  76. package/dist/routes/api.js.map +1 -0
  77. package/dist/routes/scrapbook-file.d.ts +19 -0
  78. package/dist/routes/scrapbook-file.d.ts.map +1 -0
  79. package/dist/routes/scrapbook-file.js +77 -0
  80. package/dist/routes/scrapbook-file.js.map +1 -0
  81. package/dist/routes/scrapbook-mutations.d.ts +33 -0
  82. package/dist/routes/scrapbook-mutations.d.ts.map +1 -0
  83. package/dist/routes/scrapbook-mutations.js +310 -0
  84. package/dist/routes/scrapbook-mutations.js.map +1 -0
  85. package/dist/server.d.ts +52 -0
  86. package/dist/server.d.ts.map +1 -0
  87. package/dist/server.js +581 -0
  88. package/dist/server.js.map +1 -0
  89. package/dist/tailscale.d.ts +63 -0
  90. package/dist/tailscale.d.ts.map +1 -0
  91. package/dist/tailscale.js +118 -0
  92. package/dist/tailscale.js.map +1 -0
  93. package/package.json +60 -0
@@ -0,0 +1,434 @@
1
+ /**
2
+ * Per-post review page — `/dev/editorial-review/:slug`.
3
+ *
4
+ * Renders one workflow's current draft inside a margin-note review
5
+ * shell. The body markdown is rendered server-side (so the operator
6
+ * sees content immediately, no flash of unrendered text), with the
7
+ * frontmatter description injected as a dek paragraph after the
8
+ * body's repeated H1.
9
+ *
10
+ * Ported from `pages/dev/editorial-review/[slug].astro`. Differences:
11
+ * - The full <BlogLayout> Astro component went away; we render the
12
+ * review shell as the page body. Site-specific blog chrome was
13
+ * never useful in the review surface (the review is about the
14
+ * prose, not the page chrome).
15
+ * - Site defaults to `config.defaultSite` rather than the hardcoded
16
+ * `'editorialcontrol'` upstream used.
17
+ * - The outline-split helper lives under the plugin tree's `public/src/`
18
+ * (it's a browser module) but it's pure TS, so it can run server-side
19
+ * for the initial render too. After the marketplace-install fix
20
+ * (issue #4), `public/` was relocated from packages/studio/ into
21
+ * plugins/deskwork-studio/, hence the long relative import below.
22
+ */
23
+ import { handleGetWorkflow } from '@deskwork/core/review/handlers';
24
+ import { parseDraftFrontmatter, renderMarkdownToHtml, } from '@deskwork/core/review/render';
25
+ import { readCalendar } from '@deskwork/core/calendar';
26
+ import { findEntry, findEntryById } from '@deskwork/core/calendar-mutations';
27
+ import { splitOutline } from '@deskwork/core/outline-split';
28
+ import { html, unsafe } from "./html.js";
29
+ import { layout } from "./layout.js";
30
+ import { renderEditorialFolio } from "./chrome.js";
31
+ import { escapeHtml } from "./html.js";
32
+ import { renderScrapbookDrawer } from "./review-scrapbook-drawer.js";
33
+ import { existsSync } from 'node:fs';
34
+ import { resolveCalendarPath } from '@deskwork/core/paths';
35
+ function isSuccessBody(body) {
36
+ if (typeof body !== 'object' || body === null)
37
+ return false;
38
+ return 'workflow' in body && 'versions' in body;
39
+ }
40
+ function errorFromBody(body) {
41
+ if (typeof body === 'object' && body !== null) {
42
+ const value = Reflect.get(body, 'error');
43
+ if (typeof value === 'string')
44
+ return value;
45
+ }
46
+ return 'unknown error';
47
+ }
48
+ function pickContentKind(rawKind) {
49
+ if (rawKind === 'outline')
50
+ return 'outline';
51
+ if (rawKind === 'shortform')
52
+ return 'shortform';
53
+ return 'longform';
54
+ }
55
+ function pickSite(ctx, raw) {
56
+ if (raw && raw in ctx.config.sites)
57
+ return raw;
58
+ return ctx.config.defaultSite;
59
+ }
60
+ function stringField(v) {
61
+ return typeof v === 'string' ? v : undefined;
62
+ }
63
+ async function prepareRender(markdown, contentKind) {
64
+ const parsed = parseDraftFrontmatter(markdown);
65
+ const fm = parsed.frontmatter;
66
+ // Outline + shortform render the body as-is (no outline-split). Only
67
+ // longform pulls the optional briefing-sheet drawer out of the body.
68
+ const split = contentKind !== 'longform'
69
+ ? { body: parsed.body, outline: '', present: false, startLine: -1, endLine: -1 }
70
+ : splitOutline(parsed.body);
71
+ const bodyHtml = await renderMarkdownToHtml(split.body);
72
+ // Inject the description as a dek after the body's first <h1>.
73
+ const description = stringField(fm.description);
74
+ const dekHtml = description
75
+ ? `<p class="er-dispatch-dek">${escapeHtml(description)}</p>`
76
+ : '';
77
+ const h1Close = bodyHtml.indexOf('</h1>');
78
+ const renderedHtml = dekHtml && h1Close >= 0
79
+ ? bodyHtml.slice(0, h1Close + 5) + dekHtml + bodyHtml.slice(h1Close + 5)
80
+ : dekHtml + bodyHtml;
81
+ const outlineHtml = split.outline
82
+ ? await renderMarkdownToHtml(split.outline)
83
+ : '';
84
+ return { fm, bodyHtml: renderedHtml, outlineHtml };
85
+ }
86
+ function stateLabel(state) {
87
+ return (state ?? '').replace('-', ' ');
88
+ }
89
+ function renderVersionsStrip(versions, site, contentKind, current) {
90
+ if (versions.length <= 1)
91
+ return unsafe('');
92
+ const kindBit = contentKind === 'outline'
93
+ ? '&kind=outline'
94
+ : contentKind === 'shortform'
95
+ ? '&kind=shortform'
96
+ : '';
97
+ const links = versions
98
+ .map((v) => {
99
+ const isActive = v.version === current.version;
100
+ const href = `?site=${site}${kindBit}&v=${v.version}`;
101
+ return html `<a href="${href}" class="${isActive ? 'active' : ''}">v${v.version}</a>`;
102
+ })
103
+ .join('');
104
+ return unsafe(html `<span class="er-strip-versions">${unsafe(links)}</span>`);
105
+ }
106
+ /**
107
+ * Build the slash command that the operator pastes into Claude Code to
108
+ * advance the workflow from its current pending state. Mirrors the
109
+ * client-side button-handler logic so server-rendered "copy again"
110
+ * affordances stay consistent.
111
+ */
112
+ function pendingSkillCmd(workflow) {
113
+ const { site, slug, contentKind, state } = workflow;
114
+ if (state === 'iterating') {
115
+ return contentKind === 'outline'
116
+ ? `/deskwork:iterate --kind outline --site ${site} ${slug}`
117
+ : `/deskwork:iterate --site ${site} ${slug}`;
118
+ }
119
+ if (state === 'approved') {
120
+ // Outline-approve semantics still TBD (see editorial-review-client.ts);
121
+ // for now both kinds emit the same /deskwork:approve.
122
+ return `/deskwork:approve --site ${site} ${slug}`;
123
+ }
124
+ return '';
125
+ }
126
+ function renderControlsRight(workflow) {
127
+ const isActive = workflow.state === 'open' || workflow.state === 'in-review';
128
+ const isApproved = workflow.state === 'approved';
129
+ const isIterating = workflow.state === 'iterating';
130
+ const isTerminal = workflow.state === 'applied' || workflow.state === 'cancelled';
131
+ const buttons = [];
132
+ buttons.push(html `<button class="er-btn er-btn-small" data-action="toggle-edit" type="button">Edit</button>`);
133
+ if (isActive) {
134
+ buttons.push(html `<button class="er-btn er-btn-small er-btn-approve" data-action="approve" type="button">Approve</button>`);
135
+ buttons.push(html `<button class="er-btn er-btn-small" data-action="iterate" type="button">Iterate</button>`);
136
+ buttons.push(html `<button class="er-btn er-btn-small er-btn-reject" data-action="reject" type="button">Reject</button>`);
137
+ }
138
+ if (isApproved) {
139
+ const applyCmd = pendingSkillCmd(workflow);
140
+ buttons.push(html `<span class="er-pending-state">awaiting apply…</span>`);
141
+ buttons.push(html `<button class="er-btn er-btn-small" data-action="copy-cmd" data-cmd="${applyCmd}" title="Copy ${applyCmd} to clipboard" type="button">copy <code>/deskwork:approve</code></button>`);
142
+ buttons.push(html `<button class="er-btn er-btn-small er-btn-reject" data-action="reject" type="button">Reject</button>`);
143
+ }
144
+ if (isIterating) {
145
+ const iterateCmd = pendingSkillCmd(workflow);
146
+ buttons.push(html `<span class="er-pending-state">agent iterating…</span>`);
147
+ buttons.push(html `<button class="er-btn er-btn-small" data-action="copy-cmd" data-cmd="${iterateCmd}" title="Copy ${iterateCmd} to clipboard" type="button">copy <code>/deskwork:iterate</code></button>`);
148
+ }
149
+ if (isTerminal) {
150
+ buttons.push(html `<span class="er-pending-state er-pending-state--filed">filed (${workflow.state})</span>`);
151
+ }
152
+ buttons.push(html `<button class="er-btn er-btn-small" data-action="shortcuts" type="button" aria-label="Show keyboard shortcuts" title="Keyboard shortcuts">?</button>`);
153
+ return unsafe(`<span class="er-strip-right">${buttons.join('')}</span>`);
154
+ }
155
+ function renderError(slug, site, contentKind, message) {
156
+ const startCmd = contentKind === 'outline'
157
+ ? `/deskwork:outline --site ${site} ${slug}`
158
+ : contentKind === 'shortform'
159
+ ? `/deskwork:shortform-start --site ${site} ${slug} <platform>`
160
+ : `/deskwork:review-start --site ${site} ${slug}`;
161
+ const body = html `
162
+ <div data-review-ui="longform">
163
+ ${renderEditorialFolio('reviews', `longform · ${slug}`)}
164
+ <div class="er-error">
165
+ <h1>No galley to review.</h1>
166
+ <p><strong>Slug:</strong> <code>${slug}</code></p>
167
+ <p>${message}</p>
168
+ <p>Start one with:</p>
169
+ <p><code>${startCmd}</code></p>
170
+ <p style="margin-top: 2rem;"><a href="/dev/editorial-studio">← back to the studio</a></p>
171
+ </div>
172
+ </div>`;
173
+ return layout({
174
+ title: `Review — ${slug} — error`,
175
+ cssHrefs: [
176
+ '/static/css/editorial-review.css',
177
+ '/static/css/editorial-nav.css',
178
+ ],
179
+ bodyHtml: body,
180
+ scriptModules: [],
181
+ });
182
+ }
183
+ function renderShortcutsOverlay() {
184
+ return unsafe(html `
185
+ <div class="er-shortcuts" data-shortcuts-overlay hidden role="dialog" aria-modal="true" aria-label="Keyboard shortcuts">
186
+ <div class="er-shortcuts-backdrop" data-shortcuts-backdrop></div>
187
+ <div class="er-shortcuts-panel">
188
+ <h2>Keyboard</h2>
189
+ <dl>
190
+ <dt><kbd>e</kbd> / dbl-click</dt><dd>toggle edit mode</dd>
191
+ <dt>select text</dt><dd>leave a margin note</dd>
192
+ <dt><kbd>⌘</kbd><kbd>↵</kbd> / <kbd>ctrl</kbd><kbd>↵</kbd></dt><dd>save margin note (in composer)</dd>
193
+ <dt><kbd>a</kbd></dt><dd>approve</dd>
194
+ <dt><kbd>i</kbd></dt><dd>iterate</dd>
195
+ <dt><kbd>r</kbd></dt><dd>reject</dd>
196
+ <dt><kbd>j</kbd> / <kbd>k</kbd></dt><dd>next / previous margin note</dd>
197
+ <dt><kbd>?</kbd></dt><dd>this panel</dd>
198
+ <dt><kbd>esc</kbd></dt><dd>close / cancel composer</dd>
199
+ </dl>
200
+ <p class="er-shortcuts-footer">Press <kbd>?</kbd> anytime.</p>
201
+ </div>
202
+ </div>`);
203
+ }
204
+ function renderMarginalia() {
205
+ return unsafe(html `
206
+ <aside class="er-marginalia" data-comments-sidebar aria-label="Margin notes">
207
+ <p class="er-marginalia-head">Margin notes</p>
208
+ <p class="er-marginalia-empty" data-sidebar-empty>Select text in the draft, then either click the floating <em>Mark</em> pencil above your selection — or click anywhere here in the margin to open the note.</p>
209
+ <section class="er-marginalia-composer" data-comment-composer hidden aria-label="New margin note">
210
+ <p class="er-marginalia-composer-head">New mark</p>
211
+ <div class="er-marginalia-composer-quote" data-composer-quote></div>
212
+ <label class="er-marginalia-composer-label" for="comment-category">Mark as</label>
213
+ <select id="comment-category" class="er-marginalia-composer-select" data-comment-category>
214
+ <option value="other" selected>other</option>
215
+ <option value="voice-drift">voice-drift</option>
216
+ <option value="missing-receipt">missing-receipt</option>
217
+ <option value="tutorial-framing">tutorial-framing</option>
218
+ <option value="saas-vocabulary">saas-vocabulary</option>
219
+ <option value="fake-authority">fake-authority</option>
220
+ <option value="structural">structural</option>
221
+ </select>
222
+ <label class="er-marginalia-composer-label" for="comment-text">Note</label>
223
+ <textarea id="comment-text" class="er-marginalia-composer-textarea" data-comment-text rows="4"
224
+ placeholder="What needs attention here?"></textarea>
225
+ <div class="er-marginalia-composer-actions">
226
+ <button type="button" class="er-btn er-btn-small" data-action="cancel-comment">Cancel</button>
227
+ <button type="button" class="er-btn er-btn-small er-btn-primary" data-action="submit-comment">Leave mark</button>
228
+ </div>
229
+ </section>
230
+ <ol class="er-marginalia-list" data-sidebar-list></ol>
231
+ </aside>`);
232
+ }
233
+ function renderEditMode(outlineHasContent) {
234
+ const outlineBtnAttrs = outlineHasContent ? '' : ' hidden';
235
+ return unsafe(html `
236
+ <div class="er-edit-mode" data-edit-toolbar hidden>
237
+ <div class="er-edit-chrome">
238
+ <div class="er-edit-modes" role="tablist" aria-label="Editor mode">
239
+ <button class="er-edit-mode-btn" data-edit-view="source" type="button" aria-pressed="true">Source</button>
240
+ <button class="er-edit-mode-btn" data-edit-view="split" type="button" aria-pressed="false">Split</button>
241
+ <button class="er-edit-mode-btn" data-edit-view="preview" type="button" aria-pressed="false">Preview</button>
242
+ </div>
243
+ <div class="er-edit-actions">
244
+ <button class="er-btn er-btn-small" data-action="outline-drawer" type="button" title="Show the outline for reference (O)" aria-pressed="false"${unsafe(outlineBtnAttrs)}>Outline ↗</button>
245
+ <button class="er-btn er-btn-small" data-action="focus-mode" type="button" title="Distraction-free mode (Shift+F)" aria-pressed="false">Focus ⛶</button>
246
+ <button class="er-btn er-btn-primary" data-action="save-version" type="button">Save as new version</button>
247
+ <button class="er-btn" data-action="cancel-edit" type="button">Cancel</button>
248
+ <span class="er-edit-hint" data-edit-hint></span>
249
+ </div>
250
+ </div>
251
+ <div class="er-edit-panes" data-edit-panes data-view="source">
252
+ <div class="er-edit-source" data-edit-source aria-label="Markdown source"></div>
253
+ <div class="er-edit-preview" data-edit-preview aria-label="Rendered preview"></div>
254
+ </div>
255
+ <textarea id="draft-edit" data-draft-edit hidden></textarea>
256
+ <div class="er-focus-exit" data-focus-exit aria-hidden="true">
257
+ <button type="button" data-action="exit-focus" title="Exit focus (Esc)">← exit focus</button>
258
+ </div>
259
+ <div class="er-focus-save" data-focus-save aria-hidden="true">
260
+ <button type="button" class="er-btn er-btn-small er-btn-primary" data-action="save-version">Save</button>
261
+ <span class="er-focus-save-hint" data-focus-save-hint></span>
262
+ </div>
263
+ </div>`);
264
+ }
265
+ function renderOutlineDrawer(outlineHtml) {
266
+ const hidden = outlineHtml ? '' : ' hidden';
267
+ return unsafe(html `
268
+ <button class="er-outline-tab" data-outline-tab type="button" aria-label="Show outline"${unsafe(hidden)}>
269
+ <span class="er-outline-tab-label">Outline</span>
270
+ </button>
271
+ <aside class="er-outline-drawer" data-outline-drawer aria-label="Outline reference" hidden>
272
+ <header class="er-outline-drawer-head">
273
+ <span class="er-outline-drawer-kicker">Briefing sheet</span>
274
+ <button type="button" class="er-outline-drawer-close" data-outline-close aria-label="Close outline (O or Esc)">×</button>
275
+ </header>
276
+ <div class="er-outline-drawer-body" data-outline-drawer-body>${unsafe(outlineHtml)}</div>
277
+ <footer class="er-outline-drawer-foot">
278
+ <span>Read-only · edit via <code>/editorial-iterate --kind outline</code></span>
279
+ </footer>
280
+ </aside>`);
281
+ }
282
+ /**
283
+ * Resolve the calendar entry that backs this review surface. Callers
284
+ * have either an `entryId` (id-canonical route) or a slug (legacy
285
+ * route) to work with. Returns `null` when no calendar entry matches —
286
+ * ad-hoc workflows + pre-doctor entries fall through to the slug-
287
+ * template legacy path elsewhere.
288
+ *
289
+ * Failures (calendar absent, parse error) are swallowed to null so a
290
+ * transient calendar issue never blocks the review render.
291
+ */
292
+ function lookupReviewEntry(ctx, site, lookup, fallbackSlug) {
293
+ try {
294
+ const calendarPath = resolveCalendarPath(ctx.projectRoot, ctx.config, site);
295
+ if (!existsSync(calendarPath))
296
+ return null;
297
+ const cal = readCalendar(calendarPath);
298
+ if (lookup.kind === 'id') {
299
+ const byId = findEntryById(cal, lookup.entryId);
300
+ if (byId !== undefined)
301
+ return byId;
302
+ }
303
+ const slug = lookup.kind === 'workflow' ? fallbackSlug : lookup.slug;
304
+ const bySlug = findEntry(cal, slug);
305
+ return bySlug ?? null;
306
+ }
307
+ catch {
308
+ return null;
309
+ }
310
+ }
311
+ export async function renderReviewPage(ctx, lookup, query, getIndex) {
312
+ const queryKind = pickContentKind(query.kind ?? null);
313
+ // Workflow-id lookup short-circuits site + entryId resolution: the
314
+ // workflow record carries everything we need to render. Phase 21c
315
+ // added this path so the dashboard's shortform matrix (and any other
316
+ // surface that knows a workflow id) can deep-link to the unified
317
+ // review surface without first knowing the calendar entry id.
318
+ const fetched = lookup.kind === 'workflow'
319
+ ? handleGetWorkflow(ctx.projectRoot, ctx.config, {
320
+ id: lookup.workflowId,
321
+ entryId: null,
322
+ site: null,
323
+ slug: null,
324
+ contentKind: null,
325
+ platform: null,
326
+ channel: null,
327
+ })
328
+ : handleGetWorkflow(ctx.projectRoot, ctx.config, {
329
+ id: null,
330
+ entryId: lookup.kind === 'id' ? lookup.entryId : null,
331
+ site: pickSite(ctx, query.site),
332
+ slug: lookup.slug,
333
+ contentKind: queryKind,
334
+ platform: null,
335
+ channel: null,
336
+ });
337
+ // Slug used in error messages and the title fallback. For
338
+ // workflow-id lookups we don't know the slug until the fetch
339
+ // succeeds, so use the id as a placeholder for any pre-fetch error.
340
+ const lookupSlug = lookup.kind === 'workflow' ? lookup.workflowId : lookup.slug;
341
+ // Site for the chrome / outbound links. Workflow-id lookups carry
342
+ // their own site through the fetched workflow record.
343
+ let resolvedSite = pickSite(ctx, query.site);
344
+ if (fetched.status !== 200 || !isSuccessBody(fetched.body)) {
345
+ return renderError(lookupSlug, resolvedSite, queryKind, errorFromBody(fetched.body));
346
+ }
347
+ const { workflow, versions } = fetched.body;
348
+ // Workflow-id paths drive contentKind from the workflow itself —
349
+ // it's the source of truth, not the URL kind hint.
350
+ const contentKind = lookup.kind === 'workflow' ? workflow.contentKind : queryKind;
351
+ if (lookup.kind === 'workflow')
352
+ resolvedSite = workflow.site;
353
+ const slug = workflow.slug;
354
+ const requested = query.version ? parseInt(query.version, 10) : workflow.currentVersion;
355
+ const currentVersion = versions.find((v) => v.version === requested) ?? versions[versions.length - 1];
356
+ if (!currentVersion) {
357
+ return renderError(slug, resolvedSite, contentKind, 'no current version on this workflow');
358
+ }
359
+ const { fm, bodyHtml, outlineHtml } = await prepareRender(currentVersion.markdown, contentKind);
360
+ const draftState = { workflow, currentVersion, versions };
361
+ const titleField = stringField(fm.title) ?? `Draft: ${slug}`;
362
+ // Phase 19c+: look up the calendar entry so the scrapbook drawer +
363
+ // inline-text loader can resolve the on-disk scrapbook directory via
364
+ // the content index when a frontmatter-id binding exists. Falls back
365
+ // to slug-template addressing when no entry / no id is present.
366
+ // Shortform skips the scrapbook drawer entirely — different surface
367
+ // shape, no margin-note workflow.
368
+ const reviewEntry = lookupReviewEntry(ctx, resolvedSite, lookup, slug);
369
+ const reviewIndex = getIndex ? getIndex(resolvedSite) : undefined;
370
+ const isShortform = contentKind === 'shortform';
371
+ // Phase 21c — shortform header. Renders above the editor on the
372
+ // unified review surface so the operator sees the platform (and
373
+ // channel, if any) at a glance. Reuses existing `--er-*` design
374
+ // tokens; no new CSS introduced.
375
+ const shortformMeta = isShortform
376
+ ? unsafe(html `
377
+ <div class="er-shortform-meta">
378
+ <span class="er-platform">${workflow.platform ?? 'other'}</span>
379
+ ${workflow.channel
380
+ ? unsafe(html `<span class="er-channel">${workflow.channel}</span>`)
381
+ : ''}
382
+ </div>`)
383
+ : unsafe('');
384
+ const reviewUiAttr = isShortform ? 'shortform' : 'longform';
385
+ const folioSpine = isShortform
386
+ ? `shortform · ${workflow.platform ?? '?'}${workflow.channel ? ` · ${workflow.channel}` : ''} · ${slug}`
387
+ : `longform · ${slug}`;
388
+ const body = html `
389
+ <div data-review-ui="${reviewUiAttr}" class="er-review-shell">
390
+ ${renderEditorialFolio('reviews', folioSpine)}
391
+ ${shortformMeta}
392
+ <div class="er-draft-frame">
393
+ <div id="draft-body" data-draft-body
394
+ title="Double-click to edit · select text to leave a margin note">${unsafe(bodyHtml)}</div>
395
+ ${renderEditMode(outlineHtml.length > 0)}
396
+ </div>
397
+ <div class="er-strip">
398
+ <a class="er-strip-back" href="/dev/editorial-studio" title="Back to the editorial studio">← studio</a>
399
+ <span class="er-strip-galley">Galley <em>№ ${currentVersion.version}</em></span>
400
+ <span class="er-strip-slug">${workflow.site} / ${workflow.slug}</span>
401
+ ${renderVersionsStrip(versions, resolvedSite, contentKind, currentVersion)}
402
+ <span class="er-strip-center">
403
+ <span class="er-stamp er-stamp-big er-stamp-${workflow.state}" data-state-label>
404
+ ${stateLabel(workflow.state)}
405
+ </span>
406
+ <span class="er-strip-hint" aria-hidden="true">select text to mark · double-click to edit · <kbd>?</kbd> for shortcuts</span>
407
+ </span>
408
+ ${renderControlsRight(workflow)}
409
+ </div>
410
+ ${renderMarginalia()}
411
+ <button class="er-pencil-btn" data-add-comment-btn hidden type="button">Mark</button>
412
+ ${isShortform ? unsafe('') : renderOutlineDrawer(outlineHtml)}
413
+ ${isShortform
414
+ ? unsafe('')
415
+ : renderScrapbookDrawer(ctx, resolvedSite, reviewEntry, workflow.slug, reviewIndex)}
416
+ <div class="er-toast" data-toast hidden></div>
417
+ ${renderShortcutsOverlay()}
418
+ <div class="er-poll-indicator" data-poll>auto-refresh · 8s</div>
419
+ </div>`;
420
+ return layout({
421
+ title: `${titleField} — Review`,
422
+ cssHrefs: [
423
+ '/static/css/editorial-review.css',
424
+ '/static/css/editorial-nav.css',
425
+ '/static/css/blog-figure.css',
426
+ '/static/css/review-viewport.css',
427
+ '/static/css/scrap-row.css',
428
+ ],
429
+ bodyHtml: body,
430
+ embeddedJson: [{ id: 'draft-state', data: draftState }],
431
+ scriptModules: ['/static/dist/editorial-review-client.js'],
432
+ });
433
+ }
434
+ //# sourceMappingURL=review.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/pages/review.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Scrapbook viewer — `/dev/scrapbook/:site/<path>` (path may include `/`).
3
+ *
4
+ * Reads the scrapbook directory at the given path and lists every
5
+ * file with type chips + relative timestamps, plus secret items
6
+ * (inside `scrapbook/secret/`) in a quiet second section. Empty
7
+ * scrapbooks render an empty state with quick-add affordances.
8
+ *
9
+ * The `path` argument is the hierarchical address of the scrapbook —
10
+ * any slash-separated kebab-case identifier under the site's
11
+ * contentDir. It does not need to correspond to a calendar entry;
12
+ * organizational nodes (e.g. `the-outbound/characters` with no
13
+ * own README) can host their own scrapbooks too.
14
+ *
15
+ * Port of `pages/dev/scrapbook/[site]/[slug].astro`. Layout swap
16
+ * (Astro `<Layout>` → studio shell) and CSS link added; structurally
17
+ * similar otherwise.
18
+ */
19
+ import type { StudioContext } from '../routes/api.ts';
20
+ export declare function renderScrapbookPage(ctx: StudioContext, site: string, path: string): string;
21
+ //# sourceMappingURL=scrapbook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scrapbook.d.ts","sourceRoot":"","sources":["../../src/pages/scrapbook.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAQH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AA2NtD,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,MAAM,CAkDR"}