@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.
Files changed (56) hide show
  1. package/dist/lib/editorial-skills-catalogue.d.ts +15 -1
  2. package/dist/lib/editorial-skills-catalogue.d.ts.map +1 -1
  3. package/dist/lib/editorial-skills-catalogue.js +66 -59
  4. package/dist/lib/editorial-skills-catalogue.js.map +1 -1
  5. package/dist/lib/entry-resolver.d.ts +22 -0
  6. package/dist/lib/entry-resolver.d.ts.map +1 -0
  7. package/dist/lib/entry-resolver.js +42 -0
  8. package/dist/lib/entry-resolver.js.map +1 -0
  9. package/dist/lib/stage-affordances.d.ts +31 -0
  10. package/dist/lib/stage-affordances.d.ts.map +1 -0
  11. package/dist/lib/stage-affordances.js +37 -0
  12. package/dist/lib/stage-affordances.js.map +1 -0
  13. package/dist/pages/dashboard/affordances.d.ts +41 -0
  14. package/dist/pages/dashboard/affordances.d.ts.map +1 -0
  15. package/dist/pages/dashboard/affordances.js +87 -0
  16. package/dist/pages/dashboard/affordances.js.map +1 -0
  17. package/dist/pages/dashboard/affordances.ts +95 -0
  18. package/dist/pages/dashboard/data.d.ts +24 -0
  19. package/dist/pages/dashboard/data.d.ts.map +1 -0
  20. package/dist/pages/dashboard/data.js +49 -0
  21. package/dist/pages/dashboard/data.js.map +1 -0
  22. package/dist/pages/dashboard/data.ts +56 -0
  23. package/dist/pages/dashboard/header.d.ts +13 -0
  24. package/dist/pages/dashboard/header.d.ts.map +1 -0
  25. package/dist/pages/dashboard/header.js +70 -0
  26. package/dist/pages/dashboard/header.js.map +1 -0
  27. package/dist/pages/dashboard/header.ts +80 -0
  28. package/dist/pages/dashboard/section.d.ts +37 -0
  29. package/dist/pages/dashboard/section.d.ts.map +1 -0
  30. package/dist/pages/dashboard/section.js +117 -0
  31. package/dist/pages/dashboard/section.js.map +1 -0
  32. package/dist/pages/dashboard/section.ts +132 -0
  33. package/dist/pages/dashboard.d.ts +30 -21
  34. package/dist/pages/dashboard.d.ts.map +1 -1
  35. package/dist/pages/dashboard.js +34 -799
  36. package/dist/pages/dashboard.js.map +1 -1
  37. package/dist/pages/dashboard.ts +44 -980
  38. package/dist/pages/entry-review.d.ts +25 -0
  39. package/dist/pages/entry-review.d.ts.map +1 -0
  40. package/dist/pages/entry-review.js +148 -0
  41. package/dist/pages/entry-review.js.map +1 -0
  42. package/dist/pages/entry-review.ts +185 -0
  43. package/dist/pages/help.d.ts +10 -5
  44. package/dist/pages/help.d.ts.map +1 -1
  45. package/dist/pages/help.js +113 -99
  46. package/dist/pages/help.js.map +1 -1
  47. package/dist/pages/help.ts +113 -99
  48. package/dist/pages/index.d.ts +13 -1
  49. package/dist/pages/index.d.ts.map +1 -1
  50. package/dist/pages/index.js +39 -24
  51. package/dist/pages/index.js.map +1 -1
  52. package/dist/pages/index.ts +43 -27
  53. package/dist/server.d.ts.map +1 -1
  54. package/dist/server.js +23 -3
  55. package/dist/server.js.map +1 -1
  56. package/package.json +4 -4
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Single-stage section renderer.
3
+ *
4
+ * Pipeline-redesign Task 34. Each of the eight stage sections renders
5
+ * with a section heading (stage name + entry count) and either a list
6
+ * of rows or an empty-state placeholder. Per-row HTML carries the
7
+ * sidecar-derived state inline (iteration count + reviewState badge)
8
+ * so an operator can see at a glance where each entry sits without
9
+ * opening it.
10
+ */
11
+
12
+ import { html, unsafe, type RawHtml } from '../html.ts';
13
+ import type { Entry, Stage } from '@deskwork/core/schema/entry';
14
+ import {
15
+ iterationForCurrentStage,
16
+ renderReviewStateBadge,
17
+ renderRowActions,
18
+ } from './affordances.ts';
19
+
20
+ const STAGE_ORNAMENTS: Record<Stage, string> = {
21
+ Ideas: '◇',
22
+ Planned: '§',
23
+ Outlining: '⊹',
24
+ Drafting: '✎',
25
+ Final: '※',
26
+ Published: '✓',
27
+ Blocked: '⊘',
28
+ Cancelled: '✗',
29
+ };
30
+
31
+ const STAGE_EMPTY_MESSAGES: Record<Stage, string> = {
32
+ Ideas: 'No open ideas. Run /deskwork:add to capture one.',
33
+ Planned: 'Nothing planned. /deskwork:approve <slug> to graduate an idea.',
34
+ Outlining: 'Nothing in outlining.',
35
+ Drafting: 'No posts in drafting.',
36
+ Final: 'Nothing in final review.',
37
+ Published: 'No published posts yet.',
38
+ Blocked: 'Nothing blocked.',
39
+ Cancelled: 'No cancelled entries.',
40
+ };
41
+
42
+ /**
43
+ * Render one entry as a single dashboard row. Carries inline:
44
+ * - slug (linked to the review surface)
45
+ * - title
46
+ * - iteration count for the entry's currentStage
47
+ * - reviewState badge (or an em-dash placeholder)
48
+ * - updatedAt timestamp
49
+ * - per-stage action buttons
50
+ */
51
+ export function renderRow(entry: Entry, index: number): RawHtml {
52
+ const iteration = iterationForCurrentStage(entry);
53
+ const reviewLink = `/dev/editorial-review/${entry.uuid}`;
54
+ const search = [entry.slug, entry.title, entry.keywords.join(' ')].join(' ').toLowerCase();
55
+ // Hierarchical entries (slugs containing `/`) get a visual indent
56
+ // marker the CSS layer reads. Storage stays flat; this is display-only.
57
+ const depth = entry.slug.split('/').length - 1;
58
+ const depthAttrs =
59
+ depth > 0
60
+ ? unsafe(html` data-depth="${depth}" style="--er-row-depth: ${depth}"`)
61
+ : '';
62
+
63
+ return unsafe(html`
64
+ <div class="er-calendar-row-wrap" data-row-wrap data-search="${search}"${depthAttrs}>
65
+ <div class="er-calendar-row" data-stage="${entry.currentStage}"
66
+ data-uuid="${entry.uuid}" data-slug="${entry.slug}" data-search="${search}">
67
+ <span class="er-row-num">№ ${String(index + 1).padStart(2, '0')}</span>
68
+ <div class="er-calendar-body">
69
+ <span class="er-row-slug"><a href="${reviewLink}"
70
+ title="open the review surface">${entry.slug}</a></span>
71
+ <span class="er-calendar-title">${entry.title}</span>
72
+ <span class="er-calendar-meta er-calendar-meta-iteration"
73
+ data-iteration="${iteration}">iteration: ${iteration}</span>
74
+ <span class="er-calendar-meta er-calendar-meta-updated"
75
+ title="${entry.updatedAt}">${formatDate(entry.updatedAt)}</span>
76
+ </div>
77
+ <span class="er-calendar-status">${renderReviewStateBadge(entry.reviewState)}</span>
78
+ ${renderRowActions(entry)}
79
+ </div>
80
+ </div>`);
81
+ }
82
+
83
+ /**
84
+ * Render one full stage section: heading + ornaments + count + rows.
85
+ * Empty stages render the placeholder message — keeping the section
86
+ * visible (rather than collapsing it) so the operator sees that the
87
+ * stage exists and is currently empty.
88
+ */
89
+ export function renderStageSection(stage: Stage, entries: readonly Entry[]): RawHtml {
90
+ const body =
91
+ entries.length === 0
92
+ ? unsafe(html`<div class="er-empty" data-empty-stage="${stage}"
93
+ style="padding: 1rem 0.25rem; font-size: 0.95rem;">
94
+ ${STAGE_EMPTY_MESSAGES[stage]}
95
+ </div>`)
96
+ : unsafe(entries.map((e, i) => renderRow(e, i).__raw).join(''));
97
+
98
+ return unsafe(html`
99
+ <section class="er-section" id="stage-${stage.toLowerCase()}" data-stage-section="${stage}">
100
+ <h2 class="er-section-head">
101
+ <span>${stage}</span>
102
+ <span class="ornament">${STAGE_ORNAMENTS[stage]}</span>
103
+ <span class="count">№ ${entries.length}</span>
104
+ </h2>
105
+ ${body}
106
+ </section>`);
107
+ }
108
+
109
+ /**
110
+ * Render the reserved Distribution placeholder. Stays a separate
111
+ * sibling of the stage sections — distribution records (shortform
112
+ * cross-posts) are tracked under their own model and the dashboard
113
+ * surfaces only a placeholder here until that integration lands.
114
+ */
115
+ export function renderDistributionPlaceholder(): RawHtml {
116
+ return unsafe(html`
117
+ <section class="er-section" id="stage-distribution" data-stage-section="Distribution">
118
+ <h2 class="er-section-head">
119
+ <span>Distribution</span>
120
+ <span class="ornament">⌘</span>
121
+ </h2>
122
+ <div class="er-empty" style="padding: 1rem 0.25rem; font-size: 0.95rem;">
123
+ Reserved for shortform DistributionRecords — separate model.
124
+ </div>
125
+ </section>`);
126
+ }
127
+
128
+ function formatDate(iso: string): string {
129
+ // Trim to YYYY-MM-DD for compact display. Full timestamp is on the
130
+ // <span title="...">.
131
+ return iso.slice(0, 10);
132
+ }
@@ -1,32 +1,41 @@
1
1
  /**
2
2
  * Studio dashboard page — `/dev/editorial-studio`.
3
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.
4
+ * Pipeline-redesign Task 34. The dashboard renders eight stage
5
+ * sections Ideas Planned Outlining Drafting Final
6
+ * Published, plus Blocked and Cancelled backed by sidecar reads
7
+ * under `<projectRoot>/.deskwork/entries/*.json`. Each row carries
8
+ * the entry's iteration count for its current stage and a
9
+ * reviewState badge so an operator can see at a glance where each
10
+ * entry sits without opening it.
9
11
  *
10
- * The audiocontrol original was tightly coupled to two hardcoded sites
11
- * (`'audiocontrol' | 'editorialcontrol'`) and the `feature-image`
12
- * pipeline. Both go away here:
12
+ * Replaces the legacy calendar.md + workflow store rendering. The
13
+ * scaffold (folio, masthead, filter strip, layout) is preserved so
14
+ * existing CSS keeps working.
13
15
  *
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`.
16
+ * The renderer's data flow:
17
+ * 1. loadDashboardData reads every sidecar and groups by stage.
18
+ * 2. Each of the eight stages renders via `renderStageSection`.
19
+ * 3. The Distribution placeholder pins beneath the stage sections.
20
+ *
21
+ * The legacy export `renderDashboard` stays — server.ts wires it as
22
+ * the page handler. The `getIndex` parameter is preserved for
23
+ * signature compatibility with the override resolver in server.ts;
24
+ * the new dashboard does not currently consume it (sidecars are the
25
+ * data source, not the on-disk content tree).
20
26
  */
21
- import type { ContentIndex } from '@deskwork/core/content-index';
22
27
  import type { StudioContext } from '../routes/api.ts';
28
+ import type { ContentIndex } from '@deskwork/core/content-index';
23
29
  /**
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.
30
+ * Per-request content-index getter. Preserved for compatibility with
31
+ * `runTemplateOverride` in server.ts the override resolver calls
32
+ * the dashboard with `(ctx, getIndex)` and we keep the signature
33
+ * symmetric. Sidecar-driven rendering does not consume it directly.
29
34
  */
30
35
  export type DashboardIndexGetter = (site: string) => ContentIndex;
31
- export declare function renderDashboard(ctx: StudioContext, getIndex?: DashboardIndexGetter): string;
36
+ /**
37
+ * Render the studio dashboard. Async because sidecar reads hit disk;
38
+ * the route handler in server.ts awaits the result before sending it.
39
+ */
40
+ export declare function renderDashboard(ctx: StudioContext, getIndex?: DashboardIndexGetter): Promise<string>;
32
41
  //# sourceMappingURL=dashboard.d.ts.map
@@ -1 +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;AA84BlE,wBAAgB,eAAe,CAC7B,GAAG,EAAE,aAAa,EAClB,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,MAAM,CAwDR"}
1
+ {"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../src/pages/dashboard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAUtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEjE;;;;;GAKG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,YAAY,CAAC;AAElE;;;GAGG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,aAAa,EAClB,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,OAAO,CAAC,MAAM,CAAC,CAsCjB"}