@deskwork/studio 0.18.0 → 0.19.1
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/pages/dashboard/affordances.d.ts +19 -24
- package/dist/pages/dashboard/affordances.d.ts.map +1 -1
- package/dist/pages/dashboard/affordances.js +37 -36
- package/dist/pages/dashboard/affordances.js.map +1 -1
- package/dist/pages/dashboard/affordances.ts +41 -40
- package/dist/pages/dashboard/header.d.ts +9 -3
- package/dist/pages/dashboard/header.d.ts.map +1 -1
- package/dist/pages/dashboard/header.js +9 -29
- package/dist/pages/dashboard/header.js.map +1 -1
- package/dist/pages/dashboard/header.ts +9 -31
- package/dist/pages/dashboard/section.d.ts +42 -15
- package/dist/pages/dashboard/section.d.ts.map +1 -1
- package/dist/pages/dashboard/section.js +118 -69
- package/dist/pages/dashboard/section.js.map +1 -1
- package/dist/pages/dashboard/section.ts +120 -74
- package/dist/pages/dashboard.d.ts +23 -17
- package/dist/pages/dashboard.d.ts.map +1 -1
- package/dist/pages/dashboard.js +105 -25
- package/dist/pages/dashboard.js.map +1 -1
- package/dist/pages/dashboard.ts +107 -26
- package/dist/pages/help.js +1 -1
- package/dist/pages/help.ts +1 -1
- package/dist/pages/index.d.ts +8 -6
- package/dist/pages/index.d.ts.map +1 -1
- package/dist/pages/index.js +16 -14
- package/dist/pages/index.js.map +1 -1
- package/dist/pages/index.ts +16 -14
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +11 -0
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
package/dist/pages/dashboard.js
CHANGED
|
@@ -1,28 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Studio dashboard page — `/dev/editorial-studio`.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
4
|
+
* The dashboard renders eight stage sections — Ideas → Planned →
|
|
5
|
+
* Outlining → Drafting → Final → Published, plus Blocked and
|
|
6
|
+
* Cancelled — backed by sidecar reads under
|
|
7
|
+
* `<projectRoot>/.deskwork/entries/*.json`, with a Distribution
|
|
8
|
+
* placeholder pinned at the end. Each row carries the entry's slug,
|
|
9
|
+
* title, updated-at timestamp, and stage-gated verb buttons that
|
|
10
|
+
* clipboard-copy `/deskwork:<verb> <slug>` (THESIS Consequence 2 —
|
|
11
|
+
* the studio routes commands; skills do the work). On phone (≤600px)
|
|
12
|
+
* each stage section is collapsed by default and fronted by a tile
|
|
13
|
+
* (see Compact-1 in DESIGN-STANDARDS.md); on desktop everything is
|
|
14
|
+
* expanded with the existing `<h2 class="er-section-head">` heading
|
|
15
|
+
* carrying the stage name.
|
|
11
16
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
17
|
+
* Per DESKWORK-STATE-MACHINE.md (Commandment III), reviewState is
|
|
18
|
+
* RETIRED. Rows do NOT carry per-stage iteration counts or
|
|
19
|
+
* reviewState badges; that legacy "at-a-glance" surfacing was
|
|
20
|
+
* removed in v0.19.
|
|
15
21
|
*
|
|
16
22
|
* The renderer's data flow:
|
|
17
23
|
* 1. loadDashboardData reads every sidecar and groups by stage.
|
|
18
|
-
* 2. Each
|
|
19
|
-
* 3. The Distribution placeholder
|
|
24
|
+
* 2. Each stage renders via `renderStageSection`.
|
|
25
|
+
* 3. The Distribution placeholder renders below the stage sections.
|
|
26
|
+
* 4. The mobile-only Compose chrome (FAB + slide-up sheet) renders
|
|
27
|
+
* at the page tail; CSS hides it on desktop.
|
|
20
28
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* the new dashboard does not currently consume it (sidecars are the
|
|
25
|
-
* data source, not the on-disk content tree).
|
|
29
|
+
* `getIndex` is preserved for signature compatibility with the
|
|
30
|
+
* override resolver in server.ts; the dashboard does not consume it
|
|
31
|
+
* (sidecars are the data source, not the on-disk content tree).
|
|
26
32
|
*/
|
|
27
33
|
import { html, unsafe } from "./html.js";
|
|
28
34
|
import { layout } from "./layout.js";
|
|
@@ -30,7 +36,6 @@ import { renderEditorialFolio } from "./chrome.js";
|
|
|
30
36
|
import { loadDashboardData, DASHBOARD_STAGE_ORDER } from "./dashboard/data.js";
|
|
31
37
|
import { renderStageSection, renderDistributionPlaceholder, } from "./dashboard/section.js";
|
|
32
38
|
import { renderHeader, renderFilterStrip } from "./dashboard/header.js";
|
|
33
|
-
import { renderPressQueue } from "./dashboard/press-queue.js";
|
|
34
39
|
/**
|
|
35
40
|
* Render the studio dashboard. Async because sidecar reads hit disk;
|
|
36
41
|
* the route handler in server.ts awaits the result before sending it.
|
|
@@ -45,19 +50,20 @@ export async function renderDashboard(ctx, getIndex) {
|
|
|
45
50
|
const bucket = data.byStage.get(stage) ?? [];
|
|
46
51
|
return renderStageSection(stage, bucket, defaultSite).__raw;
|
|
47
52
|
}).join('\n');
|
|
53
|
+
// The press queue (right-rail on desktop) was removed in v0.19
|
|
54
|
+
// per DESKWORK-STATE-MACHINE.md Commandment III — its primary
|
|
55
|
+
// purpose was surfacing review-state, which is RETIRED. The
|
|
56
|
+
// archive entry at docs/studio-design/ACCEPTED/2026-05-09-press-queue-removed/
|
|
57
|
+
// captures the rationale.
|
|
48
58
|
const body = html `
|
|
49
59
|
${renderEditorialFolio('dashboard', 'press-check')}
|
|
50
60
|
${renderHeader(data, ctx.projectRoot, now)}
|
|
51
61
|
<main class="er-container">
|
|
52
62
|
${renderFilterStrip()}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
${unsafe(stageSections)}
|
|
56
|
-
${renderDistributionPlaceholder()}
|
|
57
|
-
</div>
|
|
58
|
-
${renderPressQueue(data.entries, defaultSite, now)}
|
|
59
|
-
</div>
|
|
63
|
+
${unsafe(stageSections)}
|
|
64
|
+
${renderDistributionPlaceholder()}
|
|
60
65
|
</main>
|
|
66
|
+
${renderComposeChrome()}
|
|
61
67
|
<div class="er-toast" data-toast hidden></div>
|
|
62
68
|
<div class="er-poll-indicator" data-poll>auto-refresh · 10s</div>`;
|
|
63
69
|
return layout({
|
|
@@ -66,10 +72,84 @@ export async function renderDashboard(ctx, getIndex) {
|
|
|
66
72
|
'/static/css/editorial-review.css',
|
|
67
73
|
'/static/css/editorial-nav.css',
|
|
68
74
|
'/static/css/editorial-studio.css',
|
|
75
|
+
'/static/css/dashboard-mobile.css',
|
|
69
76
|
],
|
|
70
77
|
bodyAttrs: 'data-review-ui="studio"',
|
|
71
78
|
bodyHtml: body,
|
|
72
79
|
scriptModules: ['editorial-studio-client'],
|
|
73
80
|
});
|
|
74
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Mobile-only Compose chrome — a floating "+ Compose" chip at bottom-right
|
|
84
|
+
* and a slide-up sheet listing creation verbs. The chip and sheet are
|
|
85
|
+
* `display: none` on desktop (see dashboard-mobile.css); on phone the
|
|
86
|
+
* chip is the operator's primary path to /deskwork:add, /deskwork:ingest,
|
|
87
|
+
* and /deskwork:shortform-start.
|
|
88
|
+
*
|
|
89
|
+
* Each verb card is a `<button data-compose-verb data-copy="...">`. The
|
|
90
|
+
* client-side controller in `dashboard/compose-chip.ts` handles the
|
|
91
|
+
* clipboard write via copyOrShowFallback (THESIS Consequence 2 — agent
|
|
92
|
+
* does the work; the studio routes intent). We deliberately do NOT use
|
|
93
|
+
* the existing `.er-copy-btn` class on these buttons because that class
|
|
94
|
+
* triggers a textContent swap on success, which would clobber the rich
|
|
95
|
+
* verb-card markup; the compose-chip controller handles feedback via a
|
|
96
|
+
* `.is-copied` class instead.
|
|
97
|
+
*/
|
|
98
|
+
function renderComposeChrome() {
|
|
99
|
+
return unsafe(html `
|
|
100
|
+
<button class="er-compose-fab" data-compose-fab type="button" aria-controls="er-compose-sheet" aria-expanded="false">
|
|
101
|
+
<span class="er-compose-fab-glyph" aria-hidden="true">+</span>
|
|
102
|
+
<span class="er-compose-fab-label">Compose</span>
|
|
103
|
+
</button>
|
|
104
|
+
<section
|
|
105
|
+
class="er-compose-sheet"
|
|
106
|
+
id="er-compose-sheet"
|
|
107
|
+
data-compose-sheet
|
|
108
|
+
hidden
|
|
109
|
+
role="dialog"
|
|
110
|
+
aria-modal="false"
|
|
111
|
+
aria-label="Compose creation verbs"
|
|
112
|
+
>
|
|
113
|
+
<div class="er-compose-scrim" data-compose-scrim></div>
|
|
114
|
+
<div class="er-compose-panel">
|
|
115
|
+
<button class="er-compose-handle" data-compose-handle type="button" aria-label="Drag to dismiss compose sheet">
|
|
116
|
+
<span class="er-compose-handle-bar" aria-hidden="true"></span>
|
|
117
|
+
</button>
|
|
118
|
+
<header class="er-compose-head">
|
|
119
|
+
<span class="er-compose-kicker">+ Compose · creation verbs</span>
|
|
120
|
+
<span class="er-compose-meta">tap copies the command</span>
|
|
121
|
+
<button class="er-compose-close" data-compose-close type="button" aria-label="Close compose sheet">×</button>
|
|
122
|
+
</header>
|
|
123
|
+
<div class="er-compose-body">
|
|
124
|
+
<button class="er-compose-verb" data-compose-verb data-copy="/deskwork:add" type="button">
|
|
125
|
+
<span class="er-compose-verb-head">
|
|
126
|
+
<span class="er-compose-verb-glyph" aria-hidden="true">+</span>
|
|
127
|
+
<span class="er-compose-verb-name">New idea</span>
|
|
128
|
+
<span class="er-compose-verb-cmd">/deskwork:add</span>
|
|
129
|
+
</span>
|
|
130
|
+
<span class="er-compose-verb-desc">Capture a new pitch as an Ideas-stage entry. Scaffolds an idea.md with sidecar; the agent picks up from there.</span>
|
|
131
|
+
<span class="er-compose-verb-foot">tap → clipboard · paste in Claude Code</span>
|
|
132
|
+
</button>
|
|
133
|
+
<button class="er-compose-verb" data-compose-verb data-copy="/deskwork:ingest" type="button">
|
|
134
|
+
<span class="er-compose-verb-head">
|
|
135
|
+
<span class="er-compose-verb-glyph" aria-hidden="true">⤓</span>
|
|
136
|
+
<span class="er-compose-verb-name">Ingest existing</span>
|
|
137
|
+
<span class="er-compose-verb-cmd">/deskwork:ingest</span>
|
|
138
|
+
</span>
|
|
139
|
+
<span class="er-compose-verb-desc">Backfill markdown that already exists on disk. Walks files / globs, derives slug + state + date, and (after dry-run) writes calendar rows.</span>
|
|
140
|
+
<span class="er-compose-verb-foot">tap → clipboard · paste in Claude Code</span>
|
|
141
|
+
</button>
|
|
142
|
+
<button class="er-compose-verb" data-compose-verb data-copy="/deskwork:shortform-start" type="button">
|
|
143
|
+
<span class="er-compose-verb-head">
|
|
144
|
+
<span class="er-compose-verb-glyph" aria-hidden="true">⊜</span>
|
|
145
|
+
<span class="er-compose-verb-name">Shortform start</span>
|
|
146
|
+
<span class="er-compose-verb-cmd">/deskwork:shortform-start</span>
|
|
147
|
+
</span>
|
|
148
|
+
<span class="er-compose-verb-desc">Start a LinkedIn / Reddit / YouTube / Instagram draft for a Published or Drafting entry. Same review pipeline as longform.</span>
|
|
149
|
+
<span class="er-compose-verb-foot">tap → clipboard · paste in Claude Code</span>
|
|
150
|
+
</button>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
</section>`);
|
|
154
|
+
}
|
|
75
155
|
//# sourceMappingURL=dashboard.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../src/pages/dashboard.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../src/pages/dashboard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAGH,OAAO,EAAE,IAAI,EAAE,MAAM,EAAgB,MAAM,WAAW,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EACL,kBAAkB,EAClB,6BAA6B,GAC9B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAWxE;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,GAAkB,EAClB,QAA+B;IAE/B,iEAAiE;IACjE,KAAK,QAAQ,CAAC;IAEd,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACtD,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC;IAE7C,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC;IAC3C,MAAM,aAAa,GAAG,qBAAqB,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACxD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QAC7C,OAAO,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC,KAAK,CAAC;IAC9D,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,+DAA+D;IAC/D,8DAA8D;IAC9D,4DAA4D;IAC5D,+EAA+E;IAC/E,0BAA0B;IAC1B,MAAM,IAAI,GAAG,IAAI,CAAA;IACf,oBAAoB,CAAC,WAAW,EAAE,aAAa,CAAC;IAChD,YAAY,CAAC,IAAI,EAAE,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC;;MAEtC,iBAAiB,EAAE;MACnB,MAAM,CAAC,aAAa,CAAC;MACrB,6BAA6B,EAAE;;IAEjC,mBAAmB,EAAE;;oEAE2C,CAAC;IAEnE,OAAO,MAAM,CAAC;QACZ,KAAK,EAAE,mBAAmB;QAC1B,QAAQ,EAAE;YACR,kCAAkC;YAClC,+BAA+B;YAC/B,kCAAkC;YAClC,kCAAkC;SACnC;QACD,SAAS,EAAE,yBAAyB;QACpC,QAAQ,EAAE,IAAI;QACd,aAAa,EAAE,CAAC,yBAAyB,CAAC;KAC3C,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,SAAS,mBAAmB;IAC1B,OAAO,MAAM,CAAC,IAAI,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAsDP,CAAC,CAAC;AACf,CAAC"}
|
package/dist/pages/dashboard.ts
CHANGED
|
@@ -1,32 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Studio dashboard page — `/dev/editorial-studio`.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
4
|
+
* The dashboard renders eight stage sections — Ideas → Planned →
|
|
5
|
+
* Outlining → Drafting → Final → Published, plus Blocked and
|
|
6
|
+
* Cancelled — backed by sidecar reads under
|
|
7
|
+
* `<projectRoot>/.deskwork/entries/*.json`, with a Distribution
|
|
8
|
+
* placeholder pinned at the end. Each row carries the entry's slug,
|
|
9
|
+
* title, updated-at timestamp, and stage-gated verb buttons that
|
|
10
|
+
* clipboard-copy `/deskwork:<verb> <slug>` (THESIS Consequence 2 —
|
|
11
|
+
* the studio routes commands; skills do the work). On phone (≤600px)
|
|
12
|
+
* each stage section is collapsed by default and fronted by a tile
|
|
13
|
+
* (see Compact-1 in DESIGN-STANDARDS.md); on desktop everything is
|
|
14
|
+
* expanded with the existing `<h2 class="er-section-head">` heading
|
|
15
|
+
* carrying the stage name.
|
|
11
16
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
17
|
+
* Per DESKWORK-STATE-MACHINE.md (Commandment III), reviewState is
|
|
18
|
+
* RETIRED. Rows do NOT carry per-stage iteration counts or
|
|
19
|
+
* reviewState badges; that legacy "at-a-glance" surfacing was
|
|
20
|
+
* removed in v0.19.
|
|
15
21
|
*
|
|
16
22
|
* The renderer's data flow:
|
|
17
23
|
* 1. loadDashboardData reads every sidecar and groups by stage.
|
|
18
|
-
* 2. Each
|
|
19
|
-
* 3. The Distribution placeholder
|
|
24
|
+
* 2. Each stage renders via `renderStageSection`.
|
|
25
|
+
* 3. The Distribution placeholder renders below the stage sections.
|
|
26
|
+
* 4. The mobile-only Compose chrome (FAB + slide-up sheet) renders
|
|
27
|
+
* at the page tail; CSS hides it on desktop.
|
|
20
28
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* the new dashboard does not currently consume it (sidecars are the
|
|
25
|
-
* data source, not the on-disk content tree).
|
|
29
|
+
* `getIndex` is preserved for signature compatibility with the
|
|
30
|
+
* override resolver in server.ts; the dashboard does not consume it
|
|
31
|
+
* (sidecars are the data source, not the on-disk content tree).
|
|
26
32
|
*/
|
|
27
33
|
|
|
28
34
|
import type { StudioContext } from '../routes/api.ts';
|
|
29
|
-
import { html, unsafe } from './html.ts';
|
|
35
|
+
import { html, unsafe, type RawHtml } from './html.ts';
|
|
30
36
|
import { layout } from './layout.ts';
|
|
31
37
|
import { renderEditorialFolio } from './chrome.ts';
|
|
32
38
|
import { loadDashboardData, DASHBOARD_STAGE_ORDER } from './dashboard/data.ts';
|
|
@@ -35,7 +41,6 @@ import {
|
|
|
35
41
|
renderDistributionPlaceholder,
|
|
36
42
|
} from './dashboard/section.ts';
|
|
37
43
|
import { renderHeader, renderFilterStrip } from './dashboard/header.ts';
|
|
38
|
-
import { renderPressQueue } from './dashboard/press-queue.ts';
|
|
39
44
|
import type { ContentIndex } from '@deskwork/core/content-index';
|
|
40
45
|
|
|
41
46
|
/**
|
|
@@ -66,19 +71,20 @@ export async function renderDashboard(
|
|
|
66
71
|
return renderStageSection(stage, bucket, defaultSite).__raw;
|
|
67
72
|
}).join('\n');
|
|
68
73
|
|
|
74
|
+
// The press queue (right-rail on desktop) was removed in v0.19
|
|
75
|
+
// per DESKWORK-STATE-MACHINE.md Commandment III — its primary
|
|
76
|
+
// purpose was surfacing review-state, which is RETIRED. The
|
|
77
|
+
// archive entry at docs/studio-design/ACCEPTED/2026-05-09-press-queue-removed/
|
|
78
|
+
// captures the rationale.
|
|
69
79
|
const body = html`
|
|
70
80
|
${renderEditorialFolio('dashboard', 'press-check')}
|
|
71
81
|
${renderHeader(data, ctx.projectRoot, now)}
|
|
72
82
|
<main class="er-container">
|
|
73
83
|
${renderFilterStrip()}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
${unsafe(stageSections)}
|
|
77
|
-
${renderDistributionPlaceholder()}
|
|
78
|
-
</div>
|
|
79
|
-
${renderPressQueue(data.entries, defaultSite, now)}
|
|
80
|
-
</div>
|
|
84
|
+
${unsafe(stageSections)}
|
|
85
|
+
${renderDistributionPlaceholder()}
|
|
81
86
|
</main>
|
|
87
|
+
${renderComposeChrome()}
|
|
82
88
|
<div class="er-toast" data-toast hidden></div>
|
|
83
89
|
<div class="er-poll-indicator" data-poll>auto-refresh · 10s</div>`;
|
|
84
90
|
|
|
@@ -88,9 +94,84 @@ export async function renderDashboard(
|
|
|
88
94
|
'/static/css/editorial-review.css',
|
|
89
95
|
'/static/css/editorial-nav.css',
|
|
90
96
|
'/static/css/editorial-studio.css',
|
|
97
|
+
'/static/css/dashboard-mobile.css',
|
|
91
98
|
],
|
|
92
99
|
bodyAttrs: 'data-review-ui="studio"',
|
|
93
100
|
bodyHtml: body,
|
|
94
101
|
scriptModules: ['editorial-studio-client'],
|
|
95
102
|
});
|
|
96
103
|
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Mobile-only Compose chrome — a floating "+ Compose" chip at bottom-right
|
|
107
|
+
* and a slide-up sheet listing creation verbs. The chip and sheet are
|
|
108
|
+
* `display: none` on desktop (see dashboard-mobile.css); on phone the
|
|
109
|
+
* chip is the operator's primary path to /deskwork:add, /deskwork:ingest,
|
|
110
|
+
* and /deskwork:shortform-start.
|
|
111
|
+
*
|
|
112
|
+
* Each verb card is a `<button data-compose-verb data-copy="...">`. The
|
|
113
|
+
* client-side controller in `dashboard/compose-chip.ts` handles the
|
|
114
|
+
* clipboard write via copyOrShowFallback (THESIS Consequence 2 — agent
|
|
115
|
+
* does the work; the studio routes intent). We deliberately do NOT use
|
|
116
|
+
* the existing `.er-copy-btn` class on these buttons because that class
|
|
117
|
+
* triggers a textContent swap on success, which would clobber the rich
|
|
118
|
+
* verb-card markup; the compose-chip controller handles feedback via a
|
|
119
|
+
* `.is-copied` class instead.
|
|
120
|
+
*/
|
|
121
|
+
function renderComposeChrome(): RawHtml {
|
|
122
|
+
return unsafe(html`
|
|
123
|
+
<button class="er-compose-fab" data-compose-fab type="button" aria-controls="er-compose-sheet" aria-expanded="false">
|
|
124
|
+
<span class="er-compose-fab-glyph" aria-hidden="true">+</span>
|
|
125
|
+
<span class="er-compose-fab-label">Compose</span>
|
|
126
|
+
</button>
|
|
127
|
+
<section
|
|
128
|
+
class="er-compose-sheet"
|
|
129
|
+
id="er-compose-sheet"
|
|
130
|
+
data-compose-sheet
|
|
131
|
+
hidden
|
|
132
|
+
role="dialog"
|
|
133
|
+
aria-modal="false"
|
|
134
|
+
aria-label="Compose creation verbs"
|
|
135
|
+
>
|
|
136
|
+
<div class="er-compose-scrim" data-compose-scrim></div>
|
|
137
|
+
<div class="er-compose-panel">
|
|
138
|
+
<button class="er-compose-handle" data-compose-handle type="button" aria-label="Drag to dismiss compose sheet">
|
|
139
|
+
<span class="er-compose-handle-bar" aria-hidden="true"></span>
|
|
140
|
+
</button>
|
|
141
|
+
<header class="er-compose-head">
|
|
142
|
+
<span class="er-compose-kicker">+ Compose · creation verbs</span>
|
|
143
|
+
<span class="er-compose-meta">tap copies the command</span>
|
|
144
|
+
<button class="er-compose-close" data-compose-close type="button" aria-label="Close compose sheet">×</button>
|
|
145
|
+
</header>
|
|
146
|
+
<div class="er-compose-body">
|
|
147
|
+
<button class="er-compose-verb" data-compose-verb data-copy="/deskwork:add" type="button">
|
|
148
|
+
<span class="er-compose-verb-head">
|
|
149
|
+
<span class="er-compose-verb-glyph" aria-hidden="true">+</span>
|
|
150
|
+
<span class="er-compose-verb-name">New idea</span>
|
|
151
|
+
<span class="er-compose-verb-cmd">/deskwork:add</span>
|
|
152
|
+
</span>
|
|
153
|
+
<span class="er-compose-verb-desc">Capture a new pitch as an Ideas-stage entry. Scaffolds an idea.md with sidecar; the agent picks up from there.</span>
|
|
154
|
+
<span class="er-compose-verb-foot">tap → clipboard · paste in Claude Code</span>
|
|
155
|
+
</button>
|
|
156
|
+
<button class="er-compose-verb" data-compose-verb data-copy="/deskwork:ingest" type="button">
|
|
157
|
+
<span class="er-compose-verb-head">
|
|
158
|
+
<span class="er-compose-verb-glyph" aria-hidden="true">⤓</span>
|
|
159
|
+
<span class="er-compose-verb-name">Ingest existing</span>
|
|
160
|
+
<span class="er-compose-verb-cmd">/deskwork:ingest</span>
|
|
161
|
+
</span>
|
|
162
|
+
<span class="er-compose-verb-desc">Backfill markdown that already exists on disk. Walks files / globs, derives slug + state + date, and (after dry-run) writes calendar rows.</span>
|
|
163
|
+
<span class="er-compose-verb-foot">tap → clipboard · paste in Claude Code</span>
|
|
164
|
+
</button>
|
|
165
|
+
<button class="er-compose-verb" data-compose-verb data-copy="/deskwork:shortform-start" type="button">
|
|
166
|
+
<span class="er-compose-verb-head">
|
|
167
|
+
<span class="er-compose-verb-glyph" aria-hidden="true">⊜</span>
|
|
168
|
+
<span class="er-compose-verb-name">Shortform start</span>
|
|
169
|
+
<span class="er-compose-verb-cmd">/deskwork:shortform-start</span>
|
|
170
|
+
</span>
|
|
171
|
+
<span class="er-compose-verb-desc">Start a LinkedIn / Reddit / YouTube / Instagram draft for a Published or Drafting entry. Same review pipeline as longform.</span>
|
|
172
|
+
<span class="er-compose-verb-foot">tap → clipboard · paste in Claude Code</span>
|
|
173
|
+
</button>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
</section>`);
|
|
177
|
+
}
|
package/dist/pages/help.js
CHANGED
|
@@ -241,7 +241,7 @@ function renderStudioSection() {
|
|
|
241
241
|
<div class="eh-panel">
|
|
242
242
|
<p class="eh-panel-head">Secondary surfaces</p>
|
|
243
243
|
<h4>Entry review</h4>
|
|
244
|
-
<p><code>/dev/editorial-review/entry/<uuid></code>. The current-stage artifact (idea.md, plan.md, outline.md, or index.md) renders inside the review surface. Select text for a margin note; double-click anywhere to edit the markdown in place. The fixed strip's Approve / Iterate / (Reject — disabled, see <a href="https://github.com/audiocontrol-org/deskwork/issues/173">#173</a>) buttons COPY the corresponding skill command (<code>/deskwork:approve <slug></code>, etc.) to your clipboard — paste into a Claude Code chat to run. The skill reads marginalia, applies editorial judgment, edits the file (
|
|
244
|
+
<p><code>/dev/editorial-review/entry/<uuid></code>. The current-stage artifact (idea.md, plan.md, outline.md, or index.md) renders inside the review surface. Select text for a margin note; double-click anywhere to edit the markdown in place. The fixed strip's Approve / Iterate / (Reject — disabled, see <a href="https://github.com/audiocontrol-org/deskwork/issues/173">#173</a>) buttons COPY the corresponding skill command (<code>/deskwork:approve <slug></code>, etc.) to your clipboard — paste into a Claude Code chat to run. The skill reads marginalia, applies editorial judgment, edits the file (iterate), or graduates the entry to the next pipeline stage (approve). Iterate is stage-gated — only available on Ideas / Planned / Outlining / Drafting; Final locks content. The review is keyed by entry uuid, not workflow id.</p>
|
|
245
245
|
<h4>Shortform review</h4>
|
|
246
246
|
<p><code>/dev/editorial-review-shortform</code>. Cards grouped by platform. Each card has a version header, an editable textarea, and save · approve · iterate · reject controls.</p>
|
|
247
247
|
<h4>Keyboard</h4>
|
package/dist/pages/help.ts
CHANGED
|
@@ -259,7 +259,7 @@ function renderStudioSection(): RawHtml {
|
|
|
259
259
|
<div class="eh-panel">
|
|
260
260
|
<p class="eh-panel-head">Secondary surfaces</p>
|
|
261
261
|
<h4>Entry review</h4>
|
|
262
|
-
<p><code>/dev/editorial-review/entry/<uuid></code>. The current-stage artifact (idea.md, plan.md, outline.md, or index.md) renders inside the review surface. Select text for a margin note; double-click anywhere to edit the markdown in place. The fixed strip's Approve / Iterate / (Reject — disabled, see <a href="https://github.com/audiocontrol-org/deskwork/issues/173">#173</a>) buttons COPY the corresponding skill command (<code>/deskwork:approve <slug></code>, etc.) to your clipboard — paste into a Claude Code chat to run. The skill reads marginalia, applies editorial judgment, edits the file (
|
|
262
|
+
<p><code>/dev/editorial-review/entry/<uuid></code>. The current-stage artifact (idea.md, plan.md, outline.md, or index.md) renders inside the review surface. Select text for a margin note; double-click anywhere to edit the markdown in place. The fixed strip's Approve / Iterate / (Reject — disabled, see <a href="https://github.com/audiocontrol-org/deskwork/issues/173">#173</a>) buttons COPY the corresponding skill command (<code>/deskwork:approve <slug></code>, etc.) to your clipboard — paste into a Claude Code chat to run. The skill reads marginalia, applies editorial judgment, edits the file (iterate), or graduates the entry to the next pipeline stage (approve). Iterate is stage-gated — only available on Ideas / Planned / Outlining / Drafting; Final locks content. The review is keyed by entry uuid, not workflow id.</p>
|
|
263
263
|
<h4>Shortform review</h4>
|
|
264
264
|
<p><code>/dev/editorial-review-shortform</code>. Cards grouped by platform. Each card has a version header, an editable textarea, and save · approve · iterate · reject controls.</p>
|
|
265
265
|
<h4>Keyboard</h4>
|
package/dist/pages/index.d.ts
CHANGED
|
@@ -20,13 +20,15 @@ import type { Entry } from '@deskwork/core/schema/entry';
|
|
|
20
20
|
import type { StudioContext } from '../routes/api.ts';
|
|
21
21
|
/**
|
|
22
22
|
* Pick the entry that should be the default Longform-reviews target —
|
|
23
|
-
* the most-
|
|
24
|
-
*
|
|
25
|
-
*
|
|
23
|
+
* the most-recently-updated entry in a stage that permits edits
|
|
24
|
+
* (Ideas / Planned / Outlining / Drafting). Returns null when no
|
|
25
|
+
* candidate exists.
|
|
26
26
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* entry
|
|
27
|
+
* Per DESKWORK-STATE-MACHINE.md (v5): the previous reviewState filter
|
|
28
|
+
* (in-review / iterating) is retired. Stage IS the state machine; the
|
|
29
|
+
* editable-stage filter approximates "the entry the operator is most
|
|
30
|
+
* likely actively working on" without depending on the retired
|
|
31
|
+
* reviewState concept.
|
|
30
32
|
*/
|
|
31
33
|
export declare function pickDefaultLongformEntry(entries: readonly Entry[]): Entry | null;
|
|
32
34
|
export declare function renderStudioIndex(ctx: StudioContext): Promise<string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/pages/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAqDtD
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/pages/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAqDtD;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,SAAS,KAAK,EAAE,GACxB,KAAK,GAAG,IAAI,CAMd;AAmKD,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CA8B3E"}
|
package/dist/pages/index.js
CHANGED
|
@@ -30,18 +30,19 @@ const LONGFORM_PIPELINE_STAGES = new Set([
|
|
|
30
30
|
]);
|
|
31
31
|
/**
|
|
32
32
|
* Pick the entry that should be the default Longform-reviews target —
|
|
33
|
-
* the most-
|
|
34
|
-
*
|
|
35
|
-
*
|
|
33
|
+
* the most-recently-updated entry in a stage that permits edits
|
|
34
|
+
* (Ideas / Planned / Outlining / Drafting). Returns null when no
|
|
35
|
+
* candidate exists.
|
|
36
36
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* entry
|
|
37
|
+
* Per DESKWORK-STATE-MACHINE.md (v5): the previous reviewState filter
|
|
38
|
+
* (in-review / iterating) is retired. Stage IS the state machine; the
|
|
39
|
+
* editable-stage filter approximates "the entry the operator is most
|
|
40
|
+
* likely actively working on" without depending on the retired
|
|
41
|
+
* reviewState concept.
|
|
40
42
|
*/
|
|
41
43
|
export function pickDefaultLongformEntry(entries) {
|
|
42
44
|
const candidates = entries
|
|
43
45
|
.filter((e) => LONGFORM_PIPELINE_STAGES.has(e.currentStage))
|
|
44
|
-
.filter((e) => e.reviewState === 'in-review' || e.reviewState === 'iterating')
|
|
45
46
|
.slice()
|
|
46
47
|
.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
47
48
|
return candidates[0] ?? null;
|
|
@@ -56,13 +57,14 @@ async function buildSections(ctx) {
|
|
|
56
57
|
}
|
|
57
58
|
})();
|
|
58
59
|
const longformDefaultEntry = pickDefaultLongformEntry(entries);
|
|
59
|
-
// Issue #107: III links to the most-
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
// entry-keyed review route
|
|
60
|
+
// Issue #107: III links to the most-recently-updated longform entry
|
|
61
|
+
// in a stage that permits edits (Ideas / Planned / Outlining /
|
|
62
|
+
// Drafting), else falls back to the dashboard's Drafting section
|
|
63
|
+
// anchor. The link target is the entry-keyed review route
|
|
64
|
+
// `/dev/editorial-review/entry/<uuid>`.
|
|
63
65
|
const longformLinkHref = longformDefaultEntry !== null
|
|
64
66
|
? `/dev/editorial-review/entry/${longformDefaultEntry.uuid}`
|
|
65
|
-
: '/dev/editorial-studio#stage-
|
|
67
|
+
: '/dev/editorial-studio#stage-drafting';
|
|
66
68
|
return [
|
|
67
69
|
{
|
|
68
70
|
ornament: '¶',
|
|
@@ -100,8 +102,8 @@ async function buildSections(ctx) {
|
|
|
100
102
|
desc: 'Per-entry margin notes, decisions, iterate flow.',
|
|
101
103
|
hint: 'entry-by-entry',
|
|
102
104
|
postHint: longformDefaultEntry !== null
|
|
103
|
-
? `Defaults to the most-
|
|
104
|
-
: 'Defaults to the dashboard\'s
|
|
105
|
+
? `Defaults to the most-recently-updated longform (${longformDefaultEntry.slug}). Or reach via the Dashboard or Content view.`
|
|
106
|
+
: 'Defaults to the dashboard\'s Drafting section. Add or ingest a longform entry to populate the per-entry deep-link.',
|
|
105
107
|
},
|
|
106
108
|
],
|
|
107
109
|
},
|
package/dist/pages/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/pages/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAGzD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAgB,MAAM,WAAW,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAEnD,iFAAiF;AACjF,MAAM,wBAAwB,GAAuC,IAAI,GAAG,CAAC;IAC3E,OAAO;IACP,SAAS;IACT,WAAW;IACX,UAAU;IACV,OAAO;CACR,CAAC,CAAC;AAyCH
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/pages/index.ts"],"names":[],"mappings":"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"}
|
package/dist/pages/index.ts
CHANGED
|
@@ -74,20 +74,21 @@ interface IndexSection {
|
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
76
|
* Pick the entry that should be the default Longform-reviews target —
|
|
77
|
-
* the most-
|
|
78
|
-
*
|
|
79
|
-
*
|
|
77
|
+
* the most-recently-updated entry in a stage that permits edits
|
|
78
|
+
* (Ideas / Planned / Outlining / Drafting). Returns null when no
|
|
79
|
+
* candidate exists.
|
|
80
80
|
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
* entry
|
|
81
|
+
* Per DESKWORK-STATE-MACHINE.md (v5): the previous reviewState filter
|
|
82
|
+
* (in-review / iterating) is retired. Stage IS the state machine; the
|
|
83
|
+
* editable-stage filter approximates "the entry the operator is most
|
|
84
|
+
* likely actively working on" without depending on the retired
|
|
85
|
+
* reviewState concept.
|
|
84
86
|
*/
|
|
85
87
|
export function pickDefaultLongformEntry(
|
|
86
88
|
entries: readonly Entry[],
|
|
87
89
|
): Entry | null {
|
|
88
90
|
const candidates = entries
|
|
89
91
|
.filter((e) => LONGFORM_PIPELINE_STAGES.has(e.currentStage))
|
|
90
|
-
.filter((e) => e.reviewState === 'in-review' || e.reviewState === 'iterating')
|
|
91
92
|
.slice()
|
|
92
93
|
.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
93
94
|
return candidates[0] ?? null;
|
|
@@ -102,14 +103,15 @@ async function buildSections(ctx: StudioContext): Promise<readonly IndexSection[
|
|
|
102
103
|
}
|
|
103
104
|
})();
|
|
104
105
|
const longformDefaultEntry = pickDefaultLongformEntry(entries);
|
|
105
|
-
// Issue #107: III links to the most-
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
// entry-keyed review route
|
|
106
|
+
// Issue #107: III links to the most-recently-updated longform entry
|
|
107
|
+
// in a stage that permits edits (Ideas / Planned / Outlining /
|
|
108
|
+
// Drafting), else falls back to the dashboard's Drafting section
|
|
109
|
+
// anchor. The link target is the entry-keyed review route
|
|
110
|
+
// `/dev/editorial-review/entry/<uuid>`.
|
|
109
111
|
const longformLinkHref =
|
|
110
112
|
longformDefaultEntry !== null
|
|
111
113
|
? `/dev/editorial-review/entry/${longformDefaultEntry.uuid}`
|
|
112
|
-
: '/dev/editorial-studio#stage-
|
|
114
|
+
: '/dev/editorial-studio#stage-drafting';
|
|
113
115
|
|
|
114
116
|
return [
|
|
115
117
|
{
|
|
@@ -149,8 +151,8 @@ async function buildSections(ctx: StudioContext): Promise<readonly IndexSection[
|
|
|
149
151
|
hint: 'entry-by-entry',
|
|
150
152
|
postHint:
|
|
151
153
|
longformDefaultEntry !== null
|
|
152
|
-
? `Defaults to the most-
|
|
153
|
-
: 'Defaults to the dashboard\'s
|
|
154
|
+
? `Defaults to the most-recently-updated longform (${longformDefaultEntry.slug}). Or reach via the Dashboard or Content view.`
|
|
155
|
+
: 'Defaults to the dashboard\'s Drafting section. Add or ingest a longform entry to populate the per-entry deep-link.',
|
|
154
156
|
},
|
|
155
157
|
],
|
|
156
158
|
},
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAS5B,OAAO,EAAmB,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAwBtE,UAAU,OAAO;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,YAAY,EAAE,OAAO,CAAC;IACtB;;;;;OAKG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,qEAAqE;IACrE,WAAW,EAAE,OAAO,CAAC;CACtB;AAKD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CA8CpD;AAkFD,wBAAgB,SAAS,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAS5B,OAAO,EAAmB,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAwBtE,UAAU,OAAO;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,YAAY,EAAE,OAAO,CAAC;IACtB;;;;;OAKG;IACH,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,qEAAqE;IACrE,WAAW,EAAE,OAAO,CAAC;CACtB;AAKD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CA8CpD;AAkFD,wBAAgB,SAAS,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAwPlD"}
|
package/dist/server.js
CHANGED
|
@@ -383,6 +383,17 @@ export function createApp(ctx) {
|
|
|
383
383
|
}));
|
|
384
384
|
// Convenience root redirect to the studio index.
|
|
385
385
|
app.get('/', (c) => c.redirect('/dev/'));
|
|
386
|
+
// Document root — serves top-level project files (DESKWORK-STATE-MACHINE.md,
|
|
387
|
+
// THESIS.md, README.md, DESIGN-STANDARDS.md, etc.) so the studio's URL
|
|
388
|
+
// namespace mirrors the project filesystem layout. Registered LAST so
|
|
389
|
+
// every more-specific route (`/dev/*`, `/api/*`, `/static/*`, the `/`
|
|
390
|
+
// redirect) matches first; only unmatched paths fall through to a
|
|
391
|
+
// filesystem lookup against the project root. Symlinks in `public/`
|
|
392
|
+
// didn't survive serveStatic's resolution (operator-reported 2026-05-09);
|
|
393
|
+
// serving the project root directly removes the need for symlinks at all.
|
|
394
|
+
app.use('*', serveStatic({
|
|
395
|
+
root: ctx.projectRoot,
|
|
396
|
+
}));
|
|
386
397
|
return app;
|
|
387
398
|
}
|
|
388
399
|
async function main() {
|