@deskwork/studio 0.10.1 → 0.11.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.
- package/dist/lib/editorial-skills-catalogue.d.ts +15 -1
- package/dist/lib/editorial-skills-catalogue.d.ts.map +1 -1
- package/dist/lib/editorial-skills-catalogue.js +66 -59
- package/dist/lib/editorial-skills-catalogue.js.map +1 -1
- package/dist/lib/entry-resolver.d.ts +22 -0
- package/dist/lib/entry-resolver.d.ts.map +1 -0
- package/dist/lib/entry-resolver.js +42 -0
- package/dist/lib/entry-resolver.js.map +1 -0
- package/dist/lib/stage-affordances.d.ts +31 -0
- package/dist/lib/stage-affordances.d.ts.map +1 -0
- package/dist/lib/stage-affordances.js +37 -0
- package/dist/lib/stage-affordances.js.map +1 -0
- package/dist/pages/dashboard/affordances.d.ts +41 -0
- package/dist/pages/dashboard/affordances.d.ts.map +1 -0
- package/dist/pages/dashboard/affordances.js +87 -0
- package/dist/pages/dashboard/affordances.js.map +1 -0
- package/dist/pages/dashboard/affordances.ts +95 -0
- package/dist/pages/dashboard/data.d.ts +24 -0
- package/dist/pages/dashboard/data.d.ts.map +1 -0
- package/dist/pages/dashboard/data.js +49 -0
- package/dist/pages/dashboard/data.js.map +1 -0
- package/dist/pages/dashboard/data.ts +56 -0
- package/dist/pages/dashboard/header.d.ts +13 -0
- package/dist/pages/dashboard/header.d.ts.map +1 -0
- package/dist/pages/dashboard/header.js +70 -0
- package/dist/pages/dashboard/header.js.map +1 -0
- package/dist/pages/dashboard/header.ts +80 -0
- package/dist/pages/dashboard/section.d.ts +37 -0
- package/dist/pages/dashboard/section.d.ts.map +1 -0
- package/dist/pages/dashboard/section.js +117 -0
- package/dist/pages/dashboard/section.js.map +1 -0
- package/dist/pages/dashboard/section.ts +132 -0
- package/dist/pages/dashboard.d.ts +30 -21
- package/dist/pages/dashboard.d.ts.map +1 -1
- package/dist/pages/dashboard.js +34 -799
- package/dist/pages/dashboard.js.map +1 -1
- package/dist/pages/dashboard.ts +44 -980
- package/dist/pages/entry-review.d.ts +25 -0
- package/dist/pages/entry-review.d.ts.map +1 -0
- package/dist/pages/entry-review.js +148 -0
- package/dist/pages/entry-review.js.map +1 -0
- package/dist/pages/entry-review.ts +185 -0
- package/dist/pages/help.d.ts +10 -5
- package/dist/pages/help.d.ts.map +1 -1
- package/dist/pages/help.js +113 -99
- package/dist/pages/help.js.map +1 -1
- package/dist/pages/help.ts +113 -99
- package/dist/pages/index.d.ts +13 -1
- package/dist/pages/index.d.ts.map +1 -1
- package/dist/pages/index.js +39 -24
- package/dist/pages/index.js.map +1 -1
- package/dist/pages/index.ts +43 -27
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +23 -3
- package/dist/server.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entry-uuid keyed review surface — `/dev/editorial-review/entry/:entryId`.
|
|
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.
|
|
18
|
+
*/
|
|
19
|
+
interface EntryReviewResult {
|
|
20
|
+
status: 200 | 404;
|
|
21
|
+
html: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function renderEntryReviewPage(projectRoot: string, entryId: string): Promise<EntryReviewResult>;
|
|
24
|
+
export {};
|
|
25
|
+
//# sourceMappingURL=entry-review.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry-review.d.ts","sourceRoot":"","sources":["../../src/pages/entry-review.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AA+IH,UAAU,iBAAiB;IACzB,MAAM,EAAE,GAAG,GAAG,GAAG,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,iBAAiB,CAAC,CAgB5B"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entry-uuid keyed review surface — `/dev/editorial-review/entry/:entryId`.
|
|
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.
|
|
18
|
+
*/
|
|
19
|
+
import { resolveEntry } from "../lib/entry-resolver.js";
|
|
20
|
+
import { getAffordances } from "../lib/stage-affordances.js";
|
|
21
|
+
import { html, unsafe } from "./html.js";
|
|
22
|
+
import { layout } from "./layout.js";
|
|
23
|
+
const STAGE_PICKER_OPTIONS = [
|
|
24
|
+
'Ideas',
|
|
25
|
+
'Planned',
|
|
26
|
+
'Outlining',
|
|
27
|
+
'Drafting',
|
|
28
|
+
'Final',
|
|
29
|
+
];
|
|
30
|
+
const CONTROL_LABELS = {
|
|
31
|
+
save: 'Save',
|
|
32
|
+
iterate: 'Iterate',
|
|
33
|
+
approve: 'Approve',
|
|
34
|
+
reject: 'Reject',
|
|
35
|
+
'historical-stage-dropdown': 'Historical stage',
|
|
36
|
+
'view-only': 'Read-only',
|
|
37
|
+
'fork-placeholder': 'Fork (coming)',
|
|
38
|
+
'induct-to': 'Induct to',
|
|
39
|
+
};
|
|
40
|
+
function renderControl(control, entry) {
|
|
41
|
+
const label = CONTROL_LABELS[control] ?? control;
|
|
42
|
+
if (control === 'induct-to') {
|
|
43
|
+
const options = STAGE_PICKER_OPTIONS.map((s) => unsafe(html `<option value="${s}">${s}</option>`));
|
|
44
|
+
return unsafe(html `
|
|
45
|
+
<label class="er-entry-control er-entry-control--induct">
|
|
46
|
+
<span class="er-entry-control-label">${label}</span>
|
|
47
|
+
<select name="induct-to" data-entry-uuid="${entry.uuid}">
|
|
48
|
+
${options}
|
|
49
|
+
</select>
|
|
50
|
+
</label>`);
|
|
51
|
+
}
|
|
52
|
+
if (control === 'historical-stage-dropdown') {
|
|
53
|
+
const stages = Object.keys(entry.iterationByStage);
|
|
54
|
+
if (stages.length === 0) {
|
|
55
|
+
return unsafe('');
|
|
56
|
+
}
|
|
57
|
+
const options = stages.map((s) => unsafe(html `<option value="${s}">${s}</option>`));
|
|
58
|
+
return unsafe(html `
|
|
59
|
+
<label class="er-entry-control er-entry-control--history">
|
|
60
|
+
<span class="er-entry-control-label">${label}</span>
|
|
61
|
+
<select name="history-stage" data-entry-uuid="${entry.uuid}">
|
|
62
|
+
${options}
|
|
63
|
+
</select>
|
|
64
|
+
</label>`);
|
|
65
|
+
}
|
|
66
|
+
if (control === 'view-only') {
|
|
67
|
+
return unsafe(html `<span class="er-entry-control er-entry-control--readonly">${label}</span>`);
|
|
68
|
+
}
|
|
69
|
+
if (control === 'fork-placeholder') {
|
|
70
|
+
return unsafe(html `<button class="er-entry-control er-entry-control--button" type="button" disabled data-control="fork">${label}</button>`);
|
|
71
|
+
}
|
|
72
|
+
return unsafe(html `<button class="er-entry-control er-entry-control--button" type="button" data-control="${control}" data-entry-uuid="${entry.uuid}">${label}</button>`);
|
|
73
|
+
}
|
|
74
|
+
function renderControls(entry, affordances) {
|
|
75
|
+
const buttons = affordances.controls.map((c) => renderControl(c, entry));
|
|
76
|
+
const className = affordances.mutable
|
|
77
|
+
? 'er-entry-controls er-entry-controls--mutable'
|
|
78
|
+
: 'er-entry-controls er-entry-controls--readonly';
|
|
79
|
+
return unsafe(html `<nav class="${className}" aria-label="Entry controls">${buttons}</nav>`);
|
|
80
|
+
}
|
|
81
|
+
function renderArtifact(body, mutable) {
|
|
82
|
+
if (mutable) {
|
|
83
|
+
return unsafe(html `
|
|
84
|
+
<textarea class="er-entry-body" name="body" rows="24" data-mutable="true">${body}</textarea>`);
|
|
85
|
+
}
|
|
86
|
+
return unsafe(html `
|
|
87
|
+
<pre class="er-entry-body er-entry-body--readonly" data-mutable="false">${body}</pre>`);
|
|
88
|
+
}
|
|
89
|
+
function renderEntryReview(entry, artifactBody, artifactPath, affordances) {
|
|
90
|
+
const stageBadge = unsafe(html `<span class="er-entry-stage" data-stage="${entry.currentStage}">${entry.currentStage}</span>`);
|
|
91
|
+
const priorBadge = entry.priorStage
|
|
92
|
+
? unsafe(html `<span class="er-entry-prior-stage">paused from ${entry.priorStage}</span>`)
|
|
93
|
+
: '';
|
|
94
|
+
const body = html `
|
|
95
|
+
<main class="er-entry-shell" data-entry-uuid="${entry.uuid}">
|
|
96
|
+
<header class="er-entry-head">
|
|
97
|
+
<p class="er-entry-kicker">Editorial Review · entry</p>
|
|
98
|
+
<h1 class="er-entry-title">${entry.title}</h1>
|
|
99
|
+
<p class="er-entry-meta">
|
|
100
|
+
${stageBadge}
|
|
101
|
+
${priorBadge}
|
|
102
|
+
<code class="er-entry-uuid">${entry.uuid}</code>
|
|
103
|
+
</p>
|
|
104
|
+
<p class="er-entry-artifact-path"><code>${artifactPath}</code></p>
|
|
105
|
+
</header>
|
|
106
|
+
${renderControls(entry, affordances)}
|
|
107
|
+
<section class="er-entry-artifact">
|
|
108
|
+
${renderArtifact(artifactBody, affordances.mutable)}
|
|
109
|
+
</section>
|
|
110
|
+
</main>`;
|
|
111
|
+
return layout({
|
|
112
|
+
title: `${entry.title} — entry review — dev`,
|
|
113
|
+
cssHrefs: ['/static/css/editorial-review.css'],
|
|
114
|
+
bodyAttrs: 'data-review-ui="entry-review"',
|
|
115
|
+
bodyHtml: body,
|
|
116
|
+
scriptModules: [],
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
function renderNotFound(entryId, reason) {
|
|
120
|
+
const body = html `
|
|
121
|
+
<main class="er-entry-shell er-entry-shell--missing">
|
|
122
|
+
<h1>Entry not found</h1>
|
|
123
|
+
<p>No sidecar matched <code>${entryId}</code>.</p>
|
|
124
|
+
<p class="er-entry-detail">${reason}</p>
|
|
125
|
+
<p><a href="/dev/editorial-studio">Back to the studio</a></p>
|
|
126
|
+
</main>`;
|
|
127
|
+
return layout({
|
|
128
|
+
title: 'Entry not found — dev',
|
|
129
|
+
cssHrefs: ['/static/css/editorial-review.css'],
|
|
130
|
+
bodyAttrs: 'data-review-ui="entry-review-missing"',
|
|
131
|
+
bodyHtml: body,
|
|
132
|
+
scriptModules: [],
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
export async function renderEntryReviewPage(projectRoot, entryId) {
|
|
136
|
+
let resolved;
|
|
137
|
+
try {
|
|
138
|
+
resolved = await resolveEntry(projectRoot, entryId);
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
142
|
+
return { status: 404, html: renderNotFound(entryId, reason) };
|
|
143
|
+
}
|
|
144
|
+
const affordances = getAffordances(resolved.entry);
|
|
145
|
+
const html = renderEntryReview(resolved.entry, resolved.artifactBody, resolved.artifactPath, affordances);
|
|
146
|
+
return { status: 200, html };
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=entry-review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry-review.js","sourceRoot":"","sources":["../../src/pages/entry-review.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entry-uuid keyed review surface — `/dev/editorial-review/entry/:entryId`.
|
|
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.
|
|
18
|
+
*/
|
|
19
|
+
|
|
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: [],
|
|
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
|
+
}
|
package/dist/pages/help.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* The Compositor's Manual — `/dev/editorial-help`.
|
|
3
3
|
*
|
|
4
4
|
* Static (read-only) operator manual. Renders six sections:
|
|
5
|
-
* I — the working model (
|
|
5
|
+
* I — the working model (eight-stage pipeline + universal verbs)
|
|
6
6
|
* II — three tracks (longform / shortform / distribution)
|
|
7
7
|
* III — the skill catalogue (specimen grid)
|
|
8
8
|
* IV — the studio surfaces, described
|
|
@@ -14,10 +14,15 @@
|
|
|
14
14
|
* `editorial-skills-catalogue.ts` so a single edit shows up here, in
|
|
15
15
|
* any future docs generator, and in any CLI inventory.
|
|
16
16
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
17
|
+
* Pipeline-redesign vocabulary (Phase 6, Task 37): the longform model
|
|
18
|
+
* is the entry-centric eight-stage pipeline (Ideas → Planned →
|
|
19
|
+
* Outlining → Drafting → Final → Published; Blocked / Cancelled
|
|
20
|
+
* off-pipeline) operated by universal verbs (`/deskwork:add`,
|
|
21
|
+
* `/deskwork:iterate`, `/deskwork:approve`, `/deskwork:publish`,
|
|
22
|
+
* `/deskwork:block`, `/deskwork:cancel`, `/deskwork:induct`). The
|
|
23
|
+
* stage-named skills of the prior model (`plan`, `outline`, `draft`,
|
|
24
|
+
* `pause`, `resume`, `review-start`, `review-cancel`) are retired.
|
|
25
|
+
* Shortform + distribution still use the workflow-object model.
|
|
21
26
|
*/
|
|
22
27
|
import type { StudioContext } from '../routes/api.ts';
|
|
23
28
|
export declare function renderHelpPage(ctx: StudioContext): string;
|
package/dist/pages/help.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../src/pages/help.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../src/pages/help.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAOH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AA0ZtD,wBAAgB,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CA4BzD"}
|