@ascribed/review 0.2.0 → 0.2.1-next.9

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.
@@ -1,3 +1,4 @@
1
+ import { DATA_OVERLAY, DATA_UI } from "../names.js";
1
2
  /** `"1 comment"`, `"2 comments"`. */
2
3
  export function plural(count, one, many = `${one}s`) {
3
4
  return `${count} ${count === 1 ? one : many}`;
@@ -163,8 +164,8 @@ export function blockText(block) {
163
164
  if (!(node instanceof Element))
164
165
  return;
165
166
  const tag = node.tagName.toUpperCase();
166
- if (node.hasAttribute("data-ascribe-overlay") ||
167
- (node.hasAttribute("data-ascribe-ui") && tag !== "INS") ||
167
+ if (node.hasAttribute(DATA_OVERLAY) ||
168
+ (node.hasAttribute(DATA_UI) && tag !== "INS") ||
168
169
  ["SCRIPT", "STYLE", "TEMPLATE", "NOSCRIPT"].includes(tag) ||
169
170
  unseen(node)) {
170
171
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/overlay/text.ts"],"names":[],"mappings":"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","sourcesContent":["// What the overlay says about threads: where each one is, when a comment was\n// made, and counts in words.\nimport type { LocatedThread } from \"../place/place.js\";\nimport type { Author } from \"../shared/types.js\";\n\n/** `\"1 comment\"`, `\"2 comments\"`. */\nexport function plural(count: number, one: string, many = `${one}s`): string {\n return `${count} ${count === 1 ? one : many}`;\n}\n\n/** The last segment of a content path. */\nfunction fileName(path: string): string {\n return path.slice(path.lastIndexOf(\"/\") + 1);\n}\n\n/** `rollouts.md:28`, or `rollouts.md:28-30`. */\nexport function linesLabel(path: string, lines: { first: number; last: number }): string {\n const range = lines.first === lines.last ? `${lines.first}` : `${lines.first}-${lines.last}`;\n return `${fileName(path)}:${range}`;\n}\n\n/** Where a thread is, for its card's header. */\nexport function whereLabel(thread: LocatedThread): string {\n const file = fileName(thread.path);\n if (thread.detached === \"file\") return `On ${file} as a whole`;\n if (thread.detached === \"line-gone\") {\n const line = thread.originalLine ?? thread.line;\n const commit = thread.originalCommit?.slice(0, 7);\n if (line === null) return `Was on ${file}, since removed`;\n return `Was on ${file}, line ${line}${commit ? ` at ${commit}` : \"\"}, since removed`;\n }\n if (thread.lines === undefined) return file;\n if (thread.detached === \"no-block\")\n return `${linesLabel(thread.path, thread.lines)}, not on this page`;\n if (thread.kind === \"review\" && thread.side === \"LEFT\") return file;\n return linesLabel(thread.path, thread.lines);\n}\n\n/** Where a thread is, for a list of them: shorter, and naming removed text. */\nexport function listLabel(thread: LocatedThread): string {\n const file = fileName(thread.path);\n if (thread.detached !== undefined) return `no block, was ${file}`;\n if (thread.kind === \"review\" && thread.side === \"LEFT\") return `removed text in ${file}`;\n return thread.lines === undefined ? file : linesLabel(thread.path, thread.lines);\n}\n\n/** Whether the thread is on removed text. */\nexport function onRemovedText(thread: LocatedThread): boolean {\n return thread.kind === \"review\" && thread.side === \"LEFT\";\n}\n\n/** Whether any comment of the thread is unsent. */\nexport function hasUnsent(thread: LocatedThread): boolean {\n return thread.comments.some((comment) => comment.pending);\n}\n\n/** A comment's author's name, `\"you\"` for the viewer. */\nexport function authorName(author: Author | null, viewer: string): string {\n if (author === null) return \"ghost\";\n return author.login === viewer ? \"you\" : author.login;\n}\n\n/** Two letters standing for an author, for the avatar's place. */\nexport function initials(author: Author | null): string {\n return (\n (author?.login ?? \"?\")\n .replace(/[^A-Za-z0-9]/g, \"\")\n .slice(0, 2)\n .toUpperCase() || \"?\"\n );\n}\n\n/** `\"just now\"`, `\"5 minutes ago\"`, `\"yesterday\"`, or a date. */\nexport function relativeTime(iso: string, now: number): string {\n const then = Date.parse(iso);\n if (Number.isNaN(then)) return \"\";\n const seconds = Math.max(0, Math.round((now - then) / 1000));\n if (seconds < 60) return \"just now\";\n const minutes = Math.round(seconds / 60);\n if (minutes < 60) return `${plural(minutes, \"minute\")} ago`;\n const hours = Math.round(minutes / 60);\n if (hours < 24) return `${plural(hours, \"hour\")} ago`;\n const days = Math.round(hours / 24);\n if (days === 1) return \"yesterday\";\n if (days < 30) return `${days} days ago`;\n return new Date(then).toISOString().slice(0, 10);\n}\n\n/** Elements whose text starts on a line of its own. */\nconst BLOCK_TAGS = new Set([\n \"ADDRESS\",\n \"ARTICLE\",\n \"ASIDE\",\n \"BLOCKQUOTE\",\n \"DD\",\n \"DETAILS\",\n \"DIV\",\n \"DL\",\n \"DT\",\n \"FIGCAPTION\",\n \"FIGURE\",\n \"FOOTER\",\n \"H1\",\n \"H2\",\n \"H3\",\n \"H4\",\n \"H5\",\n \"H6\",\n \"HEADER\",\n \"HR\",\n \"LI\",\n \"OL\",\n \"P\",\n \"PRE\",\n \"SECTION\",\n \"SUMMARY\",\n \"TABLE\",\n \"TR\",\n \"UL\",\n]);\n\n/** Classes that hide text from sight but not from screen readers. */\nconst SCREEN_READER_ONLY = [\"sr-only\", \"visually-hidden\", \"sl-sr-only\"];\n\n/** Whether a reader can't see `el`. */\nfunction unseen(el: Element): boolean {\n if (\n el.hasAttribute(\"hidden\") ||\n el.getAttribute(\"aria-hidden\") === \"true\" ||\n SCREEN_READER_ONLY.some((name) => el.classList.contains(name))\n ) {\n return true;\n }\n const style = el.ownerDocument.defaultView?.getComputedStyle(el);\n return style?.display === \"none\" || style?.visibility === \"hidden\";\n}\n\n/**\n * A block's text as a reader sees it, for quoting: links as their text, a\n * line for each paragraph, list item, or row, numbered items with their\n * numbers, images as their alt text, and code as written. What the marks and\n * the overlay add is left out, but for the words a change inserted, and so\n * is anything a reader can't see.\n */\nexport function blockText(block: Element): string {\n // Each line, and whether it's code, which keeps its spaces.\n const lines: { text: string; pre: boolean }[] = [];\n let line = \"\";\n let linePre = false;\n const breakLine = (): void => {\n if (line.trim() !== \"\") lines.push({ text: line, pre: linePre });\n line = \"\";\n linePre = false;\n };\n const walk = (node: Node, pre: boolean): void => {\n if (node.nodeType === 3) {\n const text = node.nodeValue ?? \"\";\n if (!pre) {\n line += text.replace(/\\s+/g, \" \");\n return;\n }\n const [first = \"\", ...rest] = text.split(\"\\n\");\n line += first;\n linePre = true;\n for (const next of rest) {\n lines.push({ text: line, pre: true });\n line = next;\n }\n return;\n }\n if (!(node instanceof Element)) return;\n const tag = node.tagName.toUpperCase();\n if (\n node.hasAttribute(\"data-ascribe-overlay\") ||\n (node.hasAttribute(\"data-ascribe-ui\") && tag !== \"INS\") ||\n [\"SCRIPT\", \"STYLE\", \"TEMPLATE\", \"NOSCRIPT\"].includes(tag) ||\n unseen(node)\n ) {\n return;\n }\n if (tag === \"BR\") {\n breakLine();\n return;\n }\n if (tag === \"IMG\") {\n line += node.getAttribute(\"alt\") ?? \"\";\n return;\n }\n const block = BLOCK_TAGS.has(tag);\n if (block) breakLine();\n if ((tag === \"TD\" || tag === \"TH\") && node.previousElementSibling !== null) line += \" | \";\n if (tag === \"LI\" && node.parentElement?.tagName.toUpperCase() === \"OL\") {\n const list = node.parentElement;\n const start = Number(list.getAttribute(\"start\") ?? \"1\");\n const items = Array.from(list.children).filter((c) => c.tagName.toUpperCase() === \"LI\");\n line += `${(Number.isFinite(start) ? start : 1) + items.indexOf(node)}. `;\n }\n for (const child of Array.from(node.childNodes)) walk(child, pre || tag === \"PRE\");\n if (block) breakLine();\n };\n walk(block, false);\n breakLine();\n return lines.map(({ text, pre }) => (pre ? text.trimEnd() : text.trim())).join(\"\\n\");\n}\n"]}
1
+ {"version":3,"file":"text.js","sourceRoot":"","sources":["../../src/overlay/text.ts"],"names":[],"mappings":"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","sourcesContent":["// What the overlay says about threads: where each one is, when a comment was\n// made, and counts in words.\nimport type { LocatedThread } from \"../place/place.js\";\nimport type { Author } from \"../shared/types.js\";\nimport { DATA_OVERLAY, DATA_UI } from \"../names.js\";\n\n/** `\"1 comment\"`, `\"2 comments\"`. */\nexport function plural(count: number, one: string, many = `${one}s`): string {\n return `${count} ${count === 1 ? one : many}`;\n}\n\n/** The last segment of a content path. */\nfunction fileName(path: string): string {\n return path.slice(path.lastIndexOf(\"/\") + 1);\n}\n\n/** `rollouts.md:28`, or `rollouts.md:28-30`. */\nexport function linesLabel(path: string, lines: { first: number; last: number }): string {\n const range = lines.first === lines.last ? `${lines.first}` : `${lines.first}-${lines.last}`;\n return `${fileName(path)}:${range}`;\n}\n\n/** Where a thread is, for its card's header. */\nexport function whereLabel(thread: LocatedThread): string {\n const file = fileName(thread.path);\n if (thread.detached === \"file\") return `On ${file} as a whole`;\n if (thread.detached === \"line-gone\") {\n const line = thread.originalLine ?? thread.line;\n const commit = thread.originalCommit?.slice(0, 7);\n if (line === null) return `Was on ${file}, since removed`;\n return `Was on ${file}, line ${line}${commit ? ` at ${commit}` : \"\"}, since removed`;\n }\n if (thread.lines === undefined) return file;\n if (thread.detached === \"no-block\")\n return `${linesLabel(thread.path, thread.lines)}, not on this page`;\n if (thread.kind === \"review\" && thread.side === \"LEFT\") return file;\n return linesLabel(thread.path, thread.lines);\n}\n\n/** Where a thread is, for a list of them: shorter, and naming removed text. */\nexport function listLabel(thread: LocatedThread): string {\n const file = fileName(thread.path);\n if (thread.detached !== undefined) return `no block, was ${file}`;\n if (thread.kind === \"review\" && thread.side === \"LEFT\") return `removed text in ${file}`;\n return thread.lines === undefined ? file : linesLabel(thread.path, thread.lines);\n}\n\n/** Whether the thread is on removed text. */\nexport function onRemovedText(thread: LocatedThread): boolean {\n return thread.kind === \"review\" && thread.side === \"LEFT\";\n}\n\n/** Whether any comment of the thread is unsent. */\nexport function hasUnsent(thread: LocatedThread): boolean {\n return thread.comments.some((comment) => comment.pending);\n}\n\n/** A comment's author's name, `\"you\"` for the viewer. */\nexport function authorName(author: Author | null, viewer: string): string {\n if (author === null) return \"ghost\";\n return author.login === viewer ? \"you\" : author.login;\n}\n\n/** Two letters standing for an author, for the avatar's place. */\nexport function initials(author: Author | null): string {\n return (\n (author?.login ?? \"?\")\n .replace(/[^A-Za-z0-9]/g, \"\")\n .slice(0, 2)\n .toUpperCase() || \"?\"\n );\n}\n\n/** `\"just now\"`, `\"5 minutes ago\"`, `\"yesterday\"`, or a date. */\nexport function relativeTime(iso: string, now: number): string {\n const then = Date.parse(iso);\n if (Number.isNaN(then)) return \"\";\n const seconds = Math.max(0, Math.round((now - then) / 1000));\n if (seconds < 60) return \"just now\";\n const minutes = Math.round(seconds / 60);\n if (minutes < 60) return `${plural(minutes, \"minute\")} ago`;\n const hours = Math.round(minutes / 60);\n if (hours < 24) return `${plural(hours, \"hour\")} ago`;\n const days = Math.round(hours / 24);\n if (days === 1) return \"yesterday\";\n if (days < 30) return `${days} days ago`;\n return new Date(then).toISOString().slice(0, 10);\n}\n\n/** Elements whose text starts on a line of its own. */\nconst BLOCK_TAGS = new Set([\n \"ADDRESS\",\n \"ARTICLE\",\n \"ASIDE\",\n \"BLOCKQUOTE\",\n \"DD\",\n \"DETAILS\",\n \"DIV\",\n \"DL\",\n \"DT\",\n \"FIGCAPTION\",\n \"FIGURE\",\n \"FOOTER\",\n \"H1\",\n \"H2\",\n \"H3\",\n \"H4\",\n \"H5\",\n \"H6\",\n \"HEADER\",\n \"HR\",\n \"LI\",\n \"OL\",\n \"P\",\n \"PRE\",\n \"SECTION\",\n \"SUMMARY\",\n \"TABLE\",\n \"TR\",\n \"UL\",\n]);\n\n/** Classes that hide text from sight but not from screen readers. */\nconst SCREEN_READER_ONLY = [\"sr-only\", \"visually-hidden\", \"sl-sr-only\"];\n\n/** Whether a reader can't see `el`. */\nfunction unseen(el: Element): boolean {\n if (\n el.hasAttribute(\"hidden\") ||\n el.getAttribute(\"aria-hidden\") === \"true\" ||\n SCREEN_READER_ONLY.some((name) => el.classList.contains(name))\n ) {\n return true;\n }\n const style = el.ownerDocument.defaultView?.getComputedStyle(el);\n return style?.display === \"none\" || style?.visibility === \"hidden\";\n}\n\n/**\n * A block's text as a reader sees it, for quoting: links as their text, a\n * line for each paragraph, list item, or row, numbered items with their\n * numbers, images as their alt text, and code as written. What the marks and\n * the overlay add is left out, but for the words a change inserted, and so\n * is anything a reader can't see.\n */\nexport function blockText(block: Element): string {\n // Each line, and whether it's code, which keeps its spaces.\n const lines: { text: string; pre: boolean }[] = [];\n let line = \"\";\n let linePre = false;\n const breakLine = (): void => {\n if (line.trim() !== \"\") lines.push({ text: line, pre: linePre });\n line = \"\";\n linePre = false;\n };\n const walk = (node: Node, pre: boolean): void => {\n if (node.nodeType === 3) {\n const text = node.nodeValue ?? \"\";\n if (!pre) {\n line += text.replace(/\\s+/g, \" \");\n return;\n }\n const [first = \"\", ...rest] = text.split(\"\\n\");\n line += first;\n linePre = true;\n for (const next of rest) {\n lines.push({ text: line, pre: true });\n line = next;\n }\n return;\n }\n if (!(node instanceof Element)) return;\n const tag = node.tagName.toUpperCase();\n if (\n node.hasAttribute(DATA_OVERLAY) ||\n (node.hasAttribute(DATA_UI) && tag !== \"INS\") ||\n [\"SCRIPT\", \"STYLE\", \"TEMPLATE\", \"NOSCRIPT\"].includes(tag) ||\n unseen(node)\n ) {\n return;\n }\n if (tag === \"BR\") {\n breakLine();\n return;\n }\n if (tag === \"IMG\") {\n line += node.getAttribute(\"alt\") ?? \"\";\n return;\n }\n const block = BLOCK_TAGS.has(tag);\n if (block) breakLine();\n if ((tag === \"TD\" || tag === \"TH\") && node.previousElementSibling !== null) line += \" | \";\n if (tag === \"LI\" && node.parentElement?.tagName.toUpperCase() === \"OL\") {\n const list = node.parentElement;\n const start = Number(list.getAttribute(\"start\") ?? \"1\");\n const items = Array.from(list.children).filter((c) => c.tagName.toUpperCase() === \"LI\");\n line += `${(Number.isFinite(start) ? start : 1) + items.indexOf(node)}. `;\n }\n for (const child of Array.from(node.childNodes)) walk(child, pre || tag === \"PRE\");\n if (block) breakLine();\n };\n walk(block, false);\n breakLine();\n return lines.map(({ text, pre }) => (pre ? text.trimEnd() : text.trim())).join(\"\\n\");\n}\n"]}
@@ -1,10 +1,6 @@
1
- /** Where a rendered block came from: its `data-ascribe-source` and `data-ascribe-via`. */
2
- export interface Anchor {
3
- /** `<path>:<first>-<last>`. */
4
- source: string;
5
- /** The includes it came through, outermost first, each `<path>:<line>`. */
6
- via: string[];
7
- }
1
+ import type { Anchor } from "../shapes.js";
2
+ /** Where a rendered block came from: its `data-ascribe-source` and `data-ascribe-via`, as `ascribe diff` writes them. */
3
+ export type { Anchor };
8
4
  /** A block's source, parsed. `path` is decoded. */
9
5
  export interface SourceRange {
10
6
  path: string;
@@ -1 +1 @@
1
- {"version":3,"file":"anchor.js","sourceRoot":"","sources":["../../src/place/anchor.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,mEAAmE;AACnE,4EAA4E;AAiB5E,8EAA8E;AAC9E,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACjC,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;IAC5D,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACrC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9B,IAAI,KAAK,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK;QAAE,OAAO,SAAS,CAAC;IAChD,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAC/B,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,YAAY,CAAC,KAAkB;IAC7C,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;AAClE,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI;SACR,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CACf,KAAK,CAAC,IAAI,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE;QACrD,MAAM,IAAI,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QACvC,OAAO,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC;YAClC,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;IAC7D,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CACZ;SACA,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3D,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,SAAS,CAAC,MAAc;IACtC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,CAAC","sourcesContent":["// Source anchors: `<path>:<first>-<last>` for a block, `<path>:<line>` for an\n// include. The path is a content path, `/`-separated, each segment\n// percent-encoded except for ASCII letters, digits, `-`, `.`, `_`, and `~`.\n\n/** Where a rendered block came from: its `data-ascribe-source` and `data-ascribe-via`. */\nexport interface Anchor {\n /** `<path>:<first>-<last>`. */\n source: string;\n /** The includes it came through, outermost first, each `<path>:<line>`. */\n via: string[];\n}\n\n/** A block's source, parsed. `path` is decoded. */\nexport interface SourceRange {\n path: string;\n first: number;\n last: number;\n}\n\n/** Parses `<path>:<first>-<last>`, or returns `undefined` if it isn't one. */\nexport function parseSource(source: string): SourceRange | undefined {\n const colon = source.lastIndexOf(\":\");\n if (colon <= 0) return undefined;\n const match = /^(\\d+)-(\\d+)$/.exec(source.slice(colon + 1));\n if (match === null) return undefined;\n const first = Number(match[1]);\n const last = Number(match[2]);\n if (first < 1 || last < first) return undefined;\n let path: string;\n try {\n path = decodePath(source.slice(0, colon));\n } catch {\n return undefined;\n }\n return { path, first, last };\n}\n\n/** Writes a block's source for a decoded content path. */\nexport function formatSource(range: SourceRange): string {\n return `${encodePath(range.path)}:${range.first}-${range.last}`;\n}\n\n/** Percent-encodes each segment of a `/`-separated path, as anchors do. */\nexport function encodePath(path: string): string {\n return path\n .split(\"/\")\n .map((segment) =>\n Array.from(new TextEncoder().encode(segment), (byte) => {\n const char = String.fromCharCode(byte);\n return /[A-Za-z0-9\\-._~]/.test(char)\n ? char\n : `%${byte.toString(16).toUpperCase().padStart(2, \"0\")}`;\n }).join(\"\"),\n )\n .join(\"/\");\n}\n\n/** The inverse of `encodePath`. */\nexport function decodePath(path: string): string {\n return path.split(\"/\").map(decodeURIComponent).join(\"/\");\n}\n\n/** A string that identifies an anchor, `via` included. */\nexport function anchorKey(anchor: Anchor): string {\n return [anchor.source, ...anchor.via].join(\" \");\n}\n"]}
1
+ {"version":3,"file":"anchor.js","sourceRoot":"","sources":["../../src/place/anchor.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,mEAAmE;AACnE,4EAA4E;AAc5E,8EAA8E;AAC9E,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IACjC,MAAM,KAAK,GAAG,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;IAC5D,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACrC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9B,IAAI,KAAK,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK;QAAE,OAAO,SAAS,CAAC;IAChD,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;AAC/B,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,YAAY,CAAC,KAAkB;IAC7C,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;AAClE,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI;SACR,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CACf,KAAK,CAAC,IAAI,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE;QACrD,MAAM,IAAI,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QACvC,OAAO,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC;YAClC,CAAC,CAAC,IAAI;YACN,CAAC,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;IAC7D,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CACZ;SACA,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC3D,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,SAAS,CAAC,MAAc;IACtC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,CAAC","sourcesContent":["// Source anchors: `<path>:<first>-<last>` for a block, `<path>:<line>` for an\n// include. The path is a content path, `/`-separated, each segment\n// percent-encoded except for ASCII letters, digits, `-`, `.`, `_`, and `~`.\n\nimport type { Anchor } from \"../shapes.js\";\n\n/** Where a rendered block came from: its `data-ascribe-source` and `data-ascribe-via`, as `ascribe diff` writes them. */\nexport type { Anchor };\n\n/** A block's source, parsed. `path` is decoded. */\nexport interface SourceRange {\n path: string;\n first: number;\n last: number;\n}\n\n/** Parses `<path>:<first>-<last>`, or returns `undefined` if it isn't one. */\nexport function parseSource(source: string): SourceRange | undefined {\n const colon = source.lastIndexOf(\":\");\n if (colon <= 0) return undefined;\n const match = /^(\\d+)-(\\d+)$/.exec(source.slice(colon + 1));\n if (match === null) return undefined;\n const first = Number(match[1]);\n const last = Number(match[2]);\n if (first < 1 || last < first) return undefined;\n let path: string;\n try {\n path = decodePath(source.slice(0, colon));\n } catch {\n return undefined;\n }\n return { path, first, last };\n}\n\n/** Writes a block's source for a decoded content path. */\nexport function formatSource(range: SourceRange): string {\n return `${encodePath(range.path)}:${range.first}-${range.last}`;\n}\n\n/** Percent-encodes each segment of a `/`-separated path, as anchors do. */\nexport function encodePath(path: string): string {\n return path\n .split(\"/\")\n .map((segment) =>\n Array.from(new TextEncoder().encode(segment), (byte) => {\n const char = String.fromCharCode(byte);\n return /[A-Za-z0-9\\-._~]/.test(char)\n ? char\n : `%${byte.toString(16).toUpperCase().padStart(2, \"0\")}`;\n }).join(\"\"),\n )\n .join(\"/\");\n}\n\n/** The inverse of `encodePath`. */\nexport function decodePath(path: string): string {\n return path.split(\"/\").map(decodeURIComponent).join(\"/\");\n}\n\n/** A string that identifies an anchor, `via` included. */\nexport function anchorKey(anchor: Anchor): string {\n return [anchor.source, ...anchor.via].join(\" \");\n}\n"]}
@@ -0,0 +1,253 @@
1
+ /**
2
+ * Where a block's text is written: the README's anchor grammar, the same
3
+ * string the rendered page carries in `data-ascribe-source` and
4
+ * `data-ascribe-via`.
5
+ */
6
+ export interface Anchor {
7
+ /**
8
+ * `<path>:<first>-<last>`: the file's content path, percent-encoded by
9
+ * segment, and the block's first and last lines, from 1.
10
+ */
11
+ source: string;
12
+ /**
13
+ * The includes the block came through, outermost first, each
14
+ * `<path>:<line>`. Empty for a block written in the page itself.
15
+ */
16
+ via: string[];
17
+ }
18
+ /** The base of a comparison, in the report. */
19
+ export interface BaseInfo {
20
+ /** The revision asked for, or the default branch used. */
21
+ requested: string;
22
+ /** The commit it names. */
23
+ commit: string;
24
+ /**
25
+ * The merge base of that commit and `HEAD`, which the comparison reads;
26
+ * `null` with `--base-exact`, which reads `commit`.
27
+ */
28
+ merge_base: string | null;
29
+ }
30
+ /** One build's changed pages. */
31
+ export interface BuildData {
32
+ /** The build's name. */
33
+ build: string;
34
+ /** Its changed pages, in path order. */
35
+ pages: PageData[];
36
+ }
37
+ /** What changed in one build. */
38
+ export interface BuildDiff {
39
+ /** The build's name. */
40
+ build: string;
41
+ /** The pages that changed, in path order. */
42
+ pages: PageDiff[];
43
+ }
44
+ /** One block's change. */
45
+ export interface Change {
46
+ /** What happened to it. */
47
+ kind: ChangeKind;
48
+ /**
49
+ * For a removed block, and a moved block's old place: the block of the
50
+ * new version it came after, among its siblings. Absent when it was
51
+ * first.
52
+ */
53
+ after?: Anchor;
54
+ /** Where it's written now: every kind but `removed`. */
55
+ now?: Anchor;
56
+ /**
57
+ * For a removed block, and a moved block's old place: the block of the
58
+ * new version it was inside. Absent at the top of the page.
59
+ */
60
+ parent?: Anchor;
61
+ /**
62
+ * For a removed block, its text, whitespace collapsed, so it can be
63
+ * shown where it was.
64
+ */
65
+ text?: string;
66
+ /** Where it was written: every kind but `added`. */
67
+ was?: Anchor;
68
+ /** For changed prose, the words that differ. */
69
+ words?: Words;
70
+ }
71
+ /** The kinds of block change. */
72
+ export type ChangeKind = "changed" | "added" | "removed" | "moved";
73
+ /** How many changes of each kind a page has. */
74
+ export interface Counts {
75
+ /** Blocks whose content changed. */
76
+ changed: number;
77
+ /** Blocks only in the new version. */
78
+ added: number;
79
+ /** Blocks only in the old version. */
80
+ removed: number;
81
+ /** Blocks in both, somewhere else. */
82
+ moved: number;
83
+ }
84
+ /** The whole report, as `ascribe diff --format json` writes it. */
85
+ export interface DiffReport {
86
+ /** `SCHEMA_VERSION`. */
87
+ schema_version: number;
88
+ /** The version of Ascribe that wrote it. */
89
+ ascribe_version: string;
90
+ /** What was compared with. */
91
+ base: BaseInfo;
92
+ /** Where the project is. */
93
+ repository: RepositoryInfo;
94
+ /**
95
+ * How many errors `ascribe check` finds in the working tree, for the
96
+ * builds compared. The comparison runs regardless, but a page with an
97
+ * error may not render as it will once it's fixed, so a reviewer should
98
+ * know. `diff_project` sets it; `Report::new` leaves it zero.
99
+ */
100
+ working_tree_errors: number;
101
+ /** What changed, per build, in the order asked for. */
102
+ builds: BuildDiff[];
103
+ }
104
+ /** An image a page refers to. */
105
+ export interface ImageRef {
106
+ /** The image's source file. */
107
+ path: string;
108
+ /** Its size, when it's too large to include. */
109
+ bytes?: number;
110
+ /** Its key in `images`, when it's included. */
111
+ image?: string;
112
+ }
113
+ /** How many changed pages a report renders, and how many it left out. */
114
+ export interface Limit {
115
+ /** The most it renders. */
116
+ pages: number;
117
+ /** How many it left out. */
118
+ omitted: number;
119
+ }
120
+ /** One changed page: `ascribe diff --format json`'s, and its renderings. */
121
+ export interface PageData {
122
+ /** The page's content path. */
123
+ path: string;
124
+ /** Its route: the new one, or for a removed page the old one. */
125
+ route: string;
126
+ /**
127
+ * Whether the build publishes it only now, only before, or in both
128
+ * with a difference.
129
+ */
130
+ status: PageStatus;
131
+ /** Whether the page's own file changed (or exists on one side only). */
132
+ own_file_changed: boolean;
133
+ /**
134
+ * The other changed files the page's change can come from: fragments it
135
+ * includes and pages its links take a title or a heading from, in path
136
+ * order; then the snippets whose code changed, by address
137
+ * (`code:app.py#main`); and `ascribe.toml` (last) when the content model
138
+ * is a cause.
139
+ */
140
+ because: string[];
141
+ /**
142
+ * What changed about the page itself besides its blocks, in this
143
+ * order: `title`, `frontmatter`, `availability` (the page-level one),
144
+ * and `route`. Empty for an added or removed page.
145
+ */
146
+ page_changed: string[];
147
+ /** How many changes of each kind. */
148
+ counts: Counts;
149
+ /**
150
+ * The block-level changes, in the page's order, a removed block where
151
+ * it was. Empty for an added or removed page.
152
+ */
153
+ changes: Change[];
154
+ /** Its title, when the build has one. */
155
+ title: string | null;
156
+ /** The page now, as a key of `pages`; `null` when there's none. */
157
+ now: string | null;
158
+ /** The page before, as a key of `pages`; `null` when there's none. */
159
+ was: string | null;
160
+ /** Whether it's beyond the limit, so not rendered. */
161
+ omitted: boolean;
162
+ }
163
+ /** What changed on one page of a build. */
164
+ export interface PageDiff {
165
+ /** The page's content path. */
166
+ path: string;
167
+ /** Its route: the new one, or for a removed page the old one. */
168
+ route: string;
169
+ /**
170
+ * Whether the build publishes it only now, only before, or in both
171
+ * with a difference.
172
+ */
173
+ status: PageStatus;
174
+ /** Whether the page's own file changed (or exists on one side only). */
175
+ own_file_changed: boolean;
176
+ /**
177
+ * The other changed files the page's change can come from: fragments it
178
+ * includes and pages its links take a title or a heading from, in path
179
+ * order; then the snippets whose code changed, by address
180
+ * (`code:app.py#main`); and `ascribe.toml` (last) when the content model
181
+ * is a cause.
182
+ */
183
+ because: string[];
184
+ /**
185
+ * What changed about the page itself besides its blocks, in this
186
+ * order: `title`, `frontmatter`, `availability` (the page-level one),
187
+ * and `route`. Empty for an added or removed page.
188
+ */
189
+ page_changed: string[];
190
+ /** How many changes of each kind. */
191
+ counts: Counts;
192
+ /**
193
+ * The block-level changes, in the page's order, a removed block where
194
+ * it was. Empty for an added or removed page.
195
+ */
196
+ changes: Change[];
197
+ }
198
+ /** Whether a page is new, gone, or different. */
199
+ export type PageStatus = "added" | "removed" | "changed";
200
+ /** A page rendered: its HTML, and what each image reference in it is. */
201
+ export interface RenderedPage {
202
+ /** The page's content, rendered with source anchors. */
203
+ html: string;
204
+ /** Each image reference the HTML writes, and what it is. */
205
+ images: Record<string, ImageRef>;
206
+ }
207
+ /**
208
+ * The data the report's script draws from. Its keys, like the JSON
209
+ * report's, are snake_case.
210
+ */
211
+ export interface ReportData {
212
+ /** The version of Ascribe that wrote it. */
213
+ ascribe_version: string;
214
+ /** What was compared with. */
215
+ base: BaseInfo;
216
+ /** How many errors `ascribe check` finds in the working tree. */
217
+ working_tree_errors: number;
218
+ /** What changed, per build. */
219
+ builds: BuildData[];
220
+ /** Each rendered page, once however many builds render it alike. */
221
+ pages: Record<string, RenderedPage>;
222
+ /** Each image, as a `data:` URL, once however many pages use it. */
223
+ images: Record<string, string>;
224
+ /** How many changed pages the report renders, and how many it left out. */
225
+ limit: Limit;
226
+ /** The size, in bytes, above which an image isn't included. */
227
+ image_limit: number;
228
+ }
229
+ /** The repository, in the report. */
230
+ export interface RepositoryInfo {
231
+ /** The repository's top-level directory, as git prints it. */
232
+ root: string;
233
+ /**
234
+ * The project's folder inside it, with a trailing `/`, or empty when
235
+ * the project is at the repository's root.
236
+ */
237
+ project_prefix: string;
238
+ }
239
+ /**
240
+ * The words that differ inside a changed block of prose. Ranges are
241
+ * `[start, end)` in characters (Unicode scalar values) of each side's
242
+ * `text`, which is the block's text with whitespace collapsed.
243
+ */
244
+ export interface Words {
245
+ /** Ranges in `now_text`: words added or replacing others. */
246
+ now: [number, number][];
247
+ /** Ranges in `was_text`: words removed or replaced. */
248
+ was: [number, number][];
249
+ /** The block's text now. */
250
+ now_text: string;
251
+ /** The block's text before. */
252
+ was_text: string;
253
+ }
package/dist/shapes.js ADDED
@@ -0,0 +1,9 @@
1
+ // The JSON Ascribe writes that this package reads, as TypeScript types.
2
+ // Generated from the Rust types that write it, through the JSON Schemas in
3
+ // schemas/, by crates/ascribe-cli/src/shapes.rs. Change the Rust types, then
4
+ // run `ASCRIBE_BLESS=1 cargo test -p ascribe-cli shapes`. Don't edit it.
5
+ //
6
+ // - DiffReport (`ascribe diff --format json`)
7
+ // - ReportData (`ascribe diff --format html`, for the report's script)
8
+ export {};
9
+ //# sourceMappingURL=shapes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shapes.js","sourceRoot":"","sources":["../src/shapes.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,2EAA2E;AAC3E,6EAA6E;AAC7E,yEAAyE;AACzE,EAAE;AACF,8CAA8C;AAC9C,uEAAuE","sourcesContent":["// The JSON Ascribe writes that this package reads, as TypeScript types.\n// Generated from the Rust types that write it, through the JSON Schemas in\n// schemas/, by crates/ascribe-cli/src/shapes.rs. Change the Rust types, then\n// run `ASCRIBE_BLESS=1 cargo test -p ascribe-cli shapes`. Don't edit it.\n//\n// - DiffReport (`ascribe diff --format json`)\n// - ReportData (`ascribe diff --format html`, for the report's script)\n\n/**\n * Where a block's text is written: the README's anchor grammar, the same\n * string the rendered page carries in `data-ascribe-source` and\n * `data-ascribe-via`.\n */\nexport interface Anchor {\n /**\n * `<path>:<first>-<last>`: the file's content path, percent-encoded by\n * segment, and the block's first and last lines, from 1.\n */\n source: string;\n /**\n * The includes the block came through, outermost first, each\n * `<path>:<line>`. Empty for a block written in the page itself.\n */\n via: string[];\n}\n\n/** The base of a comparison, in the report. */\nexport interface BaseInfo {\n /** The revision asked for, or the default branch used. */\n requested: string;\n /** The commit it names. */\n commit: string;\n /**\n * The merge base of that commit and `HEAD`, which the comparison reads;\n * `null` with `--base-exact`, which reads `commit`.\n */\n merge_base: string | null;\n}\n\n/** One build's changed pages. */\nexport interface BuildData {\n /** The build's name. */\n build: string;\n /** Its changed pages, in path order. */\n pages: PageData[];\n}\n\n/** What changed in one build. */\nexport interface BuildDiff {\n /** The build's name. */\n build: string;\n /** The pages that changed, in path order. */\n pages: PageDiff[];\n}\n\n/** One block's change. */\nexport interface Change {\n /** What happened to it. */\n kind: ChangeKind;\n /**\n * For a removed block, and a moved block's old place: the block of the\n * new version it came after, among its siblings. Absent when it was\n * first.\n */\n after?: Anchor;\n /** Where it's written now: every kind but `removed`. */\n now?: Anchor;\n /**\n * For a removed block, and a moved block's old place: the block of the\n * new version it was inside. Absent at the top of the page.\n */\n parent?: Anchor;\n /**\n * For a removed block, its text, whitespace collapsed, so it can be\n * shown where it was.\n */\n text?: string;\n /** Where it was written: every kind but `added`. */\n was?: Anchor;\n /** For changed prose, the words that differ. */\n words?: Words;\n}\n\n/** The kinds of block change. */\nexport type ChangeKind = \"changed\" | \"added\" | \"removed\" | \"moved\";\n\n/** How many changes of each kind a page has. */\nexport interface Counts {\n /** Blocks whose content changed. */\n changed: number;\n /** Blocks only in the new version. */\n added: number;\n /** Blocks only in the old version. */\n removed: number;\n /** Blocks in both, somewhere else. */\n moved: number;\n}\n\n/** The whole report, as `ascribe diff --format json` writes it. */\nexport interface DiffReport {\n /** `SCHEMA_VERSION`. */\n schema_version: number;\n /** The version of Ascribe that wrote it. */\n ascribe_version: string;\n /** What was compared with. */\n base: BaseInfo;\n /** Where the project is. */\n repository: RepositoryInfo;\n /**\n * How many errors `ascribe check` finds in the working tree, for the\n * builds compared. The comparison runs regardless, but a page with an\n * error may not render as it will once it's fixed, so a reviewer should\n * know. `diff_project` sets it; `Report::new` leaves it zero.\n */\n working_tree_errors: number;\n /** What changed, per build, in the order asked for. */\n builds: BuildDiff[];\n}\n\n/** An image a page refers to. */\nexport interface ImageRef {\n /** The image's source file. */\n path: string;\n /** Its size, when it's too large to include. */\n bytes?: number;\n /** Its key in `images`, when it's included. */\n image?: string;\n}\n\n/** How many changed pages a report renders, and how many it left out. */\nexport interface Limit {\n /** The most it renders. */\n pages: number;\n /** How many it left out. */\n omitted: number;\n}\n\n/** One changed page: `ascribe diff --format json`'s, and its renderings. */\nexport interface PageData {\n /** The page's content path. */\n path: string;\n /** Its route: the new one, or for a removed page the old one. */\n route: string;\n /**\n * Whether the build publishes it only now, only before, or in both\n * with a difference.\n */\n status: PageStatus;\n /** Whether the page's own file changed (or exists on one side only). */\n own_file_changed: boolean;\n /**\n * The other changed files the page's change can come from: fragments it\n * includes and pages its links take a title or a heading from, in path\n * order; then the snippets whose code changed, by address\n * (`code:app.py#main`); and `ascribe.toml` (last) when the content model\n * is a cause.\n */\n because: string[];\n /**\n * What changed about the page itself besides its blocks, in this\n * order: `title`, `frontmatter`, `availability` (the page-level one),\n * and `route`. Empty for an added or removed page.\n */\n page_changed: string[];\n /** How many changes of each kind. */\n counts: Counts;\n /**\n * The block-level changes, in the page's order, a removed block where\n * it was. Empty for an added or removed page.\n */\n changes: Change[];\n /** Its title, when the build has one. */\n title: string | null;\n /** The page now, as a key of `pages`; `null` when there's none. */\n now: string | null;\n /** The page before, as a key of `pages`; `null` when there's none. */\n was: string | null;\n /** Whether it's beyond the limit, so not rendered. */\n omitted: boolean;\n}\n\n/** What changed on one page of a build. */\nexport interface PageDiff {\n /** The page's content path. */\n path: string;\n /** Its route: the new one, or for a removed page the old one. */\n route: string;\n /**\n * Whether the build publishes it only now, only before, or in both\n * with a difference.\n */\n status: PageStatus;\n /** Whether the page's own file changed (or exists on one side only). */\n own_file_changed: boolean;\n /**\n * The other changed files the page's change can come from: fragments it\n * includes and pages its links take a title or a heading from, in path\n * order; then the snippets whose code changed, by address\n * (`code:app.py#main`); and `ascribe.toml` (last) when the content model\n * is a cause.\n */\n because: string[];\n /**\n * What changed about the page itself besides its blocks, in this\n * order: `title`, `frontmatter`, `availability` (the page-level one),\n * and `route`. Empty for an added or removed page.\n */\n page_changed: string[];\n /** How many changes of each kind. */\n counts: Counts;\n /**\n * The block-level changes, in the page's order, a removed block where\n * it was. Empty for an added or removed page.\n */\n changes: Change[];\n}\n\n/** Whether a page is new, gone, or different. */\nexport type PageStatus = \"added\" | \"removed\" | \"changed\";\n\n/** A page rendered: its HTML, and what each image reference in it is. */\nexport interface RenderedPage {\n /** The page's content, rendered with source anchors. */\n html: string;\n /** Each image reference the HTML writes, and what it is. */\n images: Record<string, ImageRef>;\n}\n\n/**\n * The data the report's script draws from. Its keys, like the JSON\n * report's, are snake_case.\n */\nexport interface ReportData {\n /** The version of Ascribe that wrote it. */\n ascribe_version: string;\n /** What was compared with. */\n base: BaseInfo;\n /** How many errors `ascribe check` finds in the working tree. */\n working_tree_errors: number;\n /** What changed, per build. */\n builds: BuildData[];\n /** Each rendered page, once however many builds render it alike. */\n pages: Record<string, RenderedPage>;\n /** Each image, as a `data:` URL, once however many pages use it. */\n images: Record<string, string>;\n /** How many changed pages the report renders, and how many it left out. */\n limit: Limit;\n /** The size, in bytes, above which an image isn't included. */\n image_limit: number;\n}\n\n/** The repository, in the report. */\nexport interface RepositoryInfo {\n /** The repository's top-level directory, as git prints it. */\n root: string;\n /**\n * The project's folder inside it, with a trailing `/`, or empty when\n * the project is at the repository's root.\n */\n project_prefix: string;\n}\n\n/**\n * The words that differ inside a changed block of prose. Ranges are\n * `[start, end)` in characters (Unicode scalar values) of each side's\n * `text`, which is the block's text with whitespace collapsed.\n */\nexport interface Words {\n /** Ranges in `now_text`: words added or replacing others. */\n now: [number, number][];\n /** Ranges in `was_text`: words removed or replaced. */\n was: [number, number][];\n /** The block's text now. */\n now_text: string;\n /** The block's text before. */\n was_text: string;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ascribed/review",
3
- "version": "0.2.0",
3
+ "version": "0.2.1-next.9",
4
4
  "description": "Reviewing Ascribe pages as readers see them: changed blocks marked on the rendered page, and a pull request's review threads beside them.",
5
5
  "type": "module",
6
6
  "license": "MPL-2.0",
@@ -40,7 +40,7 @@
40
40
  "node": ">=24"
41
41
  },
42
42
  "devDependencies": {
43
- "@ascribed/elements": "0.2.0",
43
+ "@ascribed/elements": "0.2.1-next.9",
44
44
  "@types/node": "^24.0.0",
45
45
  "esbuild": "^0.28.2",
46
46
  "jsdom": "^30.1.1"
@@ -51,6 +51,7 @@
51
51
  "dependencies": {
52
52
  "smol-toml": "^1.9.0"
53
53
  },
54
+ "gitHead": "52da4c48b9cb985c8e97d926aafa4041fc47f2eb",
54
55
  "scripts": {
55
56
  "build": "tsc -p tsconfig.build.json",
56
57
  "typecheck": "tsc --noEmit",