@deskwork/studio 0.9.8 → 0.10.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.js +17 -17
- package/dist/lib/editorial-skills-catalogue.js.map +1 -1
- package/dist/pages/content-detail.d.ts.map +1 -1
- package/dist/pages/content-detail.js +13 -25
- package/dist/pages/content-detail.js.map +1 -1
- package/dist/pages/content-detail.ts +14 -24
- package/dist/pages/dashboard.d.ts.map +1 -1
- package/dist/pages/dashboard.js +58 -24
- package/dist/pages/dashboard.js.map +1 -1
- package/dist/pages/dashboard.ts +61 -24
- package/dist/pages/help.js +25 -25
- package/dist/pages/help.js.map +1 -1
- package/dist/pages/help.ts +25 -25
- package/dist/pages/index.d.ts +1 -1
- package/dist/pages/index.d.ts.map +1 -1
- package/dist/pages/index.js +129 -83
- package/dist/pages/index.js.map +1 -1
- package/dist/pages/index.ts +145 -84
- package/dist/pages/review.js +3 -3
- package/dist/pages/review.ts +3 -3
- package/dist/pages/shortform.js +1 -1
- package/dist/pages/shortform.ts +1 -1
- package/package.json +2 -2
package/dist/pages/index.ts
CHANGED
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
* Read-only — links to existing routes only. No editing capability here.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
+
import { readWorkflows } from '@deskwork/core/review/pipeline';
|
|
21
|
+
import type { DraftWorkflowItem } from '@deskwork/core/review/types';
|
|
20
22
|
import type { StudioContext } from '../routes/api.ts';
|
|
21
23
|
import { html, unsafe, type RawHtml } from './html.ts';
|
|
22
24
|
import { layout } from './layout.ts';
|
|
@@ -29,8 +31,17 @@ interface IndexEntry {
|
|
|
29
31
|
titleHtml: string;
|
|
30
32
|
/** Plain-text fallback for accessibility (used as link text). */
|
|
31
33
|
titleText: string;
|
|
32
|
-
/** Route path. When `template` is set,
|
|
34
|
+
/** Route path. When `template` is set, the path is shown but the
|
|
35
|
+
* hyperlink target comes from `linkHref` instead. */
|
|
33
36
|
route: string;
|
|
37
|
+
/**
|
|
38
|
+
* Explicit link target. When set, the title becomes a link to this
|
|
39
|
+
* URL — even for templated entries (where the visual route hint
|
|
40
|
+
* stays alongside as a placeholder). When omitted, behavior depends
|
|
41
|
+
* on `template`: non-templated entries link to `route`; templated
|
|
42
|
+
* entries render as plain text.
|
|
43
|
+
*/
|
|
44
|
+
linkHref?: string;
|
|
34
45
|
/**
|
|
35
46
|
* For templated routes (longform reviews, scrapbook), this is the
|
|
36
47
|
* placeholder text shown in red-pencil italic. The route string still
|
|
@@ -52,90 +63,139 @@ interface IndexSection {
|
|
|
52
63
|
entries: IndexEntry[];
|
|
53
64
|
}
|
|
54
65
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
]
|
|
66
|
+
/**
|
|
67
|
+
* Pick the workflow that should be the default Longform-reviews target —
|
|
68
|
+
* the most-recent open longform workflow (in-review or open). Returns
|
|
69
|
+
* null when no candidate exists; the caller falls back to the dashboard's
|
|
70
|
+
* Review section anchor.
|
|
71
|
+
*/
|
|
72
|
+
function pickDefaultLongformWorkflow(
|
|
73
|
+
workflows: readonly DraftWorkflowItem[],
|
|
74
|
+
): DraftWorkflowItem | null {
|
|
75
|
+
const candidates = workflows
|
|
76
|
+
.filter((w) => w.contentKind === 'longform')
|
|
77
|
+
.filter((w) => w.state === 'in-review' || w.state === 'open')
|
|
78
|
+
.slice()
|
|
79
|
+
.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
80
|
+
return candidates[0] ?? null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function buildSections(ctx: StudioContext): readonly IndexSection[] {
|
|
84
|
+
const workflows: readonly DraftWorkflowItem[] = (() => {
|
|
85
|
+
try {
|
|
86
|
+
return readWorkflows(ctx.projectRoot, ctx.config);
|
|
87
|
+
} catch {
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
})();
|
|
91
|
+
const longformDefault = pickDefaultLongformWorkflow(workflows);
|
|
92
|
+
// Issue #107: III links to the most-recent in-review longform when
|
|
93
|
+
// one exists, else falls back to the dashboard's Review section
|
|
94
|
+
// anchor (`#stage-review`, mounted in sub-phase D). The visual
|
|
95
|
+
// template hint stays alongside the link so adopters still see the
|
|
96
|
+
// URL shape — `<slug>` placeholder shown in red-pencil italic.
|
|
97
|
+
const longformLinkHref =
|
|
98
|
+
longformDefault !== null
|
|
99
|
+
? `/dev/editorial-review/${longformDefault.id}`
|
|
100
|
+
: '/dev/editorial-studio#stage-review';
|
|
101
|
+
|
|
102
|
+
return [
|
|
103
|
+
{
|
|
104
|
+
ornament: '¶',
|
|
105
|
+
name: 'Pipeline',
|
|
106
|
+
count: 'i. — 1 surface',
|
|
107
|
+
entries: [
|
|
108
|
+
{
|
|
109
|
+
numeral: 'I',
|
|
110
|
+
titleHtml: 'Dashboard',
|
|
111
|
+
titleText: 'Dashboard',
|
|
112
|
+
route: '/dev/editorial-studio',
|
|
113
|
+
desc: 'Press-check. The calendar across all sites; awaiting press; recent proofs; voice-drift signal.',
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
ornament: '¶',
|
|
119
|
+
name: 'Review desk',
|
|
120
|
+
count: 'ii.–iii. — 2 surfaces',
|
|
121
|
+
entries: [
|
|
122
|
+
{
|
|
123
|
+
numeral: 'II',
|
|
124
|
+
titleHtml: 'Shortform reviews',
|
|
125
|
+
titleText: 'Shortform reviews',
|
|
126
|
+
route: '/dev/editorial-review-shortform',
|
|
127
|
+
desc: 'Cross-platform copy desk. Reddit, LinkedIn, YouTube, Instagram — galley slips, one per platform.',
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
numeral: 'III',
|
|
131
|
+
titleHtml: 'Longform reviews',
|
|
132
|
+
titleText: 'Longform reviews',
|
|
133
|
+
route: '/dev/editorial-review/<slug>',
|
|
134
|
+
linkHref: longformLinkHref,
|
|
135
|
+
template: { prefix: '/dev/editorial-review/', placeholder: '<slug>' },
|
|
136
|
+
desc: 'Per-entry margin notes, decisions, iterate flow.',
|
|
137
|
+
hint: 'entry-by-entry',
|
|
138
|
+
postHint:
|
|
139
|
+
longformDefault !== null
|
|
140
|
+
? `Defaults to the most-recent in-review longform (${longformDefault.slug}). Or reach via the Dashboard or Content view.`
|
|
141
|
+
: 'Defaults to the dashboard\'s Review section. Open a longform workflow to populate the per-entry deep-link.',
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
ornament: '¶',
|
|
147
|
+
name: 'Browse',
|
|
148
|
+
count: 'iv.–v. — 2 surfaces',
|
|
149
|
+
entries: [
|
|
150
|
+
{
|
|
151
|
+
numeral: 'IV',
|
|
152
|
+
titleHtml: 'Content view',
|
|
153
|
+
titleText: 'Content view',
|
|
154
|
+
route: '/dev/content',
|
|
155
|
+
desc: 'The shape of the work. A drillable tree of nodes; click any to read its head matter and browse its scrapbook.',
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
numeral: 'V',
|
|
159
|
+
titleHtml: 'Scrapbook',
|
|
160
|
+
titleText: 'Scrapbook',
|
|
161
|
+
route: '/dev/scrapbook/<site>/<path>',
|
|
162
|
+
// Issue #107: scrapbook is reached by drilling into a content
|
|
163
|
+
// node. Default link points at the content view; the URL
|
|
164
|
+
// template hint stays so adopters see the addressing shape.
|
|
165
|
+
linkHref: '/dev/content',
|
|
166
|
+
template: { prefix: '/dev/scrapbook/', placeholder: '<site>/<path>' },
|
|
167
|
+
desc: 'Research, receipts, working notes. Addressed by hierarchical path; secret items appear in their own section.',
|
|
168
|
+
hint: 'path-addressed',
|
|
169
|
+
postHint: "Reach via the Content view's per-node drawer, or address directly.",
|
|
170
|
+
},
|
|
171
|
+
],
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
ornament: '¶',
|
|
175
|
+
name: 'Reference',
|
|
176
|
+
count: 'vi. — 1 surface',
|
|
177
|
+
entries: [
|
|
178
|
+
{
|
|
179
|
+
numeral: 'VI',
|
|
180
|
+
titleHtml: "The Compositor's <em>Manual</em>",
|
|
181
|
+
titleText: "The Compositor's Manual",
|
|
182
|
+
route: '/dev/editorial-help',
|
|
183
|
+
desc: 'The workflow, the skill catalogue, the names of the things — read once, return when the work asks.',
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
},
|
|
187
|
+
];
|
|
188
|
+
}
|
|
133
189
|
|
|
134
190
|
function renderEntryTitle(entry: IndexEntry): string {
|
|
135
|
-
|
|
191
|
+
// Explicit linkHref wins. Otherwise: non-templated entries link to
|
|
192
|
+
// their route; templated entries with no fallback render as plain
|
|
193
|
+
// text (the route is templated, can't be linked verbatim).
|
|
194
|
+
const href = entry.linkHref ?? (entry.template ? null : entry.route);
|
|
195
|
+
if (href === null) {
|
|
136
196
|
return html`<span class="er-toc-entry__title">${unsafe(entry.titleHtml)}</span>`;
|
|
137
197
|
}
|
|
138
|
-
return html`<a class="er-toc-entry__title" href="${
|
|
198
|
+
return html`<a class="er-toc-entry__title" href="${href}">${unsafe(entry.titleHtml)}</a>`;
|
|
139
199
|
}
|
|
140
200
|
|
|
141
201
|
function renderEntryRoute(entry: IndexEntry): string {
|
|
@@ -181,7 +241,8 @@ function renderSection(section: IndexSection): RawHtml {
|
|
|
181
241
|
</section>`);
|
|
182
242
|
}
|
|
183
243
|
|
|
184
|
-
export function renderStudioIndex(
|
|
244
|
+
export function renderStudioIndex(ctx: StudioContext): string {
|
|
245
|
+
const sections = buildSections(ctx);
|
|
185
246
|
const body = html`
|
|
186
247
|
${renderEditorialFolio('index', 'index of the press')}
|
|
187
248
|
<main class="er-toc-page">
|
|
@@ -193,9 +254,9 @@ export function renderStudioIndex(_ctx: StudioContext): string {
|
|
|
193
254
|
Begin where the work is.
|
|
194
255
|
</p>
|
|
195
256
|
</header>
|
|
196
|
-
${
|
|
257
|
+
${sections.map(renderSection)}
|
|
197
258
|
<footer class="er-toc-colophon">
|
|
198
|
-
Pressed in the deskwork studio
|
|
259
|
+
Pressed in the deskwork studio.<br>
|
|
199
260
|
<span class="er-toc-colophon__rule"></span>
|
|
200
261
|
</footer>
|
|
201
262
|
</main>`;
|
package/dist/pages/review.js
CHANGED
|
@@ -190,9 +190,9 @@ function renderShortcutsOverlay() {
|
|
|
190
190
|
<dt><kbd>e</kbd> / dbl-click</dt><dd>toggle edit mode</dd>
|
|
191
191
|
<dt>select text</dt><dd>leave a margin note</dd>
|
|
192
192
|
<dt><kbd>⌘</kbd><kbd>↵</kbd> / <kbd>ctrl</kbd><kbd>↵</kbd></dt><dd>save margin note (in composer)</dd>
|
|
193
|
-
<dt><kbd>a</kbd></dt><dd>approve</dd>
|
|
194
|
-
<dt><kbd>i</kbd></dt><dd>iterate</dd>
|
|
195
|
-
<dt><kbd>r</kbd></dt><dd>reject</dd>
|
|
193
|
+
<dt><kbd>a</kbd> <kbd>a</kbd></dt><dd>approve <em>— press twice within 500ms; first press arms, second fires</em></dd>
|
|
194
|
+
<dt><kbd>i</kbd> <kbd>i</kbd></dt><dd>iterate <em>— press twice within 500ms</em></dd>
|
|
195
|
+
<dt><kbd>r</kbd> <kbd>r</kbd></dt><dd>reject <em>— press twice within 500ms</em></dd>
|
|
196
196
|
<dt><kbd>j</kbd> / <kbd>k</kbd></dt><dd>next / previous margin note</dd>
|
|
197
197
|
<dt><kbd>?</kbd></dt><dd>this panel</dd>
|
|
198
198
|
<dt><kbd>esc</kbd></dt><dd>close / cancel composer</dd>
|
package/dist/pages/review.ts
CHANGED
|
@@ -275,9 +275,9 @@ function renderShortcutsOverlay(): RawHtml {
|
|
|
275
275
|
<dt><kbd>e</kbd> / dbl-click</dt><dd>toggle edit mode</dd>
|
|
276
276
|
<dt>select text</dt><dd>leave a margin note</dd>
|
|
277
277
|
<dt><kbd>⌘</kbd><kbd>↵</kbd> / <kbd>ctrl</kbd><kbd>↵</kbd></dt><dd>save margin note (in composer)</dd>
|
|
278
|
-
<dt><kbd>a</kbd></dt><dd>approve</dd>
|
|
279
|
-
<dt><kbd>i</kbd></dt><dd>iterate</dd>
|
|
280
|
-
<dt><kbd>r</kbd></dt><dd>reject</dd>
|
|
278
|
+
<dt><kbd>a</kbd> <kbd>a</kbd></dt><dd>approve <em>— press twice within 500ms; first press arms, second fires</em></dd>
|
|
279
|
+
<dt><kbd>i</kbd> <kbd>i</kbd></dt><dd>iterate <em>— press twice within 500ms</em></dd>
|
|
280
|
+
<dt><kbd>r</kbd> <kbd>r</kbd></dt><dd>reject <em>— press twice within 500ms</em></dd>
|
|
281
281
|
<dt><kbd>j</kbd> / <kbd>k</kbd></dt><dd>next / previous margin note</dd>
|
|
282
282
|
<dt><kbd>?</kbd></dt><dd>this panel</dd>
|
|
283
283
|
<dt><kbd>esc</kbd></dt><dd>close / cancel composer</dd>
|
package/dist/pages/shortform.js
CHANGED
|
@@ -95,7 +95,7 @@ function renderEmptyState() {
|
|
|
95
95
|
No short-form galleys on the desk.<br />
|
|
96
96
|
Supported platforms: <em>${platformList}</em>.<br />
|
|
97
97
|
Start a new shortform draft from the dashboard's
|
|
98
|
-
<a href="/dev/editorial-studio">
|
|
98
|
+
<a href="/dev/editorial-studio#stage-drafting">Drafting list</a>.
|
|
99
99
|
</div>`);
|
|
100
100
|
}
|
|
101
101
|
export function renderShortformPage(ctx) {
|
package/dist/pages/shortform.ts
CHANGED
|
@@ -111,7 +111,7 @@ function renderEmptyState(): RawHtml {
|
|
|
111
111
|
No short-form galleys on the desk.<br />
|
|
112
112
|
Supported platforms: <em>${platformList}</em>.<br />
|
|
113
113
|
Start a new shortform draft from the dashboard's
|
|
114
|
-
<a href="/dev/editorial-studio">
|
|
114
|
+
<a href="/dev/editorial-studio#stage-drafting">Drafting list</a>.
|
|
115
115
|
</div>`);
|
|
116
116
|
}
|
|
117
117
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deskwork/studio",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Editorial review studio — local web UI for the deskwork plugin",
|
|
6
6
|
"homepage": "https://github.com/audiocontrol-org/deskwork#readme",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"@codemirror/language": "^6.12.3",
|
|
46
46
|
"@codemirror/state": "^6.6.0",
|
|
47
47
|
"@codemirror/view": "^6.41.1",
|
|
48
|
-
"@deskwork/core": "0.
|
|
48
|
+
"@deskwork/core": "0.10.0",
|
|
49
49
|
"@hono/node-server": "^1.13.7",
|
|
50
50
|
"@lezer/highlight": "^1.2.3",
|
|
51
51
|
"esbuild": "^0.28.0",
|