archymedes-cli 2.1.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,315 @@
1
+ import {
2
+ GUTTER,
3
+ clip,
4
+ panel
5
+ } from "./archymedes-tsne2m7p.mjs";
6
+ import {
7
+ BOLD,
8
+ DIM,
9
+ UNICODE_GLYPHS,
10
+ paint,
11
+ paintAll,
12
+ roleCode,
13
+ visibleWidth
14
+ } from "./archymedes-a10k8yrk.mjs";
15
+
16
+ // src/render/code-view.ts
17
+ var MAX_DIFF_CELLS = 250000;
18
+ function diffLines(before, after) {
19
+ const left = before === "" ? [] : before.split(`
20
+ `);
21
+ const right = after === "" ? [] : after.split(`
22
+ `);
23
+ if (left.length * right.length > MAX_DIFF_CELLS) {
24
+ return [
25
+ ...left.map((text) => ({ kind: "remove", text })),
26
+ ...right.map((text) => ({ kind: "add", text }))
27
+ ];
28
+ }
29
+ const lengths = Array.from({ length: left.length + 1 }, () => new Array(right.length + 1).fill(0));
30
+ for (let i2 = left.length - 1;i2 >= 0; i2 -= 1) {
31
+ for (let j2 = right.length - 1;j2 >= 0; j2 -= 1) {
32
+ lengths[i2][j2] = left[i2] === right[j2] ? lengths[i2 + 1][j2 + 1] + 1 : Math.max(lengths[i2 + 1][j2], lengths[i2][j2 + 1]);
33
+ }
34
+ }
35
+ const result = [];
36
+ let i = 0;
37
+ let j = 0;
38
+ while (i < left.length && j < right.length) {
39
+ if (left[i] === right[j]) {
40
+ result.push({ kind: "context", text: left[i] });
41
+ i += 1;
42
+ j += 1;
43
+ } else if (lengths[i + 1][j] >= lengths[i][j + 1]) {
44
+ result.push({ kind: "remove", text: left[i] });
45
+ i += 1;
46
+ } else {
47
+ result.push({ kind: "add", text: right[j] });
48
+ j += 1;
49
+ }
50
+ }
51
+ while (i < left.length) {
52
+ result.push({ kind: "remove", text: left[i] });
53
+ i += 1;
54
+ }
55
+ while (j < right.length) {
56
+ result.push({ kind: "add", text: right[j] });
57
+ j += 1;
58
+ }
59
+ return result;
60
+ }
61
+ function diffStat(diff) {
62
+ return {
63
+ added: diff.filter((line) => line.kind === "add").length,
64
+ removed: diff.filter((line) => line.kind === "remove").length
65
+ };
66
+ }
67
+ function condenseDiff(diff, context = 2) {
68
+ const keep = new Array(diff.length).fill(false);
69
+ diff.forEach((line, index) => {
70
+ if (line.kind === "context")
71
+ return;
72
+ for (let offset = -context;offset <= context; offset += 1) {
73
+ const target = index + offset;
74
+ if (target >= 0 && target < diff.length)
75
+ keep[target] = true;
76
+ }
77
+ });
78
+ const result = [];
79
+ let skipped = 0;
80
+ diff.forEach((line, index) => {
81
+ if (keep[index]) {
82
+ if (skipped > 0) {
83
+ result.push({ kind: "gap", text: `${skipped} unchanged line${skipped === 1 ? "" : "s"}` });
84
+ skipped = 0;
85
+ }
86
+ result.push(line);
87
+ return;
88
+ }
89
+ skipped += 1;
90
+ });
91
+ if (skipped > 0)
92
+ result.push({ kind: "gap", text: `${skipped} unchanged line${skipped === 1 ? "" : "s"}` });
93
+ return result;
94
+ }
95
+ function languageOf(filePath) {
96
+ const name = filePath.split(/[\\/]/).pop() ?? "";
97
+ const extension = name.includes(".") ? name.split(".").pop().toLowerCase() : "";
98
+ const byExtension = {
99
+ ts: "typescript",
100
+ tsx: "tsx",
101
+ js: "javascript",
102
+ jsx: "jsx",
103
+ mjs: "javascript",
104
+ cjs: "javascript",
105
+ py: "python",
106
+ rb: "ruby",
107
+ go: "go",
108
+ rs: "rust",
109
+ java: "java",
110
+ kt: "kotlin",
111
+ swift: "swift",
112
+ c: "c",
113
+ h: "c",
114
+ cc: "cpp",
115
+ cpp: "cpp",
116
+ hpp: "cpp",
117
+ cs: "csharp",
118
+ php: "php",
119
+ sh: "shell",
120
+ bash: "shell",
121
+ zsh: "shell",
122
+ fish: "shell",
123
+ json: "json",
124
+ yml: "yaml",
125
+ yaml: "yaml",
126
+ toml: "toml",
127
+ ini: "ini",
128
+ xml: "xml",
129
+ html: "html",
130
+ css: "css",
131
+ scss: "scss",
132
+ sql: "sql",
133
+ md: "markdown",
134
+ mdx: "markdown",
135
+ txt: "text"
136
+ };
137
+ if (byExtension[extension])
138
+ return byExtension[extension];
139
+ if (/^(?:dockerfile|makefile|justfile)$/i.test(name))
140
+ return name.toLowerCase();
141
+ return extension || "text";
142
+ }
143
+ var KEYWORDS = new Set([
144
+ "async",
145
+ "await",
146
+ "break",
147
+ "case",
148
+ "catch",
149
+ "class",
150
+ "const",
151
+ "continue",
152
+ "def",
153
+ "default",
154
+ "elif",
155
+ "else",
156
+ "end",
157
+ "enum",
158
+ "export",
159
+ "extends",
160
+ "finally",
161
+ "fn",
162
+ "for",
163
+ "from",
164
+ "func",
165
+ "function",
166
+ "if",
167
+ "impl",
168
+ "import",
169
+ "in",
170
+ "interface",
171
+ "let",
172
+ "match",
173
+ "mod",
174
+ "new",
175
+ "package",
176
+ "private",
177
+ "public",
178
+ "pub",
179
+ "raise",
180
+ "return",
181
+ "static",
182
+ "struct",
183
+ "switch",
184
+ "throw",
185
+ "trait",
186
+ "try",
187
+ "type",
188
+ "use",
189
+ "var",
190
+ "void",
191
+ "while",
192
+ "with",
193
+ "yield"
194
+ ]);
195
+ var LITERALS = new Set(["true", "false", "null", "nil", "None", "True", "False", "undefined", "self", "this"]);
196
+ function highlightCode(line, depth, palette) {
197
+ if (depth === "none")
198
+ return line;
199
+ const commentAt = findCommentStart(line);
200
+ const code = commentAt === -1 ? line : line.slice(0, commentAt);
201
+ const comment = commentAt === -1 ? "" : paint(line.slice(commentAt), DIM, depth);
202
+ const painted = code.replace(/("(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|`(?:[^`\\]|\\.)*`)|\b(\d+(?:\.\d+)?)\b|\b([A-Za-z_$][\w$]*)\b/g, (whole, text, numeric, word) => {
203
+ if (text !== undefined)
204
+ return paint(text, roleCode("success", palette, depth), depth);
205
+ if (numeric !== undefined)
206
+ return paint(numeric, roleCode("accent", palette, depth), depth);
207
+ if (word !== undefined && KEYWORDS.has(word))
208
+ return paint(word, roleCode("primary", palette, depth), depth);
209
+ if (word !== undefined && LITERALS.has(word))
210
+ return paint(word, roleCode("accent", palette, depth), depth);
211
+ return whole;
212
+ });
213
+ return `${painted}${comment}`;
214
+ }
215
+ function findCommentStart(line) {
216
+ let quote;
217
+ for (let index = 0;index < line.length; index += 1) {
218
+ const character = line[index];
219
+ if (quote) {
220
+ if (character === "\\") {
221
+ index += 1;
222
+ continue;
223
+ }
224
+ if (character === quote)
225
+ quote = undefined;
226
+ continue;
227
+ }
228
+ if (character === '"' || character === "'" || character === "`") {
229
+ quote = character;
230
+ continue;
231
+ }
232
+ if (character === "#")
233
+ return index;
234
+ if (character === "/" && line[index + 1] === "/")
235
+ return index;
236
+ if (character === "-" && line[index + 1] === "-")
237
+ return index;
238
+ }
239
+ return -1;
240
+ }
241
+ function renderCode(content, style, options = {}) {
242
+ const glyphs = style.glyphs ?? UNICODE_GLYPHS;
243
+ const lines = content.replace(/\n$/, "").split(`
244
+ `);
245
+ const maxLines = options.maxLines ?? 16;
246
+ const start = options.startLine ?? 1;
247
+ const numberWidth = String(start + lines.length - 1).length;
248
+ const bodyWidth = Math.max(8, style.width - GUTTER.length - numberWidth - 4);
249
+ const render = (from, to) => lines.slice(from, to).map((line, index) => {
250
+ const number = paint(String(start + from + index).padStart(numberWidth), DIM, style.depth);
251
+ const painted = options.highlight === false ? line : highlightCode(line, style.depth, style.palette);
252
+ return `${number} ${clip(painted, bodyWidth, glyphs)}`;
253
+ });
254
+ const hidden = Math.max(0, lines.length - maxLines);
255
+ const shown = render(0, hidden > 0 ? maxLines : lines.length);
256
+ return {
257
+ text: shown.join(`
258
+ `),
259
+ hidden,
260
+ full: render(0, lines.length).join(`
261
+ `)
262
+ };
263
+ }
264
+ function renderDiff(diff, style, options = {}) {
265
+ const glyphs = style.glyphs ?? UNICODE_GLYPHS;
266
+ const maxLines = options.maxLines ?? 16;
267
+ const condensed = condenseDiff(diff);
268
+ const bodyWidth = Math.max(8, style.width - GUTTER.length - 4);
269
+ const row = (line) => {
270
+ if (line.kind === "gap")
271
+ return paint(`${glyphs.middot.repeat(3)} ${line.text}`, DIM, style.depth);
272
+ const mark = line.kind === "add" ? glyphs.plus : line.kind === "remove" ? glyphs.minus : " ";
273
+ const code = line.kind === "add" ? roleCode("success", style.palette, style.depth) : line.kind === "remove" ? roleCode("error", style.palette, style.depth) : DIM;
274
+ const body = line.kind === "context" ? paint(line.text, DIM, style.depth) : highlightCode(line.text, style.depth, style.palette);
275
+ return `${paint(mark, code, style.depth)} ${clip(body, bodyWidth, glyphs)}`;
276
+ };
277
+ const rows = condensed.map(row);
278
+ const hidden = Math.max(0, rows.length - maxLines);
279
+ return {
280
+ text: (hidden > 0 ? rows.slice(0, maxLines) : rows).join(`
281
+ `),
282
+ hidden,
283
+ full: diff.map(row).join(`
284
+ `)
285
+ };
286
+ }
287
+ function renderFileChange(change, style, options = {}) {
288
+ const language = languageOf(change.path);
289
+ const block = change.kind === "edit" ? renderDiff(diffLines(change.before ?? "", change.after ?? ""), style, { maxLines: options.maxLines }) : renderCode(change.content ?? "", style, { maxLines: options.maxLines, language });
290
+ const badge = change.kind === "edit" ? (() => {
291
+ const stat = diffStat(diffLines(change.before ?? "", change.after ?? ""));
292
+ return `+${stat.added} -${stat.removed}`;
293
+ })() : `${(change.content ?? "").split(`
294
+ `).length} lines · ${language}`;
295
+ const body = block.text.split(`
296
+ `);
297
+ const foot = block.hidden > 0 && options.expandHint ? [paint(`${(style.glyphs ?? UNICODE_GLYPHS).collapsed} ${block.hidden} more line${block.hidden === 1 ? "" : "s"} — ${options.expandHint}`, DIM, style.depth)] : [];
298
+ return {
299
+ text: panel([...body, ...foot], style, { title: `${change.kind === "edit" ? "diff" : "new file"} · ${change.path}`, badge, tone: change.kind === "edit" ? "accent" : "good" }),
300
+ hidden: block.hidden,
301
+ full: panel(block.full.split(`
302
+ `), style, { title: `${change.kind === "edit" ? "diff" : "new file"} · ${change.path}`, badge, tone: change.kind === "edit" ? "accent" : "good" })
303
+ };
304
+ }
305
+ function fenceHeader(language, style) {
306
+ const glyphs = style.glyphs ?? UNICODE_GLYPHS;
307
+ const label = language ? ` ${language} ` : glyphs.boxHorizontal.repeat(4);
308
+ const width = Math.max(0, style.width - GUTTER.length - visibleWidth(label) - 3);
309
+ return `${GUTTER}${paint(glyphs.boxTopLeft + glyphs.boxHorizontal, DIM, style.depth)}${paintAll(label, [roleCode("primary", style.palette, style.depth), BOLD], style.depth)}${paint(glyphs.boxHorizontal.repeat(Math.max(0, width)), DIM, style.depth)}`;
310
+ }
311
+ function describeChange(stat, depth, palette) {
312
+ return `${paint(`+${stat.added}`, roleCode("success", palette, depth), depth)} ${paint(`-${stat.removed}`, roleCode("error", palette, depth), depth)}`;
313
+ }
314
+
315
+ export { diffLines, diffStat, languageOf, highlightCode, renderCode, renderDiff, renderFileChange, fenceHeader, describeChange };