@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,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared scrapbook-item renderer.
|
|
3
|
+
*
|
|
4
|
+
* Three studio surfaces consume this module:
|
|
5
|
+
*
|
|
6
|
+
* 1. Standalone scrapbook viewer — `pages/scrapbook.ts`
|
|
7
|
+
* 2. Review-page drawer (Phase 16c) — `pages/review.ts`
|
|
8
|
+
* 3. Bird's-eye content view detail panel (Phase 16d) — `pages/content.ts`
|
|
9
|
+
*
|
|
10
|
+
* Goal: the operator sees consistent in-browser preview behavior wherever
|
|
11
|
+
* a scrapbook item appears. The standalone viewer keeps its richer
|
|
12
|
+
* disclosure / edit / rename / delete affordances; the read-only views
|
|
13
|
+
* (review drawer, content-view detail panel) reuse the same kind chip,
|
|
14
|
+
* filename, size, mtime, and the same in-browser preview rules:
|
|
15
|
+
*
|
|
16
|
+
* - Image kinds (`png`, `jpg`, `jpeg`, `webp`, `gif`, `svg`)
|
|
17
|
+
* render an inline thumbnail with a small overlay action to view
|
|
18
|
+
* full-size; clicking opens the served file in a new tab. Both
|
|
19
|
+
* image and PDF surfaces use a read-only binary endpoint
|
|
20
|
+
* (`GET /api/dev/scrapbook-file`) that the studio adds for these
|
|
21
|
+
* read-only views — distinct from the (not-yet-ported) full
|
|
22
|
+
* scrapbook CRUD API.
|
|
23
|
+
* - PDF — embedded via `<iframe>` using the same binary endpoint. The
|
|
24
|
+
* browser renders it natively.
|
|
25
|
+
* - Plain text / JSON — inline-truncated `<pre>` preview. Operators
|
|
26
|
+
* can read the first ~10 lines without leaving the page; a "view
|
|
27
|
+
* full" link opens the standalone viewer where the full file
|
|
28
|
+
* is mounted.
|
|
29
|
+
* - Markdown — kept as a kind-only row. Markdown is the editable
|
|
30
|
+
* surface — operators jump to the standalone viewer to edit. Showing
|
|
31
|
+
* a raw markdown preview here would either duplicate the editor or
|
|
32
|
+
* misrepresent what double-click does on this surface.
|
|
33
|
+
* - Anything else — kind chip + a download link. The browser can't
|
|
34
|
+
* render it, so make that explicit.
|
|
35
|
+
*
|
|
36
|
+
* Inline previews for text + JSON come from the server side: the
|
|
37
|
+
* standalone viewer only loads body content on disclosure (lazy), but
|
|
38
|
+
* the read-only renderers want the preview embedded at server-render
|
|
39
|
+
* time so operators don't see a flash. Callers pass an
|
|
40
|
+
* `inlinePreviewLoader` that knows how to read the first N bytes of
|
|
41
|
+
* the file and return a string; this module composes the result into
|
|
42
|
+
* the row HTML.
|
|
43
|
+
*/
|
|
44
|
+
import { formatRelativeTime, formatSize, } from '@deskwork/core/scrapbook';
|
|
45
|
+
import { html, unsafe } from "../pages/html.js";
|
|
46
|
+
const DEFAULT_PREVIEW_BYTES = 800;
|
|
47
|
+
const TEXT_PREVIEW_LINES = 8;
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// URL helpers
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
/**
|
|
52
|
+
* Build the URL to fetch a scrapbook file's raw bytes from the
|
|
53
|
+
* read-only binary endpoint. The endpoint is read-only by design —
|
|
54
|
+
* Phase 16's image / PDF previews need a stable URL, but full
|
|
55
|
+
* scrapbook CRUD remains in the standalone viewer's surface.
|
|
56
|
+
*/
|
|
57
|
+
export function scrapbookFileUrl(address, filename, opts = {}) {
|
|
58
|
+
const params = new URLSearchParams({
|
|
59
|
+
site: address.site,
|
|
60
|
+
path: address.path,
|
|
61
|
+
name: filename,
|
|
62
|
+
});
|
|
63
|
+
if (opts.secret)
|
|
64
|
+
params.set('secret', '1');
|
|
65
|
+
return `/api/dev/scrapbook-file?${params.toString()}`;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Build the URL to the standalone scrapbook viewer for an address —
|
|
69
|
+
* the operator's "open scrapbook" jumping-off point.
|
|
70
|
+
*/
|
|
71
|
+
export function scrapbookViewerUrl(address) {
|
|
72
|
+
// The path is already kebab-case + slash-separated; encodeURI keeps
|
|
73
|
+
// the slashes literal while escaping anything else (defensive).
|
|
74
|
+
return `/dev/scrapbook/${address.site}/${encodeURI(address.path)}`;
|
|
75
|
+
}
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
// Per-kind preview detection
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
const IMAGE_EXTENSIONS = new Set([
|
|
80
|
+
'.png',
|
|
81
|
+
'.jpg',
|
|
82
|
+
'.jpeg',
|
|
83
|
+
'.gif',
|
|
84
|
+
'.webp',
|
|
85
|
+
'.svg',
|
|
86
|
+
]);
|
|
87
|
+
const PDF_EXTENSIONS = new Set(['.pdf']);
|
|
88
|
+
function lowerExt(filename) {
|
|
89
|
+
const dot = filename.lastIndexOf('.');
|
|
90
|
+
return dot < 0 ? '' : filename.slice(dot).toLowerCase();
|
|
91
|
+
}
|
|
92
|
+
function isImageFilename(filename) {
|
|
93
|
+
return IMAGE_EXTENSIONS.has(lowerExt(filename));
|
|
94
|
+
}
|
|
95
|
+
function isPdfFilename(filename) {
|
|
96
|
+
return PDF_EXTENSIONS.has(lowerExt(filename));
|
|
97
|
+
}
|
|
98
|
+
function kindLabel(kind) {
|
|
99
|
+
return kind === 'other' ? '·' : kind.toUpperCase();
|
|
100
|
+
}
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
// Inline preview helpers
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
function truncateForInline(raw, lines) {
|
|
105
|
+
const split = raw.split('\n').slice(0, lines);
|
|
106
|
+
// Append an ellipsis line when truncation actually happened. The
|
|
107
|
+
// visual fade-out gradient handles the polish; the ellipsis line
|
|
108
|
+
// makes the truncation explicit even with CSS off.
|
|
109
|
+
const truncated = raw.split('\n').length > lines || raw.length > 1024;
|
|
110
|
+
return truncated ? `${split.join('\n')}\n…` : split.join('\n');
|
|
111
|
+
}
|
|
112
|
+
function loadInlineText(filename, options) {
|
|
113
|
+
const loader = options.inlinePreviewLoader;
|
|
114
|
+
if (!loader)
|
|
115
|
+
return null;
|
|
116
|
+
const max = options.inlinePreviewMaxBytes ?? DEFAULT_PREVIEW_BYTES;
|
|
117
|
+
const raw = loader(filename, max);
|
|
118
|
+
if (raw === null)
|
|
119
|
+
return null;
|
|
120
|
+
return truncateForInline(raw, TEXT_PREVIEW_LINES);
|
|
121
|
+
}
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
// Read-only row renderer
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
/**
|
|
126
|
+
* Render a single scrapbook item as a read-only row for the review
|
|
127
|
+
* drawer or the content-view detail panel. Returns the row HTML wrapped
|
|
128
|
+
* in `unsafe(...)`.
|
|
129
|
+
*
|
|
130
|
+
* Visual posture: kind chip + filename + size + mtime, with an inline
|
|
131
|
+
* preview block beneath the row for kinds the browser can render in
|
|
132
|
+
* place (image thumbnail, text/JSON truncated, PDF embed). Markdown +
|
|
133
|
+
* unrenderable kinds keep a single-line row.
|
|
134
|
+
*/
|
|
135
|
+
export function renderReadOnlyScrapbookRow(address, item, opts = {}) {
|
|
136
|
+
const fileUrl = scrapbookFileUrl(address, item.name);
|
|
137
|
+
const sizeText = formatSize(item.size);
|
|
138
|
+
const mtimeText = formatRelativeTime(item.mtime);
|
|
139
|
+
if (isImageFilename(item.name)) {
|
|
140
|
+
return unsafe(html `
|
|
141
|
+
<div class="scrap scrap--img" data-kind="img" data-filename="${item.name}">
|
|
142
|
+
<a class="scrap__thumb-link" href="${fileUrl}" target="_blank" rel="noopener"
|
|
143
|
+
aria-label="Open ${item.name} in a new tab">
|
|
144
|
+
<img class="scrap__thumb" loading="lazy" alt="" src="${fileUrl}">
|
|
145
|
+
</a>
|
|
146
|
+
<span class="scrap__name">${item.name}</span>
|
|
147
|
+
<span class="scrap__size">${sizeText}</span>
|
|
148
|
+
<span class="scrap__mtime">${mtimeText}</span>
|
|
149
|
+
</div>`);
|
|
150
|
+
}
|
|
151
|
+
if (isPdfFilename(item.name)) {
|
|
152
|
+
return unsafe(html `
|
|
153
|
+
<div class="scrap scrap--pdf" data-kind="pdf" data-filename="${item.name}">
|
|
154
|
+
<span class="scrap__kind">PDF</span>
|
|
155
|
+
<span class="scrap__name">
|
|
156
|
+
<a class="scrap__name-link" href="${fileUrl}" target="_blank" rel="noopener">${item.name}</a>
|
|
157
|
+
</span>
|
|
158
|
+
<span class="scrap__size">${sizeText}</span>
|
|
159
|
+
<span class="scrap__mtime">${mtimeText}</span>
|
|
160
|
+
<iframe class="scrap__pdf-frame" src="${fileUrl}#view=FitH" title="${item.name}"
|
|
161
|
+
aria-label="PDF preview of ${item.name}"></iframe>
|
|
162
|
+
</div>`);
|
|
163
|
+
}
|
|
164
|
+
if (item.kind === 'txt' || item.kind === 'json') {
|
|
165
|
+
const inline = loadInlineText(item.name, opts);
|
|
166
|
+
if (inline !== null) {
|
|
167
|
+
return unsafe(html `
|
|
168
|
+
<div class="scrap scrap--with-preview" data-kind="${item.kind}" data-filename="${item.name}">
|
|
169
|
+
<span class="scrap__kind">${kindLabel(item.kind)}</span>
|
|
170
|
+
<span class="scrap__name">
|
|
171
|
+
<a class="scrap__name-link" href="${fileUrl}" target="_blank" rel="noopener">${item.name}</a>
|
|
172
|
+
</span>
|
|
173
|
+
<span class="scrap__size">${sizeText}</span>
|
|
174
|
+
<span class="scrap__mtime">${mtimeText}</span>
|
|
175
|
+
<pre class="scrap__inline-preview">${inline}</pre>
|
|
176
|
+
</div>`);
|
|
177
|
+
}
|
|
178
|
+
// No loader provided — fall through to the single-line row.
|
|
179
|
+
}
|
|
180
|
+
// Default row — kind chip + filename + size + mtime. Markdown lands
|
|
181
|
+
// here so operators jump to the standalone viewer to edit.
|
|
182
|
+
const linkAttr = item.kind === 'md' ? '' : ' target="_blank" rel="noopener"';
|
|
183
|
+
const href = item.kind === 'md' ? scrapbookViewerUrl(address) : fileUrl;
|
|
184
|
+
return unsafe(html `
|
|
185
|
+
<div class="scrap" data-kind="${item.kind}" data-filename="${item.name}">
|
|
186
|
+
<span class="scrap__kind">${kindLabel(item.kind)}</span>
|
|
187
|
+
<span class="scrap__name">
|
|
188
|
+
<a class="scrap__name-link" href="${href}"${unsafe(linkAttr)}>${item.name}</a>
|
|
189
|
+
</span>
|
|
190
|
+
<span class="scrap__size">${sizeText}</span>
|
|
191
|
+
<span class="scrap__mtime">${mtimeText}</span>
|
|
192
|
+
</div>`);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Render an empty-state row for a scrapbook drawer / panel that has
|
|
196
|
+
* no items. Shown faded so the operator still sees the section exists
|
|
197
|
+
* for this node.
|
|
198
|
+
*/
|
|
199
|
+
export function renderEmptyScrapbookRow() {
|
|
200
|
+
return unsafe(html `
|
|
201
|
+
<div class="scrap scrap--empty" data-state="empty">
|
|
202
|
+
<span class="scrap__empty-text">no scrapbook items</span>
|
|
203
|
+
</div>`);
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=scrapbook-item.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scrapbook-item.js","sourceRoot":"","sources":["../../src/components/scrapbook-item.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Editorial skill catalogue — the source of truth for the specimen grid
|
|
3
|
+
* on /dev/editorial-help and any future CLI or doc generator that needs
|
|
4
|
+
* the same inventory.
|
|
5
|
+
*
|
|
6
|
+
* Each skill carries four fields: `slug`, `kind` (see below), `desc`,
|
|
7
|
+
* `when`, `changes`, and an optional `flags` string. The `kind` drives
|
|
8
|
+
* the corner stamp on the help-page specimen card:
|
|
9
|
+
*
|
|
10
|
+
* - cognitive → red pencil — Claude Code drafts/revises prose
|
|
11
|
+
* - mechanical → proof blue — state transition, disk write, no writing
|
|
12
|
+
* - readonly → faded ink — reports, audits, listings
|
|
13
|
+
* - voice → stamp purple — called by other skills, not directly
|
|
14
|
+
*/
|
|
15
|
+
export type SkillKind = 'cognitive' | 'mechanical' | 'readonly' | 'voice';
|
|
16
|
+
export interface Skill {
|
|
17
|
+
slug: string;
|
|
18
|
+
kind: SkillKind;
|
|
19
|
+
desc: string;
|
|
20
|
+
when: string;
|
|
21
|
+
changes: string;
|
|
22
|
+
flags?: string;
|
|
23
|
+
}
|
|
24
|
+
/** Display label per kind. Used by the help-page specimen stamps. */
|
|
25
|
+
export declare const KIND_LABEL: Readonly<Record<SkillKind, string>>;
|
|
26
|
+
/**
|
|
27
|
+
* The 22 editorial skills that ship with this repository, in the order
|
|
28
|
+
* they appear in `.claude/skills/` (alphabetical). Voice skills belong
|
|
29
|
+
* at the end of any UI listing — see `SKILLS_SORTED` below.
|
|
30
|
+
*/
|
|
31
|
+
export declare const SKILLS: readonly Skill[];
|
|
32
|
+
export declare const SKILLS_SORTED: readonly Skill[];
|
|
33
|
+
//# sourceMappingURL=editorial-skills-catalogue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editorial-skills-catalogue.d.ts","sourceRoot":"","sources":["../../src/lib/editorial-skills-catalogue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,SAAS,GAAG,WAAW,GAAG,YAAY,GAAG,UAAU,GAAG,OAAO,CAAC;AAE1E,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qEAAqE;AACrE,eAAO,MAAM,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAK1D,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,MAAM,EAAE,SAAS,KAAK,EA8KlC,CAAC;AAUF,eAAO,MAAM,aAAa,EAAE,SAAS,KAAK,EAA6B,CAAC"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Editorial skill catalogue — the source of truth for the specimen grid
|
|
3
|
+
* on /dev/editorial-help and any future CLI or doc generator that needs
|
|
4
|
+
* the same inventory.
|
|
5
|
+
*
|
|
6
|
+
* Each skill carries four fields: `slug`, `kind` (see below), `desc`,
|
|
7
|
+
* `when`, `changes`, and an optional `flags` string. The `kind` drives
|
|
8
|
+
* the corner stamp on the help-page specimen card:
|
|
9
|
+
*
|
|
10
|
+
* - cognitive → red pencil — Claude Code drafts/revises prose
|
|
11
|
+
* - mechanical → proof blue — state transition, disk write, no writing
|
|
12
|
+
* - readonly → faded ink — reports, audits, listings
|
|
13
|
+
* - voice → stamp purple — called by other skills, not directly
|
|
14
|
+
*/
|
|
15
|
+
/** Display label per kind. Used by the help-page specimen stamps. */
|
|
16
|
+
export const KIND_LABEL = {
|
|
17
|
+
cognitive: 'cognitive',
|
|
18
|
+
mechanical: 'mechanical',
|
|
19
|
+
readonly: 'read-only',
|
|
20
|
+
voice: 'voice',
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* The 22 editorial skills that ship with this repository, in the order
|
|
24
|
+
* they appear in `.claude/skills/` (alphabetical). Voice skills belong
|
|
25
|
+
* at the end of any UI listing — see `SKILLS_SORTED` below.
|
|
26
|
+
*/
|
|
27
|
+
export const SKILLS = [
|
|
28
|
+
{
|
|
29
|
+
slug: 'editorial-help',
|
|
30
|
+
kind: 'readonly',
|
|
31
|
+
desc: 'Print the workflow overview and calendar status across every stage.',
|
|
32
|
+
when: 'When you have forgotten the shape of the pipeline, or want a full situation report.',
|
|
33
|
+
changes: 'Nothing. Read-only.',
|
|
34
|
+
flags: '--site <slug>',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
slug: 'editorial-status',
|
|
38
|
+
kind: 'readonly',
|
|
39
|
+
desc: 'Calendar status in a compact form — just the stage columns, no prose.',
|
|
40
|
+
when: 'Between sessions. Quick roll-call of what is where.',
|
|
41
|
+
changes: 'Nothing. Read-only.',
|
|
42
|
+
flags: '--site <slug>',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
slug: 'editorial-add',
|
|
46
|
+
kind: 'mechanical',
|
|
47
|
+
desc: 'Capture a new idea in the Ideas stage — title, optional description, content type.',
|
|
48
|
+
when: 'The instant an idea is worth persisting. Pre-commit, not post-draft.',
|
|
49
|
+
changes: 'Appends a row to the calendar under Ideas. No file scaffolded yet.',
|
|
50
|
+
flags: '--site <slug>',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
slug: 'editorial-plan',
|
|
54
|
+
kind: 'cognitive',
|
|
55
|
+
desc: 'Promote Ideas → Planned; set target keywords and topic tags.',
|
|
56
|
+
when: 'The idea has matured enough to point at a shape.',
|
|
57
|
+
changes: 'Moves the calendar row; writes keywords onto the entry.',
|
|
58
|
+
flags: '--site <slug> <slug>',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
slug: 'editorial-draft',
|
|
62
|
+
kind: 'mechanical',
|
|
63
|
+
desc: 'Scaffold the blog post directory + frontmatter from a Planned entry and advance to Drafting.',
|
|
64
|
+
when: 'Ready to begin writing. Can also be triggered from the studio button.',
|
|
65
|
+
changes: 'Creates src/sites/<site>/content/blog/<slug>.md with frontmatter (state: draft); calendar stage flips to Drafting.',
|
|
66
|
+
flags: '--site <slug> <slug>',
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
slug: 'editorial-draft-review',
|
|
70
|
+
kind: 'mechanical',
|
|
71
|
+
desc: 'Enqueue an existing blog draft into the editorial-review pipeline and print the /dev/editorial-review URL.',
|
|
72
|
+
when: 'The draft is written and ready for annotation + iteration.',
|
|
73
|
+
changes: 'Opens a review workflow in state open. No edit to the draft itself.',
|
|
74
|
+
flags: '--site <slug> <slug>',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
slug: 'editorial-iterate',
|
|
78
|
+
kind: 'cognitive',
|
|
79
|
+
desc: 'Revise a draft based on margin-note comments using the site voice skill; appends a new DraftVersion.',
|
|
80
|
+
when: 'After the operator clicks Iterate in the review page.',
|
|
81
|
+
changes: 'Writes a new version to the review journal; workflow flips iterating → in-review.',
|
|
82
|
+
flags: '<workflow-id> or --site <slug> <slug>',
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
slug: 'editorial-approve',
|
|
86
|
+
kind: 'mechanical',
|
|
87
|
+
desc: 'Write the approved draft version to its destination (blog file for longform; shortform copy into the calendar) and transition to applied.',
|
|
88
|
+
when: 'After the operator clicks Approve in the review page.',
|
|
89
|
+
changes: 'Overwrites the destination file; workflow becomes applied; calendar untouched (publish is separate).',
|
|
90
|
+
flags: '<workflow-id>',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
slug: 'editorial-review-cancel',
|
|
94
|
+
kind: 'mechanical',
|
|
95
|
+
desc: 'Cancel an active review workflow; leaves the source file untouched.',
|
|
96
|
+
when: 'A draft has been abandoned or replaced; clean up the pipeline.',
|
|
97
|
+
changes: 'Workflow transitions to cancelled terminal state. Source file not modified.',
|
|
98
|
+
flags: '<workflow-id>',
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
slug: 'editorial-review-help',
|
|
102
|
+
kind: 'readonly',
|
|
103
|
+
desc: 'Editorial-review pipeline state across all active workflows + next action per workflow.',
|
|
104
|
+
when: 'Resuming review work across multiple drafts or sites.',
|
|
105
|
+
changes: 'Nothing. Read-only.',
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
slug: 'editorial-review-report',
|
|
109
|
+
kind: 'readonly',
|
|
110
|
+
desc: 'Aggregate comment-annotation categories across completed workflows — which voice-skill principles are drifting most.',
|
|
111
|
+
when: 'Monthly-ish. Inputs for voice-skill revisions.',
|
|
112
|
+
changes: 'Nothing. Read-only.',
|
|
113
|
+
flags: '--site <slug>',
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
slug: 'editorial-publish',
|
|
117
|
+
kind: 'mechanical',
|
|
118
|
+
desc: 'Flip a Drafting or Review entry to Published and stamp today’s date.',
|
|
119
|
+
when: 'The blog file is live. Usually after /editorial-approve and a human commit.',
|
|
120
|
+
changes: 'Sets datePublished on the entry; stage becomes Published. Does not commit.',
|
|
121
|
+
flags: '--site <slug> <slug>',
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
slug: 'editorial-shortform-draft',
|
|
125
|
+
kind: 'cognitive',
|
|
126
|
+
desc: 'Draft a social post (Reddit title+body, YouTube description, LinkedIn, newsletter) for a published entry, using the site voice. Enqueues a shortform review workflow.',
|
|
127
|
+
when: 'After /editorial-publish, once the post is live and worth amplifying.',
|
|
128
|
+
changes: 'Creates a new review workflow with contentKind=shortform and the drafted copy as v1.',
|
|
129
|
+
flags: '--site <slug> <slug> <platform> [channel]',
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
slug: 'editorial-distribute',
|
|
133
|
+
kind: 'mechanical',
|
|
134
|
+
desc: 'Record that a published post was shared to a social platform — URL, date, sub-channel.',
|
|
135
|
+
when: 'After you actually hit post. Closes the loop with analytics.',
|
|
136
|
+
changes: 'Appends a DistributionRecord to the calendar file.',
|
|
137
|
+
flags: '--site <slug> <slug> <platform> <url>',
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
slug: 'editorial-social-review',
|
|
141
|
+
kind: 'readonly',
|
|
142
|
+
desc: 'Matrix of published posts × social platforms showing which combinations have been shared.',
|
|
143
|
+
when: 'Looking for cross-post holes.',
|
|
144
|
+
changes: 'Nothing. Read-only.',
|
|
145
|
+
flags: '--site <slug>',
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
slug: 'editorial-reddit-sync',
|
|
149
|
+
kind: 'mechanical',
|
|
150
|
+
desc: 'Pull recent Reddit submissions via the API; upsert DistributionRecords for any that reference the site’s posts or videos.',
|
|
151
|
+
when: 'Reconciling distribution state without re-entering by hand.',
|
|
152
|
+
changes: 'Adds or updates DistributionRecord rows in the calendar.',
|
|
153
|
+
flags: '--site <slug>',
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
slug: 'editorial-reddit-opportunities',
|
|
157
|
+
kind: 'cognitive',
|
|
158
|
+
desc: 'For a published post, list relevant subreddits split into already-shared (skip) and unshared candidates with subscriber count and self-promo hints.',
|
|
159
|
+
when: 'Planning a cross-post run.',
|
|
160
|
+
changes: 'Nothing. Read-only.',
|
|
161
|
+
flags: '--site <slug> <slug>',
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
slug: 'editorial-cross-link-review',
|
|
165
|
+
kind: 'readonly',
|
|
166
|
+
desc: 'Audit bidirectional linking between blog posts and YouTube videos; flag missing reciprocal links.',
|
|
167
|
+
when: 'Before shipping a YouTube entry, or as a periodic audit.',
|
|
168
|
+
changes: 'Nothing. Read-only.',
|
|
169
|
+
flags: '--site <slug>',
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
slug: 'editorial-performance',
|
|
173
|
+
kind: 'readonly',
|
|
174
|
+
desc: 'Analytics metrics for published posts; flags underperformers that might warrant revision or a better cross-post.',
|
|
175
|
+
when: 'Cadence review. Not for individual posts.',
|
|
176
|
+
changes: 'Nothing. Read-only.',
|
|
177
|
+
flags: '--site <slug>',
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
slug: 'editorial-suggest',
|
|
181
|
+
kind: 'cognitive',
|
|
182
|
+
desc: 'Pull analytics and suggest new ideas for the Ideas stage based on observed queries and gaps.',
|
|
183
|
+
when: 'When the Ideas column is thin.',
|
|
184
|
+
changes: 'Prints suggestions. Does not write them — pair with /editorial-add.',
|
|
185
|
+
flags: '--site <slug>',
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
slug: 'audiocontrol-voice',
|
|
189
|
+
kind: 'voice',
|
|
190
|
+
desc: 'Voice skill for audiocontrol.org — service-manual register, hardware-specific vocabulary, dated specs.',
|
|
191
|
+
when: 'Called by /editorial-iterate and /editorial-shortform-draft when site=audiocontrol. Not invoked directly by the operator.',
|
|
192
|
+
changes: 'Nothing. Provides the register for the caller.',
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
slug: 'editorialcontrol-voice',
|
|
196
|
+
kind: 'voice',
|
|
197
|
+
desc: 'Voice skill for editorialcontrol.org — publication register, argument-driven, magazine typography.',
|
|
198
|
+
when: 'Called by /editorial-iterate and /editorial-shortform-draft when site=editorialcontrol. Not invoked directly.',
|
|
199
|
+
changes: 'Nothing. Provides the register for the caller.',
|
|
200
|
+
},
|
|
201
|
+
];
|
|
202
|
+
/**
|
|
203
|
+
* Display order for UI listings: non-voice skills alphabetised first,
|
|
204
|
+
* voice skills alphabetised at the end. Voice skills are infrastructure
|
|
205
|
+
* for other skills, not directly invocable — putting them at the bottom
|
|
206
|
+
* reflects that.
|
|
207
|
+
*/
|
|
208
|
+
const NON_VOICE = SKILLS.filter((s) => s.kind !== 'voice').slice().sort((a, b) => a.slug.localeCompare(b.slug));
|
|
209
|
+
const VOICE = SKILLS.filter((s) => s.kind === 'voice').slice().sort((a, b) => a.slug.localeCompare(b.slug));
|
|
210
|
+
export const SKILLS_SORTED = [...NON_VOICE, ...VOICE];
|
|
211
|
+
//# sourceMappingURL=editorial-skills-catalogue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editorial-skills-catalogue.js","sourceRoot":"","sources":["../../src/lib/editorial-skills-catalogue.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 23f — studio override-render helper.
|
|
3
|
+
*
|
|
4
|
+
* Page renderers consult the override resolver at the top of each
|
|
5
|
+
* render. When a `templates/<name>.ts` file exists in the project's
|
|
6
|
+
* `.deskwork/`, the renderer loads it and delegates the entire render
|
|
7
|
+
* to the override module's `default` export. The default export is
|
|
8
|
+
* called with the same arguments as the built-in renderer.
|
|
9
|
+
*
|
|
10
|
+
* The override contract:
|
|
11
|
+
* - Module exports a `default` function.
|
|
12
|
+
* - Function signature must match the built-in renderer (the type
|
|
13
|
+
* parameter `Args` here makes that explicit at the call site).
|
|
14
|
+
* - Function returns either a string (sync renderers) or a
|
|
15
|
+
* Promise<string> (async renderers). Both are awaited in
|
|
16
|
+
* `runOverride`.
|
|
17
|
+
*
|
|
18
|
+
* If the override exists but its `default` export is missing or not
|
|
19
|
+
* a function, we throw a descriptive error rather than falling back
|
|
20
|
+
* to the built-in renderer. Operators get a loud, actionable failure
|
|
21
|
+
* instead of a silent miss.
|
|
22
|
+
*/
|
|
23
|
+
import type { OverrideResolver } from '@deskwork/core/overrides';
|
|
24
|
+
import type { StudioContext } from '../routes/api.ts';
|
|
25
|
+
/**
|
|
26
|
+
* Return a resolver for `ctx`. When the context already carries one
|
|
27
|
+
* (production boot), reuse it; otherwise build a fresh resolver from
|
|
28
|
+
* `ctx.projectRoot`. The result is always a real OverrideResolver —
|
|
29
|
+
* there is no skip path.
|
|
30
|
+
*/
|
|
31
|
+
export declare function getResolver(ctx: StudioContext): OverrideResolver;
|
|
32
|
+
/**
|
|
33
|
+
* Load and execute a templates override.
|
|
34
|
+
*
|
|
35
|
+
* `name` is the template basename (no extension). When no override is
|
|
36
|
+
* registered, returns `null` — the caller proceeds with its built-in
|
|
37
|
+
* renderer. When an override IS registered, the module is dynamically
|
|
38
|
+
* imported and its `default` export is called with `args`.
|
|
39
|
+
*/
|
|
40
|
+
export declare function runTemplateOverride<Args extends readonly unknown[]>(ctx: StudioContext, name: string, args: Args): Promise<string | null>;
|
|
41
|
+
//# sourceMappingURL=override-render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"override-render.d.ts","sourceRoot":"","sources":["../../src/lib/override-render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,aAAa,GAAG,gBAAgB,CAGhE;AAYD;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,SAAS,SAAS,OAAO,EAAE,EACvE,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,IAAI,GACT,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA2BxB"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 23f — studio override-render helper.
|
|
3
|
+
*
|
|
4
|
+
* Page renderers consult the override resolver at the top of each
|
|
5
|
+
* render. When a `templates/<name>.ts` file exists in the project's
|
|
6
|
+
* `.deskwork/`, the renderer loads it and delegates the entire render
|
|
7
|
+
* to the override module's `default` export. The default export is
|
|
8
|
+
* called with the same arguments as the built-in renderer.
|
|
9
|
+
*
|
|
10
|
+
* The override contract:
|
|
11
|
+
* - Module exports a `default` function.
|
|
12
|
+
* - Function signature must match the built-in renderer (the type
|
|
13
|
+
* parameter `Args` here makes that explicit at the call site).
|
|
14
|
+
* - Function returns either a string (sync renderers) or a
|
|
15
|
+
* Promise<string> (async renderers). Both are awaited in
|
|
16
|
+
* `runOverride`.
|
|
17
|
+
*
|
|
18
|
+
* If the override exists but its `default` export is missing or not
|
|
19
|
+
* a function, we throw a descriptive error rather than falling back
|
|
20
|
+
* to the built-in renderer. Operators get a loud, actionable failure
|
|
21
|
+
* instead of a silent miss.
|
|
22
|
+
*/
|
|
23
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
24
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
25
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
26
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return path;
|
|
30
|
+
};
|
|
31
|
+
import { createOverrideResolver } from '@deskwork/core/overrides';
|
|
32
|
+
/**
|
|
33
|
+
* Return a resolver for `ctx`. When the context already carries one
|
|
34
|
+
* (production boot), reuse it; otherwise build a fresh resolver from
|
|
35
|
+
* `ctx.projectRoot`. The result is always a real OverrideResolver —
|
|
36
|
+
* there is no skip path.
|
|
37
|
+
*/
|
|
38
|
+
export function getResolver(ctx) {
|
|
39
|
+
if (ctx.resolver)
|
|
40
|
+
return ctx.resolver;
|
|
41
|
+
return createOverrideResolver(ctx.projectRoot);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Discriminator for a module's default export. Narrows `unknown` to
|
|
45
|
+
* `(...args: Args) => string | Promise<string>` without `as`-casting.
|
|
46
|
+
*/
|
|
47
|
+
function isOverrideRenderer(value) {
|
|
48
|
+
return typeof value === 'function';
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Load and execute a templates override.
|
|
52
|
+
*
|
|
53
|
+
* `name` is the template basename (no extension). When no override is
|
|
54
|
+
* registered, returns `null` — the caller proceeds with its built-in
|
|
55
|
+
* renderer. When an override IS registered, the module is dynamically
|
|
56
|
+
* imported and its `default` export is called with `args`.
|
|
57
|
+
*/
|
|
58
|
+
export async function runTemplateOverride(ctx, name, args) {
|
|
59
|
+
const resolver = getResolver(ctx);
|
|
60
|
+
const path = resolver.template(name);
|
|
61
|
+
if (path === null)
|
|
62
|
+
return null;
|
|
63
|
+
// Dynamic import of an absolute file path. The plugin's runtime tsx
|
|
64
|
+
// loader (the studio is always invoked through tsx — see the
|
|
65
|
+
// server.ts shebang) makes `.ts` imports work at runtime.
|
|
66
|
+
const mod = await import(__rewriteRelativeImportExtension(path));
|
|
67
|
+
if (typeof mod !== 'object' || mod === null) {
|
|
68
|
+
throw new Error(`template override at ${path} did not export a module object`);
|
|
69
|
+
}
|
|
70
|
+
const fn = Reflect.get(mod, 'default');
|
|
71
|
+
if (!isOverrideRenderer(fn)) {
|
|
72
|
+
throw new Error(`template override at ${path} must export a 'default' function (got ${typeof fn})`);
|
|
73
|
+
}
|
|
74
|
+
const out = await fn(...args);
|
|
75
|
+
if (typeof out !== 'string') {
|
|
76
|
+
throw new Error(`template override at ${path} returned a ${typeof out}; expected string`);
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=override-render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"override-render.js","sourceRoot":"","sources":["../../src/lib/override-render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;;;;;;;;;AAGH,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAGlE;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,GAAkB;IAC5C,IAAI,GAAG,CAAC,QAAQ;QAAE,OAAO,GAAG,CAAC,QAAQ,CAAC;IACtC,OAAO,sBAAsB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;AACjD,CAAC;AAED;;;GAGG;AACH,SAAS,kBAAkB,CACzB,KAAc;IAEd,OAAO,OAAO,KAAK,KAAK,UAAU,CAAC;AACrC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,GAAkB,EAClB,IAAY,EACZ,IAAU;IAEV,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAClC,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAE/B,oEAAoE;IACpE,6DAA6D;IAC7D,0DAA0D;IAC1D,MAAM,GAAG,GAAY,MAAM,MAAM,kCAAC,IAAI,EAAC,CAAC;IACxC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QAC5C,MAAM,IAAI,KAAK,CACb,wBAAwB,IAAI,iCAAiC,CAC9D,CAAC;IACJ,CAAC;IACD,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACvC,IAAI,CAAC,kBAAkB,CAAO,EAAE,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CACb,wBAAwB,IAAI,0CAA0C,OAAO,EAAE,GAAG,CACnF,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;IAC9B,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CACb,wBAAwB,IAAI,eAAe,OAAO,GAAG,mBAAmB,CACzE,CAAC;IACJ,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|