@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.
- package/dist/build-client-assets.d.ts +51 -0
- package/dist/build-client-assets.d.ts.map +1 -0
- package/dist/build-client-assets.js +341 -0
- package/dist/build-client-assets.js.map +1 -0
- package/dist/components/scrapbook-item.d.ts +108 -0
- package/dist/components/scrapbook-item.d.ts.map +1 -0
- package/dist/components/scrapbook-item.js +205 -0
- package/dist/components/scrapbook-item.js.map +1 -0
- package/dist/lib/editorial-skills-catalogue.d.ts +33 -0
- package/dist/lib/editorial-skills-catalogue.d.ts.map +1 -0
- package/dist/lib/editorial-skills-catalogue.js +211 -0
- package/dist/lib/editorial-skills-catalogue.js.map +1 -0
- package/dist/lib/override-render.d.ts +41 -0
- package/dist/lib/override-render.d.ts.map +1 -0
- package/dist/lib/override-render.js +80 -0
- package/dist/lib/override-render.js.map +1 -0
- package/dist/listen.d.ts +78 -0
- package/dist/listen.d.ts.map +1 -0
- package/dist/listen.js +155 -0
- package/dist/listen.js.map +1 -0
- package/dist/pages/chrome.d.ts +26 -0
- package/dist/pages/chrome.d.ts.map +1 -0
- package/dist/pages/chrome.js +50 -0
- package/dist/pages/chrome.js.map +1 -0
- package/dist/pages/content-detail.d.ts +14 -0
- package/dist/pages/content-detail.d.ts.map +1 -0
- package/dist/pages/content-detail.js +279 -0
- package/dist/pages/content-detail.js.map +1 -0
- package/dist/pages/content.d.ts +39 -0
- package/dist/pages/content.d.ts.map +1 -0
- package/dist/pages/content.js +414 -0
- package/dist/pages/content.js.map +1 -0
- package/dist/pages/dashboard.d.ts +32 -0
- package/dist/pages/dashboard.d.ts.map +1 -0
- package/dist/pages/dashboard.js +803 -0
- package/dist/pages/dashboard.js.map +1 -0
- package/dist/pages/help.d.ts +24 -0
- package/dist/pages/help.d.ts.map +1 -0
- package/dist/pages/help.js +433 -0
- package/dist/pages/help.js.map +1 -0
- package/dist/pages/html.d.ts +35 -0
- package/dist/pages/html.d.ts.map +1 -0
- package/dist/pages/html.js +73 -0
- package/dist/pages/html.js.map +1 -0
- package/dist/pages/index.d.ts +21 -0
- package/dist/pages/index.d.ts.map +1 -0
- package/dist/pages/index.js +174 -0
- package/dist/pages/index.js.map +1 -0
- package/dist/pages/layout.d.ts +33 -0
- package/dist/pages/layout.d.ts.map +1 -0
- package/dist/pages/layout.js +50 -0
- package/dist/pages/layout.js.map +1 -0
- package/dist/pages/review-scrapbook-drawer.d.ts +20 -0
- package/dist/pages/review-scrapbook-drawer.d.ts.map +1 -0
- package/dist/pages/review-scrapbook-drawer.js +98 -0
- package/dist/pages/review-scrapbook-drawer.js.map +1 -0
- package/dist/pages/review.d.ts +68 -0
- package/dist/pages/review.d.ts.map +1 -0
- package/dist/pages/review.js +434 -0
- package/dist/pages/review.js.map +1 -0
- package/dist/pages/scrapbook.d.ts +21 -0
- package/dist/pages/scrapbook.d.ts.map +1 -0
- package/dist/pages/scrapbook.js +250 -0
- package/dist/pages/scrapbook.js.map +1 -0
- package/dist/pages/shortform.d.ts +17 -0
- package/dist/pages/shortform.d.ts.map +1 -0
- package/dist/pages/shortform.js +142 -0
- package/dist/pages/shortform.js.map +1 -0
- package/dist/request-context.d.ts +52 -0
- package/dist/request-context.d.ts.map +1 -0
- package/dist/request-context.js +84 -0
- package/dist/request-context.js.map +1 -0
- package/dist/routes/api.d.ts +36 -0
- package/dist/routes/api.d.ts.map +1 -0
- package/dist/routes/api.js +175 -0
- package/dist/routes/api.js.map +1 -0
- package/dist/routes/scrapbook-file.d.ts +19 -0
- package/dist/routes/scrapbook-file.d.ts.map +1 -0
- package/dist/routes/scrapbook-file.js +77 -0
- package/dist/routes/scrapbook-file.js.map +1 -0
- package/dist/routes/scrapbook-mutations.d.ts +33 -0
- package/dist/routes/scrapbook-mutations.d.ts.map +1 -0
- package/dist/routes/scrapbook-mutations.js +310 -0
- package/dist/routes/scrapbook-mutations.js.map +1 -0
- package/dist/server.d.ts +52 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +581 -0
- package/dist/server.js.map +1 -0
- package/dist/tailscale.d.ts +63 -0
- package/dist/tailscale.d.ts.map +1 -0
- package/dist/tailscale.js +118 -0
- package/dist/tailscale.js.map +1 -0
- 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"}
|