@deskwork/studio 0.17.1 → 0.19.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/pages/dashboard/affordances.d.ts +19 -24
- package/dist/pages/dashboard/affordances.d.ts.map +1 -1
- package/dist/pages/dashboard/affordances.js +52 -41
- package/dist/pages/dashboard/affordances.js.map +1 -1
- package/dist/pages/dashboard/affordances.ts +56 -45
- 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 +10 -30
- package/dist/pages/dashboard/header.js.map +1 -1
- package/dist/pages/dashboard/header.ts +10 -32
- 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 -49
- package/dist/pages/dashboard/section.js.map +1 -1
- package/dist/pages/dashboard/section.ts +120 -53
- 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/entry-review/decision-strip.d.ts.map +1 -1
- package/dist/pages/entry-review/decision-strip.js +4 -0
- package/dist/pages/entry-review/decision-strip.js.map +1 -1
- package/dist/pages/entry-review/edit-toolbar.d.ts +1 -1
- package/dist/pages/entry-review/edit-toolbar.d.ts.map +1 -1
- package/dist/pages/entry-review/edit-toolbar.js +9 -1
- package/dist/pages/entry-review/edit-toolbar.js.map +1 -1
- package/dist/pages/entry-review/index.d.ts.map +1 -1
- package/dist/pages/entry-review/index.js +14 -4
- package/dist/pages/entry-review/index.js.map +1 -1
- package/dist/pages/entry-review/mobile-bar.d.ts +62 -0
- package/dist/pages/entry-review/mobile-bar.d.ts.map +1 -0
- package/dist/pages/entry-review/mobile-bar.js +155 -0
- package/dist/pages/entry-review/mobile-bar.js.map +1 -0
- package/dist/pages/entry-review/outline-drawer.d.ts +17 -7
- package/dist/pages/entry-review/outline-drawer.d.ts.map +1 -1
- package/dist/pages/entry-review/outline-drawer.js +58 -14
- package/dist/pages/entry-review/outline-drawer.js.map +1 -1
- 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/pages/layout.js +1 -1
- package/dist/pages/layout.ts +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +12 -1
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mobile bottom-bar + sheet host for the entry-keyed review surface.
|
|
3
|
+
*
|
|
4
|
+
* Renders a 3-tab bottom bar and a sheet host that slides up from the
|
|
5
|
+
* viewport bottom. Visible only at <48rem (phone widths) — see
|
|
6
|
+
* editorial-review.css. Above that the bar and sheet are display:none
|
|
7
|
+
* and the desktop layout (in-flow marginalia column, outline-drawer
|
|
8
|
+
* overlay, decision strip in the top strip) carries the surface.
|
|
9
|
+
*
|
|
10
|
+
* Design references:
|
|
11
|
+
* - Review tabs: plugins/deskwork-studio/public/mockups/mobile-1-bottom-sheet.html
|
|
12
|
+
* - Editor tabs: plugins/deskwork-studio/public/mockups/editor-2-press-check-tabbar.html
|
|
13
|
+
*
|
|
14
|
+
* The bar carries SIX tab buttons; CSS shows three or four at a time
|
|
15
|
+
* keyed off `body[data-edit-mode="editing"]`:
|
|
16
|
+
* - Review mode: Outline · Notes · Scrapbook · Actions (4-column grid)
|
|
17
|
+
* - Edit mode: Format · Notes · Save (3-column grid)
|
|
18
|
+
* The Notes tab is shared between modes (review notes the operator
|
|
19
|
+
* leaves carry across into edit). The bar's data-mode attribute is
|
|
20
|
+
* also flipped by the client so additional state-dependent styles
|
|
21
|
+
* (Save dirty glow, etc.) can key off it. The Scrapbook tab uses a
|
|
22
|
+
* grid glyph (▦) and a kraft-tone count badge to distinguish it
|
|
23
|
+
* from the red-pencil Notes badge — folio is "context for the
|
|
24
|
+
* entry," not an action peer of decisions.
|
|
25
|
+
*
|
|
26
|
+
* The sheet has five content slots (outline / notes / actions /
|
|
27
|
+
* scrapbook / format).
|
|
28
|
+
* The client controller (`entry-review/mobile-sheet-bar.ts`) populates
|
|
29
|
+
* each at first open. Slot sources:
|
|
30
|
+
* - outline: clone of `.er-outline-drawer-body`
|
|
31
|
+
* - notes: actual `[data-sidebar-list]` element MOVED in on phone
|
|
32
|
+
* (preserves event listeners; see mobile-sheet-bar.ts)
|
|
33
|
+
* - actions: rendered fresh from the decision verbs
|
|
34
|
+
* - format: the press-check key grid rendered server-side below
|
|
35
|
+
*
|
|
36
|
+
* The Save tab is NOT a sheet — it triggers the existing
|
|
37
|
+
* `[data-action="save-version"]` handler directly (the one allowed
|
|
38
|
+
* file mutation per THESIS Consequence 2).
|
|
39
|
+
*/
|
|
40
|
+
import { html, unsafe } from "../html.js";
|
|
41
|
+
export function renderMobileBar() {
|
|
42
|
+
return unsafe(html `
|
|
43
|
+
<nav class="er-mobile-bar" data-mobile-bar aria-label="Surface tabs">
|
|
44
|
+
<button class="er-mobile-tab er-mobile-tab--review" data-mobile-sheet="outline" type="button" aria-controls="er-mobile-sheet" aria-expanded="false">
|
|
45
|
+
<span class="er-mobile-tab-glyph" aria-hidden="true">§</span>
|
|
46
|
+
<span class="er-mobile-tab-label">Outline</span>
|
|
47
|
+
</button>
|
|
48
|
+
<button class="er-mobile-tab er-mobile-tab--edit" data-mobile-sheet="format" type="button" aria-controls="er-mobile-sheet" aria-expanded="false">
|
|
49
|
+
<span class="er-mobile-tab-glyph" aria-hidden="true">¶</span>
|
|
50
|
+
<span class="er-mobile-tab-label">Format</span>
|
|
51
|
+
</button>
|
|
52
|
+
<button class="er-mobile-tab" data-mobile-sheet="notes" type="button" aria-controls="er-mobile-sheet" aria-expanded="false">
|
|
53
|
+
<span class="er-mobile-tab-glyph" aria-hidden="true">✎</span>
|
|
54
|
+
<span class="er-mobile-tab-label">Notes</span>
|
|
55
|
+
<span class="er-mobile-tab-count" data-notes-count hidden>0</span>
|
|
56
|
+
</button>
|
|
57
|
+
<button class="er-mobile-tab er-mobile-tab--review er-mobile-tab--scrapbook" data-mobile-sheet="scrapbook" type="button" aria-controls="er-mobile-sheet" aria-expanded="false">
|
|
58
|
+
<span class="er-mobile-tab-glyph" aria-hidden="true">▦</span>
|
|
59
|
+
<span class="er-mobile-tab-label">Scrapbook</span>
|
|
60
|
+
<span class="er-mobile-tab-count er-mobile-tab-count--kraft" data-scrapbook-count hidden>0</span>
|
|
61
|
+
</button>
|
|
62
|
+
<button class="er-mobile-tab er-mobile-tab--review" data-mobile-sheet="actions" type="button" aria-controls="er-mobile-sheet" aria-expanded="false">
|
|
63
|
+
<span class="er-mobile-tab-glyph" aria-hidden="true">⊕</span>
|
|
64
|
+
<span class="er-mobile-tab-label">Actions</span>
|
|
65
|
+
</button>
|
|
66
|
+
<button class="er-mobile-tab er-mobile-tab--edit er-mobile-tab--save" data-mobile-action="save" type="button">
|
|
67
|
+
<span class="er-mobile-tab-glyph" aria-hidden="true">⊕</span>
|
|
68
|
+
<span class="er-mobile-tab-label">Save</span>
|
|
69
|
+
</button>
|
|
70
|
+
</nav>`);
|
|
71
|
+
}
|
|
72
|
+
function renderFormatGrid() {
|
|
73
|
+
return html `
|
|
74
|
+
<div class="er-fkey-section">Headings</div>
|
|
75
|
+
<div class="er-fkey-grid">
|
|
76
|
+
<button type="button" class="er-fkey er-fkey--h1" data-fkey="h1"><span class="er-fkey-face">H1</span><span class="er-fkey-label">Title</span></button>
|
|
77
|
+
<button type="button" class="er-fkey er-fkey--h2" data-fkey="h2"><span class="er-fkey-face">H2</span><span class="er-fkey-label">Section</span></button>
|
|
78
|
+
<button type="button" class="er-fkey er-fkey--h3" data-fkey="h3"><span class="er-fkey-face">H3</span><span class="er-fkey-label">Sub</span></button>
|
|
79
|
+
<button type="button" class="er-fkey er-fkey--hr" data-fkey="hr"><span class="er-fkey-face">— · — · —</span><span class="er-fkey-label">Rule</span></button>
|
|
80
|
+
</div>
|
|
81
|
+
<div class="er-fkey-section">Inline</div>
|
|
82
|
+
<div class="er-fkey-grid">
|
|
83
|
+
<button type="button" class="er-fkey er-fkey--bold" data-fkey="bold"><span class="er-fkey-face"><strong>B</strong></span><span class="er-fkey-label">Bold</span></button>
|
|
84
|
+
<button type="button" class="er-fkey er-fkey--em" data-fkey="em"><span class="er-fkey-face"><em>I</em></span><span class="er-fkey-label">Italic</span></button>
|
|
85
|
+
<button type="button" class="er-fkey er-fkey--code" data-fkey="code"><span class="er-fkey-face">\` \`</span><span class="er-fkey-label">Code</span></button>
|
|
86
|
+
<button type="button" class="er-fkey er-fkey--link" data-fkey="link"><span class="er-fkey-face">link</span><span class="er-fkey-label">Link</span></button>
|
|
87
|
+
</div>
|
|
88
|
+
<div class="er-fkey-section">Block</div>
|
|
89
|
+
<div class="er-fkey-grid">
|
|
90
|
+
<button type="button" class="er-fkey er-fkey--list" data-fkey="list"><span class="er-fkey-face">— ·<br>— ·</span><span class="er-fkey-label">List</span></button>
|
|
91
|
+
<button type="button" class="er-fkey er-fkey--ol" data-fkey="ol"><span class="er-fkey-face">1·<br>2·</span><span class="er-fkey-label">Numbered</span></button>
|
|
92
|
+
<button type="button" class="er-fkey er-fkey--quote" data-fkey="quote"><span class="er-fkey-face">"</span><span class="er-fkey-label">Quote</span></button>
|
|
93
|
+
<button type="button" class="er-fkey er-fkey--fence" data-fkey="fence"><span class="er-fkey-face">\`\`\`</span><span class="er-fkey-label">Code block</span></button>
|
|
94
|
+
</div>`;
|
|
95
|
+
}
|
|
96
|
+
export function renderMobileSheet() {
|
|
97
|
+
return unsafe(html `
|
|
98
|
+
<section
|
|
99
|
+
class="er-mobile-sheet"
|
|
100
|
+
id="er-mobile-sheet"
|
|
101
|
+
data-mobile-sheet-host
|
|
102
|
+
hidden
|
|
103
|
+
aria-label="Surface sheet"
|
|
104
|
+
role="dialog"
|
|
105
|
+
aria-modal="false"
|
|
106
|
+
>
|
|
107
|
+
<button class="er-mobile-sheet-handle" data-mobile-sheet-handle type="button" aria-label="Drag to dismiss the sheet">
|
|
108
|
+
<span class="er-mobile-sheet-handle-bar" aria-hidden="true"></span>
|
|
109
|
+
</button>
|
|
110
|
+
<header class="er-mobile-sheet-head">
|
|
111
|
+
<span class="er-mobile-sheet-kicker" data-mobile-sheet-kicker></span>
|
|
112
|
+
<span class="er-mobile-sheet-meta" data-mobile-sheet-meta></span>
|
|
113
|
+
<button class="er-mobile-sheet-close" data-mobile-sheet-close type="button" aria-label="Close sheet">×</button>
|
|
114
|
+
</header>
|
|
115
|
+
<div class="er-mobile-sheet-body" data-mobile-sheet-body>
|
|
116
|
+
<div class="er-mobile-sheet-slot" data-mobile-sheet-slot="outline" hidden></div>
|
|
117
|
+
<div class="er-mobile-sheet-slot" data-mobile-sheet-slot="notes" hidden></div>
|
|
118
|
+
<div class="er-mobile-sheet-slot" data-mobile-sheet-slot="actions" hidden></div>
|
|
119
|
+
<div class="er-mobile-sheet-slot er-mobile-sheet-slot--scrapbook" data-mobile-sheet-slot="scrapbook" hidden></div>
|
|
120
|
+
<div class="er-mobile-sheet-slot er-mobile-sheet-slot--format" data-mobile-sheet-slot="format" hidden>${unsafe(renderFormatGrid())}</div>
|
|
121
|
+
</div>
|
|
122
|
+
</section>`);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Phone-only Source/Preview pill rendered into the top strip when in
|
|
126
|
+
* edit mode. Uses the same `data-edit-view` attribute as the desktop
|
|
127
|
+
* edit toolbar so `editModeBtns` (queried via that attribute in
|
|
128
|
+
* entry-review-client.ts) auto-binds clicks. CSS reveals it only on
|
|
129
|
+
* phone + edit mode; the desktop edit toolbar is hidden in that
|
|
130
|
+
* combination.
|
|
131
|
+
*/
|
|
132
|
+
export function renderStripModeSegment() {
|
|
133
|
+
return unsafe(html `
|
|
134
|
+
<span class="er-strip-mode-mobile" data-strip-mode-mobile aria-hidden="true">
|
|
135
|
+
<button type="button" class="er-strip-mode-btn" data-edit-view="source" aria-pressed="true">Source</button>
|
|
136
|
+
<button type="button" class="er-strip-mode-btn" data-edit-view="preview" aria-pressed="false">Preview</button>
|
|
137
|
+
</span>`);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Phone-only "✕ Done" exit affordance in the strip. Visible only when
|
|
141
|
+
* editing on phone (CSS-gated). Click dispatches into the existing
|
|
142
|
+
* toggle-edit handler — preserves the confirmDiscard prompt when the
|
|
143
|
+
* buffer is dirty, immediate exit when clean. Sits alongside the
|
|
144
|
+
* existing back-link (which continues to navigate home regardless of
|
|
145
|
+
* mode) so the operator always has both "leave editor" and "leave
|
|
146
|
+
* page" affordances available.
|
|
147
|
+
*/
|
|
148
|
+
export function renderStripEditExit() {
|
|
149
|
+
return unsafe(html `
|
|
150
|
+
<button type="button" class="er-strip-edit-done" data-strip-edit-done aria-label="Exit editor">
|
|
151
|
+
<span class="er-strip-edit-done-glyph" aria-hidden="true">✕</span>
|
|
152
|
+
<span class="er-strip-edit-done-label">Done</span>
|
|
153
|
+
</button>`);
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=mobile-bar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mobile-bar.js","sourceRoot":"","sources":["../../../src/pages/entry-review/mobile-bar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,EAAgB,MAAM,YAAY,CAAC;AAExD,MAAM,UAAU,eAAe;IAC7B,OAAO,MAAM,CAAC,IAAI,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;WA4BT,CAAC,CAAC;AACb,CAAC;AAED,SAAS,gBAAgB;IACvB,OAAO,IAAI,CAAA;;;;;;;;;;;;;;;;;;;;;WAqBF,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,MAAM,CAAC,IAAI,CAAA;;;;;;;;;;;;;;;;;;;;;;;gHAuB4F,MAAM,CAAC,gBAAgB,EAAE,CAAC;;eAE3H,CAAC,CAAC;AACjB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB;IACpC,OAAO,MAAM,CAAC,IAAI,CAAA;;;;YAIR,CAAC,CAAC;AACd,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO,MAAM,CAAC,IAAI,CAAA;;;;cAIN,CAAC,CAAC;AAChB,CAAC"}
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Outline drawer for the entry-keyed press-check surface
|
|
2
|
+
* Outline + TOC drawer for the entry-keyed press-check surface.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Two stacked sections inside the same drawer:
|
|
5
|
+
* 1. Curated outline (#169-pre, T9). Conditional on the entry's body
|
|
6
|
+
* markdown carrying a `## Outline` section (extracted via
|
|
7
|
+
* `splitOutline`). When present, this is the operator's hand-
|
|
8
|
+
* authored briefing sheet — read-only here; edit via
|
|
9
|
+
* `/deskwork:iterate --kind outline`.
|
|
10
|
+
* 2. Table of contents (#244). Auto-extracted from the rendered
|
|
11
|
+
* body's h2/h3/h4 headings (each carries a slugified `id` from
|
|
12
|
+
* rehype-slug). Always visible when there are 2+ entries; clicks
|
|
13
|
+
* jump to the heading via native `<a href="#id">` smooth-scroll.
|
|
14
|
+
*
|
|
15
|
+
* The pull-tab on the left edge surfaces whenever EITHER section has
|
|
16
|
+
* content. The drawer is `position: fixed` so it never scrolls out
|
|
17
|
+
* of view.
|
|
9
18
|
*/
|
|
10
19
|
import { type RawHtml } from '../html.ts';
|
|
11
|
-
|
|
20
|
+
import type { TocEntry } from '@deskwork/core/review/toc';
|
|
21
|
+
export declare function renderOutlineDrawer(outlineHtml: string, tocEntries?: readonly TocEntry[]): RawHtml;
|
|
12
22
|
//# sourceMappingURL=outline-drawer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outline-drawer.d.ts","sourceRoot":"","sources":["../../../src/pages/entry-review/outline-drawer.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"outline-drawer.d.ts","sourceRoot":"","sources":["../../../src/pages/entry-review/outline-drawer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAgB,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAE1D,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,MAAM,EACnB,UAAU,GAAE,SAAS,QAAQ,EAAO,GACnC,OAAO,CAiCT"}
|
|
@@ -1,28 +1,72 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Outline drawer for the entry-keyed press-check surface
|
|
2
|
+
* Outline + TOC drawer for the entry-keyed press-check surface.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Two stacked sections inside the same drawer:
|
|
5
|
+
* 1. Curated outline (#169-pre, T9). Conditional on the entry's body
|
|
6
|
+
* markdown carrying a `## Outline` section (extracted via
|
|
7
|
+
* `splitOutline`). When present, this is the operator's hand-
|
|
8
|
+
* authored briefing sheet — read-only here; edit via
|
|
9
|
+
* `/deskwork:iterate --kind outline`.
|
|
10
|
+
* 2. Table of contents (#244). Auto-extracted from the rendered
|
|
11
|
+
* body's h2/h3/h4 headings (each carries a slugified `id` from
|
|
12
|
+
* rehype-slug). Always visible when there are 2+ entries; clicks
|
|
13
|
+
* jump to the heading via native `<a href="#id">` smooth-scroll.
|
|
14
|
+
*
|
|
15
|
+
* The pull-tab on the left edge surfaces whenever EITHER section has
|
|
16
|
+
* content. The drawer is `position: fixed` so it never scrolls out
|
|
17
|
+
* of view.
|
|
9
18
|
*/
|
|
10
19
|
import { html, unsafe } from "../html.js";
|
|
11
|
-
export function renderOutlineDrawer(outlineHtml) {
|
|
12
|
-
const
|
|
20
|
+
export function renderOutlineDrawer(outlineHtml, tocEntries = []) {
|
|
21
|
+
const hasOutline = outlineHtml.length > 0;
|
|
22
|
+
const hasToc = tocEntries.length >= 2;
|
|
23
|
+
const tabHidden = hasOutline || hasToc ? '' : ' hidden';
|
|
24
|
+
// Drawer kicker label: prefer "Outline" when there's a curated
|
|
25
|
+
// outline (the briefing-sheet heritage); otherwise "Contents" since
|
|
26
|
+
// the auto-TOC is the dominant content. Both sections may render
|
|
27
|
+
// simultaneously when the entry has both.
|
|
28
|
+
const kicker = hasOutline ? 'Briefing sheet' : 'Contents';
|
|
13
29
|
return unsafe(html `
|
|
14
|
-
<button class="er-outline-tab" data-outline-tab type="button" aria-label="Show outline"${unsafe(
|
|
30
|
+
<button class="er-outline-tab" data-outline-tab type="button" aria-label="Show outline / contents"${unsafe(tabHidden)}>
|
|
15
31
|
<span class="er-outline-tab-label">Outline</span>
|
|
16
32
|
</button>
|
|
17
|
-
<aside class="er-outline-drawer" data-outline-drawer aria-label="Outline
|
|
33
|
+
<aside class="er-outline-drawer" data-outline-drawer aria-label="Outline + table of contents" hidden>
|
|
18
34
|
<header class="er-outline-drawer-head">
|
|
19
|
-
<
|
|
20
|
-
|
|
35
|
+
<button type="button" class="er-outline-drawer-stow" data-outline-close aria-label="Hide outline (O or Esc)" title="Hide outline (O or Esc)">
|
|
36
|
+
<span aria-hidden="true">‹</span>
|
|
37
|
+
</button>
|
|
38
|
+
<span class="er-outline-drawer-kicker">${kicker}</span>
|
|
21
39
|
</header>
|
|
22
|
-
<div class="er-outline-drawer-body" data-outline-drawer-body
|
|
40
|
+
<div class="er-outline-drawer-body" data-outline-drawer-body>
|
|
41
|
+
${unsafe(hasOutline ? outlineHtml : '')}
|
|
42
|
+
${unsafe(hasOutline && hasToc ? '<hr class="er-outline-drawer-rule" />' : '')}
|
|
43
|
+
${unsafe(hasToc ? renderTocList(tocEntries) : '')}
|
|
44
|
+
</div>
|
|
23
45
|
<footer class="er-outline-drawer-foot">
|
|
24
|
-
|
|
46
|
+
${unsafe(hasOutline
|
|
47
|
+
? '<span>Read-only · edit via <code>/deskwork:iterate --kind outline</code></span>'
|
|
48
|
+
: '<span>Auto-extracted from headings · jump-link to navigate</span>')}
|
|
25
49
|
</footer>
|
|
26
50
|
</aside>`);
|
|
27
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Render the auto-TOC as an indented list of anchor links. Depth maps
|
|
54
|
+
* directly to nesting level: depth-2 entries are top-level, depth-3
|
|
55
|
+
* entries indent once, depth-4 entries indent twice. The flat HTML
|
|
56
|
+
* shape is easier to style than nested <ul>s and reads correctly with
|
|
57
|
+
* a screen reader as long as the depth class carries an aria
|
|
58
|
+
* attribute (set in CSS via `aria-level`).
|
|
59
|
+
*/
|
|
60
|
+
function renderTocList(entries) {
|
|
61
|
+
const items = entries
|
|
62
|
+
.map((e) => html `
|
|
63
|
+
<li class="er-toc-item er-toc-item--d${e.depth}" data-toc-target="${e.id}">
|
|
64
|
+
<a class="er-toc-link" href="#${e.id}" data-toc-link>${e.text}</a>
|
|
65
|
+
</li>`)
|
|
66
|
+
.join('');
|
|
67
|
+
return html `
|
|
68
|
+
<nav class="er-toc" aria-label="Table of contents">
|
|
69
|
+
<ul class="er-toc-list">${unsafe(items)}</ul>
|
|
70
|
+
</nav>`;
|
|
71
|
+
}
|
|
28
72
|
//# sourceMappingURL=outline-drawer.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outline-drawer.js","sourceRoot":"","sources":["../../../src/pages/entry-review/outline-drawer.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"outline-drawer.js","sourceRoot":"","sources":["../../../src/pages/entry-review/outline-drawer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,EAAgB,MAAM,YAAY,CAAC;AAGxD,MAAM,UAAU,mBAAmB,CACjC,WAAmB,EACnB,aAAkC,EAAE;IAEpC,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,IAAI,CAAC,CAAC;IACtC,MAAM,SAAS,GAAG,UAAU,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAExD,+DAA+D;IAC/D,oEAAoE;IACpE,iEAAiE;IACjE,0CAA0C;IAC1C,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,UAAU,CAAC;IAE1D,OAAO,MAAM,CAAC,IAAI,CAAA;wGACoF,MAAM,CAAC,SAAS,CAAC;;;;;;;;iDAQxE,MAAM;;;UAG7C,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;UACrC,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,CAAC,CAAC,uCAAuC,CAAC,CAAC,CAAC,EAAE,CAAC;UAC3E,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;;;UAG/C,MAAM,CAAC,UAAU;QACjB,CAAC,CAAC,iFAAiF;QACnF,CAAC,CAAC,mEAAmE,CAAC;;aAEnE,CAAC,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,aAAa,CAAC,OAA4B;IACjD,MAAM,KAAK,GAAG,OAAO;SAClB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAA;6CACyB,CAAC,CAAC,KAAK,sBAAsB,CAAC,CAAC,EAAE;wCACtC,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC,IAAI;YACzD,CAAC;SACR,IAAI,CAAC,EAAE,CAAC,CAAC;IACZ,OAAO,IAAI,CAAA;;gCAEmB,MAAM,CAAC,KAAK,CAAC;WAClC,CAAC;AACZ,CAAC"}
|
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/pages/layout.js
CHANGED
|
@@ -31,7 +31,7 @@ export function layout(options) {
|
|
|
31
31
|
<html lang="en">
|
|
32
32
|
<head>
|
|
33
33
|
<meta charset="utf-8">
|
|
34
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
34
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, interactive-widget=resizes-content">
|
|
35
35
|
<meta name="robots" content="noindex">
|
|
36
36
|
<title>${escapeHtml(title)}</title>
|
|
37
37
|
${cssTags}
|
package/dist/pages/layout.ts
CHANGED
|
@@ -80,7 +80,7 @@ export function layout(options: LayoutOptions): string {
|
|
|
80
80
|
<html lang="en">
|
|
81
81
|
<head>
|
|
82
82
|
<meta charset="utf-8">
|
|
83
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
83
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, interactive-widget=resizes-content">
|
|
84
84
|
<meta name="robots" content="noindex">
|
|
85
85
|
<title>${escapeHtml(title)}</title>
|
|
86
86
|
${cssTags}
|
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() {
|
|
@@ -466,7 +477,7 @@ async function main() {
|
|
|
466
477
|
const { join } = await import('node:path');
|
|
467
478
|
const viteRoot = join(pluginRoot(), 'public');
|
|
468
479
|
const vite = await createViteServer({
|
|
469
|
-
server: { middlewareMode: true },
|
|
480
|
+
server: { middlewareMode: true, allowedHosts: true },
|
|
470
481
|
appType: 'custom',
|
|
471
482
|
root: viteRoot,
|
|
472
483
|
});
|