@commentray/core 0.0.1 → 0.0.4

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 (97) hide show
  1. package/dist/anchors.d.ts +6 -0
  2. package/dist/anchors.d.ts.map +1 -1
  3. package/dist/anchors.js +11 -0
  4. package/dist/anchors.js.map +1 -1
  5. package/dist/angles-toml.d.ts +18 -0
  6. package/dist/angles-toml.d.ts.map +1 -0
  7. package/dist/angles-toml.js +62 -0
  8. package/dist/angles-toml.js.map +1 -0
  9. package/dist/angles.d.ts +6 -0
  10. package/dist/angles.d.ts.map +1 -0
  11. package/dist/angles.js +14 -0
  12. package/dist/angles.js.map +1 -0
  13. package/dist/block-scroll-pickers.d.ts +22 -0
  14. package/dist/block-scroll-pickers.d.ts.map +1 -0
  15. package/dist/block-scroll-pickers.js +41 -0
  16. package/dist/block-scroll-pickers.js.map +1 -0
  17. package/dist/block-snippet.d.ts +13 -0
  18. package/dist/block-snippet.d.ts.map +1 -0
  19. package/dist/block-snippet.js +30 -0
  20. package/dist/block-snippet.js.map +1 -0
  21. package/dist/blocks.d.ts +66 -0
  22. package/dist/blocks.d.ts.map +1 -0
  23. package/dist/blocks.js +101 -0
  24. package/dist/blocks.js.map +1 -0
  25. package/dist/commentray-index-renames.d.ts +21 -0
  26. package/dist/commentray-index-renames.d.ts.map +1 -0
  27. package/dist/commentray-index-renames.js +85 -0
  28. package/dist/commentray-index-renames.js.map +1 -0
  29. package/dist/commentray-path-resolution.d.ts +20 -0
  30. package/dist/commentray-path-resolution.d.ts.map +1 -0
  31. package/dist/commentray-path-resolution.js +38 -0
  32. package/dist/commentray-path-resolution.js.map +1 -0
  33. package/dist/config.d.ts +48 -0
  34. package/dist/config.d.ts.map +1 -1
  35. package/dist/config.js +72 -6
  36. package/dist/config.js.map +1 -1
  37. package/dist/github-url.d.ts +14 -0
  38. package/dist/github-url.d.ts.map +1 -0
  39. package/dist/github-url.js +25 -0
  40. package/dist/github-url.js.map +1 -0
  41. package/dist/index-normalize.d.ts +10 -0
  42. package/dist/index-normalize.d.ts.map +1 -0
  43. package/dist/index-normalize.js +60 -0
  44. package/dist/index-normalize.js.map +1 -0
  45. package/dist/index-schema-messages.d.ts +6 -0
  46. package/dist/index-schema-messages.d.ts.map +1 -0
  47. package/dist/index-schema-messages.js +26 -0
  48. package/dist/index-schema-messages.js.map +1 -0
  49. package/dist/index.d.ts +25 -6
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +16 -4
  52. package/dist/index.js.map +1 -1
  53. package/dist/marker-ids.d.ts +19 -0
  54. package/dist/marker-ids.d.ts.map +1 -0
  55. package/dist/marker-ids.js +38 -0
  56. package/dist/marker-ids.js.map +1 -0
  57. package/dist/marker-validation.d.ts +22 -0
  58. package/dist/marker-validation.d.ts.map +1 -0
  59. package/dist/marker-validation.js +138 -0
  60. package/dist/marker-validation.js.map +1 -0
  61. package/dist/metadata.d.ts.map +1 -1
  62. package/dist/metadata.js +84 -29
  63. package/dist/metadata.js.map +1 -1
  64. package/dist/migrate.d.ts.map +1 -1
  65. package/dist/migrate.js +52 -14
  66. package/dist/migrate.js.map +1 -1
  67. package/dist/model.d.ts +24 -2
  68. package/dist/model.d.ts.map +1 -1
  69. package/dist/model.js +17 -1
  70. package/dist/model.js.map +1 -1
  71. package/dist/paths.d.ts +18 -2
  72. package/dist/paths.d.ts.map +1 -1
  73. package/dist/paths.js +34 -3
  74. package/dist/paths.js.map +1 -1
  75. package/dist/region-marker-convert.d.ts +27 -0
  76. package/dist/region-marker-convert.d.ts.map +1 -0
  77. package/dist/region-marker-convert.js +78 -0
  78. package/dist/region-marker-convert.js.map +1 -0
  79. package/dist/scm/git-scm-provider.d.ts +7 -1
  80. package/dist/scm/git-scm-provider.d.ts.map +1 -1
  81. package/dist/scm/git-scm-provider.js +42 -0
  82. package/dist/scm/git-scm-provider.js.map +1 -1
  83. package/dist/scm/scm-provider.d.ts +10 -0
  84. package/dist/scm/scm-provider.d.ts.map +1 -1
  85. package/dist/scroll-sync.d.ts +12 -0
  86. package/dist/scroll-sync.d.ts.map +1 -0
  87. package/dist/scroll-sync.js +59 -0
  88. package/dist/scroll-sync.js.map +1 -0
  89. package/dist/source-markers.d.ts +28 -0
  90. package/dist/source-markers.d.ts.map +1 -0
  91. package/dist/source-markers.js +241 -0
  92. package/dist/source-markers.js.map +1 -0
  93. package/dist/validate-project.d.ts +15 -1
  94. package/dist/validate-project.d.ts.map +1 -1
  95. package/dist/validate-project.js +87 -14
  96. package/dist/validate-project.js.map +1 -1
  97. package/package.json +6 -2
package/dist/anchors.d.ts CHANGED
@@ -8,6 +8,9 @@ export type ParsedAnchor = {
8
8
  } | {
9
9
  kind: "symbol";
10
10
  name: string;
11
+ } | {
12
+ kind: "marker";
13
+ id: string;
11
14
  } | {
12
15
  kind: "opaque";
13
16
  raw: string;
@@ -16,7 +19,10 @@ export type ParsedAnchor = {
16
19
  * Minimal anchor grammar (versioned; see docs/spec/anchors.md).
17
20
  * - lines:12-34
18
21
  * - symbol:SomeName
22
+ * - marker:<id> (paired **region** comments in source — `//#region commentray:<id>` /
23
+ * `//#endregion commentray:<id>` in JS/TS, matching [Region Marker](https://marketplace.visualstudio.com/items?itemName=txava.region-marker) defaults; legacy `commentray:start id=<id>` / `commentray:end` still parses)
19
24
  */
20
25
  export declare function parseAnchor(anchor: string): ParsedAnchor;
21
26
  export declare function formatLineRange(range: LineRange): string;
27
+ export declare function formatMarkerAnchor(markerId: string): string;
22
28
  //# sourceMappingURL=anchors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"anchors.d.ts","sourceRoot":"","sources":["../src/anchors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvD,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpC;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAkBxD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAExD"}
1
+ {"version":3,"file":"anchors.d.ts","sourceRoot":"","sources":["../src/anchors.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,SAAS,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvD,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,SAAS,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpC;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAuBxD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAExD;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE3D"}
package/dist/anchors.js CHANGED
@@ -1,10 +1,18 @@
1
+ import { assertValidMarkerId, MARKER_ID_BODY } from "./marker-ids.js";
1
2
  /**
2
3
  * Minimal anchor grammar (versioned; see docs/spec/anchors.md).
3
4
  * - lines:12-34
4
5
  * - symbol:SomeName
6
+ * - marker:<id> (paired **region** comments in source — `//#region commentray:<id>` /
7
+ * `//#endregion commentray:<id>` in JS/TS, matching [Region Marker](https://marketplace.visualstudio.com/items?itemName=txava.region-marker) defaults; legacy `commentray:start id=<id>` / `commentray:end` still parses)
5
8
  */
6
9
  export function parseAnchor(anchor) {
7
10
  const trimmed = anchor.trim();
11
+ const markerMatch = new RegExp(`^marker:(${MARKER_ID_BODY})$`, "i").exec(trimmed);
12
+ if (markerMatch) {
13
+ const id = assertValidMarkerId(markerMatch[1]);
14
+ return { kind: "marker", id };
15
+ }
8
16
  const linesMatch = /^lines:(\d+)-(\d+)$/.exec(trimmed);
9
17
  if (linesMatch) {
10
18
  const start = Number(linesMatch[1]);
@@ -26,4 +34,7 @@ export function parseAnchor(anchor) {
26
34
  export function formatLineRange(range) {
27
35
  return `lines:${range.start}-${range.end}`;
28
36
  }
37
+ export function formatMarkerAnchor(markerId) {
38
+ return `marker:${assertValidMarkerId(markerId)}`;
39
+ }
29
40
  //# sourceMappingURL=anchors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"anchors.js","sourceRoot":"","sources":["../src/anchors.ts"],"names":[],"mappings":"AAOA;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAC9B,MAAM,UAAU,GAAG,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvD,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,GAAG,GAAG,KAAK,EAAE,CAAC;YACjF,MAAM,IAAI,KAAK,CAAC,yBAAyB,MAAM,EAAE,CAAC,CAAC;QACrD,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC;IAClD,CAAC;IACD,MAAM,WAAW,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAClD,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACnC,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,MAAM,EAAE,CAAC,CAAC;QAC/D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAClC,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAgB;IAC9C,OAAO,SAAS,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;AAC7C,CAAC"}
1
+ {"version":3,"file":"anchors.js","sourceRoot":"","sources":["../src/anchors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAUtE;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,MAAc;IACxC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IAC9B,MAAM,WAAW,GAAG,IAAI,MAAM,CAAC,YAAY,cAAc,IAAI,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAClF,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,EAAE,GAAG,mBAAmB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/C,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;IAChC,CAAC;IACD,MAAM,UAAU,GAAG,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvD,IAAI,UAAU,EAAE,CAAC;QACf,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QAClC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,IAAI,GAAG,GAAG,KAAK,EAAE,CAAC;YACjF,MAAM,IAAI,KAAK,CAAC,yBAAyB,MAAM,EAAE,CAAC,CAAC;QACrD,CAAC;QACD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC;IAClD,CAAC;IACD,MAAM,WAAW,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAClD,IAAI,WAAW,EAAE,CAAC;QAChB,MAAM,IAAI,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACnC,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,MAAM,EAAE,CAAC,CAAC;QAC/D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAClC,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,KAAgB;IAC9C,OAAO,SAAS,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,QAAgB;IACjD,OAAO,UAAU,mBAAmB,CAAC,QAAQ,CAAC,EAAE,CAAC;AACnD,CAAC"}
@@ -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":"AAQA,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,22 @@
1
+ /** One block as needed for scroll correlation (0-based commentray line, 1-based source range). */
2
+ export type BlockScrollLink = {
3
+ /** Same id as `<!-- commentray:block id=… -->` and `index.json` blocks[]. */
4
+ id: string;
5
+ commentrayLine: number;
6
+ sourceStart: number;
7
+ sourceEnd: number;
8
+ };
9
+ /**
10
+ * Choose which commentray line (0-based) to reveal so the commentary matches
11
+ * the top of the source viewport. Prefers the block whose source range
12
+ * **contains** the top line; otherwise the nearest preceding block; if above
13
+ * all blocks, the first block.
14
+ */
15
+ export declare function pickCommentrayLineForSourceScroll(blocks: BlockScrollLink[], topSourceLine1Based: number): number | null;
16
+ /**
17
+ * Choose a 0-based source line to reveal from the top of the commentray
18
+ * viewport: the block whose marker is at or above that line wins; reveal the
19
+ * start of its source range.
20
+ */
21
+ export declare function pickSourceLine0ForCommentrayScroll(blocks: BlockScrollLink[], topCommentrayLine0Based: number): number | null;
22
+ //# sourceMappingURL=block-scroll-pickers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-scroll-pickers.d.ts","sourceRoot":"","sources":["../src/block-scroll-pickers.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,MAAM,MAAM,eAAe,GAAG;IAC5B,6EAA6E;IAC7E,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,iCAAiC,CAC/C,MAAM,EAAE,eAAe,EAAE,EACzB,mBAAmB,EAAE,MAAM,GAC1B,MAAM,GAAG,IAAI,CAaf;AAED;;;;GAIG;AACH,wBAAgB,kCAAkC,CAChD,MAAM,EAAE,eAAe,EAAE,EACzB,uBAAuB,EAAE,MAAM,GAC9B,MAAM,GAAG,IAAI,CAQf"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Choose which commentray line (0-based) to reveal so the commentary matches
3
+ * the top of the source viewport. Prefers the block whose source range
4
+ * **contains** the top line; otherwise the nearest preceding block; if above
5
+ * all blocks, the first block.
6
+ */
7
+ export function pickCommentrayLineForSourceScroll(blocks, topSourceLine1Based) {
8
+ if (blocks.length === 0)
9
+ return null;
10
+ const inside = blocks.find((b) => b.sourceStart <= topSourceLine1Based && topSourceLine1Based <= b.sourceEnd);
11
+ if (inside)
12
+ return inside.commentrayLine;
13
+ if (topSourceLine1Based < blocks[0].sourceStart)
14
+ return blocks[0].commentrayLine;
15
+ let best = blocks[0];
16
+ for (const b of blocks) {
17
+ if (b.sourceStart <= topSourceLine1Based)
18
+ best = b;
19
+ else
20
+ break;
21
+ }
22
+ return best.commentrayLine;
23
+ }
24
+ /**
25
+ * Choose a 0-based source line to reveal from the top of the commentray
26
+ * viewport: the block whose marker is at or above that line wins; reveal the
27
+ * start of its source range.
28
+ */
29
+ export function pickSourceLine0ForCommentrayScroll(blocks, topCommentrayLine0Based) {
30
+ if (blocks.length === 0)
31
+ return null;
32
+ let best = blocks[0];
33
+ for (const b of blocks) {
34
+ if (b.commentrayLine <= topCommentrayLine0Based)
35
+ best = b;
36
+ else
37
+ break;
38
+ }
39
+ return best.sourceStart - 1;
40
+ }
41
+ //# sourceMappingURL=block-scroll-pickers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-scroll-pickers.js","sourceRoot":"","sources":["../src/block-scroll-pickers.ts"],"names":[],"mappings":"AASA;;;;;GAKG;AACH,MAAM,UAAU,iCAAiC,CAC/C,MAAyB,EACzB,mBAA2B;IAE3B,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACrC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CACxB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,IAAI,mBAAmB,IAAI,mBAAmB,IAAI,CAAC,CAAC,SAAS,CAClF,CAAC;IACF,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC,cAAc,CAAC;IACzC,IAAI,mBAAmB,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW;QAAE,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC;IACjF,IAAI,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACrB,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,CAAC,WAAW,IAAI,mBAAmB;YAAE,IAAI,GAAG,CAAC,CAAC;;YAC9C,MAAM;IACb,CAAC;IACD,OAAO,IAAI,CAAC,cAAc,CAAC;AAC7B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kCAAkC,CAChD,MAAyB,EACzB,uBAA+B;IAE/B,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACrC,IAAI,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACrB,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,CAAC,cAAc,IAAI,uBAAuB;YAAE,IAAI,GAAG,CAAC,CAAC;;YACrD,MAAM;IACb,CAAC;IACD,OAAO,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC;AAC9B,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Self-contained, diff-inspired capture of the source lines a block refers to.
3
+ * Stored as a single string in index.json (not a nested JSON object).
4
+ *
5
+ * Format (v1): header line `commentray-snippet/v1`, then one line per source
6
+ * line using unified-diff **context** style: each line begins with a single
7
+ * space followed by the trimmed source line (empty lines are a lone leading space).
8
+ */
9
+ export declare const COMMENTRAY_SNIPPET_HEADER_V1: "commentray-snippet/v1";
10
+ export declare function buildCommentraySnippetV1(trimmedLines: string[]): string;
11
+ /** Returns trimmed lines, or null if the string is not a valid v1 snippet. */
12
+ export declare function parseCommentraySnippetV1(snippet: string): string[] | null;
13
+ //# sourceMappingURL=block-snippet.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-snippet.d.ts","sourceRoot":"","sources":["../src/block-snippet.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,4BAA4B,EAAG,uBAAgC,CAAC;AAE7E,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,MAAM,CAKvE;AAED,8EAA8E;AAC9E,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAUzE"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Self-contained, diff-inspired capture of the source lines a block refers to.
3
+ * Stored as a single string in index.json (not a nested JSON object).
4
+ *
5
+ * Format (v1): header line `commentray-snippet/v1`, then one line per source
6
+ * line using unified-diff **context** style: each line begins with a single
7
+ * space followed by the trimmed source line (empty lines are a lone leading space).
8
+ */
9
+ export const COMMENTRAY_SNIPPET_HEADER_V1 = "commentray-snippet/v1";
10
+ export function buildCommentraySnippetV1(trimmedLines) {
11
+ const body = trimmedLines.map((line) => ` ${line}`).join("\n");
12
+ return body.length === 0
13
+ ? COMMENTRAY_SNIPPET_HEADER_V1
14
+ : `${COMMENTRAY_SNIPPET_HEADER_V1}\n${body}`;
15
+ }
16
+ /** Returns trimmed lines, or null if the string is not a valid v1 snippet. */
17
+ export function parseCommentraySnippetV1(snippet) {
18
+ const lines = snippet.replace(/\r\n/g, "\n").split("\n");
19
+ if (lines.length === 0 || lines[0] !== COMMENTRAY_SNIPPET_HEADER_V1)
20
+ return null;
21
+ const out = [];
22
+ for (let i = 1; i < lines.length; i++) {
23
+ const line = lines[i];
24
+ if (!line.startsWith(" "))
25
+ return null;
26
+ out.push(line.slice(1));
27
+ }
28
+ return out;
29
+ }
30
+ //# sourceMappingURL=block-snippet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"block-snippet.js","sourceRoot":"","sources":["../src/block-snippet.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,uBAAgC,CAAC;AAE7E,MAAM,UAAU,wBAAwB,CAAC,YAAsB;IAC7D,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/D,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC;QACtB,CAAC,CAAC,4BAA4B;QAC9B,CAAC,CAAC,GAAG,4BAA4B,KAAK,IAAI,EAAE,CAAC;AACjD,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,wBAAwB,CAAC,OAAe;IACtD,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACzD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,4BAA4B;QAAE,OAAO,IAAI,CAAC;IACjF,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QACvC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,GAAG,CAAC;AACb,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":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAwB,MAAM,YAAY,CAAC;AAEzF,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 { buildCommentraySnippetV1 } from "./block-snippet.js";
2
+ import { formatMarkerAnchor } from "./anchors.js";
3
+ import { assertValidMarkerId } from "./marker-ids.js";
4
+ const BLOCK_MARKER_PREFIX = "<!-- commentray:block id=";
5
+ const BLOCK_MARKER_SUFFIX = " -->";
6
+ const CARET_PLACEHOLDER = "_(write commentary here)_";
7
+ /**
8
+ * Create a block (domain entity) together with the Markdown fragment that
9
+ * carries it in the commentary file. Pure: no I/O, deterministic when a
10
+ * fixed `rng` and `id` are supplied.
11
+ */
12
+ export function createBlockForRange(input) {
13
+ const range = clampRange(input.range, input.sourceText);
14
+ const id = input.id !== undefined ? assertValidMarkerId(input.id) : generateBlockId(input.rng);
15
+ const anchor = formatMarkerAnchor(id);
16
+ const snippet = snippetFromRange(input.sourceText, range);
17
+ const block = { id, anchor, markerId: id, snippet };
18
+ const markdown = renderBlockMarkdown({ block, sourcePath: input.sourcePath, range });
19
+ const caretLineOffset = placeholderLineOffset(markdown);
20
+ return { block, markdown, caretLineOffset };
21
+ }
22
+ /**
23
+ * Append `blockMarkdown` to existing commentary content, guaranteeing a
24
+ * single blank-line separator regardless of how the existing content ended.
25
+ */
26
+ export function appendBlockToCommentray(existing, blockMarkdown) {
27
+ const trimmed = existing.replace(/\s+$/, "");
28
+ const body = trimmed.length === 0 ? "" : `${trimmed}\n\n`;
29
+ const fragment = blockMarkdown.endsWith("\n") ? blockMarkdown : `${blockMarkdown}\n`;
30
+ return `${body}${fragment}`;
31
+ }
32
+ /**
33
+ * Return a new index with the block added under the given source file.
34
+ * The source entry is created lazily when it does not exist yet. Throws
35
+ * when the block id already exists under that source so callers cannot
36
+ * silently corrupt the index.
37
+ */
38
+ export function addBlockToIndex(index, input) {
39
+ const key = input.commentrayPath;
40
+ const existing = index.byCommentrayPath[key];
41
+ if (existing && existing.sourcePath !== input.sourcePath) {
42
+ throw new Error(`commentrayPath ${key} is already indexed for ${existing.sourcePath}, not ${input.sourcePath}`);
43
+ }
44
+ const previousBlocks = existing?.blocks ?? [];
45
+ if (previousBlocks.some((b) => b.id === input.block.id)) {
46
+ throw new Error(`block id ${input.block.id} already exists under ${key}; choose a different id`);
47
+ }
48
+ const nextEntry = {
49
+ sourcePath: input.sourcePath,
50
+ commentrayPath: input.commentrayPath,
51
+ blocks: [...previousBlocks, input.block],
52
+ };
53
+ return {
54
+ schemaVersion: index.schemaVersion,
55
+ byCommentrayPath: { ...index.byCommentrayPath, [key]: nextEntry },
56
+ };
57
+ }
58
+ const ID_ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789";
59
+ const ID_LENGTH = 6;
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 function generateBlockId(rng = Math.random) {
66
+ let id = "";
67
+ for (let i = 0; i < ID_LENGTH; i++) {
68
+ id += ID_ALPHABET[Math.floor(rng() * ID_ALPHABET.length)];
69
+ }
70
+ return id;
71
+ }
72
+ function clampRange(range, sourceText) {
73
+ const lineCount = Math.max(1, sourceText.split("\n").length);
74
+ const start = Math.max(1, Math.min(Math.floor(range.startLine), lineCount));
75
+ const endRaw = Math.max(start, Math.floor(range.endLine));
76
+ const end = Math.min(endRaw, lineCount);
77
+ return { startLine: start, endLine: end };
78
+ }
79
+ function snippetFromRange(sourceText, range) {
80
+ const lines = sourceText.split("\n");
81
+ const trimmed = [];
82
+ for (let ln = range.startLine; ln <= range.endLine; ln++) {
83
+ trimmed.push((lines[ln - 1] ?? "").trim());
84
+ }
85
+ return buildCommentraySnippetV1(trimmed);
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,21 @@
1
+ import type { ResolvedCommentrayConfig } from "./config.js";
2
+ import type { CommentrayIndex } from "./model.js";
3
+ import type { ScmPathRename } from "./scm/scm-provider.js";
4
+ export type { ScmPathRename as PathRename } from "./scm/scm-provider.js";
5
+ /**
6
+ * When Angles layout is active, returns the angle id segment from a commentray path under
7
+ * `{storage}/source/<sourcePath>/<angle>.md`, or null if the path does not match that shape.
8
+ */
9
+ export declare function inferAngleIdFromCommentrayPath(commentrayPath: string, sourcePath: string, storageDir: string): string | null;
10
+ /**
11
+ * Applies Git-style **full-path** renames to `index.json` entries:
12
+ * - exact renames on `sourcePath` and `commentrayPath` (string equality),
13
+ * - when `sourcePath` changes, recomputes `commentrayPath` from layout rules so companion paths stay paired.
14
+ *
15
+ * Renames should be sorted longest-`from` first by the caller; this function sorts defensively.
16
+ */
17
+ export declare function applyPathRenamesToCommentrayIndex(index: CommentrayIndex, renames: readonly ScmPathRename[], repoRoot: string, config: ResolvedCommentrayConfig): {
18
+ index: CommentrayIndex;
19
+ changed: boolean;
20
+ };
21
+ //# sourceMappingURL=commentray-index-renames.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commentray-index-renames.d.ts","sourceRoot":"","sources":["../src/commentray-index-renames.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAwB,MAAM,YAAY,CAAC;AACxE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAQ3D,YAAY,EAAE,aAAa,IAAI,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAazE;;;GAGG;AACH,wBAAgB,8BAA8B,CAC5C,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,MAAM,GAAG,IAAI,CASf;AAED;;;;;;GAMG;AACH,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,eAAe,EACtB,OAAO,EAAE,SAAS,aAAa,EAAE,EACjC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,wBAAwB,GAC/B;IAAE,KAAK,EAAE,eAAe,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CA6D9C"}
@@ -0,0 +1,85 @@
1
+ import { assertValidAngleId } from "./angles.js";
2
+ import { commentrayAnglesLayoutEnabled, commentrayMarkdownPath, commentrayMarkdownPathForAngle, normalizeRepoRelativePath, } from "./paths.js";
3
+ function applyExactPathRenames(repoRelativePath, renames) {
4
+ let p = normalizeRepoRelativePath(repoRelativePath);
5
+ for (const { from, to } of renames) {
6
+ if (p === from)
7
+ p = to;
8
+ }
9
+ return p;
10
+ }
11
+ /**
12
+ * When Angles layout is active, returns the angle id segment from a commentray path under
13
+ * `{storage}/source/<sourcePath>/<angle>.md`, or null if the path does not match that shape.
14
+ */
15
+ export function inferAngleIdFromCommentrayPath(commentrayPath, sourcePath, storageDir) {
16
+ const sd = normalizeRepoRelativePath(storageDir.replaceAll("\\", "/"));
17
+ const src = normalizeRepoRelativePath(sourcePath);
18
+ const prefix = `${sd}/source/${src}/`;
19
+ if (!commentrayPath.startsWith(prefix))
20
+ return null;
21
+ const rest = commentrayPath.slice(prefix.length);
22
+ if (!rest.endsWith(".md"))
23
+ return null;
24
+ const id = rest.slice(0, -".md".length);
25
+ return id.length > 0 ? id : null;
26
+ }
27
+ /**
28
+ * Applies Git-style **full-path** renames to `index.json` entries:
29
+ * - exact renames on `sourcePath` and `commentrayPath` (string equality),
30
+ * - when `sourcePath` changes, recomputes `commentrayPath` from layout rules so companion paths stay paired.
31
+ *
32
+ * Renames should be sorted longest-`from` first by the caller; this function sorts defensively.
33
+ */
34
+ export function applyPathRenamesToCommentrayIndex(index, renames, repoRoot, config) {
35
+ const sorted = [...renames]
36
+ .map((r) => ({
37
+ from: normalizeRepoRelativePath(r.from),
38
+ to: normalizeRepoRelativePath(r.to),
39
+ }))
40
+ .filter((r) => r.from !== r.to)
41
+ .sort((a, b) => b.from.length - a.from.length);
42
+ if (sorted.length === 0)
43
+ return { index, changed: false };
44
+ const anglesLayout = commentrayAnglesLayoutEnabled(repoRoot, config.storageDir);
45
+ const next = {};
46
+ let changed = false;
47
+ for (const [, entry] of Object.entries(index.byCommentrayPath)) {
48
+ const sp = applyExactPathRenames(entry.sourcePath, sorted);
49
+ let cp = applyExactPathRenames(entry.commentrayPath, sorted);
50
+ if (sp !== entry.sourcePath) {
51
+ if (anglesLayout) {
52
+ const angleId = inferAngleIdFromCommentrayPath(entry.commentrayPath, entry.sourcePath, config.storageDir);
53
+ if (angleId) {
54
+ try {
55
+ cp = commentrayMarkdownPathForAngle(sp, assertValidAngleId(angleId), config.storageDir);
56
+ }
57
+ catch {
58
+ /* keep cp from exact renames if angle segment is not a valid id */
59
+ }
60
+ }
61
+ }
62
+ else {
63
+ cp = commentrayMarkdownPath(sp, config.storageDir);
64
+ }
65
+ }
66
+ const newEntry = {
67
+ ...entry,
68
+ sourcePath: sp,
69
+ commentrayPath: cp,
70
+ };
71
+ if (sp !== entry.sourcePath || cp !== entry.commentrayPath) {
72
+ changed = true;
73
+ }
74
+ if (next[cp]) {
75
+ throw new Error(`After applying renames, two index entries map to the same commentrayPath "${cp}" ` +
76
+ `(merge or fix renames before retrying).`);
77
+ }
78
+ next[cp] = newEntry;
79
+ }
80
+ return {
81
+ index: { schemaVersion: index.schemaVersion, byCommentrayPath: next },
82
+ changed,
83
+ };
84
+ }
85
+ //# sourceMappingURL=commentray-index-renames.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commentray-index-renames.js","sourceRoot":"","sources":["../src/commentray-index-renames.ts"],"names":[],"mappings":"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"}