@deskwork/studio 0.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/dist/build-client-assets.d.ts +51 -0
  2. package/dist/build-client-assets.d.ts.map +1 -0
  3. package/dist/build-client-assets.js +341 -0
  4. package/dist/build-client-assets.js.map +1 -0
  5. package/dist/components/scrapbook-item.d.ts +108 -0
  6. package/dist/components/scrapbook-item.d.ts.map +1 -0
  7. package/dist/components/scrapbook-item.js +205 -0
  8. package/dist/components/scrapbook-item.js.map +1 -0
  9. package/dist/lib/editorial-skills-catalogue.d.ts +33 -0
  10. package/dist/lib/editorial-skills-catalogue.d.ts.map +1 -0
  11. package/dist/lib/editorial-skills-catalogue.js +211 -0
  12. package/dist/lib/editorial-skills-catalogue.js.map +1 -0
  13. package/dist/lib/override-render.d.ts +41 -0
  14. package/dist/lib/override-render.d.ts.map +1 -0
  15. package/dist/lib/override-render.js +80 -0
  16. package/dist/lib/override-render.js.map +1 -0
  17. package/dist/listen.d.ts +78 -0
  18. package/dist/listen.d.ts.map +1 -0
  19. package/dist/listen.js +155 -0
  20. package/dist/listen.js.map +1 -0
  21. package/dist/pages/chrome.d.ts +26 -0
  22. package/dist/pages/chrome.d.ts.map +1 -0
  23. package/dist/pages/chrome.js +50 -0
  24. package/dist/pages/chrome.js.map +1 -0
  25. package/dist/pages/content-detail.d.ts +14 -0
  26. package/dist/pages/content-detail.d.ts.map +1 -0
  27. package/dist/pages/content-detail.js +279 -0
  28. package/dist/pages/content-detail.js.map +1 -0
  29. package/dist/pages/content.d.ts +39 -0
  30. package/dist/pages/content.d.ts.map +1 -0
  31. package/dist/pages/content.js +414 -0
  32. package/dist/pages/content.js.map +1 -0
  33. package/dist/pages/dashboard.d.ts +32 -0
  34. package/dist/pages/dashboard.d.ts.map +1 -0
  35. package/dist/pages/dashboard.js +803 -0
  36. package/dist/pages/dashboard.js.map +1 -0
  37. package/dist/pages/help.d.ts +24 -0
  38. package/dist/pages/help.d.ts.map +1 -0
  39. package/dist/pages/help.js +433 -0
  40. package/dist/pages/help.js.map +1 -0
  41. package/dist/pages/html.d.ts +35 -0
  42. package/dist/pages/html.d.ts.map +1 -0
  43. package/dist/pages/html.js +73 -0
  44. package/dist/pages/html.js.map +1 -0
  45. package/dist/pages/index.d.ts +21 -0
  46. package/dist/pages/index.d.ts.map +1 -0
  47. package/dist/pages/index.js +174 -0
  48. package/dist/pages/index.js.map +1 -0
  49. package/dist/pages/layout.d.ts +33 -0
  50. package/dist/pages/layout.d.ts.map +1 -0
  51. package/dist/pages/layout.js +50 -0
  52. package/dist/pages/layout.js.map +1 -0
  53. package/dist/pages/review-scrapbook-drawer.d.ts +20 -0
  54. package/dist/pages/review-scrapbook-drawer.d.ts.map +1 -0
  55. package/dist/pages/review-scrapbook-drawer.js +98 -0
  56. package/dist/pages/review-scrapbook-drawer.js.map +1 -0
  57. package/dist/pages/review.d.ts +68 -0
  58. package/dist/pages/review.d.ts.map +1 -0
  59. package/dist/pages/review.js +434 -0
  60. package/dist/pages/review.js.map +1 -0
  61. package/dist/pages/scrapbook.d.ts +21 -0
  62. package/dist/pages/scrapbook.d.ts.map +1 -0
  63. package/dist/pages/scrapbook.js +250 -0
  64. package/dist/pages/scrapbook.js.map +1 -0
  65. package/dist/pages/shortform.d.ts +17 -0
  66. package/dist/pages/shortform.d.ts.map +1 -0
  67. package/dist/pages/shortform.js +142 -0
  68. package/dist/pages/shortform.js.map +1 -0
  69. package/dist/request-context.d.ts +52 -0
  70. package/dist/request-context.d.ts.map +1 -0
  71. package/dist/request-context.js +84 -0
  72. package/dist/request-context.js.map +1 -0
  73. package/dist/routes/api.d.ts +36 -0
  74. package/dist/routes/api.d.ts.map +1 -0
  75. package/dist/routes/api.js +175 -0
  76. package/dist/routes/api.js.map +1 -0
  77. package/dist/routes/scrapbook-file.d.ts +19 -0
  78. package/dist/routes/scrapbook-file.d.ts.map +1 -0
  79. package/dist/routes/scrapbook-file.js +77 -0
  80. package/dist/routes/scrapbook-file.js.map +1 -0
  81. package/dist/routes/scrapbook-mutations.d.ts +33 -0
  82. package/dist/routes/scrapbook-mutations.d.ts.map +1 -0
  83. package/dist/routes/scrapbook-mutations.js +310 -0
  84. package/dist/routes/scrapbook-mutations.js.map +1 -0
  85. package/dist/server.d.ts +52 -0
  86. package/dist/server.d.ts.map +1 -0
  87. package/dist/server.js +581 -0
  88. package/dist/server.js.map +1 -0
  89. package/dist/tailscale.d.ts +63 -0
  90. package/dist/tailscale.d.ts.map +1 -0
  91. package/dist/tailscale.js +118 -0
  92. package/dist/tailscale.js.map +1 -0
  93. package/package.json +60 -0
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Studio listener with EADDRINUSE handling (Issue #43, Phase 22).
3
+ *
4
+ * `@hono/node-server`'s `serve()` invokes `server.listen()` for us and
5
+ * returns the Node `Server`. The default crash-loud behavior on
6
+ * EADDRINUSE is unhelpful — operators running multiple deskwork-studio
7
+ * instances (one per project) hit it routinely.
8
+ *
9
+ * Two-tier behavior:
10
+ * - Default port (operator did NOT pass `--port`): try the requested
11
+ * port and, if it's in use, walk forward through a small range
12
+ * before giving up.
13
+ * - Explicit port (operator passed `--port`): respect the request.
14
+ * Fail fast on EADDRINUSE with a clear pointer at `--port <other>`.
15
+ *
16
+ * The function takes a `serveImpl` injectable so tests can substitute
17
+ * a stub that simulates EADDRINUSE without actually binding sockets.
18
+ *
19
+ * Sibling-relative imports per the project convention.
20
+ */
21
+ /**
22
+ * Minimal subset of `@hono/node-server`'s `ListeningServer` we actually
23
+ * exercise. Defining this locally avoids leaking the full union of
24
+ * Node http / http2 / https Server types into our public surface,
25
+ * and lets tests substitute lightweight stubs without satisfying the
26
+ * full Http2SecureServer interface.
27
+ */
28
+ export interface ListeningServer {
29
+ on(event: 'error', listener: (err: unknown) => void): unknown;
30
+ close(callback?: (err?: Error) => void): unknown;
31
+ }
32
+ /**
33
+ * Number of additional ports to try after the requested port when the
34
+ * operator hasn't asked for an explicit port. The total range walked
35
+ * is [port, port + AUTO_INCREMENT_RANGE). Default is 30 — enough for
36
+ * dozens of concurrent studios in a multi-project workspace.
37
+ */
38
+ export declare const AUTO_INCREMENT_RANGE = 30;
39
+ export interface ListenOptions {
40
+ /** Hono `app.fetch` — passed verbatim to the adapter. */
41
+ readonly fetch: (req: Request) => Response | Promise<Response>;
42
+ /** Initial port to try. */
43
+ readonly port: number;
44
+ /** One or more bind addresses; every address listens on the chosen port. */
45
+ readonly addresses: ReadonlyArray<string>;
46
+ /**
47
+ * True when the operator asked for a specific port via `--port`. When
48
+ * true, EADDRINUSE fails immediately. When false, the listener walks
49
+ * forward through `AUTO_INCREMENT_RANGE` ports.
50
+ */
51
+ readonly explicitPort: boolean;
52
+ }
53
+ export interface ListenResult {
54
+ /** The port that succeeded — equals `options.port` unless auto-incremented. */
55
+ readonly port: number;
56
+ /** Per-address bound servers, in `addresses` iteration order. */
57
+ readonly servers: ReadonlyArray<ListeningServer>;
58
+ /** True when the chosen port differs from the requested port. */
59
+ readonly autoIncremented: boolean;
60
+ }
61
+ export interface ServeOptions {
62
+ fetch: ListenOptions['fetch'];
63
+ port: number;
64
+ hostname: string;
65
+ }
66
+ /**
67
+ * Minimal contract a serve implementation must satisfy. The real
68
+ * implementation is `@hono/node-server`'s `serve`. Tests inject a stub.
69
+ */
70
+ export type ServeImpl = (options: ServeOptions, listening: (info: unknown) => void) => ListeningServer;
71
+ /**
72
+ * Try to bind every `address` on `port`, walking forward up to
73
+ * `AUTO_INCREMENT_RANGE` ports if the operator didn't ask for an
74
+ * explicit port. Returns the chosen port + bound servers; throws a
75
+ * descriptive Error when no port in the range works.
76
+ */
77
+ export declare function listenWithAutoIncrement(options: ListenOptions, serveImpl: ServeImpl): Promise<ListenResult>;
78
+ //# sourceMappingURL=listen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listen.d.ts","sourceRoot":"","sources":["../src/listen.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC;IAC9D,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,KAAK,IAAI,GAAG,OAAO,CAAC;CAClD;AAED;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC,MAAM,WAAW,aAAa;IAC5B,yDAAyD;IACzD,QAAQ,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC/D,2BAA2B;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,YAAY;IAC3B,+EAA+E;IAC/E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,eAAe,CAAC,CAAC;IACjD,iEAAiE;IACjE,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;CACnC;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,MAAM,SAAS,GAAG,CACtB,OAAO,EAAE,YAAY,EACrB,SAAS,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,KAC/B,eAAe,CAAC;AAqGrB;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,aAAa,EACtB,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,YAAY,CAAC,CA8CvB"}
package/dist/listen.js ADDED
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Studio listener with EADDRINUSE handling (Issue #43, Phase 22).
3
+ *
4
+ * `@hono/node-server`'s `serve()` invokes `server.listen()` for us and
5
+ * returns the Node `Server`. The default crash-loud behavior on
6
+ * EADDRINUSE is unhelpful — operators running multiple deskwork-studio
7
+ * instances (one per project) hit it routinely.
8
+ *
9
+ * Two-tier behavior:
10
+ * - Default port (operator did NOT pass `--port`): try the requested
11
+ * port and, if it's in use, walk forward through a small range
12
+ * before giving up.
13
+ * - Explicit port (operator passed `--port`): respect the request.
14
+ * Fail fast on EADDRINUSE with a clear pointer at `--port <other>`.
15
+ *
16
+ * The function takes a `serveImpl` injectable so tests can substitute
17
+ * a stub that simulates EADDRINUSE without actually binding sockets.
18
+ *
19
+ * Sibling-relative imports per the project convention.
20
+ */
21
+ /**
22
+ * Number of additional ports to try after the requested port when the
23
+ * operator hasn't asked for an explicit port. The total range walked
24
+ * is [port, port + AUTO_INCREMENT_RANGE). Default is 30 — enough for
25
+ * dozens of concurrent studios in a multi-project workspace.
26
+ */
27
+ export const AUTO_INCREMENT_RANGE = 30;
28
+ /**
29
+ * Bind one address on `port`. Resolves with the server when listening
30
+ * succeeds, rejects with the underlying error otherwise. Cleans up on
31
+ * either path so a partially-bound server doesn't leak when later
32
+ * addresses fail.
33
+ */
34
+ function listenOnAddress(serveImpl, fetchFn, port, address) {
35
+ return new Promise((resolvePromise, rejectPromise) => {
36
+ let resolved = false;
37
+ let server;
38
+ try {
39
+ server = serveImpl({ fetch: fetchFn, port, hostname: address }, () => {
40
+ if (resolved)
41
+ return;
42
+ resolved = true;
43
+ resolvePromise(server);
44
+ });
45
+ }
46
+ catch (err) {
47
+ rejectPromise(err);
48
+ return;
49
+ }
50
+ server.on('error', (err) => {
51
+ if (resolved)
52
+ return;
53
+ resolved = true;
54
+ // Best-effort close — server.listen() failed so the socket isn't
55
+ // bound, but the underlying http.Server may still hold resources.
56
+ try {
57
+ server.close();
58
+ }
59
+ catch {
60
+ // ignore — we're already on the error path
61
+ }
62
+ rejectPromise(err);
63
+ });
64
+ });
65
+ }
66
+ /**
67
+ * Close every server in `servers`. Used when one address in a multi-
68
+ * address bind fails and we need to abandon the partially-bound state
69
+ * to retry on a different port.
70
+ */
71
+ function closeAll(servers) {
72
+ return Promise.all(servers.map((s) => new Promise((res) => {
73
+ try {
74
+ s.close(() => res());
75
+ }
76
+ catch {
77
+ res();
78
+ }
79
+ }))).then(() => undefined);
80
+ }
81
+ /**
82
+ * Returns true when `err` is a Node EADDRINUSE error. Robust against
83
+ * both the legacy and modern error shapes.
84
+ */
85
+ function isAddressInUse(err) {
86
+ if (!err || typeof err !== 'object')
87
+ return false;
88
+ const code = err.code;
89
+ return typeof code === 'string' && code === 'EADDRINUSE';
90
+ }
91
+ /**
92
+ * Bind every address on a single attempt. On success, returns the
93
+ * servers; on failure, closes any partially-bound servers and re-
94
+ * throws the first error encountered.
95
+ */
96
+ async function attemptPort(serveImpl, fetchFn, port, addresses) {
97
+ const bound = [];
98
+ for (const addr of addresses) {
99
+ try {
100
+ const s = await listenOnAddress(serveImpl, fetchFn, port, addr);
101
+ bound.push(s);
102
+ }
103
+ catch (err) {
104
+ // Roll back the partial bind so the next attempt can re-try the
105
+ // same port on the same address tree.
106
+ await closeAll(bound);
107
+ throw err;
108
+ }
109
+ }
110
+ return bound;
111
+ }
112
+ /**
113
+ * Try to bind every `address` on `port`, walking forward up to
114
+ * `AUTO_INCREMENT_RANGE` ports if the operator didn't ask for an
115
+ * explicit port. Returns the chosen port + bound servers; throws a
116
+ * descriptive Error when no port in the range works.
117
+ */
118
+ export async function listenWithAutoIncrement(options, serveImpl) {
119
+ const startPort = options.port;
120
+ const maxPort = options.explicitPort
121
+ ? startPort
122
+ : startPort + AUTO_INCREMENT_RANGE - 1;
123
+ let lastError = null;
124
+ for (let p = startPort; p <= maxPort; p++) {
125
+ try {
126
+ const servers = await attemptPort(serveImpl, options.fetch, p, options.addresses);
127
+ return {
128
+ port: p,
129
+ servers,
130
+ autoIncremented: p !== startPort,
131
+ };
132
+ }
133
+ catch (err) {
134
+ lastError = err;
135
+ if (!isAddressInUse(err)) {
136
+ // Non-EADDRINUSE failures (permission, invalid hostname, etc.)
137
+ // are not improved by trying another port — surface immediately.
138
+ throw err;
139
+ }
140
+ // EADDRINUSE — loop to next port unless we're at the cap.
141
+ }
142
+ }
143
+ // Build a clear, operator-facing error message. The two failure
144
+ // shapes have different remedies; we tailor the prose accordingly.
145
+ if (options.explicitPort) {
146
+ const detail = lastError instanceof Error ? `: ${lastError.message}` : '';
147
+ throw new Error(`port ${startPort} is in use${detail}. ` +
148
+ 'The operator passed --port explicitly, so deskwork-studio refuses ' +
149
+ 'to auto-increment. Pass --port <other> or stop the existing process.');
150
+ }
151
+ throw new Error(`no free port found in range ${startPort}..${maxPort}. ` +
152
+ 'Pass --port <other> to choose a different starting point, or stop ' +
153
+ 'an existing deskwork-studio instance.');
154
+ }
155
+ //# sourceMappingURL=listen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listen.js","sourceRoot":"","sources":["../src/listen.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Editorial folio — sticky cross-page nav rendered atop every studio
3
+ * surface (dashboard, longform review, shortform, content view, scrapbook,
4
+ * help, and the studio index).
5
+ *
6
+ * Three-column grid: wordmark / nav / spine. The active surface gets a
7
+ * red-pencil tick mark drawn via `::before` — reads like an editor
8
+ * circled where you are, not a UI selected state.
9
+ *
10
+ * Phase 17: replaces the prior `renderEditorialChrome` (Writer's Catalog
11
+ * `.ed-chrome` strip), which only the bird's-eye content view used.
12
+ * The folio commits to the editorial-print design language so every
13
+ * surface shares one cross-page nav. CSS lives in
14
+ * `plugins/deskwork-studio/public/css/editorial-nav.css` and uses the
15
+ * existing `--er-*` tokens — no new variables.
16
+ */
17
+ import { type RawHtml } from './html.ts';
18
+ export type ChromeActiveLink = 'index' | 'dashboard' | 'content' | 'reviews' | 'manual';
19
+ /**
20
+ * Render the folio strip. `spineLabel` is the page-specific subtitle
21
+ * shown at the right edge ("press-check", "the shape of the work",
22
+ * "index of the press", etc.). Defaults to no spine when omitted —
23
+ * which collapses to a 2-column layout.
24
+ */
25
+ export declare function renderEditorialFolio(active: ChromeActiveLink, spineLabel?: string): RawHtml;
26
+ //# sourceMappingURL=chrome.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chrome.d.ts","sourceRoot":"","sources":["../../src/pages/chrome.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAgB,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAEvD,MAAM,MAAM,gBAAgB,GACxB,OAAO,GACP,WAAW,GACX,SAAS,GACT,SAAS,GACT,QAAQ,CAAC;AAgBb;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,gBAAgB,EACxB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAoBT"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Editorial folio — sticky cross-page nav rendered atop every studio
3
+ * surface (dashboard, longform review, shortform, content view, scrapbook,
4
+ * help, and the studio index).
5
+ *
6
+ * Three-column grid: wordmark / nav / spine. The active surface gets a
7
+ * red-pencil tick mark drawn via `::before` — reads like an editor
8
+ * circled where you are, not a UI selected state.
9
+ *
10
+ * Phase 17: replaces the prior `renderEditorialChrome` (Writer's Catalog
11
+ * `.ed-chrome` strip), which only the bird's-eye content view used.
12
+ * The folio commits to the editorial-print design language so every
13
+ * surface shares one cross-page nav. CSS lives in
14
+ * `plugins/deskwork-studio/public/css/editorial-nav.css` and uses the
15
+ * existing `--er-*` tokens — no new variables.
16
+ */
17
+ import { html, unsafe } from "./html.js";
18
+ const NAV_LINKS = [
19
+ { key: 'index', href: '/dev/', label: 'Index' },
20
+ { key: 'dashboard', href: '/dev/editorial-studio', label: 'Dashboard' },
21
+ { key: 'content', href: '/dev/content', label: 'Content' },
22
+ { key: 'reviews', href: '/dev/editorial-review-shortform', label: 'Reviews' },
23
+ { key: 'manual', href: '/dev/editorial-help', label: 'Manual' },
24
+ ];
25
+ /**
26
+ * Render the folio strip. `spineLabel` is the page-specific subtitle
27
+ * shown at the right edge ("press-check", "the shape of the work",
28
+ * "index of the press", etc.). Defaults to no spine when omitted —
29
+ * which collapses to a 2-column layout.
30
+ */
31
+ export function renderEditorialFolio(active, spineLabel) {
32
+ const links = NAV_LINKS.map((link) => {
33
+ const cls = link.key === active ? 'active' : '';
34
+ return html `<a class="${cls}" href="${link.href}">${link.label}</a>`;
35
+ }).join('');
36
+ const spine = spineLabel
37
+ ? html `<div class="er-folio-spine">${spineLabel}</div>`
38
+ : '<div class="er-folio-spine" aria-hidden="true"></div>';
39
+ return unsafe(html `
40
+ <header class="er-folio">
41
+ <div class="er-folio-inner">
42
+ <div class="er-folio-name">deskwork <em>STUDIO</em></div>
43
+ <nav class="er-folio-nav" aria-label="Studio sections">
44
+ ${unsafe(links)}
45
+ </nav>
46
+ ${unsafe(spine)}
47
+ </div>
48
+ </header>`);
49
+ }
50
+ //# sourceMappingURL=chrome.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chrome.js","sourceRoot":"","sources":["../../src/pages/chrome.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,EAAgB,MAAM,WAAW,CAAC;AAevD,MAAM,SAAS,GAAyB;IACtC,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE;IAC/C,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,uBAAuB,EAAE,KAAK,EAAE,WAAW,EAAE;IACvE,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,SAAS,EAAE;IAC1D,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,iCAAiC,EAAE,KAAK,EAAE,SAAS,EAAE;IAC7E,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,qBAAqB,EAAE,KAAK,EAAE,QAAQ,EAAE;CAChE,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAClC,MAAwB,EACxB,UAAmB;IAEnB,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;QACnC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QAChD,OAAO,IAAI,CAAA,aAAa,GAAG,WAAW,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,MAAM,CAAC;IACvE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEZ,MAAM,KAAK,GAAG,UAAU;QACtB,CAAC,CAAC,IAAI,CAAA,+BAA+B,UAAU,QAAQ;QACvD,CAAC,CAAC,uDAAuD,CAAC;IAE5D,OAAO,MAAM,CAAC,IAAI,CAAA;;;;;YAKR,MAAM,CAAC,KAAK,CAAC;;UAEf,MAAM,CAAC,KAAK,CAAC;;cAET,CAAC,CAAC;AAChB,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Detail panel for the bird's-eye content view (Phase 16d).
3
+ *
4
+ * Shows frontmatter, body preview, and scrapbook listing for a single
5
+ * tree node. Used as the right-hand panel of the drilldown view; also
6
+ * has an "empty placeholder" variant when no node is selected.
7
+ */
8
+ import type { ContentIndex } from '@deskwork/core/content-index';
9
+ import type { StudioContext } from '../routes/api.ts';
10
+ import type { ContentNode } from '@deskwork/core/content-tree';
11
+ import { type RawHtml } from './html.ts';
12
+ export declare function renderEmptyDetail(): RawHtml;
13
+ export declare function renderNodeDetail(ctx: StudioContext, site: string, node: ContentNode, index?: ContentIndex): Promise<RawHtml>;
14
+ //# sourceMappingURL=content-detail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-detail.d.ts","sourceRoot":"","sources":["../../src/pages/content-detail.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAcH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAGjE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAgB,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAUvD,wBAAgB,iBAAiB,IAAI,OAAO,CAS3C;AAsOD,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,aAAa,EAClB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,WAAW,EACjB,KAAK,CAAC,EAAE,YAAY,GACnB,OAAO,CAAC,OAAO,CAAC,CAwElB"}
@@ -0,0 +1,279 @@
1
+ /**
2
+ * Detail panel for the bird's-eye content view (Phase 16d).
3
+ *
4
+ * Shows frontmatter, body preview, and scrapbook listing for a single
5
+ * tree node. Used as the right-hand panel of the drilldown view; also
6
+ * has an "empty placeholder" variant when no node is selected.
7
+ */
8
+ import { readFileSync, existsSync } from 'node:fs';
9
+ import { join } from 'node:path';
10
+ import { formatRelativeTime, listScrapbookAtDir, scrapbookDirAtPath, scrapbookDirForEntry, } from '@deskwork/core/scrapbook';
11
+ import { resolveContentDir, } from '@deskwork/core/paths';
12
+ import { parseFrontmatter } from '@deskwork/core/frontmatter';
13
+ import { renderMarkdownToHtml } from '@deskwork/core/review/render';
14
+ import { html, unsafe } from "./html.js";
15
+ import { renderEmptyScrapbookRow, renderReadOnlyScrapbookRow, scrapbookViewerUrl, } from "../components/scrapbook-item.js";
16
+ const PREVIEW_CHAR_BUDGET = 480;
17
+ export function renderEmptyDetail() {
18
+ return unsafe(html `
19
+ <div class="detail detail--empty" data-detail-empty>
20
+ <div class="ornament" aria-hidden="true">· · ·</div>
21
+ <p class="text">
22
+ Select a node to read its head matter, preview its body, and
23
+ browse its scrapbook.
24
+ </p>
25
+ </div>`);
26
+ }
27
+ function safeReadFile(absPath) {
28
+ try {
29
+ if (!existsSync(absPath))
30
+ return null;
31
+ return readFileSync(absPath, 'utf-8');
32
+ }
33
+ catch {
34
+ return null;
35
+ }
36
+ }
37
+ function fmField(value) {
38
+ if (value === null || value === undefined)
39
+ return '';
40
+ if (typeof value === 'string')
41
+ return value;
42
+ if (typeof value === 'number' || typeof value === 'boolean')
43
+ return String(value);
44
+ if (Array.isArray(value))
45
+ return value.map(fmField).join(', ');
46
+ return JSON.stringify(value);
47
+ }
48
+ function renderFrontmatter(fm) {
49
+ const keys = Object.keys(fm);
50
+ if (keys.length === 0) {
51
+ return unsafe(html `<p class="frontmatter-empty">No frontmatter detected.</p>`);
52
+ }
53
+ const rows = keys.map((k) => html `<dt>${k}</dt><dd>${fmField(fm[k])}</dd>`);
54
+ return unsafe(html `
55
+ <dl class="frontmatter">
56
+ ${unsafe(rows.join(''))}
57
+ </dl>`);
58
+ }
59
+ async function renderBodyPreview(body) {
60
+ if (body.trim().length === 0) {
61
+ return unsafe(html `<p class="preview-empty">No body content yet.</p>`);
62
+ }
63
+ const slice = body.length > PREVIEW_CHAR_BUDGET * 2
64
+ ? `${body.slice(0, PREVIEW_CHAR_BUDGET * 2)}\n\n…`
65
+ : body;
66
+ const rendered = await renderMarkdownToHtml(slice);
67
+ return unsafe(html `<div class="preview">${unsafe(rendered)}</div>`);
68
+ }
69
+ /**
70
+ * Resolve the on-disk scrapbook directory for a content tree node.
71
+ *
72
+ * Precedence — the index-driven path is preferred so writingcontrol-
73
+ * shape entries (slug != fs path) read scrapbook items from the actual
74
+ * file location:
75
+ *
76
+ * 1. The node carries a calendar entry with a stable `id` AND a
77
+ * per-request content index is wired → resolve via
78
+ * `scrapbookDirForEntry`.
79
+ * 2. Otherwise → resolve via `scrapbookDirAtPath` against the node's
80
+ * fs-derived `path`. Works for both organizational nodes (no
81
+ * entry) and pre-doctor entries (no id binding yet) since
82
+ * `node.path` is always the structural key already validated by
83
+ * the content-tree builder.
84
+ *
85
+ * The shared scrapbook directory lookup avoids duplicating the
86
+ * fall-through logic between the inline-text loader and the scrapbook
87
+ * listing in `loadDetailRender`.
88
+ */
89
+ function resolveNodeScrapbookDir(ctx, site, node, index) {
90
+ if (node.entry !== null && node.entry.id !== undefined && node.entry.id !== '') {
91
+ return scrapbookDirForEntry(ctx.projectRoot, ctx.config, site, node.entry, index);
92
+ }
93
+ // node.path is always a valid kebab-case path (the content-tree
94
+ // builder enforces this); scrapbookDirAtPath accepts that shape.
95
+ // Used directly because the path is already filesystem-derived —
96
+ // bypassing the slug template that wouldn't resolve correctly for
97
+ // hierarchical / relocated entries.
98
+ return scrapbookDirAtPath(ctx.projectRoot, ctx.config, site, node.path);
99
+ }
100
+ function makeInlineTextLoaderForNode(ctx, site, node, index) {
101
+ let scrapbookDir;
102
+ try {
103
+ scrapbookDir = resolveNodeScrapbookDir(ctx, site, node, index);
104
+ }
105
+ catch {
106
+ // Defensive: a malformed path or unresolvable entry shouldn't blow
107
+ // up the detail panel. Fall back to the legacy slug-template
108
+ // computation — the loader will just return null for every read.
109
+ const contentDir = resolveContentDir(ctx.projectRoot, ctx.config, site);
110
+ scrapbookDir = join(contentDir, node.path, 'scrapbook');
111
+ }
112
+ return (filename, maxBytes) => {
113
+ try {
114
+ const buf = readFileSync(join(scrapbookDir, filename));
115
+ const slice = buf.subarray(0, Math.min(buf.byteLength, maxBytes));
116
+ return slice.toString('utf-8');
117
+ }
118
+ catch {
119
+ return null;
120
+ }
121
+ };
122
+ }
123
+ function renderScrapbookList(site, slug, summary, loader) {
124
+ if (!summary || (summary.items.length === 0 && summary.secretItems.length === 0)) {
125
+ return renderEmptyScrapbookRow();
126
+ }
127
+ const itemRows = summary.items.map((item) => renderReadOnlyScrapbookRow({ site, path: slug }, item, {
128
+ inlinePreviewLoader: loader,
129
+ }));
130
+ const secretRows = summary.secretItems.map((item) => renderReadOnlyScrapbookRow({ site, path: slug }, item, {
131
+ inlinePreviewLoader: loader,
132
+ }));
133
+ if (secretRows.length === 0) {
134
+ return unsafe(html `<div class="scraplist">${itemRows}</div>`);
135
+ }
136
+ return unsafe(html `
137
+ <div class="scraplist">${itemRows}</div>
138
+ <p class="scraplist-secret-head">
139
+ <span aria-hidden="true">⚿</span> secret · ${summary.secretItems.length}
140
+ </p>
141
+ <div class="scraplist scraplist--secret">${secretRows}</div>`);
142
+ }
143
+ /**
144
+ * Find the on-disk markdown for an organizational node — try
145
+ * `<slug>/index.md`, then `<slug>/README.md` (and `.mdx`/`.markdown`
146
+ * variants). Used when the node has no calendar entry but the
147
+ * filesystem walk found a directory with a recognizable index file
148
+ * (#24, v0.6.0). Returns the first match, or null.
149
+ */
150
+ function findOrganizationalIndex(contentDir, slug) {
151
+ const candidates = [
152
+ 'index.md', 'index.mdx', 'index.markdown',
153
+ 'README.md', 'README.mdx', 'README.markdown',
154
+ ];
155
+ for (const name of candidates) {
156
+ const abs = join(contentDir, slug, name);
157
+ if (existsSync(abs))
158
+ return abs;
159
+ }
160
+ return null;
161
+ }
162
+ function loadDetailRender(ctx, site, node, index) {
163
+ const contentDir = resolveContentDir(ctx.projectRoot, ctx.config, site);
164
+ let frontmatter = {};
165
+ let bodyPreview = '';
166
+ let scrapbook = null;
167
+ // Phase 19c: detail-panel file lookup uses the node's fs `path` —
168
+ // the structural key. Whether the node is a tracked entry or
169
+ // organizational, the file lookup walks `<contentDir>/<path>/`
170
+ // for an index/README. (Phase 19d will additionally consult the
171
+ // per-request content index for tracked entries to handle the case
172
+ // where the host's template differs from the file's actual path.)
173
+ const idxFile = findOrganizationalIndex(contentDir, node.path);
174
+ if (idxFile !== null) {
175
+ const raw = safeReadFile(idxFile);
176
+ if (raw !== null) {
177
+ const parsed = parseFrontmatter(raw);
178
+ frontmatter = parsed.data;
179
+ bodyPreview = parsed.body;
180
+ }
181
+ }
182
+ else if (node.hasFsDir && node.hasOwnIndex) {
183
+ // Organizational node (#24, v0.6.0): no calendar entry, but the
184
+ // fs walk found a directory with an index/README. Read that file
185
+ // for the detail panel so the operator sees the structural prose
186
+ // (e.g. "These are the characters in The Outbound") even though
187
+ // nothing about this node ships through the lifecycle pipeline.
188
+ const abs = findOrganizationalIndex(contentDir, node.path);
189
+ if (abs !== null) {
190
+ const raw = safeReadFile(abs);
191
+ if (raw !== null) {
192
+ const parsed = parseFrontmatter(raw);
193
+ frontmatter = parsed.data;
194
+ bodyPreview = parsed.body;
195
+ }
196
+ }
197
+ }
198
+ try {
199
+ // Phase 19c+: the scrapbook listing prefers the index-driven dir
200
+ // for tracked entries (id binding) and falls back to the path-
201
+ // driven dir for organizational nodes. `scrapbookDirAtPath` is the
202
+ // right primitive here because `node.path` is already filesystem-
203
+ // derived — no slug template to substitute.
204
+ const scrapDir = resolveNodeScrapbookDir(ctx, site, node, index);
205
+ scrapbook = listScrapbookAtDir(site, node.path, scrapDir);
206
+ }
207
+ catch {
208
+ scrapbook = null;
209
+ }
210
+ return { frontmatter, bodyPreview, scrapbook };
211
+ }
212
+ export async function renderNodeDetail(ctx, site, node, index) {
213
+ const detail = loadDetailRender(ctx, site, node, index);
214
+ const fmCount = Object.keys(detail.frontmatter).length;
215
+ // Phase 19d: prefer the entry's stable id for the canonical review
216
+ // URL — refactor-proof, survives slug renames. Falls back to the
217
+ // entry slug (or the node's path) when the entry has no id stamped.
218
+ const reviewKey = node.entry !== null && node.entry.id !== undefined && node.entry.id !== ''
219
+ ? node.entry.id
220
+ : (node.slug ?? node.path);
221
+ const reviewHref = `/dev/editorial-review/${encodeURI(reviewKey)}?site=${site}`;
222
+ // Scrapbook viewer addresses by fs path — every node has a
223
+ // deterministic on-disk scrapbook location at `<path>/scrapbook/`.
224
+ const scrapHref = scrapbookViewerUrl({ site, path: node.path });
225
+ const scrapDirHint = node.scrapbookCount === 0
226
+ ? '0 items · scrapbook empty'
227
+ : `${node.scrapbookCount} items · /${node.path}/scrapbook`;
228
+ const updatedHint = node.scrapbookMostRecentMtime !== null
229
+ ? html `<span class="detail__updated">last touched ${formatRelativeTime(node.scrapbookMostRecentMtime)}</span>`
230
+ : '';
231
+ const loader = makeInlineTextLoaderForNode(ctx, site, node, index);
232
+ const previewBlock = await renderBodyPreview(detail.bodyPreview);
233
+ const reviewBtn = node.entry !== null
234
+ ? html `<a class="btn btn--accent" href="${reviewHref}">Open in Review</a>`
235
+ : '';
236
+ // Phase 19c: when an entry overlay carries a slug, surface it as
237
+ // the "public URL" hover hint. The slug is the host-rendering
238
+ // engine's identifier — operators recognize it as the SEO URL,
239
+ // distinct from the fs path that drives the tree.
240
+ const publicUrlHint = node.slug !== undefined && node.slug !== node.path
241
+ ? html `<span class="detail__public-url" title="public URL on the host site">
242
+ public URL: /blog/${node.slug}
243
+ </span>`
244
+ : '';
245
+ return unsafe(html `
246
+ <div class="detail" data-node-detail data-slug="${node.path}">
247
+ <div class="detail__crumb">${node.path.replaceAll('/', ' · ')}</div>
248
+ <h2 class="detail__title">${node.title}</h2>
249
+ <p class="detail__sub">
250
+ ${node.entry?.description ?? ''}
251
+ ${unsafe(updatedHint)}
252
+ ${unsafe(publicUrlHint)}
253
+ </p>
254
+
255
+ <div class="detail__sectionhead">
256
+ Frontmatter
257
+ <span class="marg">${fmCount} ${fmCount === 1 ? 'field' : 'fields'} · raw</span>
258
+ </div>
259
+ ${renderFrontmatter(detail.frontmatter)}
260
+
261
+ <div class="detail__sectionhead">
262
+ Preview
263
+ <span class="marg">first paragraphs · markdown rendered</span>
264
+ </div>
265
+ ${previewBlock}
266
+
267
+ <div class="detail__sectionhead">
268
+ Scrapbook
269
+ <span class="marg">${scrapDirHint}</span>
270
+ </div>
271
+ ${renderScrapbookList(site, node.path, detail.scrapbook, loader)}
272
+
273
+ <div class="actions">
274
+ ${unsafe(reviewBtn)}
275
+ <a class="btn" href="${scrapHref}">Open Scrapbook</a>
276
+ </div>
277
+ </div>`);
278
+ }
279
+ //# sourceMappingURL=content-detail.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-detail.js","sourceRoot":"","sources":["../../src/pages/content-detail.ts"],"names":[],"mappings":"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"}