@deskwork/studio 0.12.1 → 0.14.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 (131) hide show
  1. package/dist/components/scrapbook-item.d.ts +9 -1
  2. package/dist/components/scrapbook-item.d.ts.map +1 -1
  3. package/dist/components/scrapbook-item.js +11 -2
  4. package/dist/components/scrapbook-item.js.map +1 -1
  5. package/dist/data/glossary.json +62 -0
  6. package/dist/lib/glossary-helper.d.ts +16 -0
  7. package/dist/lib/glossary-helper.d.ts.map +1 -0
  8. package/dist/lib/glossary-helper.js +26 -0
  9. package/dist/lib/glossary-helper.js.map +1 -0
  10. package/dist/pages/chrome.d.ts +24 -13
  11. package/dist/pages/chrome.d.ts.map +1 -1
  12. package/dist/pages/chrome.js +25 -24
  13. package/dist/pages/chrome.js.map +1 -1
  14. package/dist/pages/chrome.ts +38 -27
  15. package/dist/pages/content-detail.d.ts.map +1 -1
  16. package/dist/pages/content-detail.js +6 -5
  17. package/dist/pages/content-detail.js.map +1 -1
  18. package/dist/pages/content-detail.ts +6 -5
  19. package/dist/pages/content.d.ts.map +1 -1
  20. package/dist/pages/content.js +4 -6
  21. package/dist/pages/content.js.map +1 -1
  22. package/dist/pages/content.ts +4 -6
  23. package/dist/pages/dashboard/affordances.d.ts +6 -1
  24. package/dist/pages/dashboard/affordances.d.ts.map +1 -1
  25. package/dist/pages/dashboard/affordances.js +17 -2
  26. package/dist/pages/dashboard/affordances.js.map +1 -1
  27. package/dist/pages/dashboard/affordances.ts +18 -2
  28. package/dist/pages/dashboard/header.js +2 -2
  29. package/dist/pages/dashboard/header.ts +2 -2
  30. package/dist/pages/dashboard/section.d.ts +2 -2
  31. package/dist/pages/dashboard/section.d.ts.map +1 -1
  32. package/dist/pages/dashboard/section.js +5 -5
  33. package/dist/pages/dashboard/section.js.map +1 -1
  34. package/dist/pages/dashboard/section.ts +9 -5
  35. package/dist/pages/dashboard.d.ts.map +1 -1
  36. package/dist/pages/dashboard.js +3 -2
  37. package/dist/pages/dashboard.js.map +1 -1
  38. package/dist/pages/dashboard.ts +3 -2
  39. package/dist/pages/entry-review/data.d.ts +54 -0
  40. package/dist/pages/entry-review/data.d.ts.map +1 -0
  41. package/dist/pages/entry-review/data.js +116 -0
  42. package/dist/pages/entry-review/data.js.map +1 -0
  43. package/dist/pages/entry-review/decision-strip.d.ts +37 -0
  44. package/dist/pages/entry-review/decision-strip.d.ts.map +1 -0
  45. package/dist/pages/entry-review/decision-strip.js +137 -0
  46. package/dist/pages/entry-review/decision-strip.js.map +1 -0
  47. package/dist/pages/entry-review/edit-panes.d.ts +12 -0
  48. package/dist/pages/entry-review/edit-panes.d.ts.map +1 -0
  49. package/dist/pages/entry-review/edit-panes.js +28 -0
  50. package/dist/pages/entry-review/edit-panes.js.map +1 -0
  51. package/dist/pages/entry-review/edit-toolbar.d.ts +20 -0
  52. package/dist/pages/entry-review/edit-toolbar.d.ts.map +1 -0
  53. package/dist/pages/entry-review/edit-toolbar.js +46 -0
  54. package/dist/pages/entry-review/edit-toolbar.js.map +1 -0
  55. package/dist/pages/entry-review/index.d.ts +50 -0
  56. package/dist/pages/entry-review/index.d.ts.map +1 -0
  57. package/dist/pages/entry-review/index.js +219 -0
  58. package/dist/pages/entry-review/index.js.map +1 -0
  59. package/dist/pages/entry-review/marginalia.d.ts +28 -0
  60. package/dist/pages/entry-review/marginalia.d.ts.map +1 -0
  61. package/dist/pages/entry-review/marginalia.js +67 -0
  62. package/dist/pages/entry-review/marginalia.js.map +1 -0
  63. package/dist/pages/entry-review/not-found.d.ts +9 -0
  64. package/dist/pages/entry-review/not-found.d.ts.map +1 -0
  65. package/dist/pages/entry-review/not-found.js +34 -0
  66. package/dist/pages/entry-review/not-found.js.map +1 -0
  67. package/dist/pages/entry-review/outline-drawer.d.ts +12 -0
  68. package/dist/pages/entry-review/outline-drawer.d.ts.map +1 -0
  69. package/dist/pages/entry-review/outline-drawer.js +28 -0
  70. package/dist/pages/entry-review/outline-drawer.js.map +1 -0
  71. package/dist/pages/entry-review/shortcuts.d.ts +14 -0
  72. package/dist/pages/entry-review/shortcuts.d.ts.map +1 -0
  73. package/dist/pages/entry-review/shortcuts.js +36 -0
  74. package/dist/pages/entry-review/shortcuts.js.map +1 -0
  75. package/dist/pages/entry-review/version-strip.d.ts +33 -0
  76. package/dist/pages/entry-review/version-strip.d.ts.map +1 -0
  77. package/dist/pages/entry-review/version-strip.js +81 -0
  78. package/dist/pages/entry-review/version-strip.js.map +1 -0
  79. package/dist/pages/entry-review.d.ts +6 -21
  80. package/dist/pages/entry-review.d.ts.map +1 -1
  81. package/dist/pages/entry-review.js +6 -144
  82. package/dist/pages/entry-review.js.map +1 -1
  83. package/dist/pages/entry-review.ts +11 -181
  84. package/dist/pages/html.d.ts +2 -0
  85. package/dist/pages/html.d.ts.map +1 -1
  86. package/dist/pages/html.js +2 -0
  87. package/dist/pages/html.js.map +1 -1
  88. package/dist/pages/html.ts +4 -0
  89. package/dist/pages/index.d.ts.map +1 -1
  90. package/dist/pages/index.js +6 -10
  91. package/dist/pages/index.js.map +1 -1
  92. package/dist/pages/index.ts +6 -10
  93. package/dist/pages/layout.d.ts.map +1 -1
  94. package/dist/pages/layout.js +6 -0
  95. package/dist/pages/layout.js.map +1 -1
  96. package/dist/pages/layout.ts +7 -0
  97. package/dist/pages/review-scrapbook-drawer.d.ts +7 -0
  98. package/dist/pages/review-scrapbook-drawer.d.ts.map +1 -1
  99. package/dist/pages/review-scrapbook-drawer.js +45 -6
  100. package/dist/pages/review-scrapbook-drawer.js.map +1 -1
  101. package/dist/pages/review-scrapbook-drawer.ts +50 -6
  102. package/dist/pages/review.d.ts.map +1 -1
  103. package/dist/pages/review.js +168 -41
  104. package/dist/pages/review.js.map +1 -1
  105. package/dist/pages/review.ts +192 -41
  106. package/dist/pages/scrapbook.d.ts +7 -14
  107. package/dist/pages/scrapbook.d.ts.map +1 -1
  108. package/dist/pages/scrapbook.js +548 -192
  109. package/dist/pages/scrapbook.js.map +1 -1
  110. package/dist/pages/scrapbook.ts +588 -221
  111. package/dist/pages/shortform-review.d.ts +32 -0
  112. package/dist/pages/shortform-review.d.ts.map +1 -0
  113. package/dist/pages/shortform-review.js +270 -0
  114. package/dist/pages/shortform-review.js.map +1 -0
  115. package/dist/pages/shortform-review.ts +342 -0
  116. package/dist/pages/shortform.js +1 -1
  117. package/dist/pages/shortform.js.map +1 -1
  118. package/dist/pages/shortform.ts +1 -1
  119. package/dist/routes/api.d.ts +8 -0
  120. package/dist/routes/api.d.ts.map +1 -1
  121. package/dist/routes/api.js +115 -0
  122. package/dist/routes/api.js.map +1 -1
  123. package/dist/routes/entry-annotation-body.d.ts +26 -0
  124. package/dist/routes/entry-annotation-body.d.ts.map +1 -0
  125. package/dist/routes/entry-annotation-body.js +152 -0
  126. package/dist/routes/entry-annotation-body.js.map +1 -0
  127. package/dist/server.d.ts +1 -1
  128. package/dist/server.d.ts.map +1 -1
  129. package/dist/server.js +56 -196
  130. package/dist/server.js.map +1 -1
  131. package/package.json +4 -4
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Decision strip for the entry-keyed press-check surface (Phase 34a — T13).
3
+ *
4
+ * Edit toggle + Approve / Iterate / Reject + shortcuts (?) trigger.
5
+ * Drives the press-check decision flow on the entry-centric model:
6
+ *
7
+ * - `Approve` → POST `/api/dev/editorial-review/entry/<uuid>/decision`
8
+ * with `{decision: 'approve'}` (graduates the entry to the next stage).
9
+ * - `Iterate` → POST `/api/dev/editorial-review/entry/<uuid>/version`
10
+ * (records a new iteration via `iterateEntry`).
11
+ * - `Reject` → DISABLED, tooltipped with the GitHub-issue link for the
12
+ * pending design decision (entry-centric reject semantics are
13
+ * undefined; see #173).
14
+ *
15
+ * Affordance set is gated by `getAffordances(entry)` so the strip
16
+ * automatically degrades to read-only / induct-to / fork shapes for
17
+ * Published / Blocked / Cancelled entries (T15-prep).
18
+ *
19
+ * Per `.claude/rules/affordance-placement.md`, the destructive buttons
20
+ * (Approve, Iterate, Reject) are wrapped in `.er-shortcut-chip-wrap`
21
+ * spans carrying small chord chips beneath each button — visual cue
22
+ * for the bare-letter double-tap shortcuts (`a`, `i`, `r`).
23
+ */
24
+ import type { Entry } from '@deskwork/core/schema/entry';
25
+ import type { Affordances } from '../../lib/stage-affordances.ts';
26
+ import { type RawHtml } from '../html.ts';
27
+ interface DecisionStripOptions {
28
+ readonly entry: Entry;
29
+ readonly affordances: Affordances;
30
+ /** Whether the page is rendering a historical version (`?v=<n>`).
31
+ * When true, mutation buttons (Approve / Iterate) render disabled
32
+ * with a tooltip explaining the read-only state. */
33
+ readonly historical: boolean;
34
+ }
35
+ export declare function renderDecisionStrip(opts: DecisionStripOptions): RawHtml;
36
+ export {};
37
+ //# sourceMappingURL=decision-strip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decision-strip.d.ts","sourceRoot":"","sources":["../../../src/pages/entry-review/decision-strip.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAgB,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAyJxD,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC;;yDAEqD;IACrD,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC9B;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAMvE"}
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Decision strip for the entry-keyed press-check surface (Phase 34a — T13).
3
+ *
4
+ * Edit toggle + Approve / Iterate / Reject + shortcuts (?) trigger.
5
+ * Drives the press-check decision flow on the entry-centric model:
6
+ *
7
+ * - `Approve` → POST `/api/dev/editorial-review/entry/<uuid>/decision`
8
+ * with `{decision: 'approve'}` (graduates the entry to the next stage).
9
+ * - `Iterate` → POST `/api/dev/editorial-review/entry/<uuid>/version`
10
+ * (records a new iteration via `iterateEntry`).
11
+ * - `Reject` → DISABLED, tooltipped with the GitHub-issue link for the
12
+ * pending design decision (entry-centric reject semantics are
13
+ * undefined; see #173).
14
+ *
15
+ * Affordance set is gated by `getAffordances(entry)` so the strip
16
+ * automatically degrades to read-only / induct-to / fork shapes for
17
+ * Published / Blocked / Cancelled entries (T15-prep).
18
+ *
19
+ * Per `.claude/rules/affordance-placement.md`, the destructive buttons
20
+ * (Approve, Iterate, Reject) are wrapped in `.er-shortcut-chip-wrap`
21
+ * spans carrying small chord chips beneath each button — visual cue
22
+ * for the bare-letter double-tap shortcuts (`a`, `i`, `r`).
23
+ */
24
+ import { html, unsafe } from "../html.js";
25
+ const REJECT_ISSUE_URL = 'https://github.com/audiocontrol-org/deskwork/issues/173';
26
+ const REJECT_TOOLTIP = `reject semantics filed as #173 — see ${REJECT_ISSUE_URL}`;
27
+ const HISTORICAL_TOOLTIP = 'viewing historical version (read-only) — switch back to current to act';
28
+ const STAGE_PICKER_OPTIONS = [
29
+ 'Ideas',
30
+ 'Planned',
31
+ 'Outlining',
32
+ 'Drafting',
33
+ 'Final',
34
+ ];
35
+ /**
36
+ * Wrap an action button in a `.er-shortcut-chip-wrap` span carrying the
37
+ * chord chip. Mirrors the legacy surface verbatim — bare-letter double-
38
+ * tap with no Cmd/Ctrl modifier (#108).
39
+ */
40
+ function shortcutChipWrap(buttonHtml, letter) {
41
+ return html `<span class="er-shortcut-chip-wrap">${unsafe(buttonHtml)}<small class="er-shortcut-chip"><kbd>${letter}</kbd><kbd>${letter}</kbd></small></span>`;
42
+ }
43
+ function renderEditToggle() {
44
+ // Issue 7 — edit-mode disclosure label sits next to the Edit button.
45
+ // Initial state matches the surface's initial mode (preview).
46
+ return html `<button class="er-btn er-btn-small" data-action="toggle-edit" type="button">Edit</button><span class="er-edit-mode-label" data-mode="preview">preview</span>`;
47
+ }
48
+ function renderInductPicker(entry) {
49
+ const options = STAGE_PICKER_OPTIONS.map((s) => unsafe(html `<option value="${s}">${s}</option>`));
50
+ return html `
51
+ <label class="er-entry-control er-entry-control--induct">
52
+ <span class="er-entry-control-label">Induct to</span>
53
+ <select name="induct-to" data-entry-uuid="${entry.uuid}">
54
+ ${options}
55
+ </select>
56
+ </label>`;
57
+ }
58
+ function renderHistoricalStageDropdown(entry) {
59
+ const stages = Object.keys(entry.iterationByStage);
60
+ if (stages.length === 0)
61
+ return '';
62
+ const options = stages.map((s) => unsafe(html `<option value="${s}">${s}</option>`));
63
+ return html `
64
+ <label class="er-entry-control er-entry-control--history">
65
+ <span class="er-entry-control-label">Historical stage</span>
66
+ <select name="history-stage" data-entry-uuid="${entry.uuid}">
67
+ ${options}
68
+ </select>
69
+ </label>`;
70
+ }
71
+ function renderShortcutsBtn() {
72
+ return html `<button class="er-btn er-btn-small" data-action="shortcuts" type="button" aria-label="Show keyboard shortcuts" title="Keyboard shortcuts">?</button>`;
73
+ }
74
+ /**
75
+ * The full mutable affordance set: Edit + Approve + Iterate + Reject
76
+ * (disabled). Returned as a single concatenated HTML string so the
77
+ * caller can wrap it in `.er-strip-right`.
78
+ */
79
+ function renderMutableButtons({ entry, historical }) {
80
+ const buttons = [];
81
+ buttons.push(renderEditToggle());
82
+ // Phase 34a F2 remediation — Approve/Iterate disabled when viewing a
83
+ // historical version. The endpoints act against live sidecar state,
84
+ // not the historical snapshot; allowing the click while showing
85
+ // historical content is the same "surface lying about content"
86
+ // failure mode 34a was meant to fix. Defense-in-depth: client-side
87
+ // also short-circuits in entry-review/decision.ts.
88
+ const histDisabled = historical ? ' disabled aria-disabled="true"' : '';
89
+ const histTitle = historical ? ` title="${HISTORICAL_TOOLTIP}"` : '';
90
+ // Approve — wired to the entry-keyed decision endpoint.
91
+ buttons.push(shortcutChipWrap(html `<button class="er-btn er-btn-small er-btn-approve" data-action="approve" data-entry-uuid="${entry.uuid}" type="button"${unsafe(histDisabled)}${unsafe(histTitle)}>Approve</button>`, 'a'));
92
+ // Iterate — wired to the entry-keyed version endpoint.
93
+ buttons.push(shortcutChipWrap(html `<button class="er-btn er-btn-small" data-action="iterate" data-entry-uuid="${entry.uuid}" type="button"${unsafe(histDisabled)}${unsafe(histTitle)}>Iterate</button>`, 'i'));
94
+ // Reject — DISABLED pending the design decision in #173.
95
+ buttons.push(shortcutChipWrap(html `<button class="er-btn er-btn-small er-btn-reject" data-action="reject" data-entry-uuid="${entry.uuid}" type="button" disabled title="${REJECT_TOOLTIP}" aria-disabled="true">Reject</button>`, 'r'));
96
+ // Historical-stage dropdown — only useful when more than one stage
97
+ // has recorded iterations for this entry. The renderer returns an
98
+ // empty string when there are none; keep it conditional here so the
99
+ // strip doesn't carry an empty `<label>` for first-iteration entries.
100
+ const history = renderHistoricalStageDropdown(entry);
101
+ if (history)
102
+ buttons.push(history);
103
+ buttons.push(renderShortcutsBtn());
104
+ return buttons.join('');
105
+ }
106
+ function renderReadOnlyButtons(entry, affordances) {
107
+ const buttons = [];
108
+ for (const control of affordances.controls) {
109
+ if (control === 'view-only') {
110
+ buttons.push(html `<span class="er-entry-control er-entry-control--readonly">Read-only</span>`);
111
+ continue;
112
+ }
113
+ if (control === 'fork-placeholder') {
114
+ buttons.push(html `<button class="er-entry-control er-entry-control--button" type="button" disabled data-control="fork">Fork (coming)</button>`);
115
+ continue;
116
+ }
117
+ if (control === 'induct-to') {
118
+ buttons.push(renderInductPicker(entry));
119
+ continue;
120
+ }
121
+ // Unknown read-only control — render a label so the operator sees
122
+ // it in case a new affordance type ships without a renderer
123
+ // counterpart. Throwing here would tank the page; surfacing the
124
+ // raw control name is loud enough that the gap gets noticed.
125
+ buttons.push(html `<span class="er-entry-control">${control}</span>`);
126
+ }
127
+ buttons.push(renderShortcutsBtn());
128
+ return buttons.join('');
129
+ }
130
+ export function renderDecisionStrip(opts) {
131
+ const { entry, affordances, historical } = opts;
132
+ const inner = affordances.mutable
133
+ ? renderMutableButtons({ entry, historical })
134
+ : renderReadOnlyButtons(entry, affordances);
135
+ return unsafe(`<span class="er-strip-right">${inner}</span>`);
136
+ }
137
+ //# sourceMappingURL=decision-strip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decision-strip.js","sourceRoot":"","sources":["../../../src/pages/entry-review/decision-strip.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Edit-mode panes for the entry-keyed press-check surface (Phase 34a — T8).
3
+ *
4
+ * Relocated from `pages/review.ts:renderEditPanes`. The panes-host lives
5
+ * inside the article column (replacing `#draft-body` when editing). The
6
+ * wrapper keeps the `er-edit-mode` class so existing CSS cascades
7
+ * unchanged. The client flips `[hidden]` on the panes wrapper independently
8
+ * of the toolbar.
9
+ */
10
+ import { type RawHtml } from '../html.ts';
11
+ export declare function renderEditPanes(): RawHtml;
12
+ //# sourceMappingURL=edit-panes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit-panes.d.ts","sourceRoot":"","sources":["../../../src/pages/entry-review/edit-panes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAgB,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAExD,wBAAgB,eAAe,IAAI,OAAO,CAgBzC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Edit-mode panes for the entry-keyed press-check surface (Phase 34a — T8).
3
+ *
4
+ * Relocated from `pages/review.ts:renderEditPanes`. The panes-host lives
5
+ * inside the article column (replacing `#draft-body` when editing). The
6
+ * wrapper keeps the `er-edit-mode` class so existing CSS cascades
7
+ * unchanged. The client flips `[hidden]` on the panes wrapper independently
8
+ * of the toolbar.
9
+ */
10
+ import { html, unsafe } from "../html.js";
11
+ export function renderEditPanes() {
12
+ return unsafe(html `
13
+ <div class="er-edit-mode" data-edit-panes-host hidden>
14
+ <div class="er-edit-panes" data-edit-panes data-view="source">
15
+ <div class="er-edit-source" data-edit-source aria-label="Markdown source"></div>
16
+ <div class="er-edit-preview" data-edit-preview aria-label="Rendered preview"></div>
17
+ </div>
18
+ <textarea id="draft-edit" data-draft-edit hidden></textarea>
19
+ <div class="er-focus-exit" data-focus-exit aria-hidden="true">
20
+ <button type="button" data-action="exit-focus" title="Exit focus (Esc)">← exit focus</button>
21
+ </div>
22
+ <div class="er-focus-save" data-focus-save aria-hidden="true">
23
+ <button type="button" class="er-btn er-btn-small er-btn-primary" data-action="save-version">Save</button>
24
+ <span class="er-focus-save-hint" data-focus-save-hint></span>
25
+ </div>
26
+ </div>`);
27
+ }
28
+ //# sourceMappingURL=edit-panes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit-panes.js","sourceRoot":"","sources":["../../../src/pages/entry-review/edit-panes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,EAAgB,MAAM,YAAY,CAAC;AAExD,MAAM,UAAU,eAAe;IAC7B,OAAO,MAAM,CAAC,IAAI,CAAA;;;;;;;;;;;;;;WAcT,CAAC,CAAC;AACb,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Edit-mode toolbar for the entry-keyed press-check surface (Phase 34a — T7).
3
+ *
4
+ * Relocated from `pages/review.ts:renderEditToolbar`. The toolbar lives
5
+ * ABOVE the page-grid; the client (`entry-review-client.ts`) flips its
6
+ * `[hidden]` attribute on enter/exit. Source / Split / Preview tabs +
7
+ * Outline / Focus / Save / Cancel actions. Consumes the existing
8
+ * `editorial-review.css` rules unchanged.
9
+ *
10
+ * Save semantics: the entry-keyed model's `iterateEntry` reads on-disk
11
+ * content, not a request body, so the legacy edit-in-browser-then-save
12
+ * flow has no entry-keyed equivalent. The Save button is rendered
13
+ * `disabled` with a tooltip pointing at issue #174 (operator decision
14
+ * needed on the save shape). Edit mode itself still works — the source
15
+ * pane and the rendered preview pane are functional — only the Save
16
+ * action is intentionally inert pending the design decision.
17
+ */
18
+ import { type RawHtml } from '../html.ts';
19
+ export declare function renderEditToolbar(outlineHasContent: boolean): RawHtml;
20
+ //# sourceMappingURL=edit-toolbar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit-toolbar.d.ts","sourceRoot":"","sources":["../../../src/pages/entry-review/edit-toolbar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAgB,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAOxD,wBAAgB,iBAAiB,CAAC,iBAAiB,EAAE,OAAO,GAAG,OAAO,CAwBrE"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Edit-mode toolbar for the entry-keyed press-check surface (Phase 34a — T7).
3
+ *
4
+ * Relocated from `pages/review.ts:renderEditToolbar`. The toolbar lives
5
+ * ABOVE the page-grid; the client (`entry-review-client.ts`) flips its
6
+ * `[hidden]` attribute on enter/exit. Source / Split / Preview tabs +
7
+ * Outline / Focus / Save / Cancel actions. Consumes the existing
8
+ * `editorial-review.css` rules unchanged.
9
+ *
10
+ * Save semantics: the entry-keyed model's `iterateEntry` reads on-disk
11
+ * content, not a request body, so the legacy edit-in-browser-then-save
12
+ * flow has no entry-keyed equivalent. The Save button is rendered
13
+ * `disabled` with a tooltip pointing at issue #174 (operator decision
14
+ * needed on the save shape). Edit mode itself still works — the source
15
+ * pane and the rendered preview pane are functional — only the Save
16
+ * action is intentionally inert pending the design decision.
17
+ */
18
+ import { html, unsafe } from "../html.js";
19
+ const SAVE_ISSUE_URL = 'https://github.com/audiocontrol-org/deskwork/issues/174';
20
+ const SAVE_TOOLTIP = `Save semantics for the entry-keyed surface are pending design — see ${SAVE_ISSUE_URL}`;
21
+ export function renderEditToolbar(outlineHasContent) {
22
+ const outlineBtnAttrs = outlineHasContent ? '' : ' hidden';
23
+ // #175 Phase 34b — every button carries a tooltip naming what it
24
+ // does + the keyboard shortcut where one exists. The mode tabs
25
+ // (Source / Split / Preview) had no `title` attrs pre-fix; an
26
+ // operator who hadn't memorized them got nothing on hover. The
27
+ // trailing `?` button opens the existing shortcuts overlay so the
28
+ // full keyboard reference is one click away from the toolbar.
29
+ return unsafe(html `
30
+ <div class="er-edit-toolbar" data-edit-toolbar hidden>
31
+ <div class="er-edit-modes" role="tablist" aria-label="Editor mode">
32
+ <button class="er-edit-mode-btn" data-edit-view="source" type="button" aria-pressed="true" title="Edit markdown source only">Source</button>
33
+ <button class="er-edit-mode-btn" data-edit-view="split" type="button" aria-pressed="false" title="Source on the left, rendered preview on the right">Split</button>
34
+ <button class="er-edit-mode-btn" data-edit-view="preview" type="button" aria-pressed="false" title="Rendered preview only">Preview</button>
35
+ </div>
36
+ <div class="er-edit-actions">
37
+ <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>
38
+ <button class="er-btn er-btn-small" data-action="focus-mode" type="button" title="Distraction-free full-viewport canvas (Shift+F)" aria-pressed="false">Focus ⛶</button>
39
+ <button class="er-btn er-btn-primary" data-action="save-version" type="button" disabled aria-disabled="true" title="${SAVE_TOOLTIP}">Save as new version</button>
40
+ <button class="er-btn" data-action="cancel-edit" type="button" title="Discard unsaved edits and exit edit mode">Cancel</button>
41
+ <button class="er-btn er-btn-small" data-action="shortcuts" type="button" aria-label="Show keyboard shortcuts" title="Show all keyboard shortcuts (?)">?</button>
42
+ <span class="er-edit-hint" data-edit-hint></span>
43
+ </div>
44
+ </div>`);
45
+ }
46
+ //# sourceMappingURL=edit-toolbar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit-toolbar.js","sourceRoot":"","sources":["../../../src/pages/entry-review/edit-toolbar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,EAAgB,MAAM,YAAY,CAAC;AAExD,MAAM,cAAc,GAClB,yDAAyD,CAAC;AAC5D,MAAM,YAAY,GAChB,uEAAuE,cAAc,EAAE,CAAC;AAE1F,MAAM,UAAU,iBAAiB,CAAC,iBAA0B;IAC1D,MAAM,eAAe,GAAG,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3D,iEAAiE;IACjE,+DAA+D;IAC/D,8DAA8D;IAC9D,+DAA+D;IAC/D,kEAAkE;IAClE,8DAA8D;IAC9D,OAAO,MAAM,CAAC,IAAI,CAAA;;;;;;;;wJAQoI,MAAM,CAAC,eAAe,CAAC;;8HAEjD,YAAY;;;;;WAK/H,CAAC,CAAC;AACb,CAAC"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Top-level renderer for the entry-keyed press-check surface
3
+ * (Phase 34a Layer 2 — `/dev/editorial-review/entry/<uuid>`).
4
+ *
5
+ * Wires every chrome component (folio, version strip, edit toolbar,
6
+ * edit panes, outline drawer, marginalia column + tab, decision strip,
7
+ * scrapbook drawer, shortcuts overlay, rendered preview) against the
8
+ * entry-keyed Layer 1 data layer (sidecar + history-journal iterations
9
+ * + entry-keyed annotations).
10
+ *
11
+ * The relocated chrome shares the existing `editorial-review.css`
12
+ * stylesheet with the legacy longform surface — the chrome IS the
13
+ * existing chrome being relocated. The body data attribute is
14
+ * `data-review-ui="longform"` so the existing `er-*` rules in
15
+ * editorial-review.css cascade correctly.
16
+ *
17
+ * `entry-review.css` (the existing per-stage controller stylesheet)
18
+ * stays linked because the 404 not-found variant + future per-entry
19
+ * affordance polish lives there. The two class families coexist —
20
+ * `.er-*` from the relocated chrome and `.er-entry-*` from the stage
21
+ * controller — without collision.
22
+ *
23
+ * The StudioContext threads through so the scrapbook drawer can
24
+ * resolve the entry's site + content index (per-request memoized via
25
+ * `getRequestContentIndex`).
26
+ */
27
+ import type { ContentIndex } from '@deskwork/core/content-index';
28
+ import type { StudioContext } from '../../routes/api.ts';
29
+ export type EntryReviewIndexGetter = (site: string) => ContentIndex;
30
+ export interface EntryReviewQuery {
31
+ /** `?v=<n>` from the request URL. When set + resolves, shows that
32
+ * iteration's historical content read-only. */
33
+ readonly version?: string | null;
34
+ /** `?stage=<Stage>` from the request URL. Disambiguates historical
35
+ * lookup when an entry has the same version number recorded under
36
+ * multiple stages. Optional; omitted falls back to the first
37
+ * chronological match (single-stage case). */
38
+ readonly stage?: string | null;
39
+ }
40
+ export interface EntryReviewResult {
41
+ status: 200 | 404;
42
+ html: string;
43
+ }
44
+ /**
45
+ * Render the entry-keyed press-check surface for `entryId`. Returns a
46
+ * 404 shell when the sidecar can't be resolved; otherwise renders the
47
+ * full press-check chrome backed by Layer 1's data layer.
48
+ */
49
+ export declare function renderEntryReviewPage(ctx: StudioContext, entryId: string, query?: EntryReviewQuery, getIndex?: EntryReviewIndexGetter): Promise<EntryReviewResult>;
50
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/pages/entry-review/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAQH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAMjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAezD,MAAM,MAAM,sBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,YAAY,CAAC;AAEpE,MAAM,WAAW,gBAAgB;IAC/B;oDACgD;IAChD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC;;;mDAG+C;IAC/C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,GAAG,GAAG,GAAG,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAkGD;;;;GAIG;AACH,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,MAAM,EACf,KAAK,GAAE,gBAAqB,EAC5B,QAAQ,CAAC,EAAE,sBAAsB,GAChC,OAAO,CAAC,iBAAiB,CAAC,CAuH5B"}
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Top-level renderer for the entry-keyed press-check surface
3
+ * (Phase 34a Layer 2 — `/dev/editorial-review/entry/<uuid>`).
4
+ *
5
+ * Wires every chrome component (folio, version strip, edit toolbar,
6
+ * edit panes, outline drawer, marginalia column + tab, decision strip,
7
+ * scrapbook drawer, shortcuts overlay, rendered preview) against the
8
+ * entry-keyed Layer 1 data layer (sidecar + history-journal iterations
9
+ * + entry-keyed annotations).
10
+ *
11
+ * The relocated chrome shares the existing `editorial-review.css`
12
+ * stylesheet with the legacy longform surface — the chrome IS the
13
+ * existing chrome being relocated. The body data attribute is
14
+ * `data-review-ui="longform"` so the existing `er-*` rules in
15
+ * editorial-review.css cascade correctly.
16
+ *
17
+ * `entry-review.css` (the existing per-stage controller stylesheet)
18
+ * stays linked because the 404 not-found variant + future per-entry
19
+ * affordance polish lives there. The two class families coexist —
20
+ * `.er-*` from the relocated chrome and `.er-entry-*` from the stage
21
+ * controller — without collision.
22
+ *
23
+ * The StudioContext threads through so the scrapbook drawer can
24
+ * resolve the entry's site + content index (per-request memoized via
25
+ * `getRequestContentIndex`).
26
+ */
27
+ import { existsSync } from 'node:fs';
28
+ import { parseDraftFrontmatter, renderMarkdownToHtml, } from '@deskwork/core/review/render';
29
+ import { splitOutline } from '@deskwork/core/outline-split';
30
+ import { resolveCalendarPath } from '@deskwork/core/paths';
31
+ import { readCalendar } from '@deskwork/core/calendar';
32
+ import { findEntryById } from '@deskwork/core/calendar-mutations';
33
+ import { getAffordances } from "../../lib/stage-affordances.js";
34
+ import { html, unsafe, escapeHtml, gloss } from "../html.js";
35
+ import { layout } from "../layout.js";
36
+ import { renderEditorialFolio } from "../chrome.js";
37
+ import { renderScrapbookDrawer } from "../review-scrapbook-drawer.js";
38
+ import { loadEntryReviewData } from "./data.js";
39
+ import { renderVersionsStrip } from "./version-strip.js";
40
+ import { renderEditToolbar } from "./edit-toolbar.js";
41
+ import { renderEditPanes } from "./edit-panes.js";
42
+ import { renderOutlineDrawer } from "./outline-drawer.js";
43
+ import { renderMarginalia, renderMarginaliaTab } from "./marginalia.js";
44
+ import { renderDecisionStrip } from "./decision-strip.js";
45
+ import { renderShortcutsOverlay } from "./shortcuts.js";
46
+ import { renderEntryNotFound } from "./not-found.js";
47
+ function stringField(v) {
48
+ return typeof v === 'string' ? v : undefined;
49
+ }
50
+ /**
51
+ * Prepare the article body + the optional outline drawer content. Mirrors
52
+ * `pages/review.ts:prepareRender` but without the workflow-keyed
53
+ * contentKind switch — the entry-keyed surface is always longform-shaped
54
+ * (shortform stays on its own retirement track).
55
+ */
56
+ async function prepareRender(markdown) {
57
+ const parsed = parseDraftFrontmatter(markdown);
58
+ const fm = parsed.frontmatter;
59
+ const split = splitOutline(parsed.body);
60
+ const bodyHtml = await renderMarkdownToHtml(split.body);
61
+ // Inject the description as a dek after the body's first <h1>.
62
+ const description = stringField(fm.description);
63
+ const dekHtml = description
64
+ ? `<p class="er-dispatch-dek">${escapeHtml(description)}</p>`
65
+ : '';
66
+ const h1Close = bodyHtml.indexOf('</h1>');
67
+ const renderedHtml = dekHtml && h1Close >= 0
68
+ ? bodyHtml.slice(0, h1Close + 5) + dekHtml + bodyHtml.slice(h1Close + 5)
69
+ : dekHtml + bodyHtml;
70
+ const outlineHtml = split.outline
71
+ ? await renderMarkdownToHtml(split.outline)
72
+ : '';
73
+ return { fm, bodyHtml: renderedHtml, outlineHtml };
74
+ }
75
+ function buildState(data) {
76
+ const stage = data.entry.currentStage;
77
+ const currentVersion = data.entry.iterationByStage[stage] ?? null;
78
+ return {
79
+ entryId: data.entry.uuid,
80
+ slug: data.entry.slug,
81
+ site: data.site,
82
+ currentStage: stage,
83
+ currentVersion,
84
+ markdown: data.markdown,
85
+ historical: data.historical !== null,
86
+ };
87
+ }
88
+ /**
89
+ * The entry's site, surfaced through the `EntryReviewData` resolver,
90
+ * may not match the calendar entry shape the scrapbook drawer expects
91
+ * — the drawer accepts a CalendarEntry-shaped object directly. When the
92
+ * entry is found in a configured calendar, we pass it through; otherwise
93
+ * the drawer falls back to slug-template path resolution.
94
+ */
95
+ function lookupCalendarEntryStrict(ctx, site, entryId) {
96
+ try {
97
+ const calendarPath = resolveCalendarPath(ctx.projectRoot, ctx.config, site);
98
+ if (!existsSync(calendarPath))
99
+ return null;
100
+ const cal = readCalendar(calendarPath);
101
+ const found = findEntryById(cal, entryId);
102
+ return found ?? null;
103
+ }
104
+ catch {
105
+ return null;
106
+ }
107
+ }
108
+ /**
109
+ * Render the entry-keyed press-check surface for `entryId`. Returns a
110
+ * 404 shell when the sidecar can't be resolved; otherwise renders the
111
+ * full press-check chrome backed by Layer 1's data layer.
112
+ */
113
+ export async function renderEntryReviewPage(ctx, entryId, query = {}, getIndex) {
114
+ let data;
115
+ try {
116
+ data = await loadEntryReviewData(ctx, entryId, {
117
+ version: query.version ?? null,
118
+ stage: query.stage ?? null,
119
+ });
120
+ }
121
+ catch (err) {
122
+ const reason = err instanceof Error ? err.message : String(err);
123
+ return { status: 404, html: renderEntryNotFound(entryId, reason) };
124
+ }
125
+ const { fm, bodyHtml, outlineHtml } = await prepareRender(data.markdown);
126
+ const affordances = getAffordances(data.entry);
127
+ const state = buildState(data);
128
+ const titleField = stringField(fm.title) ?? `Draft: ${data.entry.slug}`;
129
+ // Calendar-entry lookup for the scrapbook drawer. The data loader
130
+ // already attempted this; we re-derive the strict CalendarEntry here
131
+ // because the drawer's signature wants the wider type.
132
+ const reviewEntry = data.calendarEntry !== null
133
+ ? data.calendarEntry
134
+ : lookupCalendarEntryStrict(ctx, data.site, entryId);
135
+ const reviewIndex = getIndex ? getIndex(data.site) : undefined;
136
+ // Pick a folio spine label that names the entry concisely. The
137
+ // legacy surface used `longform · <slug>`; preserve it so the
138
+ // visual signature stays continuous with the existing chrome.
139
+ const folioSpine = `longform · ${data.entry.slug}`;
140
+ const stageLabel = data.entry.currentStage;
141
+ const historicalBadge = data.historical
142
+ ? unsafe(html `<span class="er-strip-historical" title="Historical version (read-only)">historical · v${data.historical.versionNumber}</span>`)
143
+ : unsafe('');
144
+ // The page-grid composes the article column + the marginalia rail.
145
+ // Mirrors the legacy longform layout (`.er-page-grid` with the
146
+ // `.er-draft-frame` + `.er-page-gutter` + `.er-marginalia` triplet).
147
+ const pageGrid = html `
148
+ <div class="er-page-grid">
149
+ <div class="er-draft-frame">
150
+ <div id="draft-body" data-draft-body
151
+ title="Double-click to edit · select text to leave a margin note">${unsafe(bodyHtml)}</div>
152
+ ${renderEditPanes()}
153
+ </div>
154
+ <div class="er-page-gutter" aria-hidden="true"></div>
155
+ ${renderMarginalia()}
156
+ </div>`;
157
+ const scrapbookDrawer = renderScrapbookDrawer(ctx, data.site, reviewEntry, data.entry.slug, reviewIndex);
158
+ const versionStrip = renderVersionsStrip({
159
+ iterations: data.iterations,
160
+ entry: data.entry,
161
+ historicalVersion: data.historical?.versionNumber ?? null,
162
+ historicalStage: data.historical?.stage ?? null,
163
+ });
164
+ const decisionStrip = renderDecisionStrip({
165
+ entry: data.entry,
166
+ affordances,
167
+ historical: data.historical !== null,
168
+ });
169
+ const body = html `
170
+ <div data-review-ui="longform" class="er-review-shell">
171
+ ${renderEditorialFolio('longform', folioSpine)}
172
+ <div class="er-strip">
173
+ <div class="er-strip-inner">
174
+ <a class="er-strip-back" href="/dev/editorial-studio" title="Back to the editorial studio">← studio</a>
175
+ <span class="er-strip-galley">${gloss('galley')} <em>№ ${state.currentVersion ?? '—'}</em></span>
176
+ <span class="er-strip-slug">${data.site} / ${data.entry.slug}</span>
177
+ ${versionStrip}
178
+ <span class="er-strip-center">
179
+ <span class="er-stamp er-stamp-big er-stamp-${stageLabel.toLowerCase()}" data-state-label data-stage="${stageLabel}">
180
+ ${stageLabel}
181
+ </span>
182
+ ${historicalBadge}
183
+ <span class="er-strip-hint">select text to <span class="er-gloss" data-term="marginalia" tabindex="0" role="button" aria-describedby="glossary-marginalia">mark</span> · double-click to edit · <kbd>?</kbd> for shortcuts</span>
184
+ </span>
185
+ ${decisionStrip}
186
+ </div>
187
+ </div>
188
+ ${renderEditToolbar(outlineHtml.length > 0)}
189
+ <article class="er-page" data-entry-uuid="${data.entry.uuid}">
190
+ ${unsafe(pageGrid)}
191
+ </article>
192
+ ${renderMarginaliaTab()}
193
+ <button class="er-pencil-btn" data-add-comment-btn hidden type="button">Mark</button>
194
+ ${renderOutlineDrawer(outlineHtml)}
195
+ ${scrapbookDrawer}
196
+ <div class="er-toast" data-toast hidden></div>
197
+ ${renderShortcutsOverlay()}
198
+ <div class="er-poll-indicator" data-poll>auto-refresh · 8s</div>
199
+ </div>`;
200
+ return {
201
+ status: 200,
202
+ html: layout({
203
+ title: `${titleField} — Review`,
204
+ cssHrefs: [
205
+ '/static/css/editorial-review.css',
206
+ '/static/css/editorial-nav.css',
207
+ '/static/css/entry-review.css',
208
+ '/static/css/blog-figure.css',
209
+ '/static/css/review-viewport.css',
210
+ '/static/css/scrap-row.css',
211
+ ],
212
+ bodyAttrs: 'data-review-ui="entry-review"',
213
+ bodyHtml: body,
214
+ embeddedJson: [{ id: 'entry-review-state', data: state }],
215
+ scriptModules: ['entry-review-client'],
216
+ }),
217
+ };
218
+ }
219
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/pages/entry-review/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Marginalia column + stow chevron + edge pull tab for the entry-keyed
3
+ * press-check surface (Phase 34a — T10).
4
+ *
5
+ * Relocated from `pages/review.ts`. Per `.claude/rules/affordance-placement.md`,
6
+ * the toggle for "show / hide the margin-notes column" lives ON the
7
+ * marginalia component:
8
+ *
9
+ * - `.er-marginalia-stow` chevron INSIDE the marginalia head — visible
10
+ * only when marginalia is visible (it's inside `.er-marginalia`,
11
+ * which is `display: none` when stowed).
12
+ * - `.er-marginalia-tab` pull tab on the right edge of the viewport,
13
+ * visible only when stowed (CSS rule `body[data-marginalia="hidden"]
14
+ * .er-marginalia-tab { display: block }`).
15
+ *
16
+ * Both affordances + Shift+M dispatch through the same client-side
17
+ * `toggleMarginalia` handler. Mirrors the outline-drawer's pull-tab
18
+ * pattern so the project's affordance vocabulary stays consistent.
19
+ *
20
+ * The marginalia composer + saved-mark list are populated by
21
+ * `entry-review-client.ts` after page load — the server emits the
22
+ * shell + the empty composer; the client fetches annotations from
23
+ * `/api/dev/editorial-review/entry/<uuid>/annotations` and renders.
24
+ */
25
+ import { type RawHtml } from '../html.ts';
26
+ export declare function renderMarginaliaTab(): RawHtml;
27
+ export declare function renderMarginalia(): RawHtml;
28
+ //# sourceMappingURL=marginalia.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"marginalia.d.ts","sourceRoot":"","sources":["../../../src/pages/entry-review/marginalia.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAgB,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAExD,wBAAgB,mBAAmB,IAAI,OAAO,CAM7C;AAED,wBAAgB,gBAAgB,IAAI,OAAO,CAiC1C"}