@deskwork/studio 0.13.0 → 0.14.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/content-detail.d.ts.map +1 -1
- package/dist/pages/content-detail.js +5 -4
- package/dist/pages/content-detail.js.map +1 -1
- package/dist/pages/content-detail.ts +5 -4
- package/dist/pages/content.d.ts.map +1 -1
- package/dist/pages/content.js +4 -6
- package/dist/pages/content.js.map +1 -1
- package/dist/pages/content.ts +4 -6
- package/dist/pages/dashboard/affordances.d.ts +6 -1
- package/dist/pages/dashboard/affordances.d.ts.map +1 -1
- package/dist/pages/dashboard/affordances.js +17 -2
- package/dist/pages/dashboard/affordances.js.map +1 -1
- package/dist/pages/dashboard/affordances.ts +18 -2
- package/dist/pages/dashboard/header.js +2 -2
- package/dist/pages/dashboard/header.ts +2 -2
- package/dist/pages/dashboard/section.d.ts +2 -2
- package/dist/pages/dashboard/section.d.ts.map +1 -1
- package/dist/pages/dashboard/section.js +5 -5
- package/dist/pages/dashboard/section.js.map +1 -1
- package/dist/pages/dashboard/section.ts +9 -5
- package/dist/pages/dashboard.d.ts.map +1 -1
- package/dist/pages/dashboard.js +3 -2
- package/dist/pages/dashboard.js.map +1 -1
- package/dist/pages/dashboard.ts +3 -2
- package/dist/pages/entry-review/data.d.ts +54 -0
- package/dist/pages/entry-review/data.d.ts.map +1 -0
- package/dist/pages/entry-review/data.js +116 -0
- package/dist/pages/entry-review/data.js.map +1 -0
- package/dist/pages/entry-review/decision-strip.d.ts +37 -0
- package/dist/pages/entry-review/decision-strip.d.ts.map +1 -0
- package/dist/pages/entry-review/decision-strip.js +137 -0
- package/dist/pages/entry-review/decision-strip.js.map +1 -0
- package/dist/pages/entry-review/edit-panes.d.ts +12 -0
- package/dist/pages/entry-review/edit-panes.d.ts.map +1 -0
- package/dist/pages/entry-review/edit-panes.js +28 -0
- package/dist/pages/entry-review/edit-panes.js.map +1 -0
- package/dist/pages/entry-review/edit-toolbar.d.ts +20 -0
- package/dist/pages/entry-review/edit-toolbar.d.ts.map +1 -0
- package/dist/pages/entry-review/edit-toolbar.js +46 -0
- package/dist/pages/entry-review/edit-toolbar.js.map +1 -0
- package/dist/pages/entry-review/index.d.ts +50 -0
- package/dist/pages/entry-review/index.d.ts.map +1 -0
- package/dist/pages/entry-review/index.js +219 -0
- package/dist/pages/entry-review/index.js.map +1 -0
- package/dist/pages/entry-review/marginalia.d.ts +28 -0
- package/dist/pages/entry-review/marginalia.d.ts.map +1 -0
- package/dist/pages/entry-review/marginalia.js +67 -0
- package/dist/pages/entry-review/marginalia.js.map +1 -0
- package/dist/pages/entry-review/not-found.d.ts +9 -0
- package/dist/pages/entry-review/not-found.d.ts.map +1 -0
- package/dist/pages/entry-review/not-found.js +34 -0
- package/dist/pages/entry-review/not-found.js.map +1 -0
- package/dist/pages/entry-review/outline-drawer.d.ts +12 -0
- package/dist/pages/entry-review/outline-drawer.d.ts.map +1 -0
- package/dist/pages/entry-review/outline-drawer.js +28 -0
- package/dist/pages/entry-review/outline-drawer.js.map +1 -0
- package/dist/pages/entry-review/shortcuts.d.ts +14 -0
- package/dist/pages/entry-review/shortcuts.d.ts.map +1 -0
- package/dist/pages/entry-review/shortcuts.js +36 -0
- package/dist/pages/entry-review/shortcuts.js.map +1 -0
- package/dist/pages/entry-review/version-strip.d.ts +33 -0
- package/dist/pages/entry-review/version-strip.d.ts.map +1 -0
- package/dist/pages/entry-review/version-strip.js +81 -0
- package/dist/pages/entry-review/version-strip.js.map +1 -0
- package/dist/pages/entry-review.d.ts +6 -21
- package/dist/pages/entry-review.d.ts.map +1 -1
- package/dist/pages/entry-review.js +6 -144
- package/dist/pages/entry-review.js.map +1 -1
- package/dist/pages/entry-review.ts +11 -181
- package/dist/pages/index.d.ts.map +1 -1
- package/dist/pages/index.js +6 -10
- package/dist/pages/index.js.map +1 -1
- package/dist/pages/index.ts +6 -10
- package/dist/pages/scrapbook.d.ts.map +1 -1
- package/dist/pages/scrapbook.js +208 -11
- package/dist/pages/scrapbook.js.map +1 -1
- package/dist/pages/scrapbook.ts +209 -10
- package/dist/pages/shortform-review.d.ts +32 -0
- package/dist/pages/shortform-review.d.ts.map +1 -0
- package/dist/pages/shortform-review.js +270 -0
- package/dist/pages/shortform-review.js.map +1 -0
- package/dist/pages/shortform-review.ts +342 -0
- package/dist/routes/api.d.ts +8 -0
- package/dist/routes/api.d.ts.map +1 -1
- package/dist/routes/api.js +115 -0
- package/dist/routes/api.js.map +1 -1
- package/dist/routes/entry-annotation-body.d.ts +26 -0
- package/dist/routes/entry-annotation-body.d.ts.map +1 -0
- package/dist/routes/entry-annotation-body.js +152 -0
- package/dist/routes/entry-annotation-body.js.map +1 -0
- package/dist/server.d.ts +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +56 -193
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data loader for the entry-keyed press-check surface (Phase 34a Layer 2).
|
|
3
|
+
*
|
|
4
|
+
* Resolves an entry UUID to:
|
|
5
|
+
* - the sidecar (`Entry`) + on-disk artifact body (via `resolveEntry`).
|
|
6
|
+
* - the iteration journal listing for the version strip (via Layer 1's
|
|
7
|
+
* `listEntryIterations`).
|
|
8
|
+
* - the entry-keyed annotation list for the marginalia column (via
|
|
9
|
+
* Layer 1's `listEntryAnnotations`).
|
|
10
|
+
* - which site the entry belongs to, by scanning configured calendars
|
|
11
|
+
* for a matching UUID. The scrapbook drawer + content index need the
|
|
12
|
+
* site to resolve filesystem paths and build URLs.
|
|
13
|
+
* - the matching `CalendarEntry` (when present) so the scrapbook drawer
|
|
14
|
+
* can use the index-driven binding rather than slug-template paths.
|
|
15
|
+
*
|
|
16
|
+
* The version-strip can optionally render historical content (when the
|
|
17
|
+
* `?v=<n>` query param is set). When that param is present and resolves
|
|
18
|
+
* to an iteration we have content for, the renderer swaps the on-disk
|
|
19
|
+
* artifact body for the historical markdown captured in the journal.
|
|
20
|
+
*/
|
|
21
|
+
import { existsSync } from 'node:fs';
|
|
22
|
+
import { resolveEntry } from "../../lib/entry-resolver.js";
|
|
23
|
+
import { readCalendar } from '@deskwork/core/calendar';
|
|
24
|
+
import { findEntryById } from '@deskwork/core/calendar-mutations';
|
|
25
|
+
import { resolveCalendarPath } from '@deskwork/core/paths';
|
|
26
|
+
import { listEntryIterations, getEntryIteration, } from '@deskwork/core/iterate/history';
|
|
27
|
+
import { listEntryAnnotations } from '@deskwork/core/entry/annotations';
|
|
28
|
+
const VALID_STAGES = new Set([
|
|
29
|
+
'Ideas', 'Planned', 'Outlining', 'Drafting', 'Final', 'Published', 'Blocked', 'Cancelled',
|
|
30
|
+
]);
|
|
31
|
+
function parseStageParam(raw) {
|
|
32
|
+
if (raw === null || raw === undefined)
|
|
33
|
+
return undefined;
|
|
34
|
+
for (const stage of VALID_STAGES) {
|
|
35
|
+
if (stage === raw)
|
|
36
|
+
return stage;
|
|
37
|
+
}
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Locate which configured site's calendar the entry belongs to. Returns
|
|
42
|
+
* the first match (entry UUIDs are globally unique). Falls back to
|
|
43
|
+
* `config.defaultSite` with a null `calendarEntry` when no calendar
|
|
44
|
+
* carries the entry.
|
|
45
|
+
*
|
|
46
|
+
* Failures (calendar absent, parse error) fall through to the default
|
|
47
|
+
* — the press-check surface should render even when calendar resolution
|
|
48
|
+
* is wonky for an unrelated reason.
|
|
49
|
+
*/
|
|
50
|
+
function findEntrySite(ctx, entryId) {
|
|
51
|
+
for (const site of Object.keys(ctx.config.sites)) {
|
|
52
|
+
try {
|
|
53
|
+
const calendarPath = resolveCalendarPath(ctx.projectRoot, ctx.config, site);
|
|
54
|
+
if (!existsSync(calendarPath))
|
|
55
|
+
continue;
|
|
56
|
+
const cal = readCalendar(calendarPath);
|
|
57
|
+
const entry = findEntryById(cal, entryId);
|
|
58
|
+
if (entry !== undefined) {
|
|
59
|
+
return { site, calendarEntry: entry };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Calendar unreadable for this site — try the next one.
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return { site: ctx.config.defaultSite, calendarEntry: null };
|
|
68
|
+
}
|
|
69
|
+
function parseVersionParam(raw) {
|
|
70
|
+
if (raw === null || raw === undefined)
|
|
71
|
+
return null;
|
|
72
|
+
const parsed = Number.parseInt(raw, 10);
|
|
73
|
+
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
74
|
+
return null;
|
|
75
|
+
return parsed;
|
|
76
|
+
}
|
|
77
|
+
export async function loadEntryReviewData(ctx, entryId, opts = {}) {
|
|
78
|
+
const resolved = await resolveEntry(ctx.projectRoot, entryId);
|
|
79
|
+
const iterations = await listEntryIterations(ctx.projectRoot, entryId);
|
|
80
|
+
const annotations = await listEntryAnnotations(ctx.projectRoot, entryId);
|
|
81
|
+
const { site, calendarEntry } = findEntrySite(ctx, entryId);
|
|
82
|
+
// Historical-version handling. Only swap the markdown when both the
|
|
83
|
+
// version param resolves and the journal has content for it. Stage
|
|
84
|
+
// qualification disambiguates the multi-stage case (Ideas v1 +
|
|
85
|
+
// Drafting v1); when missing, the reader falls back to the first
|
|
86
|
+
// chronological match (loud warn since this means the URL was
|
|
87
|
+
// emitted by something other than the current version-strip).
|
|
88
|
+
let historical = null;
|
|
89
|
+
let markdown = resolved.artifactBody;
|
|
90
|
+
const requested = parseVersionParam(opts.version ?? null);
|
|
91
|
+
const requestedStage = parseStageParam(opts.stage ?? null);
|
|
92
|
+
if (requested !== null) {
|
|
93
|
+
if (requestedStage === undefined && opts.stage !== null && opts.stage !== undefined) {
|
|
94
|
+
// Caller passed a non-empty ?stage= that didn't match any known
|
|
95
|
+
// stage — treat as a malformed query rather than silently
|
|
96
|
+
// dropping it.
|
|
97
|
+
console.warn(`entry-review: ignoring unknown stage param "${opts.stage}" for entry ${entryId}`);
|
|
98
|
+
}
|
|
99
|
+
const found = await getEntryIteration(ctx.projectRoot, entryId, requested, requestedStage);
|
|
100
|
+
if (found !== null) {
|
|
101
|
+
historical = found;
|
|
102
|
+
markdown = found.markdown;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
entry: resolved.entry,
|
|
107
|
+
artifactPath: resolved.artifactPath,
|
|
108
|
+
markdown,
|
|
109
|
+
historical,
|
|
110
|
+
iterations,
|
|
111
|
+
annotations,
|
|
112
|
+
site,
|
|
113
|
+
calendarEntry,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=data.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data.js","sourceRoot":"","sources":["../../../src/pages/entry-review/data.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decision strip for the entry-keyed press-check surface (Phase 34a — T13).
|
|
3
|
+
*
|
|
4
|
+
* Edit toggle + Approve / Iterate / Reject + shortcuts (?) trigger.
|
|
5
|
+
* Drives the press-check decision flow on the entry-centric model:
|
|
6
|
+
*
|
|
7
|
+
* - `Approve` → POST `/api/dev/editorial-review/entry/<uuid>/decision`
|
|
8
|
+
* with `{decision: 'approve'}` (graduates the entry to the next stage).
|
|
9
|
+
* - `Iterate` → POST `/api/dev/editorial-review/entry/<uuid>/version`
|
|
10
|
+
* (records a new iteration via `iterateEntry`).
|
|
11
|
+
* - `Reject` → DISABLED, tooltipped with the GitHub-issue link for the
|
|
12
|
+
* pending design decision (entry-centric reject semantics are
|
|
13
|
+
* undefined; see #173).
|
|
14
|
+
*
|
|
15
|
+
* Affordance set is gated by `getAffordances(entry)` so the strip
|
|
16
|
+
* automatically degrades to read-only / induct-to / fork shapes for
|
|
17
|
+
* Published / Blocked / Cancelled entries (T15-prep).
|
|
18
|
+
*
|
|
19
|
+
* Per `.claude/rules/affordance-placement.md`, the destructive buttons
|
|
20
|
+
* (Approve, Iterate, Reject) are wrapped in `.er-shortcut-chip-wrap`
|
|
21
|
+
* spans carrying small chord chips beneath each button — visual cue
|
|
22
|
+
* for the bare-letter double-tap shortcuts (`a`, `i`, `r`).
|
|
23
|
+
*/
|
|
24
|
+
import type { Entry } from '@deskwork/core/schema/entry';
|
|
25
|
+
import type { Affordances } from '../../lib/stage-affordances.ts';
|
|
26
|
+
import { type RawHtml } from '../html.ts';
|
|
27
|
+
interface DecisionStripOptions {
|
|
28
|
+
readonly entry: Entry;
|
|
29
|
+
readonly affordances: Affordances;
|
|
30
|
+
/** Whether the page is rendering a historical version (`?v=<n>`).
|
|
31
|
+
* When true, mutation buttons (Approve / Iterate) render disabled
|
|
32
|
+
* with a tooltip explaining the read-only state. */
|
|
33
|
+
readonly historical: boolean;
|
|
34
|
+
}
|
|
35
|
+
export declare function renderDecisionStrip(opts: DecisionStripOptions): RawHtml;
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=decision-strip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision-strip.d.ts","sourceRoot":"","sources":["../../../src/pages/entry-review/decision-strip.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,EAAgB,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAyJxD,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAClC;;yDAEqD;IACrD,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC9B;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAMvE"}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decision strip for the entry-keyed press-check surface (Phase 34a — T13).
|
|
3
|
+
*
|
|
4
|
+
* Edit toggle + Approve / Iterate / Reject + shortcuts (?) trigger.
|
|
5
|
+
* Drives the press-check decision flow on the entry-centric model:
|
|
6
|
+
*
|
|
7
|
+
* - `Approve` → POST `/api/dev/editorial-review/entry/<uuid>/decision`
|
|
8
|
+
* with `{decision: 'approve'}` (graduates the entry to the next stage).
|
|
9
|
+
* - `Iterate` → POST `/api/dev/editorial-review/entry/<uuid>/version`
|
|
10
|
+
* (records a new iteration via `iterateEntry`).
|
|
11
|
+
* - `Reject` → DISABLED, tooltipped with the GitHub-issue link for the
|
|
12
|
+
* pending design decision (entry-centric reject semantics are
|
|
13
|
+
* undefined; see #173).
|
|
14
|
+
*
|
|
15
|
+
* Affordance set is gated by `getAffordances(entry)` so the strip
|
|
16
|
+
* automatically degrades to read-only / induct-to / fork shapes for
|
|
17
|
+
* Published / Blocked / Cancelled entries (T15-prep).
|
|
18
|
+
*
|
|
19
|
+
* Per `.claude/rules/affordance-placement.md`, the destructive buttons
|
|
20
|
+
* (Approve, Iterate, Reject) are wrapped in `.er-shortcut-chip-wrap`
|
|
21
|
+
* spans carrying small chord chips beneath each button — visual cue
|
|
22
|
+
* for the bare-letter double-tap shortcuts (`a`, `i`, `r`).
|
|
23
|
+
*/
|
|
24
|
+
import { html, unsafe } from "../html.js";
|
|
25
|
+
const REJECT_ISSUE_URL = 'https://github.com/audiocontrol-org/deskwork/issues/173';
|
|
26
|
+
const REJECT_TOOLTIP = `reject semantics filed as #173 — see ${REJECT_ISSUE_URL}`;
|
|
27
|
+
const HISTORICAL_TOOLTIP = 'viewing historical version (read-only) — switch back to current to act';
|
|
28
|
+
const STAGE_PICKER_OPTIONS = [
|
|
29
|
+
'Ideas',
|
|
30
|
+
'Planned',
|
|
31
|
+
'Outlining',
|
|
32
|
+
'Drafting',
|
|
33
|
+
'Final',
|
|
34
|
+
];
|
|
35
|
+
/**
|
|
36
|
+
* Wrap an action button in a `.er-shortcut-chip-wrap` span carrying the
|
|
37
|
+
* chord chip. Mirrors the legacy surface verbatim — bare-letter double-
|
|
38
|
+
* tap with no Cmd/Ctrl modifier (#108).
|
|
39
|
+
*/
|
|
40
|
+
function shortcutChipWrap(buttonHtml, letter) {
|
|
41
|
+
return html `<span class="er-shortcut-chip-wrap">${unsafe(buttonHtml)}<small class="er-shortcut-chip"><kbd>${letter}</kbd><kbd>${letter}</kbd></small></span>`;
|
|
42
|
+
}
|
|
43
|
+
function renderEditToggle() {
|
|
44
|
+
// Issue 7 — edit-mode disclosure label sits next to the Edit button.
|
|
45
|
+
// Initial state matches the surface's initial mode (preview).
|
|
46
|
+
return html `<button class="er-btn er-btn-small" data-action="toggle-edit" type="button">Edit</button><span class="er-edit-mode-label" data-mode="preview">preview</span>`;
|
|
47
|
+
}
|
|
48
|
+
function renderInductPicker(entry) {
|
|
49
|
+
const options = STAGE_PICKER_OPTIONS.map((s) => unsafe(html `<option value="${s}">${s}</option>`));
|
|
50
|
+
return html `
|
|
51
|
+
<label class="er-entry-control er-entry-control--induct">
|
|
52
|
+
<span class="er-entry-control-label">Induct to</span>
|
|
53
|
+
<select name="induct-to" data-entry-uuid="${entry.uuid}">
|
|
54
|
+
${options}
|
|
55
|
+
</select>
|
|
56
|
+
</label>`;
|
|
57
|
+
}
|
|
58
|
+
function renderHistoricalStageDropdown(entry) {
|
|
59
|
+
const stages = Object.keys(entry.iterationByStage);
|
|
60
|
+
if (stages.length === 0)
|
|
61
|
+
return '';
|
|
62
|
+
const options = stages.map((s) => unsafe(html `<option value="${s}">${s}</option>`));
|
|
63
|
+
return html `
|
|
64
|
+
<label class="er-entry-control er-entry-control--history">
|
|
65
|
+
<span class="er-entry-control-label">Historical stage</span>
|
|
66
|
+
<select name="history-stage" data-entry-uuid="${entry.uuid}">
|
|
67
|
+
${options}
|
|
68
|
+
</select>
|
|
69
|
+
</label>`;
|
|
70
|
+
}
|
|
71
|
+
function renderShortcutsBtn() {
|
|
72
|
+
return html `<button class="er-btn er-btn-small" data-action="shortcuts" type="button" aria-label="Show keyboard shortcuts" title="Keyboard shortcuts">?</button>`;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The full mutable affordance set: Edit + Approve + Iterate + Reject
|
|
76
|
+
* (disabled). Returned as a single concatenated HTML string so the
|
|
77
|
+
* caller can wrap it in `.er-strip-right`.
|
|
78
|
+
*/
|
|
79
|
+
function renderMutableButtons({ entry, historical }) {
|
|
80
|
+
const buttons = [];
|
|
81
|
+
buttons.push(renderEditToggle());
|
|
82
|
+
// Phase 34a F2 remediation — Approve/Iterate disabled when viewing a
|
|
83
|
+
// historical version. The endpoints act against live sidecar state,
|
|
84
|
+
// not the historical snapshot; allowing the click while showing
|
|
85
|
+
// historical content is the same "surface lying about content"
|
|
86
|
+
// failure mode 34a was meant to fix. Defense-in-depth: client-side
|
|
87
|
+
// also short-circuits in entry-review/decision.ts.
|
|
88
|
+
const histDisabled = historical ? ' disabled aria-disabled="true"' : '';
|
|
89
|
+
const histTitle = historical ? ` title="${HISTORICAL_TOOLTIP}"` : '';
|
|
90
|
+
// Approve — wired to the entry-keyed decision endpoint.
|
|
91
|
+
buttons.push(shortcutChipWrap(html `<button class="er-btn er-btn-small er-btn-approve" data-action="approve" data-entry-uuid="${entry.uuid}" type="button"${unsafe(histDisabled)}${unsafe(histTitle)}>Approve</button>`, 'a'));
|
|
92
|
+
// Iterate — wired to the entry-keyed version endpoint.
|
|
93
|
+
buttons.push(shortcutChipWrap(html `<button class="er-btn er-btn-small" data-action="iterate" data-entry-uuid="${entry.uuid}" type="button"${unsafe(histDisabled)}${unsafe(histTitle)}>Iterate</button>`, 'i'));
|
|
94
|
+
// Reject — DISABLED pending the design decision in #173.
|
|
95
|
+
buttons.push(shortcutChipWrap(html `<button class="er-btn er-btn-small er-btn-reject" data-action="reject" data-entry-uuid="${entry.uuid}" type="button" disabled title="${REJECT_TOOLTIP}" aria-disabled="true">Reject</button>`, 'r'));
|
|
96
|
+
// Historical-stage dropdown — only useful when more than one stage
|
|
97
|
+
// has recorded iterations for this entry. The renderer returns an
|
|
98
|
+
// empty string when there are none; keep it conditional here so the
|
|
99
|
+
// strip doesn't carry an empty `<label>` for first-iteration entries.
|
|
100
|
+
const history = renderHistoricalStageDropdown(entry);
|
|
101
|
+
if (history)
|
|
102
|
+
buttons.push(history);
|
|
103
|
+
buttons.push(renderShortcutsBtn());
|
|
104
|
+
return buttons.join('');
|
|
105
|
+
}
|
|
106
|
+
function renderReadOnlyButtons(entry, affordances) {
|
|
107
|
+
const buttons = [];
|
|
108
|
+
for (const control of affordances.controls) {
|
|
109
|
+
if (control === 'view-only') {
|
|
110
|
+
buttons.push(html `<span class="er-entry-control er-entry-control--readonly">Read-only</span>`);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (control === 'fork-placeholder') {
|
|
114
|
+
buttons.push(html `<button class="er-entry-control er-entry-control--button" type="button" disabled data-control="fork">Fork (coming)</button>`);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (control === 'induct-to') {
|
|
118
|
+
buttons.push(renderInductPicker(entry));
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
// Unknown read-only control — render a label so the operator sees
|
|
122
|
+
// it in case a new affordance type ships without a renderer
|
|
123
|
+
// counterpart. Throwing here would tank the page; surfacing the
|
|
124
|
+
// raw control name is loud enough that the gap gets noticed.
|
|
125
|
+
buttons.push(html `<span class="er-entry-control">${control}</span>`);
|
|
126
|
+
}
|
|
127
|
+
buttons.push(renderShortcutsBtn());
|
|
128
|
+
return buttons.join('');
|
|
129
|
+
}
|
|
130
|
+
export function renderDecisionStrip(opts) {
|
|
131
|
+
const { entry, affordances, historical } = opts;
|
|
132
|
+
const inner = affordances.mutable
|
|
133
|
+
? renderMutableButtons({ entry, historical })
|
|
134
|
+
: renderReadOnlyButtons(entry, affordances);
|
|
135
|
+
return unsafe(`<span class="er-strip-right">${inner}</span>`);
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=decision-strip.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision-strip.js","sourceRoot":"","sources":["../../../src/pages/entry-review/decision-strip.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edit-mode panes for the entry-keyed press-check surface (Phase 34a — T8).
|
|
3
|
+
*
|
|
4
|
+
* Relocated from `pages/review.ts:renderEditPanes`. The panes-host lives
|
|
5
|
+
* inside the article column (replacing `#draft-body` when editing). The
|
|
6
|
+
* wrapper keeps the `er-edit-mode` class so existing CSS cascades
|
|
7
|
+
* unchanged. The client flips `[hidden]` on the panes wrapper independently
|
|
8
|
+
* of the toolbar.
|
|
9
|
+
*/
|
|
10
|
+
import { type RawHtml } from '../html.ts';
|
|
11
|
+
export declare function renderEditPanes(): RawHtml;
|
|
12
|
+
//# sourceMappingURL=edit-panes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit-panes.d.ts","sourceRoot":"","sources":["../../../src/pages/entry-review/edit-panes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAgB,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAExD,wBAAgB,eAAe,IAAI,OAAO,CAgBzC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edit-mode panes for the entry-keyed press-check surface (Phase 34a — T8).
|
|
3
|
+
*
|
|
4
|
+
* Relocated from `pages/review.ts:renderEditPanes`. The panes-host lives
|
|
5
|
+
* inside the article column (replacing `#draft-body` when editing). The
|
|
6
|
+
* wrapper keeps the `er-edit-mode` class so existing CSS cascades
|
|
7
|
+
* unchanged. The client flips `[hidden]` on the panes wrapper independently
|
|
8
|
+
* of the toolbar.
|
|
9
|
+
*/
|
|
10
|
+
import { html, unsafe } from "../html.js";
|
|
11
|
+
export function renderEditPanes() {
|
|
12
|
+
return unsafe(html `
|
|
13
|
+
<div class="er-edit-mode" data-edit-panes-host hidden>
|
|
14
|
+
<div class="er-edit-panes" data-edit-panes data-view="source">
|
|
15
|
+
<div class="er-edit-source" data-edit-source aria-label="Markdown source"></div>
|
|
16
|
+
<div class="er-edit-preview" data-edit-preview aria-label="Rendered preview"></div>
|
|
17
|
+
</div>
|
|
18
|
+
<textarea id="draft-edit" data-draft-edit hidden></textarea>
|
|
19
|
+
<div class="er-focus-exit" data-focus-exit aria-hidden="true">
|
|
20
|
+
<button type="button" data-action="exit-focus" title="Exit focus (Esc)">← exit focus</button>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="er-focus-save" data-focus-save aria-hidden="true">
|
|
23
|
+
<button type="button" class="er-btn er-btn-small er-btn-primary" data-action="save-version">Save</button>
|
|
24
|
+
<span class="er-focus-save-hint" data-focus-save-hint></span>
|
|
25
|
+
</div>
|
|
26
|
+
</div>`);
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=edit-panes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit-panes.js","sourceRoot":"","sources":["../../../src/pages/entry-review/edit-panes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,EAAgB,MAAM,YAAY,CAAC;AAExD,MAAM,UAAU,eAAe;IAC7B,OAAO,MAAM,CAAC,IAAI,CAAA;;;;;;;;;;;;;;WAcT,CAAC,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edit-mode toolbar for the entry-keyed press-check surface (Phase 34a — T7).
|
|
3
|
+
*
|
|
4
|
+
* Relocated from `pages/review.ts:renderEditToolbar`. The toolbar lives
|
|
5
|
+
* ABOVE the page-grid; the client (`entry-review-client.ts`) flips its
|
|
6
|
+
* `[hidden]` attribute on enter/exit. Source / Split / Preview tabs +
|
|
7
|
+
* Outline / Focus / Save / Cancel actions. Consumes the existing
|
|
8
|
+
* `editorial-review.css` rules unchanged.
|
|
9
|
+
*
|
|
10
|
+
* Save semantics: the entry-keyed model's `iterateEntry` reads on-disk
|
|
11
|
+
* content, not a request body, so the legacy edit-in-browser-then-save
|
|
12
|
+
* flow has no entry-keyed equivalent. The Save button is rendered
|
|
13
|
+
* `disabled` with a tooltip pointing at issue #174 (operator decision
|
|
14
|
+
* needed on the save shape). Edit mode itself still works — the source
|
|
15
|
+
* pane and the rendered preview pane are functional — only the Save
|
|
16
|
+
* action is intentionally inert pending the design decision.
|
|
17
|
+
*/
|
|
18
|
+
import { type RawHtml } from '../html.ts';
|
|
19
|
+
export declare function renderEditToolbar(outlineHasContent: boolean): RawHtml;
|
|
20
|
+
//# sourceMappingURL=edit-toolbar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit-toolbar.d.ts","sourceRoot":"","sources":["../../../src/pages/entry-review/edit-toolbar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAgB,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAOxD,wBAAgB,iBAAiB,CAAC,iBAAiB,EAAE,OAAO,GAAG,OAAO,CAwBrE"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edit-mode toolbar for the entry-keyed press-check surface (Phase 34a — T7).
|
|
3
|
+
*
|
|
4
|
+
* Relocated from `pages/review.ts:renderEditToolbar`. The toolbar lives
|
|
5
|
+
* ABOVE the page-grid; the client (`entry-review-client.ts`) flips its
|
|
6
|
+
* `[hidden]` attribute on enter/exit. Source / Split / Preview tabs +
|
|
7
|
+
* Outline / Focus / Save / Cancel actions. Consumes the existing
|
|
8
|
+
* `editorial-review.css` rules unchanged.
|
|
9
|
+
*
|
|
10
|
+
* Save semantics: the entry-keyed model's `iterateEntry` reads on-disk
|
|
11
|
+
* content, not a request body, so the legacy edit-in-browser-then-save
|
|
12
|
+
* flow has no entry-keyed equivalent. The Save button is rendered
|
|
13
|
+
* `disabled` with a tooltip pointing at issue #174 (operator decision
|
|
14
|
+
* needed on the save shape). Edit mode itself still works — the source
|
|
15
|
+
* pane and the rendered preview pane are functional — only the Save
|
|
16
|
+
* action is intentionally inert pending the design decision.
|
|
17
|
+
*/
|
|
18
|
+
import { html, unsafe } from "../html.js";
|
|
19
|
+
const SAVE_ISSUE_URL = 'https://github.com/audiocontrol-org/deskwork/issues/174';
|
|
20
|
+
const SAVE_TOOLTIP = `Save semantics for the entry-keyed surface are pending design — see ${SAVE_ISSUE_URL}`;
|
|
21
|
+
export function renderEditToolbar(outlineHasContent) {
|
|
22
|
+
const outlineBtnAttrs = outlineHasContent ? '' : ' hidden';
|
|
23
|
+
// #175 Phase 34b — every button carries a tooltip naming what it
|
|
24
|
+
// does + the keyboard shortcut where one exists. The mode tabs
|
|
25
|
+
// (Source / Split / Preview) had no `title` attrs pre-fix; an
|
|
26
|
+
// operator who hadn't memorized them got nothing on hover. The
|
|
27
|
+
// trailing `?` button opens the existing shortcuts overlay so the
|
|
28
|
+
// full keyboard reference is one click away from the toolbar.
|
|
29
|
+
return unsafe(html `
|
|
30
|
+
<div class="er-edit-toolbar" data-edit-toolbar hidden>
|
|
31
|
+
<div class="er-edit-modes" role="tablist" aria-label="Editor mode">
|
|
32
|
+
<button class="er-edit-mode-btn" data-edit-view="source" type="button" aria-pressed="true" title="Edit markdown source only">Source</button>
|
|
33
|
+
<button class="er-edit-mode-btn" data-edit-view="split" type="button" aria-pressed="false" title="Source on the left, rendered preview on the right">Split</button>
|
|
34
|
+
<button class="er-edit-mode-btn" data-edit-view="preview" type="button" aria-pressed="false" title="Rendered preview only">Preview</button>
|
|
35
|
+
</div>
|
|
36
|
+
<div class="er-edit-actions">
|
|
37
|
+
<button class="er-btn er-btn-small" data-action="outline-drawer" type="button" title="Show the outline for reference (O)" aria-pressed="false"${unsafe(outlineBtnAttrs)}>Outline ↗</button>
|
|
38
|
+
<button class="er-btn er-btn-small" data-action="focus-mode" type="button" title="Distraction-free full-viewport canvas (Shift+F)" aria-pressed="false">Focus ⛶</button>
|
|
39
|
+
<button class="er-btn er-btn-primary" data-action="save-version" type="button" disabled aria-disabled="true" title="${SAVE_TOOLTIP}">Save as new version</button>
|
|
40
|
+
<button class="er-btn" data-action="cancel-edit" type="button" title="Discard unsaved edits and exit edit mode">Cancel</button>
|
|
41
|
+
<button class="er-btn er-btn-small" data-action="shortcuts" type="button" aria-label="Show keyboard shortcuts" title="Show all keyboard shortcuts (?)">?</button>
|
|
42
|
+
<span class="er-edit-hint" data-edit-hint></span>
|
|
43
|
+
</div>
|
|
44
|
+
</div>`);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=edit-toolbar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edit-toolbar.js","sourceRoot":"","sources":["../../../src/pages/entry-review/edit-toolbar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,EAAgB,MAAM,YAAY,CAAC;AAExD,MAAM,cAAc,GAClB,yDAAyD,CAAC;AAC5D,MAAM,YAAY,GAChB,uEAAuE,cAAc,EAAE,CAAC;AAE1F,MAAM,UAAU,iBAAiB,CAAC,iBAA0B;IAC1D,MAAM,eAAe,GAAG,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3D,iEAAiE;IACjE,+DAA+D;IAC/D,8DAA8D;IAC9D,+DAA+D;IAC/D,kEAAkE;IAClE,8DAA8D;IAC9D,OAAO,MAAM,CAAC,IAAI,CAAA;;;;;;;;wJAQoI,MAAM,CAAC,eAAe,CAAC;;8HAEjD,YAAY;;;;;WAK/H,CAAC,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Top-level renderer for the entry-keyed press-check surface
|
|
3
|
+
* (Phase 34a Layer 2 — `/dev/editorial-review/entry/<uuid>`).
|
|
4
|
+
*
|
|
5
|
+
* Wires every chrome component (folio, version strip, edit toolbar,
|
|
6
|
+
* edit panes, outline drawer, marginalia column + tab, decision strip,
|
|
7
|
+
* scrapbook drawer, shortcuts overlay, rendered preview) against the
|
|
8
|
+
* entry-keyed Layer 1 data layer (sidecar + history-journal iterations
|
|
9
|
+
* + entry-keyed annotations).
|
|
10
|
+
*
|
|
11
|
+
* The relocated chrome shares the existing `editorial-review.css`
|
|
12
|
+
* stylesheet with the legacy longform surface — the chrome IS the
|
|
13
|
+
* existing chrome being relocated. The body data attribute is
|
|
14
|
+
* `data-review-ui="longform"` so the existing `er-*` rules in
|
|
15
|
+
* editorial-review.css cascade correctly.
|
|
16
|
+
*
|
|
17
|
+
* `entry-review.css` (the existing per-stage controller stylesheet)
|
|
18
|
+
* stays linked because the 404 not-found variant + future per-entry
|
|
19
|
+
* affordance polish lives there. The two class families coexist —
|
|
20
|
+
* `.er-*` from the relocated chrome and `.er-entry-*` from the stage
|
|
21
|
+
* controller — without collision.
|
|
22
|
+
*
|
|
23
|
+
* The StudioContext threads through so the scrapbook drawer can
|
|
24
|
+
* resolve the entry's site + content index (per-request memoized via
|
|
25
|
+
* `getRequestContentIndex`).
|
|
26
|
+
*/
|
|
27
|
+
import type { ContentIndex } from '@deskwork/core/content-index';
|
|
28
|
+
import type { StudioContext } from '../../routes/api.ts';
|
|
29
|
+
export type EntryReviewIndexGetter = (site: string) => ContentIndex;
|
|
30
|
+
export interface EntryReviewQuery {
|
|
31
|
+
/** `?v=<n>` from the request URL. When set + resolves, shows that
|
|
32
|
+
* iteration's historical content read-only. */
|
|
33
|
+
readonly version?: string | null;
|
|
34
|
+
/** `?stage=<Stage>` from the request URL. Disambiguates historical
|
|
35
|
+
* lookup when an entry has the same version number recorded under
|
|
36
|
+
* multiple stages. Optional; omitted falls back to the first
|
|
37
|
+
* chronological match (single-stage case). */
|
|
38
|
+
readonly stage?: string | null;
|
|
39
|
+
}
|
|
40
|
+
export interface EntryReviewResult {
|
|
41
|
+
status: 200 | 404;
|
|
42
|
+
html: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Render the entry-keyed press-check surface for `entryId`. Returns a
|
|
46
|
+
* 404 shell when the sidecar can't be resolved; otherwise renders the
|
|
47
|
+
* full press-check chrome backed by Layer 1's data layer.
|
|
48
|
+
*/
|
|
49
|
+
export declare function renderEntryReviewPage(ctx: StudioContext, entryId: string, query?: EntryReviewQuery, getIndex?: EntryReviewIndexGetter): Promise<EntryReviewResult>;
|
|
50
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/pages/entry-review/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAQH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAMjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAezD,MAAM,MAAM,sBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,YAAY,CAAC;AAEpE,MAAM,WAAW,gBAAgB;IAC/B;oDACgD;IAChD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC;;;mDAG+C;IAC/C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,GAAG,GAAG,GAAG,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAkGD;;;;GAIG;AACH,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,MAAM,EACf,KAAK,GAAE,gBAAqB,EAC5B,QAAQ,CAAC,EAAE,sBAAsB,GAChC,OAAO,CAAC,iBAAiB,CAAC,CAuH5B"}
|