@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,39 @@
1
+ /**
2
+ * Bird's-eye content view (Phase 16d).
3
+ *
4
+ * Three render entry points sharing a Writer's-Catalog visual language
5
+ * extracted from `mockups/birds-eye-content-view.html`:
6
+ *
7
+ * - `renderContentTopLevel(ctx)` — `/dev/content` and `/dev/content/:site`
8
+ * → site cards + per-site project rollups.
9
+ * - `renderContentProject(ctx, site, project, selectedSlug)` —
10
+ * `/dev/content/:site/:project{.+}` → drilldown view with the
11
+ * per-project tree on the left and the detail panel on the right.
12
+ * - The detail panel is selected via the `?node=<slug>` query
13
+ * param. When absent, an empty placeholder renders.
14
+ *
15
+ * Read-only — no calendar mutations. Operators jump back to the
16
+ * standalone scrapbook viewer or the longform review surface for
17
+ * mutations via the inline `→ review` and `→ scrapbook` affordances
18
+ * on each tree row.
19
+ *
20
+ * Helpers:
21
+ * - `pages/content-detail.ts` — detail panel rendering.
22
+ * - `pages/chrome.ts` — cross-page editorial folio strip (`renderEditorialFolio`).
23
+ * - `components/scrapbook-item.ts` — shared scrap-row renderer.
24
+ * - `@deskwork/core/content-tree` — pure tree assembly.
25
+ */
26
+ import type { ContentIndex } from '@deskwork/core/content-index';
27
+ import type { StudioContext } from '../routes/api.ts';
28
+ /**
29
+ * Per-request index getter — supplied by the route layer (which
30
+ * pulls memoized indices off the Hono context). Optional: when
31
+ * omitted, renderers fall back to building the index per call.
32
+ */
33
+ export type IndexGetter = (site: string) => ContentIndex;
34
+ export declare function renderContentTopLevel(ctx: StudioContext, getIndex?: IndexGetter): string;
35
+ export declare function renderContentProject(ctx: StudioContext, site: string, projectSlug: string, selectedPath: string | null, getIndex?: IndexGetter): Promise<{
36
+ status: number;
37
+ html: string;
38
+ }>;
39
+ //# sourceMappingURL=content.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/pages/content.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAWH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAOjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAOtD;;;;GAIG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,YAAY,CAAC;AAqJzD,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,aAAa,EAClB,QAAQ,CAAC,EAAE,WAAW,GACrB,MAAM,CA2CR;AAkMD,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,QAAQ,CAAC,EAAE,WAAW,GACrB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CA2D3C"}
@@ -0,0 +1,414 @@
1
+ /**
2
+ * Bird's-eye content view (Phase 16d).
3
+ *
4
+ * Three render entry points sharing a Writer's-Catalog visual language
5
+ * extracted from `mockups/birds-eye-content-view.html`:
6
+ *
7
+ * - `renderContentTopLevel(ctx)` — `/dev/content` and `/dev/content/:site`
8
+ * → site cards + per-site project rollups.
9
+ * - `renderContentProject(ctx, site, project, selectedSlug)` —
10
+ * `/dev/content/:site/:project{.+}` → drilldown view with the
11
+ * per-project tree on the left and the detail panel on the right.
12
+ * - The detail panel is selected via the `?node=<slug>` query
13
+ * param. When absent, an empty placeholder renders.
14
+ *
15
+ * Read-only — no calendar mutations. Operators jump back to the
16
+ * standalone scrapbook viewer or the longform review surface for
17
+ * mutations via the inline `→ review` and `→ scrapbook` affordances
18
+ * on each tree row.
19
+ *
20
+ * Helpers:
21
+ * - `pages/content-detail.ts` — detail panel rendering.
22
+ * - `pages/chrome.ts` — cross-page editorial folio strip (`renderEditorialFolio`).
23
+ * - `components/scrapbook-item.ts` — shared scrap-row renderer.
24
+ * - `@deskwork/core/content-tree` — pure tree assembly.
25
+ */
26
+ import { readCalendar } from '@deskwork/core/calendar';
27
+ import { buildContentTree, findNode, flattenForRender, } from '@deskwork/core/content-tree';
28
+ import { formatRelativeTime, } from '@deskwork/core/scrapbook';
29
+ import { resolveCalendarPath, resolveContentDir } from '@deskwork/core/paths';
30
+ import { relative } from 'node:path';
31
+ import { html, unsafe } from "./html.js";
32
+ import { layout } from "./layout.js";
33
+ import { renderEditorialFolio } from "./chrome.js";
34
+ import { renderEmptyDetail, renderNodeDetail } from "./content-detail.js";
35
+ import { scrapbookViewerUrl } from "../components/scrapbook-item.js";
36
+ function loadProjectsForSite(ctx, site, getIndex) {
37
+ const calendarPath = resolveCalendarPath(ctx.projectRoot, ctx.config, site);
38
+ const cal = readCalendar(calendarPath);
39
+ const contentIndex = getIndex ? getIndex(site) : undefined;
40
+ const projects = buildContentTree(site, cal.entries, ctx.config, ctx.projectRoot, contentIndex !== undefined ? { contentIndex } : {});
41
+ return {
42
+ site,
43
+ host: ctx.config.sites[site].host,
44
+ projects,
45
+ };
46
+ }
47
+ function loadAllSites(ctx, getIndex) {
48
+ return Object.keys(ctx.config.sites).map((site) => loadProjectsForSite(ctx, site, getIndex));
49
+ }
50
+ function aggregateCounts(siteProjects) {
51
+ let trackedNodes = 0;
52
+ let scrapbookItems = 0;
53
+ for (const sp of siteProjects) {
54
+ for (const project of sp.projects) {
55
+ trackedNodes += project.trackedCount;
56
+ scrapbookItems += project.scrapbookCount;
57
+ }
58
+ }
59
+ return { sites: siteProjects.length, trackedNodes, scrapbookItems };
60
+ }
61
+ // ---------------------------------------------------------------------------
62
+ // Lane styling helpers
63
+ // ---------------------------------------------------------------------------
64
+ function laneToken(stage) {
65
+ return stage ? stage.toLowerCase() : 'unknown';
66
+ }
67
+ function laneLabel(stage) {
68
+ return stage ? `mostly ${stage.toLowerCase()}` : 'untracked';
69
+ }
70
+ // ---------------------------------------------------------------------------
71
+ // Top-level: site cards + project rows
72
+ // ---------------------------------------------------------------------------
73
+ function projectFormHint(project) {
74
+ if (project.totalNodes === 1)
75
+ return 'flat entry';
76
+ if (project.maxDepth >= 3)
77
+ return `nested · ${project.totalNodes} nodes`;
78
+ return `collection · ${project.totalNodes} nodes`;
79
+ }
80
+ function renderProjectRow(site, project, index) {
81
+ const num = String(index + 1).padStart(2, '0');
82
+ const lane = laneToken(project.predominantLane);
83
+ const href = `/dev/content/${site}/${encodeURI(project.rootSlug)}`;
84
+ return unsafe(html `
85
+ <a class="project-row" href="${href}">
86
+ <span class="project-row__num">${num}</span>
87
+ <span class="project-row__name">
88
+ ${project.title}
89
+ <em>${projectFormHint(project)}</em>
90
+ </span>
91
+ <span class="project-row__nodes">${project.totalNodes} nodes</span>
92
+ <span class="project-row__lane">
93
+ <span class="lane-dot lane-dot--${lane}"></span>
94
+ ${laneLabel(project.predominantLane)}
95
+ </span>
96
+ </a>`);
97
+ }
98
+ function siteTag(index) {
99
+ return index === 0 ? 'Site · primary' : 'Site · auxiliary';
100
+ }
101
+ function renderSiteCard(sp, index) {
102
+ const totalNodes = sp.projects.reduce((acc, p) => acc + p.totalNodes, 0);
103
+ const trackedNodes = sp.projects.reduce((acc, p) => acc + p.trackedCount, 0);
104
+ const scrapbookItems = sp.projects.reduce((acc, p) => acc + p.scrapbookCount, 0);
105
+ return unsafe(html `
106
+ <article class="site-card">
107
+ <div class="site-card__tag">${siteTag(index)}</div>
108
+ <h2 class="site-card__name">${sp.site}</h2>
109
+ <div class="site-card__host">${sp.host ?? '(no host configured)'}</div>
110
+ <div class="site-card__counts">
111
+ <b>${sp.projects.length}</b> root entries ·
112
+ <b>${totalNodes}</b> total nodes ·
113
+ <b>${scrapbookItems}</b> scrapbook items
114
+ </div>
115
+ <div class="site-card__projects">
116
+ ${sp.projects.map((p, i) => renderProjectRow(sp.site, p, i))}
117
+ </div>
118
+ ${sp.projects.length === 0
119
+ ? unsafe(html `
120
+ <p class="site-card__empty">No tracked content yet — run
121
+ <code>/deskwork:add</code> or
122
+ <code>/deskwork:ingest</code>.
123
+ </p>`)
124
+ : ''}
125
+ <p class="site-card__rollup">
126
+ ${trackedNodes} tracked · ${totalNodes - trackedNodes} synthetic
127
+ </p>
128
+ </article>`);
129
+ }
130
+ export function renderContentTopLevel(ctx, getIndex) {
131
+ const sites = loadAllSites(ctx, getIndex);
132
+ const counts = aggregateCounts(sites);
133
+ const body = html `
134
+ ${renderEditorialFolio('content', 'the shape of the work')}
135
+ <main class="content-page">
136
+ <header class="er-pagehead er-pagehead--split er-pagehead--compact">
137
+ <div>
138
+ <h1 class="er-pagehead__title">A <em>shape</em> of the work.</h1>
139
+ <p class="er-pagehead__deck">
140
+ The pipeline view shows where things are. This shows what's
141
+ there. Browse the corpus by its tree on disk; drill into any
142
+ node to see its content and the scrapbook hanging off it.
143
+ </p>
144
+ </div>
145
+ <p class="er-pagehead__meta">
146
+ <span><b>${counts.sites}</b> SITES</span>
147
+ <span><b>${counts.trackedNodes}</b> TRACKED NODES</span>
148
+ <span><b>${counts.scrapbookItems}</b> SCRAPBOOK ITEMS</span>
149
+ </p>
150
+ </header>
151
+ <section class="toplevel">
152
+ ${sites.map((sp, i) => renderSiteCard(sp, i))}
153
+ </section>
154
+ </main>`;
155
+ return layout({
156
+ title: 'Content — deskwork',
157
+ cssHrefs: [
158
+ '/static/css/editorial-review.css',
159
+ '/static/css/editorial-nav.css',
160
+ '/static/css/content.css',
161
+ '/static/css/scrap-row.css',
162
+ '/static/css/blog-figure.css',
163
+ ],
164
+ bodyAttrs: 'data-review-ui="studio"',
165
+ bodyHtml: body,
166
+ // #29: lightbox listener for image thumbnails in detail-panel
167
+ // scrap rows. Idempotent — safe to load on the top-level page
168
+ // too (no scrap rows there → no work).
169
+ scriptModules: ['/static/dist/content-view-client.js'],
170
+ });
171
+ }
172
+ // ---------------------------------------------------------------------------
173
+ // Drilldown view: tree + detail panel
174
+ // ---------------------------------------------------------------------------
175
+ function renderTreeBreadcrumb(site, project, selectedPath) {
176
+ const links = [];
177
+ links.push(html `<a href="/dev/content/${site}">${site}</a>`);
178
+ if (selectedPath === null) {
179
+ links.push(html `<b>${project.rootSlug}</b>`);
180
+ }
181
+ else {
182
+ const projectHref = `/dev/content/${site}/${encodeURI(project.rootSlug)}`;
183
+ links.push(html `<a href="${projectHref}">${project.rootSlug}</a>`);
184
+ const segments = selectedPath.split('/');
185
+ for (let i = 1; i < segments.length; i++) {
186
+ const path = segments.slice(0, i + 1).join('/');
187
+ const isLast = i === segments.length - 1;
188
+ if (isLast) {
189
+ links.push(html `<b>${segments[i]}</b>`);
190
+ }
191
+ else {
192
+ const href = `${projectHref}?node=${encodeURIComponent(path)}`;
193
+ links.push(html `<a href="${href}">${segments[i]}</a>`);
194
+ }
195
+ }
196
+ }
197
+ const sep = html `<span class="breadcrumb__sep" aria-hidden="true">›</span>`;
198
+ return unsafe(html `
199
+ <nav class="breadcrumb" aria-label="content tree breadcrumb">
200
+ ${unsafe(links.join(`\n${sep}\n`))}
201
+ </nav>`);
202
+ }
203
+ function nodeIcon(node) {
204
+ if (node.children.length > 0 || node.lane === null) {
205
+ return unsafe(html `<span class="tree-row__icon is-branch" aria-hidden="true">◐</span>`);
206
+ }
207
+ return unsafe(html `<span class="tree-row__icon" aria-hidden="true">·</span>`);
208
+ }
209
+ function nodeFilePathHint(node, contentDir) {
210
+ // Issue #70: when the content index has a real file binding for this
211
+ // node, display the actual on-disk path (relative to contentDir) so
212
+ // the operator sees where the file actually lives — not a slug-derived
213
+ // ghost path that may not exist on disk. Hierarchical layouts where
214
+ // the file is at `<path>/prd.md` (not `<path>/index.md`) used to
215
+ // render the wrong path here.
216
+ if (node.filePath !== undefined) {
217
+ const rel = relative(contentDir, node.filePath);
218
+ // `relative` may emit `../` segments if the file lives outside
219
+ // contentDir (shouldn't happen, but defend against it). Fall through
220
+ // to the slug-derived hint when that's the case so we never display
221
+ // an escaping path string in the UI.
222
+ if (!rel.startsWith('..'))
223
+ return `/${rel}`;
224
+ }
225
+ // Phase 19c: node.path is the fs-relative path (the structural key).
226
+ // Tracked entries display the index file shape; organizational
227
+ // nodes show the directory shape. The host's actual file basename
228
+ // could differ (README.md, .mdx, etc.) but `/index.md` is the
229
+ // universal "expected location" hint the operator reads.
230
+ if (node.entry !== null)
231
+ return `/${node.path}/index.md`;
232
+ return `/${node.path}/`;
233
+ }
234
+ /**
235
+ * Last segment of an fs-relative path. Used to detect when a tracked
236
+ * entry's public-URL slug differs from where it lives on disk —
237
+ * e.g. an entry whose `path = "projects/the-outbound-novel"` and
238
+ * `slug = "the-outbound"` (renamed directory, slug unchanged for SEO).
239
+ */
240
+ function pathLeaf(path) {
241
+ const idx = path.lastIndexOf('/');
242
+ return idx < 0 ? path : path.slice(idx + 1);
243
+ }
244
+ function renderTreeRowMeta(node) {
245
+ const meta = [];
246
+ if (node.scrapbookCount > 0) {
247
+ const word = node.scrapbookCount === 1 ? 'note' : 'notes';
248
+ meta.push(html `<span class="scrap-count">${node.scrapbookCount} ${word}</span>`);
249
+ }
250
+ if (node.scrapbookMostRecentMtime !== null) {
251
+ meta.push(html `<span class="mtime">${formatRelativeTime(node.scrapbookMostRecentMtime)}</span>`);
252
+ }
253
+ return unsafe(meta.join(''));
254
+ }
255
+ function renderTreeRowActions(node, site) {
256
+ // Phase 19d: when an entry is overlaid, prefer its stable id for
257
+ // the canonical review URL (refactor-proof — survives slug renames).
258
+ // Fall back to the entry's slug (or the node's path for organizational
259
+ // nodes) when no id is stamped — that's the legacy migration shape;
260
+ // server.ts 302-redirects it to the canonical URL.
261
+ const reviewKey = node.entry !== null && node.entry.id !== undefined && node.entry.id !== ''
262
+ ? node.entry.id
263
+ : (node.slug ?? node.path);
264
+ const reviewHref = `/dev/editorial-review/${encodeURI(reviewKey)}?site=${site}`;
265
+ // Scrapbook addressing uses fs path — every node, tracked or not,
266
+ // has a deterministic on-disk scrapbook location at `<path>/scrapbook/`.
267
+ const scrapHref = scrapbookViewerUrl({ site, path: node.path });
268
+ const reviewLink = node.entry !== null
269
+ ? html `<a class="tree-row__action tree-row__action--review" href="${reviewHref}"
270
+ tabindex="0" aria-label="Open review for ${node.title}">→ review</a>`
271
+ : '';
272
+ const scrapLink = node.scrapbookCount > 0
273
+ ? html `<a class="tree-row__action" href="${scrapHref}"
274
+ tabindex="0" aria-label="Open scrapbook for ${node.title}">→ scrapbook</a>`
275
+ : '';
276
+ return unsafe(reviewLink + scrapLink);
277
+ }
278
+ function renderTreeRow(site, project, flat, selectedPath, contentDir) {
279
+ const { node, depth, isLast } = flat;
280
+ const isSelected = selectedPath === node.path;
281
+ const isLeaf = node.children.length === 0;
282
+ const lane = laneToken(node.lane);
283
+ const projectHref = `/dev/content/${site}/${encodeURI(project.rootSlug)}`;
284
+ // Phase 19c: structural URLs key on fs path. The selection query
285
+ // parameter accepts hierarchical paths via `:path{.+}` route syntax
286
+ // upstream — encodeURIComponent preserves the `/` segments.
287
+ const nodeHref = `${projectHref}?node=${encodeURIComponent(node.path)}`;
288
+ const classes = [
289
+ 'tree-row',
290
+ isLeaf ? 'is-leaf' : 'is-branch',
291
+ isLast ? 'is-last' : '',
292
+ isSelected ? 'is-selected' : '',
293
+ ]
294
+ .filter(Boolean)
295
+ .join(' ');
296
+ // Phase 19d: render a "public URL" hover hint when an overlay entry
297
+ // exists AND the entry's slug differs from the path's leaf segment.
298
+ // The slug is the host-rendering engine's identifier (the SEO URL);
299
+ // showing it explicitly clarifies the relationship between the
300
+ // structural fs path and the public-facing URL.
301
+ const publicUrlHint = node.slug !== undefined && node.slug !== pathLeaf(node.path)
302
+ ? unsafe(html `<span class="tree-row__public-url"
303
+ title="public URL on the host site">/blog/${node.slug}</span>`)
304
+ : unsafe('');
305
+ // The HTML attribute name `data-slug` is preserved for backward
306
+ // compatibility with the client-side selectors; it now carries the
307
+ // fs path. A future cleanup can rename the attribute to data-path.
308
+ return unsafe(html `
309
+ <a class="${classes}" href="${nodeHref}" style="--depth: ${depth}"
310
+ data-slug="${node.path}" aria-current="${isSelected ? 'true' : 'false'}">
311
+ <div class="tree-row__main">
312
+ ${nodeIcon(node)}
313
+ <span class="tree-row__title">${node.title}</span>
314
+ <span class="tree-row__slug">${nodeFilePathHint(node, contentDir)}</span>
315
+ ${publicUrlHint}
316
+ </div>
317
+ <span class="tree-row__lane">
318
+ <span class="lane-dot lane-dot--${lane}"></span>
319
+ ${node.lane ? node.lane.toLowerCase() : 'untracked'}
320
+ </span>
321
+ <span class="tree-row__meta">${renderTreeRowMeta(node)}</span>
322
+ <span class="tree-row__actions">${renderTreeRowActions(node, site)}</span>
323
+ </a>`);
324
+ }
325
+ function renderTree(site, project, selectedPath, contentDir) {
326
+ const flat = flattenForRender(project.root);
327
+ return unsafe(html `
328
+ <div class="tree" role="tree">
329
+ ${flat.map((f) => renderTreeRow(site, project, f, selectedPath, contentDir))}
330
+ </div>`);
331
+ }
332
+ // ---------------------------------------------------------------------------
333
+ // Project drilldown render entry
334
+ // ---------------------------------------------------------------------------
335
+ export async function renderContentProject(ctx, site, projectSlug, selectedPath, getIndex) {
336
+ if (!(site in ctx.config.sites)) {
337
+ return { status: 404, html: renderNotFound(`unknown site: ${site}`) };
338
+ }
339
+ const sp = loadProjectsForSite(ctx, site, getIndex);
340
+ const project = sp.projects.find((p) => p.rootSlug === projectSlug);
341
+ if (!project) {
342
+ return {
343
+ status: 404,
344
+ html: renderNotFound(`unknown project: ${projectSlug} on ${site}`),
345
+ };
346
+ }
347
+ const selectedNode = selectedPath ? findNode(project, selectedPath) : null;
348
+ // Phase 19c+: hand the per-request content index to the detail
349
+ // panel. The panel uses it for id-driven scrapbook lookups so
350
+ // writingcontrol-shape entries find their items at the actual file
351
+ // location. Falls through to slug/path resolution for unbound nodes.
352
+ const detailIndex = getIndex ? getIndex(site) : undefined;
353
+ const detailBlock = selectedNode
354
+ ? await renderNodeDetail(ctx, site, selectedNode, detailIndex)
355
+ : renderEmptyDetail();
356
+ const body = html `
357
+ ${renderEditorialFolio('content', `drilldown · ${project.rootSlug}`)}
358
+ <main class="content-page">
359
+ <section class="drilldown">
360
+ <div class="drilldown__tree">
361
+ ${renderTreeBreadcrumb(site, project, selectedNode?.path ?? null)}
362
+ <header class="tree-head">
363
+ <h2 class="tree-head__title">${project.title}</h2>
364
+ <span class="tree-head__count">
365
+ ${project.totalNodes} NODES · ${project.maxDepth} LEVELS DEEP
366
+ </span>
367
+ </header>
368
+ ${renderTree(site, project, selectedNode?.path ?? null, resolveContentDir(ctx.projectRoot, ctx.config, site))}
369
+ </div>
370
+ ${detailBlock}
371
+ </section>
372
+ </main>`;
373
+ return {
374
+ status: 200,
375
+ html: layout({
376
+ title: `${project.title} · content — deskwork`,
377
+ cssHrefs: [
378
+ '/static/css/editorial-review.css',
379
+ '/static/css/editorial-nav.css',
380
+ '/static/css/content.css',
381
+ '/static/css/scrap-row.css',
382
+ '/static/css/blog-figure.css',
383
+ ],
384
+ bodyAttrs: 'data-review-ui="studio"',
385
+ bodyHtml: body,
386
+ // #29: scrap rows in the detail panel have image thumbnails;
387
+ // wire up the lightbox.
388
+ scriptModules: ['/static/dist/content-view-client.js'],
389
+ }),
390
+ };
391
+ }
392
+ function renderNotFound(message) {
393
+ const body = html `
394
+ ${renderEditorialFolio('content', 'not found')}
395
+ <main class="content-page">
396
+ <section class="content-error">
397
+ <h1>Not found</h1>
398
+ <p>${message}</p>
399
+ <p><a href="/dev/content">← back to the content view</a></p>
400
+ </section>
401
+ </main>`;
402
+ return layout({
403
+ title: 'Not found — deskwork',
404
+ cssHrefs: [
405
+ '/static/css/editorial-review.css',
406
+ '/static/css/editorial-nav.css',
407
+ '/static/css/content.css',
408
+ ],
409
+ bodyAttrs: 'data-review-ui="studio"',
410
+ bodyHtml: body,
411
+ scriptModules: [],
412
+ });
413
+ }
414
+ //# sourceMappingURL=content.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/pages/content.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Studio dashboard page — `/dev/editorial-studio`.
3
+ *
4
+ * Ported from audiocontrol.org's `editorial-studio.astro`. Reads each
5
+ * site's calendar + the review pipeline + the report, then renders the
6
+ * five-stage editorial calendar with site filtering, the shortform
7
+ * coverage matrix for Published blog entries, awaiting-press / recent-
8
+ * proofs panels, and a voice-drift signal sidebar.
9
+ *
10
+ * The audiocontrol original was tightly coupled to two hardcoded sites
11
+ * (`'audiocontrol' | 'editorialcontrol'`) and the `feature-image`
12
+ * pipeline. Both go away here:
13
+ *
14
+ * - Sites come from `ctx.config.sites`. The two-letter site label
15
+ * (was `'AC' | 'EC'`) is the first 2 letters uppercased.
16
+ * - The feature-image pipeline isn't part of deskwork core yet; the
17
+ * "feature image →" / "✓ baked" affordances are dropped. The
18
+ * scrapbook chip stays — `@deskwork/core/scrapbook` provides
19
+ * `countScrapbook`.
20
+ */
21
+ import type { ContentIndex } from '@deskwork/core/content-index';
22
+ import type { StudioContext } from '../routes/api.ts';
23
+ /**
24
+ * Per-request content-index getter. The route layer wires this to the
25
+ * Hono context's memoized cache so a single dashboard render only
26
+ * builds the index once per site even though many entries call
27
+ * `entryBodyStateOf`. When omitted (e.g., a non-route caller), the
28
+ * dashboard falls back to the slug-template path.
29
+ */
30
+ export type DashboardIndexGetter = (site: string) => ContentIndex;
31
+ export declare function renderDashboard(ctx: StudioContext, getIndex?: DashboardIndexGetter): string;
32
+ //# sourceMappingURL=dashboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../src/pages/dashboard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AA0BH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAKtD;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,YAAY,CAAC;AAy2BlE,wBAAgB,eAAe,CAC7B,GAAG,EAAE,aAAa,EAClB,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,MAAM,CAwDR"}