@commentray/core 0.0.1 → 0.0.2

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 (50) hide show
  1. package/dist/angles-toml.d.ts +18 -0
  2. package/dist/angles-toml.d.ts.map +1 -0
  3. package/dist/angles-toml.js +62 -0
  4. package/dist/angles-toml.js.map +1 -0
  5. package/dist/angles.d.ts +6 -0
  6. package/dist/angles.d.ts.map +1 -0
  7. package/dist/angles.js +14 -0
  8. package/dist/angles.js.map +1 -0
  9. package/dist/blocks.d.ts +66 -0
  10. package/dist/blocks.d.ts.map +1 -0
  11. package/dist/blocks.js +101 -0
  12. package/dist/blocks.js.map +1 -0
  13. package/dist/commentray-path-resolution.d.ts +20 -0
  14. package/dist/commentray-path-resolution.d.ts.map +1 -0
  15. package/dist/commentray-path-resolution.js +38 -0
  16. package/dist/commentray-path-resolution.js.map +1 -0
  17. package/dist/config.d.ts +48 -0
  18. package/dist/config.d.ts.map +1 -1
  19. package/dist/config.js +66 -2
  20. package/dist/config.js.map +1 -1
  21. package/dist/github-url.d.ts +14 -0
  22. package/dist/github-url.d.ts.map +1 -0
  23. package/dist/github-url.js +25 -0
  24. package/dist/github-url.js.map +1 -0
  25. package/dist/index.d.ts +14 -4
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +8 -2
  28. package/dist/index.js.map +1 -1
  29. package/dist/metadata.js +48 -22
  30. package/dist/metadata.js.map +1 -1
  31. package/dist/migrate.d.ts.map +1 -1
  32. package/dist/migrate.js +32 -10
  33. package/dist/migrate.js.map +1 -1
  34. package/dist/model.d.ts +37 -2
  35. package/dist/model.d.ts.map +1 -1
  36. package/dist/model.js +1 -1
  37. package/dist/model.js.map +1 -1
  38. package/dist/paths.d.ts +17 -1
  39. package/dist/paths.d.ts.map +1 -1
  40. package/dist/paths.js +31 -1
  41. package/dist/paths.js.map +1 -1
  42. package/dist/scroll-sync.d.ts +27 -0
  43. package/dist/scroll-sync.d.ts.map +1 -0
  44. package/dist/scroll-sync.js +80 -0
  45. package/dist/scroll-sync.js.map +1 -0
  46. package/dist/validate-project.d.ts +6 -0
  47. package/dist/validate-project.d.ts.map +1 -1
  48. package/dist/validate-project.js +45 -14
  49. package/dist/validate-project.js.map +1 -1
  50. package/package.json +1 -1
@@ -0,0 +1,18 @@
1
+ export type UpsertAngleDefinitionInput = {
2
+ id: string;
3
+ title?: string;
4
+ /** When true, set `angles.default_angle` to this id after the merge. */
5
+ makeDefault?: boolean;
6
+ };
7
+ /**
8
+ * Ensures `{storage.dir}/source/.default` exists so the repository uses **Angles** on-disk layout
9
+ * (see `docs/spec/storage.md`).
10
+ */
11
+ export declare function ensureAnglesSentinelFile(repoRoot: string, storageDir: string): Promise<void>;
12
+ /**
13
+ * Reads `.commentray.toml`, appends a new `[[angles.definitions]]` row (or throws if the id
14
+ * already exists), validates via {@link mergeCommentrayConfig}, and writes the file back using
15
+ * TOML stringify (comments and key order are not preserved).
16
+ */
17
+ export declare function upsertAngleDefinitionInCommentrayToml(repoRoot: string, input: UpsertAngleDefinitionInput): Promise<void>;
18
+ //# sourceMappingURL=angles-toml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"angles-toml.d.ts","sourceRoot":"","sources":["../src/angles-toml.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,0BAA0B,GAAG;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAMF;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAaf;AAED;;;;GAIG;AACH,wBAAsB,qCAAqC,CACzD,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,0BAA0B,GAChC,OAAO,CAAC,IAAI,CAAC,CAmCf"}
@@ -0,0 +1,62 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { parse as parseToml, stringify } from "@iarna/toml";
4
+ import { assertValidAngleId } from "./angles.js";
5
+ import { mergeCommentrayConfig } from "./config.js";
6
+ import { commentrayAnglesSentinelPath } from "./paths.js";
7
+ const MINIMAL_NEW_TOML = `[storage]
8
+ dir = ".commentray"
9
+ `;
10
+ /**
11
+ * Ensures `{storage.dir}/source/.default` exists so the repository uses **Angles** on-disk layout
12
+ * (see `docs/spec/storage.md`).
13
+ */
14
+ export async function ensureAnglesSentinelFile(repoRoot, storageDir) {
15
+ const rel = commentrayAnglesSentinelPath(storageDir);
16
+ const absolute = path.join(repoRoot, ...rel.split("/"));
17
+ await fs.mkdir(path.dirname(absolute), { recursive: true });
18
+ try {
19
+ await fs.access(absolute);
20
+ }
21
+ catch {
22
+ await fs.writeFile(absolute, "# Commentray Angles layout sentinel (see docs/spec/storage.md).\n", "utf8");
23
+ }
24
+ }
25
+ /**
26
+ * Reads `.commentray.toml`, appends a new `[[angles.definitions]]` row (or throws if the id
27
+ * already exists), validates via {@link mergeCommentrayConfig}, and writes the file back using
28
+ * TOML stringify (comments and key order are not preserved).
29
+ */
30
+ export async function upsertAngleDefinitionInCommentrayToml(repoRoot, input) {
31
+ const id = assertValidAngleId(input.id);
32
+ const configPath = path.join(repoRoot, ".commentray.toml");
33
+ let raw = "";
34
+ try {
35
+ raw = await fs.readFile(configPath, "utf8");
36
+ }
37
+ catch (err) {
38
+ const code = err.code;
39
+ if (code !== "ENOENT")
40
+ throw err;
41
+ }
42
+ const parsed = raw.trim() ? parseToml(raw) : {};
43
+ const angles = parsed.angles ?? {};
44
+ const definitions = [...(angles.definitions ?? [])];
45
+ if (definitions.some((d) => d && typeof d === "object" && "id" in d && String(d.id) === id)) {
46
+ throw new Error(`Angle "${id}" is already listed in [angles].definitions`);
47
+ }
48
+ definitions.push({
49
+ id,
50
+ title: input.title?.trim() || undefined,
51
+ });
52
+ angles.definitions = definitions;
53
+ if (input.makeDefault === true || definitions.length === 1) {
54
+ angles.default_angle = id;
55
+ }
56
+ parsed.angles = angles;
57
+ mergeCommentrayConfig(parsed);
58
+ const serialized = stringify(parsed);
59
+ const body = raw.trim() ? serialized : `${MINIMAL_NEW_TOML.trim()}\n\n${serialized}`;
60
+ await fs.writeFile(configPath, `${body}\n`, "utf8");
61
+ }
62
+ //# sourceMappingURL=angles-toml.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"angles-toml.js","sourceRoot":"","sources":["../src/angles-toml.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Validates and returns a trimmed Angle id. Used for TOML `angles.*` keys and path segments.
3
+ * Rejects empty, `.`, `..`, and anything outside `[a-zA-Z0-9_-]` so paths stay predictable and safe.
4
+ */
5
+ export declare function assertValidAngleId(angleId: string): string;
6
+ //# sourceMappingURL=angles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"angles.d.ts","sourceRoot":"","sources":["../src/angles.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAQ1D"}
package/dist/angles.js ADDED
@@ -0,0 +1,14 @@
1
+ /** Allowed characters for an Angle id (folder / file segment). */
2
+ const ANGLE_ID_RE = /^[a-zA-Z0-9_-]{1,64}$/;
3
+ /**
4
+ * Validates and returns a trimmed Angle id. Used for TOML `angles.*` keys and path segments.
5
+ * Rejects empty, `.`, `..`, and anything outside `[a-zA-Z0-9_-]` so paths stay predictable and safe.
6
+ */
7
+ export function assertValidAngleId(angleId) {
8
+ const t = angleId.trim();
9
+ if (t === "." || t === ".." || !ANGLE_ID_RE.test(t)) {
10
+ throw new Error(`Invalid angle id "${angleId}" (use 1–64 characters from [a-zA-Z0-9_-] only; not "." or "..")`);
11
+ }
12
+ return t;
13
+ }
14
+ //# sourceMappingURL=angles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"angles.js","sourceRoot":"","sources":["../src/angles.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,MAAM,WAAW,GAAG,uBAAuB,CAAC;AAE5C;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IACzB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CACb,qBAAqB,OAAO,kEAAkE,CAC/F,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,66 @@
1
+ import type { CommentrayBlock, CommentrayIndex } from "./model.js";
2
+ /** 1-based inclusive range of source lines a block points to. */
3
+ export type BlockRange = {
4
+ startLine: number;
5
+ endLine: number;
6
+ };
7
+ export type CreateBlockForRangeInput = {
8
+ /**
9
+ * Repo-relative path to the primary source file. Shown in the block's
10
+ * heading so readers can see where the range lives without leaving the
11
+ * commentary pane.
12
+ */
13
+ sourcePath: string;
14
+ /** Full source text (lines separated by `\n`). */
15
+ sourceText: string;
16
+ /** Selected range to anchor the block to. */
17
+ range: BlockRange;
18
+ /** Optional explicit id; one is generated when omitted. */
19
+ id?: string;
20
+ /** Seam for deterministic tests. Default: `Math.random`. */
21
+ rng?: () => number;
22
+ };
23
+ export type CreatedBlock = {
24
+ block: CommentrayBlock;
25
+ /**
26
+ * Markdown fragment to append to the commentray file. Starts with the
27
+ * invisible `<!-- commentray:block ... -->` marker and ends with a
28
+ * trailing newline so subsequent appends stay separated.
29
+ */
30
+ markdown: string;
31
+ /**
32
+ * 0-based line offset within `markdown` where the author's caret should
33
+ * land after insertion — the placeholder line ready to be overwritten.
34
+ */
35
+ caretLineOffset: number;
36
+ };
37
+ /**
38
+ * Create a block (domain entity) together with the Markdown fragment that
39
+ * carries it in the commentary file. Pure: no I/O, deterministic when a
40
+ * fixed `rng` and `id` are supplied.
41
+ */
42
+ export declare function createBlockForRange(input: CreateBlockForRangeInput): CreatedBlock;
43
+ /**
44
+ * Append `blockMarkdown` to existing commentary content, guaranteeing a
45
+ * single blank-line separator regardless of how the existing content ended.
46
+ */
47
+ export declare function appendBlockToCommentray(existing: string, blockMarkdown: string): string;
48
+ export type AddBlockToIndexInput = {
49
+ sourcePath: string;
50
+ commentrayPath: string;
51
+ block: CommentrayBlock;
52
+ };
53
+ /**
54
+ * Return a new index with the block added under the given source file.
55
+ * The source entry is created lazily when it does not exist yet. Throws
56
+ * when the block id already exists under that source so callers cannot
57
+ * silently corrupt the index.
58
+ */
59
+ export declare function addBlockToIndex(index: CommentrayIndex, input: AddBlockToIndexInput): CommentrayIndex;
60
+ /**
61
+ * Six-character base-36 id. Alphabet excludes uppercase so ids are case-
62
+ * insensitive-safe on filesystems and URLs. Collision space ≈ 2 billion,
63
+ * comfortably larger than any plausible per-file block count.
64
+ */
65
+ export declare function generateBlockId(rng?: () => number): string;
66
+ //# sourceMappingURL=blocks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocks.d.ts","sourceRoot":"","sources":["../src/blocks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EAEf,eAAe,EAEhB,MAAM,YAAY,CAAC;AAEpB,iEAAiE;AACjE,MAAM,MAAM,UAAU,GAAG;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,KAAK,EAAE,UAAU,CAAC;IAClB,2DAA2D;IAC3D,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,4DAA4D;IAC5D,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,eAAe,CAAC;IACvB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAMF;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,wBAAwB,GAAG,YAAY,CASjF;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAKvF;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,eAAe,CAAC;CACxB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,eAAe,EACtB,KAAK,EAAE,oBAAoB,GAC1B,eAAe,CAuBjB;AAKD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAM,MAAoB,GAAG,MAAM,CAMvE"}
package/dist/blocks.js ADDED
@@ -0,0 +1,101 @@
1
+ import { formatLineRange } from "./anchors.js";
2
+ const BLOCK_MARKER_PREFIX = "<!-- commentray:block id=";
3
+ const BLOCK_MARKER_SUFFIX = " -->";
4
+ const CARET_PLACEHOLDER = "_(write commentary here)_";
5
+ /**
6
+ * Create a block (domain entity) together with the Markdown fragment that
7
+ * carries it in the commentary file. Pure: no I/O, deterministic when a
8
+ * fixed `rng` and `id` are supplied.
9
+ */
10
+ export function createBlockForRange(input) {
11
+ const range = clampRange(input.range, input.sourceText);
12
+ const anchor = formatLineRange({ start: range.startLine, end: range.endLine });
13
+ const fingerprint = computeFingerprint(input.sourceText, range);
14
+ const id = input.id ?? generateBlockId(input.rng);
15
+ const block = { id, anchor, fingerprint };
16
+ const markdown = renderBlockMarkdown({ block, sourcePath: input.sourcePath, range });
17
+ const caretLineOffset = placeholderLineOffset(markdown);
18
+ return { block, markdown, caretLineOffset };
19
+ }
20
+ /**
21
+ * Append `blockMarkdown` to existing commentary content, guaranteeing a
22
+ * single blank-line separator regardless of how the existing content ended.
23
+ */
24
+ export function appendBlockToCommentray(existing, blockMarkdown) {
25
+ const trimmed = existing.replace(/\s+$/, "");
26
+ const body = trimmed.length === 0 ? "" : `${trimmed}\n\n`;
27
+ const fragment = blockMarkdown.endsWith("\n") ? blockMarkdown : `${blockMarkdown}\n`;
28
+ return `${body}${fragment}`;
29
+ }
30
+ /**
31
+ * Return a new index with the block added under the given source file.
32
+ * The source entry is created lazily when it does not exist yet. Throws
33
+ * when the block id already exists under that source so callers cannot
34
+ * silently corrupt the index.
35
+ */
36
+ export function addBlockToIndex(index, input) {
37
+ const key = input.commentrayPath;
38
+ const existing = index.byCommentrayPath[key];
39
+ if (existing && existing.sourcePath !== input.sourcePath) {
40
+ throw new Error(`commentrayPath ${key} is already indexed for ${existing.sourcePath}, not ${input.sourcePath}`);
41
+ }
42
+ const previousBlocks = existing?.blocks ?? [];
43
+ if (previousBlocks.some((b) => b.id === input.block.id)) {
44
+ throw new Error(`block id ${input.block.id} already exists under ${key}; choose a different id`);
45
+ }
46
+ const nextEntry = {
47
+ sourcePath: input.sourcePath,
48
+ commentrayPath: input.commentrayPath,
49
+ blocks: [...previousBlocks, input.block],
50
+ };
51
+ return {
52
+ schemaVersion: index.schemaVersion,
53
+ byCommentrayPath: { ...index.byCommentrayPath, [key]: nextEntry },
54
+ };
55
+ }
56
+ const ID_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
57
+ const ID_LENGTH = 6;
58
+ /**
59
+ * Six-character base-36 id. Alphabet excludes uppercase so ids are case-
60
+ * insensitive-safe on filesystems and URLs. Collision space ≈ 2 billion,
61
+ * comfortably larger than any plausible per-file block count.
62
+ */
63
+ export function generateBlockId(rng = Math.random) {
64
+ let id = "";
65
+ for (let i = 0; i < ID_LENGTH; i++) {
66
+ id += ID_ALPHABET[Math.floor(rng() * ID_ALPHABET.length)];
67
+ }
68
+ return id;
69
+ }
70
+ function clampRange(range, sourceText) {
71
+ const lineCount = Math.max(1, sourceText.split("\n").length);
72
+ const start = Math.max(1, Math.min(Math.floor(range.startLine), lineCount));
73
+ const endRaw = Math.max(start, Math.floor(range.endLine));
74
+ const end = Math.min(endRaw, lineCount);
75
+ return { startLine: start, endLine: end };
76
+ }
77
+ function computeFingerprint(sourceText, range) {
78
+ const lines = sourceText.split("\n");
79
+ const startText = (lines[range.startLine - 1] ?? "").trim();
80
+ const endText = (lines[range.endLine - 1] ?? "").trim();
81
+ return {
82
+ startLine: startText,
83
+ endLine: endText,
84
+ lineCount: range.endLine - range.startLine + 1,
85
+ };
86
+ }
87
+ function renderBlockMarkdown(args) {
88
+ const marker = `${BLOCK_MARKER_PREFIX}${args.block.id}${BLOCK_MARKER_SUFFIX}`;
89
+ const heading = `## \`${args.sourcePath}\` ${rangeLabel(args.range)}`;
90
+ return `${marker}\n${heading}\n\n${CARET_PLACEHOLDER}\n`;
91
+ }
92
+ function rangeLabel(range) {
93
+ if (range.startLine === range.endLine)
94
+ return `line ${range.startLine}`;
95
+ return `lines ${range.startLine}–${range.endLine}`;
96
+ }
97
+ function placeholderLineOffset(markdown) {
98
+ const lines = markdown.split("\n");
99
+ return Math.max(0, lines.indexOf(CARET_PLACEHOLDER));
100
+ }
101
+ //# sourceMappingURL=blocks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blocks.js","sourceRoot":"","sources":["../src/blocks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { ResolvedCommentrayConfig } from "./config.js";
2
+ /**
3
+ * When Angles layout is on and the user has not configured a default, tools pick this id so a
4
+ * concrete file path exists (`…/main.md`). Authors may rename via `[angles]` in `.commentray.toml`.
5
+ */
6
+ export declare const FALLBACK_DEFAULT_ANGLE_ID: "main";
7
+ export declare function defaultAngleIdForOpen(config: ResolvedCommentrayConfig): string;
8
+ export type ResolvedCommentrayMarkdownPath = {
9
+ /** Repo-relative path to the paired `.md` file. */
10
+ commentrayPath: string;
11
+ /** Present when `{storage}/source/.default` exists (Angles layout). */
12
+ angleId: string | null;
13
+ anglesLayout: boolean;
14
+ };
15
+ /**
16
+ * Resolves the commentray Markdown path for a primary source file, honoring Angles layout and
17
+ * optional explicit `angleId` (when Angles layout is active).
18
+ */
19
+ export declare function resolveCommentrayMarkdownPath(repoRoot: string, sourceRepoRelativePath: string, config: ResolvedCommentrayConfig, angleId?: string | null): ResolvedCommentrayMarkdownPath;
20
+ //# sourceMappingURL=commentray-path-resolution.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commentray-path-resolution.d.ts","sourceRoot":"","sources":["../src/commentray-path-resolution.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAQ5D;;;GAGG;AACH,eAAO,MAAM,yBAAyB,EAAG,MAAe,CAAC;AAEzD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,wBAAwB,GAAG,MAAM,CAK9E;AAED,MAAM,MAAM,8BAA8B,GAAG;IAC3C,mDAAmD;IACnD,cAAc,EAAE,MAAM,CAAC;IACvB,uEAAuE;IACvE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,MAAM,EAChB,sBAAsB,EAAE,MAAM,EAC9B,MAAM,EAAE,wBAAwB,EAChC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,GACtB,8BAA8B,CAkBhC"}
@@ -0,0 +1,38 @@
1
+ import { assertValidAngleId } from "./angles.js";
2
+ import { commentrayAnglesLayoutEnabled, commentrayMarkdownPath, commentrayMarkdownPathForAngle, } from "./paths.js";
3
+ /**
4
+ * When Angles layout is on and the user has not configured a default, tools pick this id so a
5
+ * concrete file path exists (`…/main.md`). Authors may rename via `[angles]` in `.commentray.toml`.
6
+ */
7
+ export const FALLBACK_DEFAULT_ANGLE_ID = "main";
8
+ export function defaultAngleIdForOpen(config) {
9
+ if (config.angles.defaultAngleId)
10
+ return config.angles.defaultAngleId;
11
+ const first = config.angles.definitions[0];
12
+ if (first)
13
+ return first.id;
14
+ return FALLBACK_DEFAULT_ANGLE_ID;
15
+ }
16
+ /**
17
+ * Resolves the commentray Markdown path for a primary source file, honoring Angles layout and
18
+ * optional explicit `angleId` (when Angles layout is active).
19
+ */
20
+ export function resolveCommentrayMarkdownPath(repoRoot, sourceRepoRelativePath, config, angleId) {
21
+ const anglesLayout = commentrayAnglesLayoutEnabled(repoRoot, config.storageDir);
22
+ if (!anglesLayout) {
23
+ return {
24
+ commentrayPath: commentrayMarkdownPath(sourceRepoRelativePath),
25
+ angleId: null,
26
+ anglesLayout: false,
27
+ };
28
+ }
29
+ const id = angleId !== undefined && angleId !== null && String(angleId).trim() !== ""
30
+ ? assertValidAngleId(String(angleId))
31
+ : defaultAngleIdForOpen(config);
32
+ return {
33
+ commentrayPath: commentrayMarkdownPathForAngle(sourceRepoRelativePath, id, config.storageDir),
34
+ angleId: id,
35
+ anglesLayout: true,
36
+ };
37
+ }
38
+ //# sourceMappingURL=commentray-path-resolution.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commentray-path-resolution.js","sourceRoot":"","sources":["../src/commentray-path-resolution.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EACL,6BAA6B,EAC7B,sBAAsB,EACtB,8BAA8B,GAC/B,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,MAAe,CAAC;AAEzD,MAAM,UAAU,qBAAqB,CAAC,MAAgC;IACpE,IAAI,MAAM,CAAC,MAAM,CAAC,cAAc;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC;IACtE,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IAC3C,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC,EAAE,CAAC;IAC3B,OAAO,yBAAyB,CAAC;AACnC,CAAC;AAUD;;;GAGG;AACH,MAAM,UAAU,6BAA6B,CAC3C,QAAgB,EAChB,sBAA8B,EAC9B,MAAgC,EAChC,OAAuB;IAEvB,MAAM,YAAY,GAAG,6BAA6B,CAAC,QAAQ,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;IAChF,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO;YACL,cAAc,EAAE,sBAAsB,CAAC,sBAAsB,CAAC;YAC9D,OAAO,EAAE,IAAI;YACb,YAAY,EAAE,KAAK;SACpB,CAAC;IACJ,CAAC;IACD,MAAM,EAAE,GACN,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,IAAI,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE;QACxE,CAAC,CAAC,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACrC,CAAC,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACpC,OAAO;QACL,cAAc,EAAE,8BAA8B,CAAC,sBAAsB,EAAE,EAAE,EAAE,MAAM,CAAC,UAAU,CAAC;QAC7F,OAAO,EAAE,EAAE;QACX,YAAY,EAAE,IAAI;KACnB,CAAC;AACJ,CAAC"}
package/dist/config.d.ts CHANGED
@@ -8,10 +8,29 @@ export type CommentrayToml = {
8
8
  render?: {
9
9
  mermaid?: boolean;
10
10
  syntaxTheme?: string;
11
+ /**
12
+ * When true, `https://github.com/<owner>/<repo>/blob|tree/<branch>/…` links in commentray
13
+ * Markdown are rewritten to paths relative to the generated HTML file (see
14
+ * `static_site.github_url` for owner/repo). Requires a parseable repository URL.
15
+ */
16
+ relative_github_blob_links?: boolean;
11
17
  };
12
18
  anchors?: {
13
19
  defaultStrategy?: string[];
14
20
  };
21
+ /**
22
+ * Named **Angles** — multiple commentrays per source file (see `docs/spec/storage.md`).
23
+ * Keys use snake_case in TOML (`[angles]`).
24
+ */
25
+ angles?: {
26
+ /** Which Angle is selected by default in tooling and the static viewer (must match a `definitions` id when that list is non-empty). */
27
+ default_angle?: string;
28
+ /** Optional list of known Angles with display titles for UI (static browser, editor). */
29
+ definitions?: {
30
+ id: string;
31
+ title?: string;
32
+ }[];
33
+ };
15
34
  /**
16
35
  * Optional settings for publishing a single-file static “code browser” (GitHub Pages, etc.).
17
36
  * Keys use snake_case in TOML (`[static_site]`).
@@ -27,14 +46,41 @@ export type CommentrayToml = {
27
46
  commentray_markdown?: string;
28
47
  /** @deprecated Renamed to `commentray_markdown`. */
29
48
  commentary_markdown?: string;
49
+ /** Branch name embedded in GitHub blob URLs for `related_github_files` (default `main`). */
50
+ github_blob_branch?: string;
51
+ /**
52
+ * Optional toolbar links on the static code browser: open other repo files on GitHub
53
+ * (single-page Pages deploys cannot serve arbitrary paths next to `index.html`).
54
+ */
55
+ related_github_files?: {
56
+ label?: string;
57
+ path: string;
58
+ }[];
30
59
  };
31
60
  };
61
+ export type ResolvedGithubNavLink = {
62
+ label: string;
63
+ href: string;
64
+ };
32
65
  export type ResolvedStaticSite = {
33
66
  title: string;
34
67
  introMarkdown: string;
35
68
  githubUrl: string | null;
69
+ /** Branch used when building `relatedGithubNav` blob URLs. */
70
+ githubBlobBranch: string;
36
71
  sourceFile: string;
37
72
  commentrayMarkdownFile: string;
73
+ /** Toolbar “Also on GitHub …” links for the static code browser. */
74
+ relatedGithubNav: ResolvedGithubNavLink[];
75
+ };
76
+ export type ResolvedAngleDefinition = {
77
+ id: string;
78
+ title: string;
79
+ };
80
+ export type ResolvedAngles = {
81
+ /** When `definitions` is non-empty, this must match one of them (enforced at merge). */
82
+ defaultAngleId: string | null;
83
+ definitions: ResolvedAngleDefinition[];
38
84
  };
39
85
  export type ResolvedCommentrayConfig = {
40
86
  storageDir: string;
@@ -42,10 +88,12 @@ export type ResolvedCommentrayConfig = {
42
88
  render: {
43
89
  mermaid: boolean;
44
90
  syntaxTheme: string;
91
+ relativeGithubBlobLinks: boolean;
45
92
  };
46
93
  anchors: {
47
94
  defaultStrategy: string[];
48
95
  };
96
+ angles: ResolvedAngles;
49
97
  staticSite: ResolvedStaticSite;
50
98
  };
51
99
  export declare function mergeCommentrayConfig(parsed: CommentrayToml | null): ResolvedCommentrayConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3B,GAAG,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5B,MAAM,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACrD,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACzC;;;OAGG;IACH,WAAW,CAAC,EAAE;QACZ,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,yEAAyE;QACzE,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,wFAAwF;QACxF,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,uEAAuE;QACvE,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,oDAAoD;QACpD,mBAAmB,CAAC,EAAE,MAAM,CAAC;KAC9B,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB,EAAE,MAAM,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,KAAK,CAAC;IACnB,MAAM,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAClD,OAAO,EAAE;QAAE,eAAe,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACvC,UAAU,EAAE,kBAAkB,CAAC;CAChC,CAAC;AAkFF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,GAAG,wBAAwB,CAmB7F;AAED,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAY9F"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,CAAC,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3B,GAAG,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5B,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,OAAO,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB;;;;WAIG;QACH,0BAA0B,CAAC,EAAE,OAAO,CAAC;KACtC,CAAC;IACF,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACzC;;;OAGG;IACH,MAAM,CAAC,EAAE;QACP,uIAAuI;QACvI,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,yFAAyF;QACzF,WAAW,CAAC,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;KAChD,CAAC;IACF;;;OAGG;IACH,WAAW,CAAC,EAAE;QACZ,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,yEAAyE;QACzE,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,wFAAwF;QACxF,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,uEAAuE;QACvE,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,oDAAoD;QACpD,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,4FAA4F;QAC5F,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B;;;WAGG;QACH,oBAAoB,CAAC,EAAE;YAAE,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,EAAE,CAAC;KAC3D,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,8DAA8D;IAC9D,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,oEAAoE;IACpE,gBAAgB,EAAE,qBAAqB,EAAE,CAAC;CAC3C,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpE,MAAM,MAAM,cAAc,GAAG;IAC3B,wFAAwF;IACxF,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,EAAE,uBAAuB,EAAE,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,KAAK,CAAC;IACnB,MAAM,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,uBAAuB,EAAE,OAAO,CAAA;KAAE,CAAC;IACpF,OAAO,EAAE;QAAE,eAAe,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IACvC,MAAM,EAAE,cAAc,CAAC;IACvB,UAAU,EAAE,kBAAkB,CAAC;CAChC,CAAC;AA+JF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,GAAG,wBAAwB,CAsB7F;AAED,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAY9F"}
package/dist/config.js CHANGED
@@ -1,19 +1,25 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { parse as parseToml } from "@iarna/toml";
4
+ import { assertValidAngleId } from "./angles.js";
5
+ import { githubRepoBlobFileUrl, parseGithubRepoWebUrl } from "./github-url.js";
4
6
  import { normalizeRepoRelativePath } from "./paths.js";
5
7
  const defaultStaticSite = {
6
8
  title: "Commentray",
7
9
  introMarkdown: "",
8
10
  githubUrl: null,
11
+ githubBlobBranch: "main",
9
12
  sourceFile: "README.md",
10
13
  commentrayMarkdownFile: "",
14
+ relatedGithubNav: [],
11
15
  };
16
+ const defaultAngles = { defaultAngleId: null, definitions: [] };
12
17
  const defaultConfig = {
13
18
  storageDir: ".commentray",
14
19
  scmProvider: "git",
15
- render: { mermaid: true, syntaxTheme: "github-dark" },
20
+ render: { mermaid: true, syntaxTheme: "github-dark", relativeGithubBlobLinks: false },
16
21
  anchors: { defaultStrategy: ["symbol", "lines"] },
22
+ angles: { ...defaultAngles },
17
23
  staticSite: { ...defaultStaticSite },
18
24
  };
19
25
  function nonEmptyTrimmed(s) {
@@ -52,17 +58,70 @@ function assertStorageDirNotInsideGit(value) {
52
58
  `Git treats .git/ as opaque metadata and routine operations can wipe it.`);
53
59
  }
54
60
  }
61
+ function mergeAngleDefinitions(raw) {
62
+ if (!raw?.length)
63
+ return [];
64
+ const out = [];
65
+ const seen = new Set();
66
+ for (const row of raw) {
67
+ const id = assertValidAngleId(row.id);
68
+ if (seen.has(id)) {
69
+ throw new Error(`Duplicate angles.definitions id: ${id}`);
70
+ }
71
+ seen.add(id);
72
+ const title = row.title?.trim() || id;
73
+ out.push({ id, title });
74
+ }
75
+ return out;
76
+ }
77
+ function resolveAngles(parsed) {
78
+ const a = parsed.angles;
79
+ if (!a) {
80
+ return { ...defaultAngles };
81
+ }
82
+ const definitions = mergeAngleDefinitions(a.definitions);
83
+ const defaultRaw = a.default_angle?.trim();
84
+ const defaultAngleId = defaultRaw ? assertValidAngleId(defaultRaw) : null;
85
+ if (definitions.length > 0 &&
86
+ defaultAngleId &&
87
+ !definitions.some((d) => d.id === defaultAngleId)) {
88
+ throw new Error(`angles.default_angle "${defaultAngleId}" must match one of angles.definitions (got: ${definitions.map((d) => d.id).join(", ")})`);
89
+ }
90
+ return { defaultAngleId, definitions };
91
+ }
92
+ function mergeRelatedGithubNav(githubUrl, branch, raw) {
93
+ const gh = githubUrl ? parseGithubRepoWebUrl(githubUrl) : null;
94
+ if (!gh || !raw?.length)
95
+ return [];
96
+ const b = branch.trim() || defaultStaticSite.githubBlobBranch;
97
+ const out = [];
98
+ for (const row of raw) {
99
+ if (!row?.path?.trim())
100
+ continue;
101
+ const p = normalizeRepoRelativePath(row.path.trim());
102
+ const label = row.label?.trim() || path.posix.basename(p);
103
+ out.push({
104
+ label,
105
+ href: githubRepoBlobFileUrl(gh.owner, gh.repo, b, p),
106
+ });
107
+ }
108
+ return out;
109
+ }
55
110
  function resolveStaticSite(parsed) {
56
111
  const ss = parsed.static_site;
57
112
  const mdFile = ss?.commentray_markdown?.trim() ??
58
113
  ss?.commentary_markdown?.trim() ??
59
114
  defaultStaticSite.commentrayMarkdownFile;
115
+ const githubUrl = nonEmptyTrimmed(ss?.github_url);
116
+ const githubBlobBranch = nonEmptyTrimmed(ss?.github_blob_branch) ?? defaultStaticSite.githubBlobBranch;
60
117
  return {
61
118
  title: nonEmptyTrimmed(ss?.title) ?? defaultStaticSite.title,
62
119
  introMarkdown: ss?.intro ?? defaultStaticSite.introMarkdown,
63
- githubUrl: nonEmptyTrimmed(ss?.github_url),
120
+ githubUrl,
121
+ githubBlobBranch,
64
122
  sourceFile: nonEmptyTrimmed(ss?.source_file) ?? defaultStaticSite.sourceFile,
65
123
  commentrayMarkdownFile: mdFile,
124
+ relatedGithubNav: mergeRelatedGithubNav(githubUrl, githubBlobBranch, ss?.related_github_files),
66
125
  };
67
126
  }
68
127
  function assertSafeConfigPaths(parsed) {
@@ -72,6 +131,9 @@ function assertSafeConfigPaths(parsed) {
72
131
  assertSafeRepoRelativePath("static_site.source_file", ss?.source_file);
73
132
  assertSafeRepoRelativePath("static_site.commentray_markdown", ss?.commentray_markdown);
74
133
  assertSafeRepoRelativePath("static_site.commentary_markdown", ss?.commentary_markdown);
134
+ for (let i = 0; i < (ss?.related_github_files?.length ?? 0); i++) {
135
+ assertSafeRepoRelativePath(`static_site.related_github_files[${i}].path`, ss?.related_github_files?.[i]?.path);
136
+ }
75
137
  }
76
138
  export function mergeCommentrayConfig(parsed) {
77
139
  if (!parsed)
@@ -87,10 +149,12 @@ export function mergeCommentrayConfig(parsed) {
87
149
  render: {
88
150
  mermaid: parsed.render?.mermaid ?? defaultConfig.render.mermaid,
89
151
  syntaxTheme: parsed.render?.syntaxTheme ?? defaultConfig.render.syntaxTheme,
152
+ relativeGithubBlobLinks: parsed.render?.relative_github_blob_links ?? defaultConfig.render.relativeGithubBlobLinks,
90
153
  },
91
154
  anchors: {
92
155
  defaultStrategy: parsed.anchors?.defaultStrategy ?? defaultConfig.anchors.defaultStrategy,
93
156
  },
157
+ angles: resolveAngles(parsed),
94
158
  staticSite: resolveStaticSite(parsed),
95
159
  };
96
160
  }
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Parses a GitHub repository web URL into owner + repo name (no API calls).
3
+ * Accepts optional trailing slash and `.git` suffix.
4
+ */
5
+ export declare function parseGithubRepoWebUrl(url: string): {
6
+ owner: string;
7
+ repo: string;
8
+ } | null;
9
+ /**
10
+ * Builds a `https://github.com/owner/repo/blob/<branch>/path` URL for a repo-relative file path.
11
+ * Used for static-site “open another file” links when only `_site/index.html` is deployed.
12
+ */
13
+ export declare function githubRepoBlobFileUrl(owner: string, repo: string, branch: string, repoRelativePath: string): string;
14
+ //# sourceMappingURL=github-url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-url.d.ts","sourceRoot":"","sources":["../src/github-url.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAKzF;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,MAAM,GACvB,MAAM,CAOR"}