@deskwork/core 0.14.0 → 0.15.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/entry/create.d.ts +56 -0
- package/dist/entry/create.d.ts.map +1 -0
- package/dist/entry/create.js +42 -0
- package/dist/entry/create.js.map +1 -0
- package/dist/iterate/iterate.d.ts.map +1 -1
- package/dist/iterate/iterate.js +10 -12
- package/dist/iterate/iterate.js.map +1 -1
- package/dist/rehype-rewrite-scrapbook-images.mjs +78 -0
- package/dist/review/render.d.ts +11 -1
- package/dist/review/render.d.ts.map +1 -1
- package/dist/review/render.js +27 -5
- package/dist/review/render.js.map +1 -1
- package/dist/schema/draft-annotation.d.ts +18 -18
- package/dist/schema/journal-events.d.ts +120 -120
- package/dist/scrapbook.d.ts +60 -0
- package/dist/scrapbook.d.ts.map +1 -1
- package/dist/scrapbook.js +54 -7
- package/dist/scrapbook.js.map +1 -1
- package/package.json +6 -1
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { Entry, ReviewState, Stage } from '../schema/entry.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Inputs for a freshly-minted entry sidecar.
|
|
4
|
+
*
|
|
5
|
+
* Both `deskwork add` and `deskwork ingest --apply` produce new entries
|
|
6
|
+
* that need a sidecar written under the Phase 30 contract. They differ
|
|
7
|
+
* in the optional fields (ingest knows an `artifactPath`; published
|
|
8
|
+
* candidates carry a `datePublishedDate`; legacy `Review` mapping
|
|
9
|
+
* produces a `reviewState`), but the required-field shape and the
|
|
10
|
+
* defaults (`keywords=[]`, `iterationByStage={}`, `createdAt=updatedAt=now`)
|
|
11
|
+
* are identical. This helper is the single point that knows the new-entry
|
|
12
|
+
* shape so both call sites stay aligned.
|
|
13
|
+
*/
|
|
14
|
+
export interface CreateEntryParams {
|
|
15
|
+
readonly uuid: string;
|
|
16
|
+
readonly slug: string;
|
|
17
|
+
readonly title: string;
|
|
18
|
+
readonly description?: string;
|
|
19
|
+
readonly currentStage: Stage;
|
|
20
|
+
readonly source: string;
|
|
21
|
+
readonly reviewState?: ReviewState;
|
|
22
|
+
readonly artifactPath?: string;
|
|
23
|
+
/**
|
|
24
|
+
* YYYY-MM-DD; the helper converts to a full ISO datetime so the
|
|
25
|
+
* `Entry.datePublished` schema (`z.string().datetime()`) accepts it.
|
|
26
|
+
* Only honored when `currentStage === 'Published'`.
|
|
27
|
+
*/
|
|
28
|
+
readonly datePublishedDate?: string;
|
|
29
|
+
readonly keywords?: readonly string[];
|
|
30
|
+
/**
|
|
31
|
+
* Test seam — defaults to `new Date()` when omitted. Lets unit tests
|
|
32
|
+
* pin `createdAt`/`updatedAt`/`datePublished`-derived ISO without
|
|
33
|
+
* stubbing globals.
|
|
34
|
+
*/
|
|
35
|
+
readonly now?: Date;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Build a fresh `Entry` from the supplied params, write it to
|
|
39
|
+
* `.deskwork/entries/<uuid>.json`, and return the constructed Entry
|
|
40
|
+
* (callers don't need the return value but it's cheap to provide and
|
|
41
|
+
* useful in tests).
|
|
42
|
+
*
|
|
43
|
+
* Defaults:
|
|
44
|
+
* - `keywords` → `[]`
|
|
45
|
+
* - `iterationByStage` → `{}`
|
|
46
|
+
* - `createdAt` / `updatedAt` → `now.toISOString()`
|
|
47
|
+
* - `datePublished` (only if `currentStage === 'Published'` and
|
|
48
|
+
* `datePublishedDate` is supplied) → `${datePublishedDate}T00:00:00.000Z`
|
|
49
|
+
*
|
|
50
|
+
* Phase 30 contract: every UUID in `calendar.md` must have a sidecar.
|
|
51
|
+
* Both `deskwork add` and `deskwork ingest --apply` MUST pair their
|
|
52
|
+
* calendar.md write with a `createFreshEntrySidecar` call to satisfy
|
|
53
|
+
* doctor's `calendar-sidecar` validator.
|
|
54
|
+
*/
|
|
55
|
+
export declare function createFreshEntrySidecar(projectRoot: string, params: CreateEntryParams): Promise<Entry>;
|
|
56
|
+
//# sourceMappingURL=create.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../src/entry/create.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAEpE;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,KAAK,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,WAAW,CAAC;IACnC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IACpC,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC;;;;OAIG;IACH,QAAQ,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,uBAAuB,CAC3C,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,KAAK,CAAC,CAqBhB"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { writeSidecar } from "../sidecar/write.js";
|
|
2
|
+
/**
|
|
3
|
+
* Build a fresh `Entry` from the supplied params, write it to
|
|
4
|
+
* `.deskwork/entries/<uuid>.json`, and return the constructed Entry
|
|
5
|
+
* (callers don't need the return value but it's cheap to provide and
|
|
6
|
+
* useful in tests).
|
|
7
|
+
*
|
|
8
|
+
* Defaults:
|
|
9
|
+
* - `keywords` → `[]`
|
|
10
|
+
* - `iterationByStage` → `{}`
|
|
11
|
+
* - `createdAt` / `updatedAt` → `now.toISOString()`
|
|
12
|
+
* - `datePublished` (only if `currentStage === 'Published'` and
|
|
13
|
+
* `datePublishedDate` is supplied) → `${datePublishedDate}T00:00:00.000Z`
|
|
14
|
+
*
|
|
15
|
+
* Phase 30 contract: every UUID in `calendar.md` must have a sidecar.
|
|
16
|
+
* Both `deskwork add` and `deskwork ingest --apply` MUST pair their
|
|
17
|
+
* calendar.md write with a `createFreshEntrySidecar` call to satisfy
|
|
18
|
+
* doctor's `calendar-sidecar` validator.
|
|
19
|
+
*/
|
|
20
|
+
export async function createFreshEntrySidecar(projectRoot, params) {
|
|
21
|
+
const at = (params.now ?? new Date()).toISOString();
|
|
22
|
+
const entry = {
|
|
23
|
+
uuid: params.uuid,
|
|
24
|
+
slug: params.slug,
|
|
25
|
+
title: params.title,
|
|
26
|
+
...(params.description ? { description: params.description } : {}),
|
|
27
|
+
keywords: params.keywords ? [...params.keywords] : [],
|
|
28
|
+
source: params.source,
|
|
29
|
+
currentStage: params.currentStage,
|
|
30
|
+
iterationByStage: {},
|
|
31
|
+
...(params.reviewState !== undefined ? { reviewState: params.reviewState } : {}),
|
|
32
|
+
...(params.artifactPath !== undefined ? { artifactPath: params.artifactPath } : {}),
|
|
33
|
+
...(params.currentStage === 'Published' && params.datePublishedDate
|
|
34
|
+
? { datePublished: `${params.datePublishedDate}T00:00:00.000Z` }
|
|
35
|
+
: {}),
|
|
36
|
+
createdAt: at,
|
|
37
|
+
updatedAt: at,
|
|
38
|
+
};
|
|
39
|
+
await writeSidecar(projectRoot, entry);
|
|
40
|
+
return entry;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=create.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.js","sourceRoot":"","sources":["../../src/entry/create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAuCnD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,WAAmB,EACnB,MAAyB;IAEzB,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IACpD,MAAM,KAAK,GAAU;QACnB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClE,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE;QACrD,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,gBAAgB,EAAE,EAAE;QACpB,GAAG,CAAC,MAAM,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChF,GAAG,CAAC,MAAM,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnF,GAAG,CAAC,MAAM,CAAC,YAAY,KAAK,WAAW,IAAI,MAAM,CAAC,iBAAiB;YACjE,CAAC,CAAC,EAAE,aAAa,EAAE,GAAG,MAAM,CAAC,iBAAiB,gBAAgB,EAAE;YAChE,CAAC,CAAC,EAAE,CAAC;QACP,SAAS,EAAE,EAAE;QACb,SAAS,EAAE,EAAE;KACd,CAAC;IACF,MAAM,YAAY,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IACvC,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"iterate.d.ts","sourceRoot":"","sources":["../../src/iterate/iterate.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"iterate.d.ts","sourceRoot":"","sources":["../../src/iterate/iterate.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAS,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAEvD,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;CAEd;AAED,UAAU,aAAa;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,WAAW,CAAC;CAC1B;AAaD,wBAAsB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CA8EpG"}
|
package/dist/iterate/iterate.js
CHANGED
|
@@ -3,7 +3,6 @@ import { join } from 'node:path';
|
|
|
3
3
|
import { readSidecar } from "../sidecar/read.js";
|
|
4
4
|
import { writeSidecar } from "../sidecar/write.js";
|
|
5
5
|
import { appendJournalEvent } from "../journal/append.js";
|
|
6
|
-
import { readJournalEvents } from "../journal/read.js";
|
|
7
6
|
import { getContentDir } from "../config.js";
|
|
8
7
|
const STAGE_ARTIFACT_PATH = {
|
|
9
8
|
Ideas: (slug, contentDir) => join(contentDir, slug, 'scrapbook', 'idea.md'),
|
|
@@ -38,17 +37,16 @@ export async function iterateEntry(projectRoot, opts) {
|
|
|
38
37
|
artifactPath = pathFn(sidecar.slug, contentDir);
|
|
39
38
|
}
|
|
40
39
|
const markdown = await readFile(artifactPath, 'utf8');
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
40
|
+
// Iteration is the operator's explicit "pin a new version" decision;
|
|
41
|
+
// the core helper records what was asked, not what the helper thinks
|
|
42
|
+
// counts as "real change." A real iteration can be motivated by
|
|
43
|
+
// marginalia, scrapbook additions, decisions captured outside the
|
|
44
|
+
// file body, or any reason the operator hasn't communicated to the
|
|
45
|
+
// system. Gating on a content-diff check earlier here put a hard
|
|
46
|
+
// error in front of the operator's review-surface Iterate button
|
|
47
|
+
// when they had added marginalia but not edited the file body.
|
|
48
|
+
// Removed (#188-followup): the orchestrating skill (`/deskwork:iterate`)
|
|
49
|
+
// is the right place to decide whether the file needs editing first.
|
|
52
50
|
const priorVersion = sidecar.iterationByStage[sidecar.currentStage] ?? 0;
|
|
53
51
|
const newVersion = priorVersion + 1;
|
|
54
52
|
const at = new Date().toISOString();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"iterate.js","sourceRoot":"","sources":["../../src/iterate/iterate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"iterate.js","sourceRoot":"","sources":["../../src/iterate/iterate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rewrite relative `<img src>` paths that point into the entry's
|
|
3
|
+
* scrapbook so the studio's review surface can serve them via the
|
|
4
|
+
* scrapbook-file route.
|
|
5
|
+
*
|
|
6
|
+
* Why this exists: an audit doc / draft / spec authored alongside a
|
|
7
|
+
* `scrapbook/` directory will reference its assets with relative URLs
|
|
8
|
+
* like `` or ``.
|
|
9
|
+
* Those relative URLs render correctly in any normal markdown viewer
|
|
10
|
+
* (GitHub, VS Code, IDE preview) because the renderer resolves them
|
|
11
|
+
* against the file's location on disk. On the studio's review surface
|
|
12
|
+
* the page URL is `/dev/editorial-review/entry/<uuid>`, so the same
|
|
13
|
+
* relative URL would resolve to a path the studio doesn't serve. This
|
|
14
|
+
* plugin rewrites those `<img src>` values to the absolute scrapbook-file
|
|
15
|
+
* route URL, which the studio DOES serve.
|
|
16
|
+
*
|
|
17
|
+
* The markdown source stays portable. The plugin only fires when the
|
|
18
|
+
* renderer is given `{ entryId, site }` options (i.e. the studio's
|
|
19
|
+
* review surface). Other call sites (e.g. published-site rendering)
|
|
20
|
+
* leave the relative URLs alone.
|
|
21
|
+
*
|
|
22
|
+
* @typedef {object} Options
|
|
23
|
+
* @property {string} entryId Entry UUID. Required.
|
|
24
|
+
* @property {string} site Site/collection slug. Required.
|
|
25
|
+
*
|
|
26
|
+
* @param {Options} options
|
|
27
|
+
* @returns {(tree: any) => void}
|
|
28
|
+
*/
|
|
29
|
+
export default function rehypeRewriteScrapbookImages(options) {
|
|
30
|
+
const { entryId, site } = options || {};
|
|
31
|
+
if (!entryId || !site) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
'rehypeRewriteScrapbookImages: { entryId, site } are required',
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const baseUrl =
|
|
38
|
+
`/api/dev/scrapbook-file` +
|
|
39
|
+
`?site=${encodeURIComponent(site)}` +
|
|
40
|
+
`&entryId=${encodeURIComponent(entryId)}`;
|
|
41
|
+
|
|
42
|
+
/** @param {string} src */
|
|
43
|
+
function rewrite(src) {
|
|
44
|
+
// Match leading `./scrapbook/<filename>` or `scrapbook/<filename>`.
|
|
45
|
+
// Don't rewrite absolute URLs (`http://`, `https://`, `/api/...`),
|
|
46
|
+
// anchor links (`#...`), data URIs (`data:...`), or paths into a
|
|
47
|
+
// sibling directory other than scrapbook.
|
|
48
|
+
const m = src.match(/^(?:\.\/)?scrapbook\/([^/?#]+)$/);
|
|
49
|
+
if (!m) return null;
|
|
50
|
+
const filename = m[1];
|
|
51
|
+
return `${baseUrl}&name=${encodeURIComponent(filename)}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return function transformer(tree) {
|
|
55
|
+
visit(tree, 'element', (node) => {
|
|
56
|
+
if (!node || node.tagName !== 'img') return;
|
|
57
|
+
if (!node.properties || typeof node.properties.src !== 'string') return;
|
|
58
|
+
const newSrc = rewrite(node.properties.src);
|
|
59
|
+
if (newSrc) node.properties.src = newSrc;
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Minimal hast visitor — walks every node depth-first, calls visitor
|
|
66
|
+
* for nodes whose `type` matches.
|
|
67
|
+
*
|
|
68
|
+
* @param {any} tree
|
|
69
|
+
* @param {string} typeFilter
|
|
70
|
+
* @param {(node: any) => void} fn
|
|
71
|
+
*/
|
|
72
|
+
function visit(tree, typeFilter, fn) {
|
|
73
|
+
if (!tree) return;
|
|
74
|
+
if (tree.type === typeFilter) fn(tree);
|
|
75
|
+
if (Array.isArray(tree.children)) {
|
|
76
|
+
for (const child of tree.children) visit(child, typeFilter, fn);
|
|
77
|
+
}
|
|
78
|
+
}
|
package/dist/review/render.d.ts
CHANGED
|
@@ -14,6 +14,16 @@
|
|
|
14
14
|
* the review surface needs the outline visible for annotate-and-iterate
|
|
15
15
|
* work.
|
|
16
16
|
*/
|
|
17
|
+
/** Optional rendering context. When the renderer is invoked from a
|
|
18
|
+
* surface that has an entry binding (e.g. the studio's review surface),
|
|
19
|
+
* pass `{ entryId, site }` to enable rewriting of relative
|
|
20
|
+
* `./scrapbook/<file>` image URLs to the absolute scrapbook-file route
|
|
21
|
+
* URL the studio serves. Other surfaces leave relative URLs alone so
|
|
22
|
+
* the markdown source stays portable across renderers. */
|
|
23
|
+
export interface RenderOptions {
|
|
24
|
+
readonly entryId?: string;
|
|
25
|
+
readonly site?: string;
|
|
26
|
+
}
|
|
17
27
|
export interface ParsedDraft {
|
|
18
28
|
/** Frontmatter values. Values are whatever YAML parses them to. */
|
|
19
29
|
frontmatter: Record<string, unknown>;
|
|
@@ -23,5 +33,5 @@ export interface ParsedDraft {
|
|
|
23
33
|
/** Split a draft into its frontmatter and body. */
|
|
24
34
|
export declare function parseDraftFrontmatter(markdown: string): ParsedDraft;
|
|
25
35
|
/** Render a markdown string as HTML. */
|
|
26
|
-
export declare function renderMarkdownToHtml(markdown: string): Promise<string>;
|
|
36
|
+
export declare function renderMarkdownToHtml(markdown: string, options?: RenderOptions): Promise<string>;
|
|
27
37
|
//# sourceMappingURL=render.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/review/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/review/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAeH;;;;;2DAK2D;AAC3D,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,mEAAmE;IACnE,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,0CAA0C;IAC1C,IAAI,EAAE,MAAM,CAAC;CACd;AAED,mDAAmD;AACnD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAGnE;AAED,wCAAwC;AACxC,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,MAAM,CAAC,CA6BjB"}
|
package/dist/review/render.js
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { unified } from 'unified';
|
|
18
18
|
import remarkParse from 'remark-parse';
|
|
19
|
+
import remarkGfm from 'remark-gfm';
|
|
19
20
|
import remarkRehype from 'remark-rehype';
|
|
20
21
|
import rehypeStringify from 'rehype-stringify';
|
|
21
22
|
import { parseFrontmatter } from "../frontmatter.js";
|
|
@@ -23,20 +24,41 @@ import { parseFrontmatter } from "../frontmatter.js";
|
|
|
23
24
|
import remarkImageFigure from '../remark-image-figure.mjs';
|
|
24
25
|
// @ts-expect-error — JS module without a .d.ts; the plugin is plain mdast traversal.
|
|
25
26
|
import remarkStripFirstH1 from '../remark-strip-first-h1.mjs';
|
|
27
|
+
// @ts-expect-error — JS module without a .d.ts; the plugin is a plain hast visitor.
|
|
28
|
+
import rehypeRewriteScrapbookImages from '../rehype-rewrite-scrapbook-images.mjs';
|
|
26
29
|
/** Split a draft into its frontmatter and body. */
|
|
27
30
|
export function parseDraftFrontmatter(markdown) {
|
|
28
31
|
const { data, body } = parseFrontmatter(markdown);
|
|
29
32
|
return { frontmatter: data, body };
|
|
30
33
|
}
|
|
31
34
|
/** Render a markdown string as HTML. */
|
|
32
|
-
export async function renderMarkdownToHtml(markdown) {
|
|
33
|
-
|
|
35
|
+
export async function renderMarkdownToHtml(markdown, options = {}) {
|
|
36
|
+
// remark-gfm adds GitHub Flavored Markdown — tables, strikethrough,
|
|
37
|
+
// task lists, footnotes, autolinks. Operator-authored content
|
|
38
|
+
// routinely uses tables (audit docs, comparison matrices); without
|
|
39
|
+
// gfm those rendered as raw `| col | col |` text on the review
|
|
40
|
+
// surface.
|
|
41
|
+
// Studio-surface scrapbook URL rewrite — only fires when both
|
|
42
|
+
// `entryId` and `site` are provided. The markdown source is expected
|
|
43
|
+
// to use portable relative URLs like `./scrapbook/<file>`; the
|
|
44
|
+
// rewrite produces the absolute scrapbook-file route URL that the
|
|
45
|
+
// studio serves at runtime. Other call sites (published-site
|
|
46
|
+
// rendering, tests with no entry binding) leave the relative URLs
|
|
47
|
+
// alone so the markdown stays renderable in GitHub / VS Code / any
|
|
48
|
+
// other markdown viewer.
|
|
49
|
+
const studioRewrite = options.entryId && options.site
|
|
50
|
+
? { entryId: options.entryId, site: options.site }
|
|
51
|
+
: null;
|
|
52
|
+
const base = unified()
|
|
34
53
|
.use(remarkParse)
|
|
54
|
+
.use(remarkGfm)
|
|
35
55
|
.use(remarkStripFirstH1)
|
|
36
56
|
.use(remarkImageFigure)
|
|
37
|
-
.use(remarkRehype)
|
|
38
|
-
|
|
39
|
-
.
|
|
57
|
+
.use(remarkRehype);
|
|
58
|
+
const withRewrite = studioRewrite
|
|
59
|
+
? base.use(rehypeRewriteScrapbookImages, studioRewrite)
|
|
60
|
+
: base;
|
|
61
|
+
const result = await withRewrite.use(rehypeStringify).process(markdown);
|
|
40
62
|
return String(result);
|
|
41
63
|
}
|
|
42
64
|
//# sourceMappingURL=render.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/review/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,WAAW,MAAM,cAAc,CAAC;AACvC,OAAO,YAAY,MAAM,eAAe,CAAC;AACzC,OAAO,eAAe,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,qFAAqF;AACrF,OAAO,iBAAiB,MAAM,4BAA4B,CAAC;AAC3D,qFAAqF;AACrF,OAAO,kBAAkB,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/review/render.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,WAAW,MAAM,cAAc,CAAC;AACvC,OAAO,SAAS,MAAM,YAAY,CAAC;AACnC,OAAO,YAAY,MAAM,eAAe,CAAC;AACzC,OAAO,eAAe,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,qFAAqF;AACrF,OAAO,iBAAiB,MAAM,4BAA4B,CAAC;AAC3D,qFAAqF;AACrF,OAAO,kBAAkB,MAAM,8BAA8B,CAAC;AAC9D,oFAAoF;AACpF,OAAO,4BAA4B,MAAM,wCAAwC,CAAC;AAoBlF,mDAAmD;AACnD,MAAM,UAAU,qBAAqB,CAAC,QAAgB;IACpD,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAClD,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACrC,CAAC;AAED,wCAAwC;AACxC,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,QAAgB,EAChB,UAAyB,EAAE;IAE3B,oEAAoE;IACpE,8DAA8D;IAC9D,mEAAmE;IACnE,+DAA+D;IAC/D,WAAW;IACX,8DAA8D;IAC9D,qEAAqE;IACrE,+DAA+D;IAC/D,kEAAkE;IAClE,6DAA6D;IAC7D,kEAAkE;IAClE,mEAAmE;IACnE,yBAAyB;IACzB,MAAM,aAAa,GACjB,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI;QAC7B,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE;QAClD,CAAC,CAAC,IAAI,CAAC;IACX,MAAM,IAAI,GAAG,OAAO,EAAE;SACnB,GAAG,CAAC,WAAW,CAAC;SAChB,GAAG,CAAC,SAAS,CAAC;SACd,GAAG,CAAC,kBAAkB,CAAC;SACvB,GAAG,CAAC,iBAAiB,CAAC;SACtB,GAAG,CAAC,YAAY,CAAC,CAAC;IACrB,MAAM,WAAW,GAAG,aAAa;QAC/B,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,4BAA4B,EAAE,aAAa,CAAC;QACvD,CAAC,CAAC,IAAI,CAAC;IACT,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACxE,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC;AACxB,CAAC"}
|
|
@@ -31,31 +31,31 @@ export declare const DraftAnnotationSchema: z.ZodDiscriminatedUnion<"type", [z.Z
|
|
|
31
31
|
workflowId: z.ZodString;
|
|
32
32
|
id: z.ZodString;
|
|
33
33
|
}, "strip", z.ZodTypeAny, {
|
|
34
|
+
id: string;
|
|
34
35
|
type: "comment";
|
|
35
36
|
version: number;
|
|
37
|
+
createdAt: string;
|
|
38
|
+
workflowId: string;
|
|
36
39
|
range: {
|
|
37
40
|
start: number;
|
|
38
41
|
end: number;
|
|
39
42
|
};
|
|
40
43
|
text: string;
|
|
41
|
-
createdAt: string;
|
|
42
|
-
workflowId: string;
|
|
43
|
-
id: string;
|
|
44
|
-
category?: "voice-drift" | "missing-receipt" | "tutorial-framing" | "saas-vocabulary" | "fake-authority" | "structural" | "other" | undefined;
|
|
45
44
|
anchor?: string | undefined;
|
|
45
|
+
category?: "other" | "voice-drift" | "missing-receipt" | "tutorial-framing" | "saas-vocabulary" | "fake-authority" | "structural" | undefined;
|
|
46
46
|
}, {
|
|
47
|
+
id: string;
|
|
47
48
|
type: "comment";
|
|
48
49
|
version: number;
|
|
50
|
+
createdAt: string;
|
|
51
|
+
workflowId: string;
|
|
49
52
|
range: {
|
|
50
53
|
start: number;
|
|
51
54
|
end: number;
|
|
52
55
|
};
|
|
53
56
|
text: string;
|
|
54
|
-
createdAt: string;
|
|
55
|
-
workflowId: string;
|
|
56
|
-
id: string;
|
|
57
|
-
category?: "voice-drift" | "missing-receipt" | "tutorial-framing" | "saas-vocabulary" | "fake-authority" | "structural" | "other" | undefined;
|
|
58
57
|
anchor?: string | undefined;
|
|
58
|
+
category?: "other" | "voice-drift" | "missing-receipt" | "tutorial-framing" | "saas-vocabulary" | "fake-authority" | "structural" | undefined;
|
|
59
59
|
}>, z.ZodObject<{
|
|
60
60
|
type: z.ZodLiteral<"edit">;
|
|
61
61
|
beforeVersion: z.ZodNumber;
|
|
@@ -65,18 +65,18 @@ export declare const DraftAnnotationSchema: z.ZodDiscriminatedUnion<"type", [z.Z
|
|
|
65
65
|
workflowId: z.ZodString;
|
|
66
66
|
id: z.ZodString;
|
|
67
67
|
}, "strip", z.ZodTypeAny, {
|
|
68
|
+
id: string;
|
|
68
69
|
type: "edit";
|
|
69
70
|
createdAt: string;
|
|
70
71
|
workflowId: string;
|
|
71
|
-
id: string;
|
|
72
72
|
beforeVersion: number;
|
|
73
73
|
afterMarkdown: string;
|
|
74
74
|
diff: string;
|
|
75
75
|
}, {
|
|
76
|
+
id: string;
|
|
76
77
|
type: "edit";
|
|
77
78
|
createdAt: string;
|
|
78
79
|
workflowId: string;
|
|
79
|
-
id: string;
|
|
80
80
|
beforeVersion: number;
|
|
81
81
|
afterMarkdown: string;
|
|
82
82
|
diff: string;
|
|
@@ -87,17 +87,17 @@ export declare const DraftAnnotationSchema: z.ZodDiscriminatedUnion<"type", [z.Z
|
|
|
87
87
|
workflowId: z.ZodString;
|
|
88
88
|
id: z.ZodString;
|
|
89
89
|
}, "strip", z.ZodTypeAny, {
|
|
90
|
+
id: string;
|
|
90
91
|
type: "approve";
|
|
91
92
|
version: number;
|
|
92
93
|
createdAt: string;
|
|
93
94
|
workflowId: string;
|
|
94
|
-
id: string;
|
|
95
95
|
}, {
|
|
96
|
+
id: string;
|
|
96
97
|
type: "approve";
|
|
97
98
|
version: number;
|
|
98
99
|
createdAt: string;
|
|
99
100
|
workflowId: string;
|
|
100
|
-
id: string;
|
|
101
101
|
}>, z.ZodObject<{
|
|
102
102
|
type: z.ZodLiteral<"reject">;
|
|
103
103
|
version: z.ZodNumber;
|
|
@@ -106,18 +106,18 @@ export declare const DraftAnnotationSchema: z.ZodDiscriminatedUnion<"type", [z.Z
|
|
|
106
106
|
workflowId: z.ZodString;
|
|
107
107
|
id: z.ZodString;
|
|
108
108
|
}, "strip", z.ZodTypeAny, {
|
|
109
|
+
id: string;
|
|
109
110
|
type: "reject";
|
|
110
111
|
version: number;
|
|
111
112
|
createdAt: string;
|
|
112
113
|
workflowId: string;
|
|
113
|
-
id: string;
|
|
114
114
|
reason?: string | undefined;
|
|
115
115
|
}, {
|
|
116
|
+
id: string;
|
|
116
117
|
type: "reject";
|
|
117
118
|
version: number;
|
|
118
119
|
createdAt: string;
|
|
119
120
|
workflowId: string;
|
|
120
|
-
id: string;
|
|
121
121
|
reason?: string | undefined;
|
|
122
122
|
}>, z.ZodObject<{
|
|
123
123
|
type: z.ZodLiteral<"resolve">;
|
|
@@ -127,17 +127,17 @@ export declare const DraftAnnotationSchema: z.ZodDiscriminatedUnion<"type", [z.Z
|
|
|
127
127
|
workflowId: z.ZodString;
|
|
128
128
|
id: z.ZodString;
|
|
129
129
|
}, "strip", z.ZodTypeAny, {
|
|
130
|
+
id: string;
|
|
130
131
|
type: "resolve";
|
|
131
132
|
createdAt: string;
|
|
132
133
|
workflowId: string;
|
|
133
|
-
id: string;
|
|
134
134
|
commentId: string;
|
|
135
135
|
resolved: boolean;
|
|
136
136
|
}, {
|
|
137
|
+
id: string;
|
|
137
138
|
type: "resolve";
|
|
138
139
|
createdAt: string;
|
|
139
140
|
workflowId: string;
|
|
140
|
-
id: string;
|
|
141
141
|
commentId: string;
|
|
142
142
|
resolved: boolean;
|
|
143
143
|
}>, z.ZodObject<{
|
|
@@ -150,20 +150,20 @@ export declare const DraftAnnotationSchema: z.ZodDiscriminatedUnion<"type", [z.Z
|
|
|
150
150
|
workflowId: z.ZodString;
|
|
151
151
|
id: z.ZodString;
|
|
152
152
|
}, "strip", z.ZodTypeAny, {
|
|
153
|
+
id: string;
|
|
153
154
|
type: "address";
|
|
154
155
|
version: number;
|
|
155
156
|
createdAt: string;
|
|
156
157
|
workflowId: string;
|
|
157
|
-
id: string;
|
|
158
158
|
commentId: string;
|
|
159
159
|
disposition: "addressed" | "deferred" | "wontfix";
|
|
160
160
|
reason?: string | undefined;
|
|
161
161
|
}, {
|
|
162
|
+
id: string;
|
|
162
163
|
type: "address";
|
|
163
164
|
version: number;
|
|
164
165
|
createdAt: string;
|
|
165
166
|
workflowId: string;
|
|
166
|
-
id: string;
|
|
167
167
|
commentId: string;
|
|
168
168
|
disposition: "addressed" | "deferred" | "wontfix";
|
|
169
169
|
reason?: string | undefined;
|