@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
@@ -1,185 +1,15 @@
1
1
  /**
2
- * Entry-uuid keyed review surface — `/dev/editorial-review/entry/:entryId`.
2
+ * Entry-keyed press-check surface — `/dev/editorial-review/entry/:entryId`.
3
3
  *
4
- * Pipeline-redesign Task 35. The legacy `/dev/editorial-review/:slug`
5
- * (and its `:id` UUID variant) routes are workflow-uuid + calendar-entry
6
- * keyed. This sibling route is keyed by the *entry uuid* (the sidecar
7
- * id), uses the eight-stage entry model (Task 33/34), and renders the
8
- * minimal affordance set returned by `getAffordances(entry)`.
9
- *
10
- * The two surfaces coexist during the migration window. Once the
11
- * workflow-keyed routes are retired, this becomes the canonical review
12
- * surface; until then, the dashboard's per-row "review" links continue
13
- * to point at the legacy route.
14
- *
15
- * Rendering is intentionally minimal — the goal of Task 35 is the route
16
- * shape + affordance plumbing, not a fully-styled UI. Styling will land
17
- * once the affordance set stabilizes against real entries.
4
+ * The implementation lives in the `entry-review/` directory (one
5
+ * module per chrome component, each under the 500-line cap). This
6
+ * file re-exports the public render function so the existing
7
+ * `server.ts` import keeps working.
18
8
  */
19
9
 
20
- import { resolveEntry } from '../lib/entry-resolver.ts';
21
- import { getAffordances } from '../lib/stage-affordances.ts';
22
- import type { Affordances } from '../lib/stage-affordances.ts';
23
- import type { Entry } from '@deskwork/core/schema/entry';
24
- import { html, unsafe, type RawHtml } from './html.ts';
25
- import { layout } from './layout.ts';
26
-
27
- const STAGE_PICKER_OPTIONS = [
28
- 'Ideas',
29
- 'Planned',
30
- 'Outlining',
31
- 'Drafting',
32
- 'Final',
33
- ] as const;
34
-
35
- const CONTROL_LABELS: Readonly<Record<string, string>> = {
36
- save: 'Save',
37
- iterate: 'Iterate',
38
- approve: 'Approve',
39
- reject: 'Reject',
40
- 'historical-stage-dropdown': 'Historical stage',
41
- 'view-only': 'Read-only',
42
- 'fork-placeholder': 'Fork (coming)',
43
- 'induct-to': 'Induct to',
44
- };
45
-
46
- function renderControl(control: string, entry: Entry): RawHtml {
47
- const label = CONTROL_LABELS[control] ?? control;
48
- if (control === 'induct-to') {
49
- const options = STAGE_PICKER_OPTIONS.map(
50
- (s) => unsafe(html`<option value="${s}">${s}</option>`),
51
- );
52
- return unsafe(html`
53
- <label class="er-entry-control er-entry-control--induct">
54
- <span class="er-entry-control-label">${label}</span>
55
- <select name="induct-to" data-entry-uuid="${entry.uuid}">
56
- ${options}
57
- </select>
58
- </label>`);
59
- }
60
- if (control === 'historical-stage-dropdown') {
61
- const stages = Object.keys(entry.iterationByStage);
62
- if (stages.length === 0) {
63
- return unsafe('');
64
- }
65
- const options = stages.map(
66
- (s) => unsafe(html`<option value="${s}">${s}</option>`),
67
- );
68
- return unsafe(html`
69
- <label class="er-entry-control er-entry-control--history">
70
- <span class="er-entry-control-label">${label}</span>
71
- <select name="history-stage" data-entry-uuid="${entry.uuid}">
72
- ${options}
73
- </select>
74
- </label>`);
75
- }
76
- if (control === 'view-only') {
77
- return unsafe(html`<span class="er-entry-control er-entry-control--readonly">${label}</span>`);
78
- }
79
- if (control === 'fork-placeholder') {
80
- return unsafe(
81
- html`<button class="er-entry-control er-entry-control--button" type="button" disabled data-control="fork">${label}</button>`,
82
- );
83
- }
84
- return unsafe(
85
- html`<button class="er-entry-control er-entry-control--button" type="button" data-control="${control}" data-entry-uuid="${entry.uuid}">${label}</button>`,
86
- );
87
- }
88
-
89
- function renderControls(entry: Entry, affordances: Affordances): RawHtml {
90
- const buttons = affordances.controls.map((c) => renderControl(c, entry));
91
- const className = affordances.mutable
92
- ? 'er-entry-controls er-entry-controls--mutable'
93
- : 'er-entry-controls er-entry-controls--readonly';
94
- return unsafe(html`<nav class="${className}" aria-label="Entry controls">${buttons}</nav>`);
95
- }
96
-
97
- function renderArtifact(body: string, mutable: boolean): RawHtml {
98
- if (mutable) {
99
- return unsafe(html`
100
- <textarea class="er-entry-body" name="body" rows="24" data-mutable="true">${body}</textarea>`);
101
- }
102
- return unsafe(html`
103
- <pre class="er-entry-body er-entry-body--readonly" data-mutable="false">${body}</pre>`);
104
- }
105
-
106
- function renderEntryReview(
107
- entry: Entry,
108
- artifactBody: string,
109
- artifactPath: string,
110
- affordances: Affordances,
111
- ): string {
112
- const stageBadge = unsafe(
113
- html`<span class="er-entry-stage" data-stage="${entry.currentStage}">${entry.currentStage}</span>`,
114
- );
115
- const priorBadge = entry.priorStage
116
- ? unsafe(html`<span class="er-entry-prior-stage">paused from ${entry.priorStage}</span>`)
117
- : '';
118
- const body = html`
119
- <main class="er-entry-shell" data-entry-uuid="${entry.uuid}">
120
- <header class="er-entry-head">
121
- <p class="er-entry-kicker">Editorial Review · entry</p>
122
- <h1 class="er-entry-title">${entry.title}</h1>
123
- <p class="er-entry-meta">
124
- ${stageBadge}
125
- ${priorBadge}
126
- <code class="er-entry-uuid">${entry.uuid}</code>
127
- </p>
128
- <p class="er-entry-artifact-path"><code>${artifactPath}</code></p>
129
- </header>
130
- ${renderControls(entry, affordances)}
131
- <section class="er-entry-artifact">
132
- ${renderArtifact(artifactBody, affordances.mutable)}
133
- </section>
134
- </main>`;
135
- return layout({
136
- title: `${entry.title} — entry review — dev`,
137
- cssHrefs: ['/static/css/editorial-review.css'],
138
- bodyAttrs: 'data-review-ui="entry-review"',
139
- bodyHtml: body,
140
- scriptModules: ['entry-review-client'],
141
- });
142
- }
143
-
144
- function renderNotFound(entryId: string, reason: string): string {
145
- const body = html`
146
- <main class="er-entry-shell er-entry-shell--missing">
147
- <h1>Entry not found</h1>
148
- <p>No sidecar matched <code>${entryId}</code>.</p>
149
- <p class="er-entry-detail">${reason}</p>
150
- <p><a href="/dev/editorial-studio">Back to the studio</a></p>
151
- </main>`;
152
- return layout({
153
- title: 'Entry not found — dev',
154
- cssHrefs: ['/static/css/editorial-review.css'],
155
- bodyAttrs: 'data-review-ui="entry-review-missing"',
156
- bodyHtml: body,
157
- scriptModules: [],
158
- });
159
- }
160
-
161
- interface EntryReviewResult {
162
- status: 200 | 404;
163
- html: string;
164
- }
165
-
166
- export async function renderEntryReviewPage(
167
- projectRoot: string,
168
- entryId: string,
169
- ): Promise<EntryReviewResult> {
170
- let resolved;
171
- try {
172
- resolved = await resolveEntry(projectRoot, entryId);
173
- } catch (err) {
174
- const reason = err instanceof Error ? err.message : String(err);
175
- return { status: 404, html: renderNotFound(entryId, reason) };
176
- }
177
- const affordances = getAffordances(resolved.entry);
178
- const html = renderEntryReview(
179
- resolved.entry,
180
- resolved.artifactBody,
181
- resolved.artifactPath,
182
- affordances,
183
- );
184
- return { status: 200, html };
185
- }
10
+ export {
11
+ renderEntryReviewPage,
12
+ type EntryReviewQuery,
13
+ type EntryReviewResult,
14
+ type EntryReviewIndexGetter,
15
+ } from './entry-review/index.ts';
@@ -32,4 +32,6 @@ export declare function escapeHtml(s: string): string;
32
32
  * values run through `renderValue`.
33
33
  */
34
34
  export declare function html(strings: TemplateStringsArray, ...values: unknown[]): string;
35
+ export { gloss } from '../lib/glossary-helper.ts';
36
+ export type { GlossaryKey } from '../lib/glossary-helper.ts';
35
37
  //# sourceMappingURL=html.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/pages/html.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,wCAAwC;AACxC,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,kFAAkF;AAClF,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAEzC;AAYD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAO5C;AAeD;;;GAGG;AACH,wBAAgB,IAAI,CAClB,OAAO,EAAE,oBAAoB,EAC7B,GAAG,MAAM,EAAE,OAAO,EAAE,GACnB,MAAM,CAOR"}
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/pages/html.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,wCAAwC;AACxC,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,kFAAkF;AAClF,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAEzC;AAYD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAO5C;AAeD;;;GAGG;AACH,wBAAgB,IAAI,CAClB,OAAO,EAAE,oBAAoB,EAC7B,GAAG,MAAM,EAAE,OAAO,EAAE,GACnB,MAAM,CAOR;AAGD,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAClD,YAAY,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC"}
@@ -70,4 +70,6 @@ export function html(strings, ...values) {
70
70
  }
71
71
  return out;
72
72
  }
73
+ // Re-export glossary helpers so page renderers can use them in template literals
74
+ export { gloss } from "../lib/glossary-helper.js";
73
75
  //# sourceMappingURL=html.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"html.js","sourceRoot":"","sources":["../../src/pages/html.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAOH,kFAAkF;AAClF,MAAM,UAAU,MAAM,CAAC,CAAS;IAC9B,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;AACtB,CAAC;AAED,2CAA2C;AAC3C,SAAS,KAAK,CAAC,KAAc;IAC3B,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,OAAO,IAAI,KAAK;QAChB,OAAQ,KAA4B,CAAC,KAAK,KAAK,QAAQ,CACxD,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,CAAS;IAClC,OAAO,CAAC;SACL,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC;SACxB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC;SACvB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC;SACvB,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC;SACzB,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AAC9B,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,KAAK;QAAE,OAAO,EAAE,CAAC;IACxE,IAAI,KAAK,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,KAAK,CAAC;IACrC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjE,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC;IACxD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACjF,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3D,yEAAyE;IACzE,mEAAmE;IACnE,kEAAkE;IAClE,OAAO,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AACnC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,IAAI,CAClB,OAA6B,EAC7B,GAAG,MAAiB;IAEpB,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,GAAG,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;QAClB,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM;YAAE,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
1
+ {"version":3,"file":"html.js","sourceRoot":"","sources":["../../src/pages/html.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAOH,kFAAkF;AAClF,MAAM,UAAU,MAAM,CAAC,CAAS;IAC9B,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;AACtB,CAAC;AAED,2CAA2C;AAC3C,SAAS,KAAK,CAAC,KAAc;IAC3B,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,OAAO,IAAI,KAAK;QAChB,OAAQ,KAA4B,CAAC,KAAK,KAAK,QAAQ,CACxD,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,CAAS;IAClC,OAAO,CAAC;SACL,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC;SACxB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC;SACvB,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC;SACvB,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC;SACzB,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;AAC9B,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,KAAK;QAAE,OAAO,EAAE,CAAC;IACxE,IAAI,KAAK,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,KAAK,CAAC;IACrC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjE,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,UAAU,CAAC,KAAK,CAAC,CAAC;IACxD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACjF,IAAI,OAAO,KAAK,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3D,yEAAyE;IACzE,mEAAmE;IACnE,kEAAkE;IAClE,OAAO,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AACnC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,IAAI,CAClB,OAA6B,EAC7B,GAAG,MAAiB;IAEpB,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,GAAG,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC;QAClB,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM;YAAE,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,iFAAiF;AACjF,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC"}
@@ -78,3 +78,7 @@ export function html(
78
78
  }
79
79
  return out;
80
80
  }
81
+
82
+ // Re-export glossary helpers so page renderers can use them in template literals
83
+ export { gloss } from '../lib/glossary-helper.ts';
84
+ export type { GlossaryKey } from '../lib/glossary-helper.ts';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/pages/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAqDtD;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,SAAS,KAAK,EAAE,GACxB,KAAK,GAAG,IAAI,CAOd;AAsKD,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CA8B3E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/pages/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAqDtD;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,SAAS,KAAK,EAAE,GACxB,KAAK,GAAG,IAAI,CAOd;AAkKD,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CA8B3E"}
@@ -56,14 +56,10 @@ async function buildSections(ctx) {
56
56
  }
57
57
  })();
58
58
  const longformDefaultEntry = pickDefaultLongformEntry(entries);
59
- // Issue #107 / pipeline-redesign Task 36: III links to the
60
- // most-recent in-review (or iterating) longform entry when one
61
- // exists, else falls back to the dashboard's Review section anchor
62
- // (`#stage-review`, mounted in sub-phase D). The visual template
63
- // hint stays alongside the link so adopters still see the URL
64
- // shape — `<slug>` placeholder shown in red-pencil italic. The
65
- // link target is the entry-uuid keyed review route added in
66
- // Task 35 (`/dev/editorial-review/entry/<uuid>`).
59
+ // Issue #107: III links to the most-recent in-review (or iterating)
60
+ // longform entry when one exists, else falls back to the dashboard's
61
+ // Review section anchor (`#stage-review`). The link target is the
62
+ // entry-keyed review route `/dev/editorial-review/entry/<uuid>`.
67
63
  const longformLinkHref = longformDefaultEntry !== null
68
64
  ? `/dev/editorial-review/entry/${longformDefaultEntry.uuid}`
69
65
  : '/dev/editorial-studio#stage-review';
@@ -98,9 +94,9 @@ async function buildSections(ctx) {
98
94
  numeral: 'III',
99
95
  titleHtml: 'Longform reviews',
100
96
  titleText: 'Longform reviews',
101
- route: '/dev/editorial-review/<slug>',
97
+ route: '/dev/editorial-review/entry/<uuid>',
102
98
  linkHref: longformLinkHref,
103
- template: { prefix: '/dev/editorial-review/', placeholder: '<slug>' },
99
+ template: { prefix: '/dev/editorial-review/entry/', placeholder: '<uuid>' },
104
100
  desc: 'Per-entry margin notes, decisions, iterate flow.',
105
101
  hint: 'entry-by-entry',
106
102
  postHint: longformDefaultEntry !== null
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/pages/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/pages/index.ts"],"names":[],"mappings":"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"}
@@ -102,14 +102,10 @@ async function buildSections(ctx: StudioContext): Promise<readonly IndexSection[
102
102
  }
103
103
  })();
104
104
  const longformDefaultEntry = pickDefaultLongformEntry(entries);
105
- // Issue #107 / pipeline-redesign Task 36: III links to the
106
- // most-recent in-review (or iterating) longform entry when one
107
- // exists, else falls back to the dashboard's Review section anchor
108
- // (`#stage-review`, mounted in sub-phase D). The visual template
109
- // hint stays alongside the link so adopters still see the URL
110
- // shape — `<slug>` placeholder shown in red-pencil italic. The
111
- // link target is the entry-uuid keyed review route added in
112
- // Task 35 (`/dev/editorial-review/entry/<uuid>`).
105
+ // Issue #107: III links to the most-recent in-review (or iterating)
106
+ // longform entry when one exists, else falls back to the dashboard's
107
+ // Review section anchor (`#stage-review`). The link target is the
108
+ // entry-keyed review route `/dev/editorial-review/entry/<uuid>`.
113
109
  const longformLinkHref =
114
110
  longformDefaultEntry !== null
115
111
  ? `/dev/editorial-review/entry/${longformDefaultEntry.uuid}`
@@ -146,9 +142,9 @@ async function buildSections(ctx: StudioContext): Promise<readonly IndexSection[
146
142
  numeral: 'III',
147
143
  titleHtml: 'Longform reviews',
148
144
  titleText: 'Longform reviews',
149
- route: '/dev/editorial-review/<slug>',
145
+ route: '/dev/editorial-review/entry/<uuid>',
150
146
  linkHref: longformLinkHref,
151
- template: { prefix: '/dev/editorial-review/', placeholder: '<slug>' },
147
+ template: { prefix: '/dev/editorial-review/entry/', placeholder: '<uuid>' },
152
148
  desc: 'Per-entry margin notes, decisions, iterate flow.',
153
149
  hint: 'entry-by-entry',
154
150
  postHint:
@@ -1 +1 @@
1
- {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../src/pages/layout.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,MAAM,WAAW,YAAY;IAC3B,oEAAoE;IACpE,EAAE,EAAE,MAAM,CAAC;IACX,iDAAiD;IACjD,IAAI,EAAE,OAAO,CAAC;IACd,2DAA2D;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,YAAY,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IAC3C;;;;;OAKG;IACH,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,wBAAgB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CA8CrD"}
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../src/pages/layout.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,MAAM,WAAW,YAAY;IAC3B,oEAAoE;IACpE,EAAE,EAAE,MAAM,CAAC;IACX,iDAAiD;IACjD,IAAI,EAAE,OAAO,CAAC;IACd,2DAA2D;IAC3D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,YAAY,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IAC3C;;;;;OAKG;IACH,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,wBAAgB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CAoDrD"}
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { escapeHtml } from "./html.js";
6
6
  import { clientScriptTag, viteClientTag } from "../lib/client-script.js";
7
+ import glossary from '../data/glossary.json' with { type: 'json' };
7
8
  export function layout(options) {
8
9
  const { title, cssHrefs, bodyHtml, bodyAttrs, embeddedJson, scriptModules, } = options;
9
10
  const cssTags = cssHrefs
@@ -16,10 +17,14 @@ export function layout(options) {
16
17
  return ` <script type="application/json"${idPart}${attrPart}>${escapeForScriptTag(JSON.stringify(j.data))}</script>`;
17
18
  })
18
19
  .join('\n');
20
+ // Inline glossary data into every page so the tooltip client can access it.
21
+ const glossaryInline = `<script>window.__GLOSSARY__ = ${JSON.stringify(glossary).replace(/</g, '\\u003c')};</script>`;
19
22
  const hmrTag = viteClientTag();
23
+ const glossaryClientTag = clientScriptTag('glossary-tooltip');
20
24
  const scriptTags = [
21
25
  ...(hmrTag ? [` ${hmrTag}`] : []),
22
26
  ...scriptModules.map((name) => ` ${clientScriptTag(name)}`),
27
+ ` ${glossaryClientTag}`,
23
28
  ].join('\n');
24
29
  const bodyOpen = bodyAttrs ? `<body ${bodyAttrs}>` : '<body>';
25
30
  return `<!DOCTYPE html>
@@ -30,6 +35,7 @@ export function layout(options) {
30
35
  <meta name="robots" content="noindex">
31
36
  <title>${escapeHtml(title)}</title>
32
37
  ${cssTags}
38
+ ${glossaryInline}
33
39
  </head>
34
40
  ${bodyOpen}
35
41
  ${bodyHtml}
@@ -1 +1 @@
1
- {"version":3,"file":"layout.js","sourceRoot":"","sources":["../../src/pages/layout.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAoCzE,MAAM,UAAU,MAAM,CAAC,OAAsB;IAC3C,MAAM,EACJ,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,aAAa,GACd,GAAG,OAAO,CAAC;IAEZ,MAAM,OAAO,GAAG,QAAQ;SACrB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,oCAAoC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;SACvE,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,QAAQ,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC;SAClC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5C,MAAM,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,OAAO,sCAAsC,MAAM,GAAG,QAAQ,IAAI,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC;IAC1H,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,MAAM,UAAU,GAAG;QACjB,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACpC,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;KAC/D,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,MAAM,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,SAAS,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC;IAE9D,OAAO;;;;;;aAMI,UAAU,CAAC,KAAK,CAAC;EAC5B,OAAO;;IAEL,QAAQ;EACV,QAAQ;EACR,QAAQ;EACR,UAAU;;;CAGX,CAAC;AACF,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC;AACvB,CAAC;AAED;;;;GAIG;AACH,SAAS,kBAAkB,CAAC,IAAY;IACtC,OAAO,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE,QAAQ,CAAC,CAAC;AACrD,CAAC"}
1
+ {"version":3,"file":"layout.js","sourceRoot":"","sources":["../../src/pages/layout.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACzE,OAAO,QAAQ,MAAM,uBAAuB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAoCnE,MAAM,UAAU,MAAM,CAAC,OAAsB;IAC3C,MAAM,EACJ,KAAK,EACL,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,aAAa,GACd,GAAG,OAAO,CAAC;IAEZ,MAAM,OAAO,GAAG,QAAQ;SACrB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,oCAAoC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;SACvE,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,QAAQ,GAAG,CAAC,YAAY,IAAI,EAAE,CAAC;SAClC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5C,MAAM,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,OAAO,sCAAsC,MAAM,GAAG,QAAQ,IAAI,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,WAAW,CAAC;IAC1H,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,4EAA4E;IAC5E,MAAM,cAAc,GAAG,iCAAiC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,YAAY,CAAC;IAEtH,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,MAAM,iBAAiB,GAAG,eAAe,CAAC,kBAAkB,CAAC,CAAC;IAC9D,MAAM,UAAU,GAAG;QACjB,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACpC,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9D,OAAO,iBAAiB,EAAE;KAC3B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEb,MAAM,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,SAAS,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC;IAE9D,OAAO;;;;;;aAMI,UAAU,CAAC,KAAK,CAAC;EAC5B,OAAO;MACH,cAAc;;IAEhB,QAAQ;EACV,QAAQ;EACR,QAAQ;EACR,UAAU;;;CAGX,CAAC;AACF,CAAC;AAED,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC;AACvB,CAAC;AAED;;;;GAIG;AACH,SAAS,kBAAkB,CAAC,IAAY;IACtC,OAAO,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE,QAAQ,CAAC,CAAC;AACrD,CAAC"}
@@ -5,6 +5,7 @@
5
5
 
6
6
  import { escapeHtml } from './html.ts';
7
7
  import { clientScriptTag, viteClientTag } from '../lib/client-script.ts';
8
+ import glossary from '../data/glossary.json' with { type: 'json' };
8
9
 
9
10
  export interface EmbeddedJson {
10
11
  /** `id` attribute of the `<script type="application/json">` tag. */
@@ -62,10 +63,15 @@ export function layout(options: LayoutOptions): string {
62
63
  })
63
64
  .join('\n');
64
65
 
66
+ // Inline glossary data into every page so the tooltip client can access it.
67
+ const glossaryInline = `<script>window.__GLOSSARY__ = ${JSON.stringify(glossary).replace(/</g, '\\u003c')};</script>`;
68
+
65
69
  const hmrTag = viteClientTag();
70
+ const glossaryClientTag = clientScriptTag('glossary-tooltip');
66
71
  const scriptTags = [
67
72
  ...(hmrTag ? [` ${hmrTag}`] : []),
68
73
  ...scriptModules.map((name) => ` ${clientScriptTag(name)}`),
74
+ ` ${glossaryClientTag}`,
69
75
  ].join('\n');
70
76
 
71
77
  const bodyOpen = bodyAttrs ? `<body ${bodyAttrs}>` : '<body>';
@@ -78,6 +84,7 @@ export function layout(options: LayoutOptions): string {
78
84
  <meta name="robots" content="noindex">
79
85
  <title>${escapeHtml(title)}</title>
80
86
  ${cssTags}
87
+ ${glossaryInline}
81
88
  </head>
82
89
  ${bodyOpen}
83
90
  ${bodyHtml}
@@ -11,6 +11,13 @@
11
11
  * directory via the index. This makes writingcontrol-shape entries
12
12
  * (slug != fs path) list their items at the actual file location.
13
13
  * Falls back to slug-template addressing for unbound / legacy entries.
14
+ *
15
+ * Issue #154 Dispatch D: this drawer is now a real bottom-anchored
16
+ * expandable drawer. The handle (header) is a clickable role=button
17
+ * that toggles `body[data-drawer]`; the drawer height transitions
18
+ * 4rem (collapsed) → 22rem (expanded). The standalone-viewer link is
19
+ * demoted to a small inline affordance — the primary action is now
20
+ * "expand the drawer" rather than "navigate away."
14
21
  */
15
22
  import type { ContentIndex } from '@deskwork/core/content-index';
16
23
  import type { CalendarEntry } from '@deskwork/core/types';
@@ -1 +1 @@
1
- {"version":3,"file":"review-scrapbook-drawer.d.ts","sourceRoot":"","sources":["../../src/pages/review-scrapbook-drawer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;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,EAAgB,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AA2DvD,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,CAkDT"}
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"}
@@ -11,13 +11,20 @@
11
11
  * directory via the index. This makes writingcontrol-shape entries
12
12
  * (slug != fs path) list their items at the actual file location.
13
13
  * Falls back to slug-template addressing for unbound / legacy entries.
14
+ *
15
+ * Issue #154 Dispatch D: this drawer is now a real bottom-anchored
16
+ * expandable drawer. The handle (header) is a clickable role=button
17
+ * that toggles `body[data-drawer]`; the drawer height transitions
18
+ * 4rem (collapsed) → 22rem (expanded). The standalone-viewer link is
19
+ * demoted to a small inline affordance — the primary action is now
20
+ * "expand the drawer" rather than "navigate away."
14
21
  */
15
22
  import { readFileSync } from 'node:fs';
16
23
  import { join } from 'node:path';
17
24
  import { listScrapbook, listScrapbookAtDir, scrapbookDirForEntry, } from '@deskwork/core/scrapbook';
18
25
  import { resolveContentDir } from '@deskwork/core/paths';
19
26
  import { renderEmptyScrapbookRow, renderReadOnlyScrapbookRow, scrapbookViewerUrl, } from "../components/scrapbook-item.js";
20
- import { html, unsafe } from "./html.js";
27
+ import { html, unsafe, escapeHtml } from "./html.js";
21
28
  /**
22
29
  * Build an inline-text loader for the shared scrapbook-item renderer.
23
30
  * Reads at most `maxBytes` from a file inside the scrapbook directory
@@ -48,11 +55,32 @@ function makeInlineTextLoader(ctx, site, entry, slug, index) {
48
55
  }
49
56
  function renderScrapbookDrawerItems(site, slug, items, loader) {
50
57
  if (items.length === 0) {
51
- return renderEmptyScrapbookRow();
58
+ return renderEmptyScrapbookRow({ site, path: slug });
52
59
  }
53
60
  const rows = items.map((item) => renderReadOnlyScrapbookRow({ site, path: slug }, item, { inlinePreviewLoader: loader }));
54
61
  return unsafe(rows.map((r) => r.__raw).join(''));
55
62
  }
63
+ /**
64
+ * Build the "peek" line shown in the collapsed handle — up to 3 item
65
+ * filenames separated by `·`, plus a `+ N more` suffix when there are
66
+ * additional items. Empty scrapbook renders an inline empty-state hint
67
+ * so the operator still sees the surface.
68
+ */
69
+ function renderPeek(items, secretItems) {
70
+ const all = [...items, ...secretItems];
71
+ if (all.length === 0) {
72
+ return '<span class="er-scrapbook-drawer-peek-empty">(empty — drop research here)</span>';
73
+ }
74
+ const shown = all
75
+ .slice(0, 3)
76
+ .map((i) => `<span>${escapeHtml(i.name)}</span>`)
77
+ .join('<span class="sep">·</span>');
78
+ const remaining = all.length - 3;
79
+ const suffix = remaining > 0
80
+ ? `<span class="sep">·</span><span>+ ${remaining} more</span>`
81
+ : '';
82
+ return shown + suffix;
83
+ }
56
84
  export function renderScrapbookDrawer(ctx, site, entry, slug, index) {
57
85
  const summary = (() => {
58
86
  try {
@@ -75,13 +103,24 @@ export function renderScrapbookDrawer(ctx, site, entry, slug, index) {
75
103
  const loader = makeInlineTextLoader(ctx, site, entry, slug, index);
76
104
  return unsafe(html `
77
105
  <aside class="er-scrapbook-drawer" data-scrapbook-drawer aria-label="Scrapbook for this entry">
78
- <header class="er-scrapbook-drawer-head">
79
- <span class="er-scrapbook-drawer-kicker">§ Scrapbook</span>
106
+ <header class="er-scrapbook-drawer-handle" data-drawer-toggle role="button" tabindex="0"
107
+ aria-expanded="false" aria-controls="er-scrapbook-drawer-body">
108
+ <span class="er-scrapbook-drawer-kicker"><em>§</em> Scrapbook</span>
80
109
  <span class="er-scrapbook-drawer-count">${total} ${total === 1 ? 'item' : 'items'}</span>
110
+ <span class="er-scrapbook-drawer-peek" aria-hidden="true">
111
+ ${unsafe(renderPeek(items, secretItems))}
112
+ </span>
81
113
  <a class="er-scrapbook-drawer-open" href="${scrapbookViewerUrl({ site, path: slug })}"
82
- title="Open the standalone scrapbook viewer">open ↗</a>
114
+ title="Open the standalone scrapbook viewer"
115
+ onclick="event.stopPropagation()">open viewer ↗</a>
116
+ <button class="er-scrapbook-drawer-toggle" type="button" data-drawer-toggle
117
+ aria-controls="er-scrapbook-drawer-body" tabindex="-1">
118
+ <span data-toggle-label>Expand</span>
119
+ <span class="chev" aria-hidden="true">▾</span>
120
+ </button>
83
121
  </header>
84
- <div class="er-scrapbook-drawer-body">
122
+ <div class="er-scrapbook-drawer-body" id="er-scrapbook-drawer-body"
123
+ role="region" aria-label="scrapbook items">
85
124
  ${renderScrapbookDrawerItems(site, slug, items, loader)}
86
125
  ${secretItems.length > 0
87
126
  ? unsafe(html `
@@ -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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"}
@@ -11,6 +11,13 @@
11
11
  * directory via the index. This makes writingcontrol-shape entries
12
12
  * (slug != fs path) list their items at the actual file location.
13
13
  * Falls back to slug-template addressing for unbound / legacy entries.
14
+ *
15
+ * Issue #154 Dispatch D: this drawer is now a real bottom-anchored
16
+ * expandable drawer. The handle (header) is a clickable role=button
17
+ * that toggles `body[data-drawer]`; the drawer height transitions
18
+ * 4rem (collapsed) → 22rem (expanded). The standalone-viewer link is
19
+ * demoted to a small inline affordance — the primary action is now
20
+ * "expand the drawer" rather than "navigate away."
14
21
  */
15
22
 
16
23
  import { readFileSync } from 'node:fs';
@@ -32,7 +39,7 @@ import {
32
39
  scrapbookViewerUrl,
33
40
  type InlineTextLoader,
34
41
  } from '../components/scrapbook-item.ts';
35
- import { html, unsafe, type RawHtml } from './html.ts';
42
+ import { html, unsafe, escapeHtml, type RawHtml } from './html.ts';
36
43
 
37
44
  /**
38
45
  * Build an inline-text loader for the shared scrapbook-item renderer.
@@ -79,7 +86,7 @@ function renderScrapbookDrawerItems(
79
86
  loader: InlineTextLoader,
80
87
  ): RawHtml {
81
88
  if (items.length === 0) {
82
- return renderEmptyScrapbookRow();
89
+ return renderEmptyScrapbookRow({ site, path: slug });
83
90
  }
84
91
  const rows = items.map((item) =>
85
92
  renderReadOnlyScrapbookRow(
@@ -91,6 +98,32 @@ function renderScrapbookDrawerItems(
91
98
  return unsafe(rows.map((r) => r.__raw).join(''));
92
99
  }
93
100
 
101
+ /**
102
+ * Build the "peek" line shown in the collapsed handle — up to 3 item
103
+ * filenames separated by `·`, plus a `+ N more` suffix when there are
104
+ * additional items. Empty scrapbook renders an inline empty-state hint
105
+ * so the operator still sees the surface.
106
+ */
107
+ function renderPeek(
108
+ items: readonly ScrapbookItem[],
109
+ secretItems: readonly ScrapbookItem[],
110
+ ): string {
111
+ const all = [...items, ...secretItems];
112
+ if (all.length === 0) {
113
+ return '<span class="er-scrapbook-drawer-peek-empty">(empty — drop research here)</span>';
114
+ }
115
+ const shown = all
116
+ .slice(0, 3)
117
+ .map((i) => `<span>${escapeHtml(i.name)}</span>`)
118
+ .join('<span class="sep">·</span>');
119
+ const remaining = all.length - 3;
120
+ const suffix =
121
+ remaining > 0
122
+ ? `<span class="sep">·</span><span>+ ${remaining} more</span>`
123
+ : '';
124
+ return shown + suffix;
125
+ }
126
+
94
127
  export function renderScrapbookDrawer(
95
128
  ctx: StudioContext,
96
129
  site: string,
@@ -126,13 +159,24 @@ export function renderScrapbookDrawer(
126
159
 
127
160
  return unsafe(html`
128
161
  <aside class="er-scrapbook-drawer" data-scrapbook-drawer aria-label="Scrapbook for this entry">
129
- <header class="er-scrapbook-drawer-head">
130
- <span class="er-scrapbook-drawer-kicker">§ Scrapbook</span>
162
+ <header class="er-scrapbook-drawer-handle" data-drawer-toggle role="button" tabindex="0"
163
+ aria-expanded="false" aria-controls="er-scrapbook-drawer-body">
164
+ <span class="er-scrapbook-drawer-kicker"><em>§</em> Scrapbook</span>
131
165
  <span class="er-scrapbook-drawer-count">${total} ${total === 1 ? 'item' : 'items'}</span>
166
+ <span class="er-scrapbook-drawer-peek" aria-hidden="true">
167
+ ${unsafe(renderPeek(items, secretItems))}
168
+ </span>
132
169
  <a class="er-scrapbook-drawer-open" href="${scrapbookViewerUrl({ site, path: slug })}"
133
- title="Open the standalone scrapbook viewer">open ↗</a>
170
+ title="Open the standalone scrapbook viewer"
171
+ onclick="event.stopPropagation()">open viewer ↗</a>
172
+ <button class="er-scrapbook-drawer-toggle" type="button" data-drawer-toggle
173
+ aria-controls="er-scrapbook-drawer-body" tabindex="-1">
174
+ <span data-toggle-label>Expand</span>
175
+ <span class="chev" aria-hidden="true">▾</span>
176
+ </button>
134
177
  </header>
135
- <div class="er-scrapbook-drawer-body">
178
+ <div class="er-scrapbook-drawer-body" id="er-scrapbook-drawer-body"
179
+ role="region" aria-label="scrapbook items">
136
180
  ${renderScrapbookDrawerItems(site, slug, items, loader)}
137
181
  ${
138
182
  secretItems.length > 0