@principal-ade/markdown-utils 0.1.3

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 (67) hide show
  1. package/README.md +148 -0
  2. package/dist/cjs/diff/diffPresentations.d.ts +17 -0
  3. package/dist/cjs/diff/diffPresentations.d.ts.map +1 -0
  4. package/dist/cjs/diff/diffSummary.d.ts +31 -0
  5. package/dist/cjs/diff/diffSummary.d.ts.map +1 -0
  6. package/dist/cjs/diff/index.d.ts +11 -0
  7. package/dist/cjs/diff/index.d.ts.map +1 -0
  8. package/dist/cjs/diff/matchSlides.d.ts +44 -0
  9. package/dist/cjs/diff/matchSlides.d.ts.map +1 -0
  10. package/dist/cjs/diff/textDiff.d.ts +30 -0
  11. package/dist/cjs/diff/textDiff.d.ts.map +1 -0
  12. package/dist/cjs/index.d.ts +12 -0
  13. package/dist/cjs/index.d.ts.map +1 -0
  14. package/dist/cjs/index.js +773 -0
  15. package/dist/cjs/package.json +1 -0
  16. package/dist/cjs/types/bash.d.ts +17 -0
  17. package/dist/cjs/types/bash.d.ts.map +1 -0
  18. package/dist/cjs/types/chunks.d.ts +23 -0
  19. package/dist/cjs/types/chunks.d.ts.map +1 -0
  20. package/dist/cjs/types/diff.d.ts +72 -0
  21. package/dist/cjs/types/diff.d.ts.map +1 -0
  22. package/dist/cjs/types/presentation.d.ts +48 -0
  23. package/dist/cjs/types/presentation.d.ts.map +1 -0
  24. package/dist/cjs/utils/bash-parser.d.ts +10 -0
  25. package/dist/cjs/utils/bash-parser.d.ts.map +1 -0
  26. package/dist/cjs/utils/image-urls.d.ts +15 -0
  27. package/dist/cjs/utils/image-urls.d.ts.map +1 -0
  28. package/dist/cjs/utils/markdown-parser.d.ts +7 -0
  29. package/dist/cjs/utils/markdown-parser.d.ts.map +1 -0
  30. package/dist/cjs/utils/presentation.d.ts +23 -0
  31. package/dist/cjs/utils/presentation.d.ts.map +1 -0
  32. package/dist/cjs/utils/slide-titles.d.ts +28 -0
  33. package/dist/cjs/utils/slide-titles.d.ts.map +1 -0
  34. package/dist/esm/diff/diffPresentations.d.ts +17 -0
  35. package/dist/esm/diff/diffPresentations.d.ts.map +1 -0
  36. package/dist/esm/diff/diffSummary.d.ts +31 -0
  37. package/dist/esm/diff/diffSummary.d.ts.map +1 -0
  38. package/dist/esm/diff/index.d.ts +11 -0
  39. package/dist/esm/diff/index.d.ts.map +1 -0
  40. package/dist/esm/diff/matchSlides.d.ts +44 -0
  41. package/dist/esm/diff/matchSlides.d.ts.map +1 -0
  42. package/dist/esm/diff/textDiff.d.ts +30 -0
  43. package/dist/esm/diff/textDiff.d.ts.map +1 -0
  44. package/dist/esm/index.d.ts +12 -0
  45. package/dist/esm/index.d.ts.map +1 -0
  46. package/dist/esm/index.js +741 -0
  47. package/dist/esm/package.json +1 -0
  48. package/dist/esm/types/bash.d.ts +17 -0
  49. package/dist/esm/types/bash.d.ts.map +1 -0
  50. package/dist/esm/types/chunks.d.ts +23 -0
  51. package/dist/esm/types/chunks.d.ts.map +1 -0
  52. package/dist/esm/types/diff.d.ts +72 -0
  53. package/dist/esm/types/diff.d.ts.map +1 -0
  54. package/dist/esm/types/presentation.d.ts +48 -0
  55. package/dist/esm/types/presentation.d.ts.map +1 -0
  56. package/dist/esm/utils/bash-parser.d.ts +10 -0
  57. package/dist/esm/utils/bash-parser.d.ts.map +1 -0
  58. package/dist/esm/utils/image-urls.d.ts +15 -0
  59. package/dist/esm/utils/image-urls.d.ts.map +1 -0
  60. package/dist/esm/utils/markdown-parser.d.ts +7 -0
  61. package/dist/esm/utils/markdown-parser.d.ts.map +1 -0
  62. package/dist/esm/utils/presentation.d.ts +23 -0
  63. package/dist/esm/utils/presentation.d.ts.map +1 -0
  64. package/dist/esm/utils/slide-titles.d.ts +28 -0
  65. package/dist/esm/utils/slide-titles.d.ts.map +1 -0
  66. package/dist/tsconfig.tsbuildinfo +1 -0
  67. package/package.json +48 -0
@@ -0,0 +1,741 @@
1
+ // src/types/chunks.ts
2
+ var CHUNK_TYPES = {
3
+ MARKDOWN: "markdown_chunk",
4
+ MERMAID: "mermaid_chunk",
5
+ SLIDE: "slide_chunk",
6
+ CODE: "code_chunk"
7
+ };
8
+ var isMarkdownChunk = (chunk) => chunk.type === CHUNK_TYPES.MARKDOWN;
9
+ var isMermaidChunk = (chunk) => chunk.type === CHUNK_TYPES.MERMAID;
10
+ var isCodeChunk = (chunk) => chunk.type === CHUNK_TYPES.CODE;
11
+ // src/types/presentation.ts
12
+ var MarkdownSourceType;
13
+ ((MarkdownSourceType2) => {
14
+ MarkdownSourceType2["WORKSPACE_FILE"] = "workspace_file";
15
+ MarkdownSourceType2["REMOTE_FILE"] = "remote_file";
16
+ MarkdownSourceType2["GITHUB_FILE"] = "github_file";
17
+ MarkdownSourceType2["DRAFT"] = "draft";
18
+ MarkdownSourceType2["GITHUB_ISSUE"] = "github_issue";
19
+ MarkdownSourceType2["GITHUB_PULL_REQUEST"] = "github_pull_request";
20
+ MarkdownSourceType2["GITHUB_GIST"] = "github_gist";
21
+ })(MarkdownSourceType ||= {});
22
+ // src/utils/markdown-parser.ts
23
+ function hashMarkdownString(str) {
24
+ let hash = 0;
25
+ for (let i = 0;i < str.length; i++) {
26
+ const char = str.charCodeAt(i);
27
+ hash = (hash << 5) - hash + char;
28
+ hash = hash & hash;
29
+ }
30
+ return Math.abs(hash).toString(36);
31
+ }
32
+ function parseMarkdownChunks(markdownContent, idPrefix, customParsers) {
33
+ try {
34
+ if (typeof markdownContent !== "string") {
35
+ throw new Error("Invalid markdown content provided");
36
+ }
37
+ if (!markdownContent || markdownContent.trim() === "") {
38
+ return [];
39
+ }
40
+ const chunks = [];
41
+ const mermaidRegex = /^```mermaid\n([\s\S]*?)\n^```$/gm;
42
+ let lastIndex = 0;
43
+ let match;
44
+ let partCounter = 0;
45
+ while ((match = mermaidRegex.exec(markdownContent)) !== null) {
46
+ partCounter++;
47
+ if (match.index > lastIndex) {
48
+ const mdContent = markdownContent.substring(lastIndex, match.index);
49
+ if (mdContent.trim()) {
50
+ chunks.push({
51
+ type: CHUNK_TYPES.MARKDOWN,
52
+ content: mdContent,
53
+ id: `${idPrefix}-md-${partCounter}-${hashMarkdownString(mdContent)}`
54
+ });
55
+ }
56
+ }
57
+ partCounter++;
58
+ const mermaidContent = match[1].trim();
59
+ chunks.push({
60
+ type: CHUNK_TYPES.MERMAID,
61
+ content: mermaidContent,
62
+ id: `${idPrefix}-mermaid-${partCounter}-${hashMarkdownString(mermaidContent)}`
63
+ });
64
+ lastIndex = match.index + match[0].length;
65
+ }
66
+ if (lastIndex < markdownContent.length) {
67
+ partCounter++;
68
+ const remainingMdContent = markdownContent.substring(lastIndex);
69
+ if (remainingMdContent.trim()) {
70
+ chunks.push({
71
+ type: CHUNK_TYPES.MARKDOWN,
72
+ content: remainingMdContent,
73
+ id: `${idPrefix}-md-${partCounter}-${hashMarkdownString(remainingMdContent)}`
74
+ });
75
+ }
76
+ }
77
+ if (chunks.length === 0 && markdownContent.trim()) {
78
+ chunks.push({
79
+ type: CHUNK_TYPES.MARKDOWN,
80
+ content: markdownContent,
81
+ id: `${idPrefix}-md-only-${hashMarkdownString(markdownContent)}`
82
+ });
83
+ }
84
+ if (customParsers && customParsers.length > 0) {
85
+ let processedChunks = chunks;
86
+ for (const parser of customParsers) {
87
+ const newChunks = [];
88
+ for (const chunk of processedChunks) {
89
+ if (chunk.type === CHUNK_TYPES.MARKDOWN) {
90
+ const parsed = parser(chunk.content, chunk.id);
91
+ if (parsed.length > 0) {
92
+ newChunks.push(...parsed);
93
+ } else {
94
+ newChunks.push(chunk);
95
+ }
96
+ } else {
97
+ newChunks.push(chunk);
98
+ }
99
+ }
100
+ processedChunks = newChunks;
101
+ }
102
+ return processedChunks;
103
+ }
104
+ return chunks;
105
+ } catch (error) {
106
+ console.error("Error in parseMarkdownChunks:", error);
107
+ return markdownContent ? [
108
+ {
109
+ type: CHUNK_TYPES.MARKDOWN,
110
+ content: markdownContent,
111
+ id: `${idPrefix}-md-error-fallback-${hashMarkdownString(markdownContent)}`
112
+ }
113
+ ] : [];
114
+ }
115
+ }
116
+ // src/utils/presentation.ts
117
+ function hashMarkdownString2(str) {
118
+ let hash = 0;
119
+ for (let i = 0;i < str.length; i++) {
120
+ const char = str.charCodeAt(i);
121
+ hash = (hash << 5) - hash + char;
122
+ hash = hash & hash;
123
+ }
124
+ return Math.abs(hash).toString(36);
125
+ }
126
+ function extractSlideTitle(content) {
127
+ const lines = content.split(`
128
+ `).filter((line) => line.trim());
129
+ for (const line of lines) {
130
+ const headingMatch = line.match(/^#+\s+(.+)$/);
131
+ if (headingMatch) {
132
+ return headingMatch[1].trim();
133
+ }
134
+ }
135
+ if (lines.length > 0) {
136
+ const firstLine = lines[0];
137
+ return firstLine.length > 50 ? firstLine.substring(0, 47) + "..." : firstLine;
138
+ }
139
+ return "Untitled Slide";
140
+ }
141
+ function parseMarkdownIntoPresentationFromSource(source, customParsers) {
142
+ const presentation = parseMarkdownIntoPresentation(source.content, source.repositoryInfo, customParsers);
143
+ presentation.source = source;
144
+ return presentation;
145
+ }
146
+ function parseMarkdownIntoPresentation(markdownContent, repositoryInfo, customParsers) {
147
+ const hasMultipleHeaders = detectMultipleHeaders(markdownContent);
148
+ if (!hasMultipleHeaders) {
149
+ const id = `slide-0-${hashMarkdownString2(markdownContent)}`;
150
+ const chunks = parseMarkdownChunks(markdownContent, id, customParsers);
151
+ return {
152
+ slides: [
153
+ {
154
+ id,
155
+ title: extractSlideTitle(markdownContent),
156
+ location: {
157
+ startLine: 0,
158
+ endLine: markdownContent.split(`
159
+ `).length - 1,
160
+ content: markdownContent
161
+ },
162
+ chunks
163
+ }
164
+ ],
165
+ originalContent: markdownContent,
166
+ repositoryInfo
167
+ };
168
+ }
169
+ const lines = markdownContent.split(`
170
+ `);
171
+ const slides = [];
172
+ let currentSlideLines = [];
173
+ let currentSlideStartLine = 0;
174
+ let inCodeBlock = false;
175
+ let codeBlockDelimiter = "";
176
+ for (let i = 0;i < lines.length; i++) {
177
+ const line = lines[i];
178
+ if (line.trim().startsWith("```") || line.trim().startsWith("~~~")) {
179
+ if (!inCodeBlock) {
180
+ inCodeBlock = true;
181
+ codeBlockDelimiter = line.trim().substring(0, 3);
182
+ } else if (line.trim().startsWith(codeBlockDelimiter)) {
183
+ inCodeBlock = false;
184
+ codeBlockDelimiter = "";
185
+ }
186
+ }
187
+ const isDelimiter = !inCodeBlock && line.trim().startsWith("#") && !line.trim().startsWith("###");
188
+ if (isDelimiter && currentSlideLines.length > 0) {
189
+ const slideContent = currentSlideLines.join(`
190
+ `);
191
+ const slideId = `slide-${slides.length}-${hashMarkdownString2(slideContent)}`;
192
+ const chunks = parseMarkdownChunks(slideContent, slideId, customParsers);
193
+ slides.push({
194
+ id: slideId,
195
+ title: extractSlideTitle(slideContent),
196
+ location: {
197
+ startLine: currentSlideStartLine,
198
+ endLine: i - 1,
199
+ content: slideContent
200
+ },
201
+ chunks
202
+ });
203
+ currentSlideLines = [line];
204
+ currentSlideStartLine = i;
205
+ } else {
206
+ currentSlideLines.push(line);
207
+ }
208
+ }
209
+ if (currentSlideLines.length > 0) {
210
+ const slideContent = currentSlideLines.join(`
211
+ `);
212
+ const slideId = `slide-${slides.length}-${hashMarkdownString2(slideContent)}`;
213
+ const chunks = parseMarkdownChunks(slideContent, slideId, customParsers);
214
+ slides.push({
215
+ id: slideId,
216
+ title: extractSlideTitle(slideContent),
217
+ location: {
218
+ startLine: currentSlideStartLine,
219
+ endLine: lines.length - 1,
220
+ content: slideContent
221
+ },
222
+ chunks
223
+ });
224
+ }
225
+ return {
226
+ slides,
227
+ originalContent: markdownContent,
228
+ repositoryInfo
229
+ };
230
+ }
231
+ function detectMultipleHeaders(markdownContent) {
232
+ const lines = markdownContent.split(`
233
+ `);
234
+ let h1Count = 0;
235
+ let h2Count = 0;
236
+ let inCodeBlock = false;
237
+ let codeBlockDelimiter = "";
238
+ for (const line of lines) {
239
+ if (line.trim().startsWith("```") || line.trim().startsWith("~~~")) {
240
+ if (!inCodeBlock) {
241
+ inCodeBlock = true;
242
+ codeBlockDelimiter = line.trim().substring(0, 3);
243
+ } else if (line.trim().startsWith(codeBlockDelimiter)) {
244
+ inCodeBlock = false;
245
+ codeBlockDelimiter = "";
246
+ }
247
+ }
248
+ if (!inCodeBlock) {
249
+ if (line.trim().startsWith("#") && !line.trim().startsWith("###")) {
250
+ if (!line.trim().startsWith("##")) {
251
+ h1Count++;
252
+ } else {
253
+ h2Count++;
254
+ }
255
+ }
256
+ }
257
+ }
258
+ const totalHeaders = h1Count + h2Count;
259
+ return totalHeaders > 1;
260
+ }
261
+ function serializePresentationToMarkdown(presentation) {
262
+ return presentation.slides.map((slide) => slide.location.content).join(`
263
+ `);
264
+ }
265
+ function updatePresentationSlide(presentation, slideIndex, newContent, customParsers) {
266
+ if (slideIndex < 0 || slideIndex >= presentation.slides.length) {
267
+ throw new Error("Invalid slide index");
268
+ }
269
+ const updatedSlides = [...presentation.slides];
270
+ const slide = updatedSlides[slideIndex];
271
+ const newChunks = parseMarkdownChunks(newContent, slide.id, customParsers);
272
+ updatedSlides[slideIndex] = {
273
+ ...slide,
274
+ location: {
275
+ ...slide.location,
276
+ content: newContent
277
+ },
278
+ chunks: newChunks,
279
+ title: extractSlideTitle(newContent)
280
+ };
281
+ return {
282
+ ...presentation,
283
+ slides: updatedSlides,
284
+ originalContent: serializePresentationToMarkdown({ ...presentation, slides: updatedSlides })
285
+ };
286
+ }
287
+ // src/utils/bash-parser.ts
288
+ function parseBashCommands(codeString) {
289
+ const lines = codeString.split(`
290
+ `);
291
+ const commands = [];
292
+ let currentCommand = "";
293
+ let commandStartLine = 0;
294
+ let currentDescription = "";
295
+ let inBackslashContinuation = false;
296
+ for (let i = 0;i < lines.length; i++) {
297
+ const rawLine = lines[i];
298
+ const line = rawLine.trim();
299
+ if (!line) {
300
+ if (currentCommand && !inBackslashContinuation) {
301
+ commands.push({
302
+ command: currentCommand.trim(),
303
+ description: currentDescription || undefined,
304
+ line: commandStartLine + 1
305
+ });
306
+ currentCommand = "";
307
+ currentDescription = "";
308
+ }
309
+ continue;
310
+ }
311
+ if (line.startsWith("#")) {
312
+ if (!currentCommand) {
313
+ currentDescription = line.substring(1).trim();
314
+ }
315
+ continue;
316
+ }
317
+ const endsWithBackslash = rawLine.trimEnd().endsWith("\\");
318
+ if (endsWithBackslash || inBackslashContinuation) {
319
+ if (!currentCommand) {
320
+ commandStartLine = i;
321
+ }
322
+ if (endsWithBackslash) {
323
+ const content = rawLine.trimEnd().slice(0, -1).trim();
324
+ currentCommand += (currentCommand ? " " : "") + content;
325
+ inBackslashContinuation = true;
326
+ } else {
327
+ currentCommand += (currentCommand ? " " : "") + line;
328
+ inBackslashContinuation = false;
329
+ }
330
+ continue;
331
+ }
332
+ const isPipeContinuation = line.startsWith("|") || line.startsWith("&&") || line.startsWith("||");
333
+ const previousLineEndsWithOperator = currentCommand && (currentCommand.trim().endsWith("|") || currentCommand.trim().endsWith("&&") || currentCommand.trim().endsWith("||") || currentCommand.trim().endsWith(";"));
334
+ if (currentCommand && (isPipeContinuation || previousLineEndsWithOperator)) {
335
+ currentCommand += (currentCommand.endsWith(" ") ? "" : " ") + line;
336
+ continue;
337
+ }
338
+ if (currentCommand) {
339
+ commands.push({
340
+ command: currentCommand.trim(),
341
+ description: currentDescription || undefined,
342
+ line: commandStartLine + 1
343
+ });
344
+ currentDescription = "";
345
+ }
346
+ commandStartLine = i;
347
+ currentCommand = line;
348
+ }
349
+ if (currentCommand) {
350
+ commands.push({
351
+ command: currentCommand.trim(),
352
+ description: currentDescription || undefined,
353
+ line: commandStartLine + 1
354
+ });
355
+ }
356
+ return commands.filter((cmd) => cmd.command.length > 0);
357
+ }
358
+ function getCommandDisplayName(command, maxLength = 50) {
359
+ if (command.description) {
360
+ return command.description.length <= maxLength ? command.description : command.description.substring(0, maxLength - 3) + "...";
361
+ }
362
+ const cmdText = command.command;
363
+ if (cmdText.length <= maxLength) {
364
+ return cmdText;
365
+ }
366
+ return cmdText.substring(0, maxLength - 3) + "...";
367
+ }
368
+ // src/utils/image-urls.ts
369
+ function isRelativeUrl(url) {
370
+ if (url.startsWith("http://") || url.startsWith("https://")) {
371
+ return false;
372
+ }
373
+ if (url.startsWith("//")) {
374
+ return false;
375
+ }
376
+ if (url.startsWith("data:")) {
377
+ return false;
378
+ }
379
+ if (url.startsWith("blob:")) {
380
+ return false;
381
+ }
382
+ return true;
383
+ }
384
+ function transformImageUrl(src, repositoryInfo) {
385
+ if (!repositoryInfo || !isRelativeUrl(src)) {
386
+ return src;
387
+ }
388
+ const { owner, repo, branch = "main", basePath = "" } = repositoryInfo;
389
+ let fullPath;
390
+ if (src.startsWith("/")) {
391
+ fullPath = src.substring(1);
392
+ } else {
393
+ let cleanPath = src;
394
+ if (cleanPath.startsWith("./")) {
395
+ cleanPath = cleanPath.substring(2);
396
+ }
397
+ if (cleanPath.startsWith("../")) {
398
+ console.warn("Relative parent directory navigation in image URLs is not fully supported:", src);
399
+ cleanPath = cleanPath.replace(/^(\.\.\/)+/, "");
400
+ }
401
+ if (basePath) {
402
+ const cleanBasePath = basePath.replace(/^\/+|\/+$/g, "");
403
+ fullPath = `${cleanBasePath}/${cleanPath}`;
404
+ } else {
405
+ fullPath = cleanPath;
406
+ }
407
+ }
408
+ const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${fullPath}`;
409
+ return rawUrl;
410
+ }
411
+ function transformMarkdownImageUrls(markdownContent, repositoryInfo) {
412
+ if (!repositoryInfo) {
413
+ return markdownContent;
414
+ }
415
+ const transformedContent = markdownContent.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (_, altText, imagePath) => {
416
+ const transformedPath = transformImageUrl(imagePath, repositoryInfo);
417
+ return `![${altText}](${transformedPath})`;
418
+ });
419
+ return transformedContent;
420
+ }
421
+ // src/utils/slide-titles.ts
422
+ function extractSlideTitle2(content, slideIndex) {
423
+ const lines = content.split(`
424
+ `);
425
+ for (const line of lines) {
426
+ const trimmedLine = line.trim();
427
+ if (trimmedLine.startsWith("#")) {
428
+ const match = trimmedLine.match(/^#{1,2}\s+(.+)$/);
429
+ if (match && match[1]) {
430
+ return match[1].replace(/\*\*/g, "").replace(/\*/g, "").replace(/`/g, "").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").trim();
431
+ }
432
+ }
433
+ }
434
+ return `Slide ${slideIndex + 1}`;
435
+ }
436
+ function extractAllSlideTitles(slides) {
437
+ return slides.map((slide, index) => extractSlideTitle2(slide, index));
438
+ }
439
+ function getAllSlideTitles(slides) {
440
+ return slides.map((slide) => slide.title);
441
+ }
442
+ function findSlideByTitle(slides, title) {
443
+ return slides.find((slide) => slide.title === title);
444
+ }
445
+ function findSlideIndexByTitle(slides, title) {
446
+ return slides.findIndex((slide) => slide.title === title);
447
+ }
448
+ // src/diff/textDiff.ts
449
+ function diffText(beforeContent, afterContent) {
450
+ if (beforeContent === "" && afterContent === "") {
451
+ return [];
452
+ }
453
+ const beforeLines = beforeContent === "" ? [] : beforeContent.split(`
454
+ `);
455
+ const afterLines = afterContent === "" ? [] : afterContent.split(`
456
+ `);
457
+ const lcs = longestCommonSubsequence(beforeLines, afterLines);
458
+ return buildDiffFromLCS(beforeLines, afterLines, lcs);
459
+ }
460
+ function longestCommonSubsequence(beforeLines, afterLines) {
461
+ const m = beforeLines.length;
462
+ const n = afterLines.length;
463
+ const lcs = Array(m + 1).fill(0).map(() => Array(n + 1).fill(0));
464
+ for (let i = 1;i <= m; i++) {
465
+ for (let j = 1;j <= n; j++) {
466
+ if (beforeLines[i - 1] === afterLines[j - 1]) {
467
+ lcs[i][j] = lcs[i - 1][j - 1] + 1;
468
+ } else {
469
+ lcs[i][j] = Math.max(lcs[i - 1][j], lcs[i][j - 1]);
470
+ }
471
+ }
472
+ }
473
+ return lcs;
474
+ }
475
+ function buildDiffFromLCS(beforeLines, afterLines, lcs) {
476
+ const result = [];
477
+ let i = beforeLines.length;
478
+ let j = afterLines.length;
479
+ while (i > 0 || j > 0) {
480
+ if (i > 0 && j > 0 && beforeLines[i - 1] === afterLines[j - 1]) {
481
+ result.unshift({
482
+ type: "unchanged",
483
+ value: beforeLines[i - 1]
484
+ });
485
+ i--;
486
+ j--;
487
+ } else if (j > 0 && (i === 0 || lcs[i][j - 1] >= lcs[i - 1][j])) {
488
+ result.unshift({
489
+ type: "add",
490
+ value: afterLines[j - 1]
491
+ });
492
+ j--;
493
+ } else if (i > 0 && (j === 0 || lcs[i][j - 1] < lcs[i - 1][j])) {
494
+ result.unshift({
495
+ type: "remove",
496
+ value: beforeLines[i - 1]
497
+ });
498
+ i--;
499
+ }
500
+ }
501
+ return result.map((diff, index) => ({
502
+ ...diff,
503
+ lineNumber: index + 1
504
+ }));
505
+ }
506
+ function normalizedTextEquals(text1, text2) {
507
+ return normalizeText(text1) === normalizeText(text2);
508
+ }
509
+ function normalizeText(text) {
510
+ return text.replace(/\r\n/g, `
511
+ `).split(`
512
+ `).map((line) => line.trimEnd()).join(`
513
+ `).trim();
514
+ }
515
+
516
+ // src/diff/matchSlides.ts
517
+ function matchSlides(beforeSlides, afterSlides) {
518
+ const matches = [];
519
+ const matchedBeforeIndices = new Set;
520
+ const matchedAfterIndices = new Set;
521
+ for (let beforeIndex = 0;beforeIndex < beforeSlides.length; beforeIndex++) {
522
+ const beforeSlide = beforeSlides[beforeIndex];
523
+ for (let afterIndex = 0;afterIndex < afterSlides.length; afterIndex++) {
524
+ if (matchedAfterIndices.has(afterIndex)) {
525
+ continue;
526
+ }
527
+ const afterSlide = afterSlides[afterIndex];
528
+ if (normalizeTitleForMatching(beforeSlide.title) === normalizeTitleForMatching(afterSlide.title)) {
529
+ matches.push({
530
+ beforeSlide,
531
+ afterSlide,
532
+ beforeIndex,
533
+ afterIndex,
534
+ matchedBy: "title"
535
+ });
536
+ matchedBeforeIndices.add(beforeIndex);
537
+ matchedAfterIndices.add(afterIndex);
538
+ break;
539
+ }
540
+ }
541
+ }
542
+ const unmatchedBeforeIndices = beforeSlides.map((_, i) => i).filter((i) => !matchedBeforeIndices.has(i));
543
+ const unmatchedAfterIndices = afterSlides.map((_, i) => i).filter((i) => !matchedAfterIndices.has(i));
544
+ const minUnmatched = Math.min(unmatchedBeforeIndices.length, unmatchedAfterIndices.length);
545
+ for (let i = 0;i < minUnmatched; i++) {
546
+ const beforeIndex = unmatchedBeforeIndices[i];
547
+ const afterIndex = unmatchedAfterIndices[i];
548
+ matches.push({
549
+ beforeSlide: beforeSlides[beforeIndex],
550
+ afterSlide: afterSlides[afterIndex],
551
+ beforeIndex,
552
+ afterIndex,
553
+ matchedBy: "position"
554
+ });
555
+ matchedBeforeIndices.add(beforeIndex);
556
+ matchedAfterIndices.add(afterIndex);
557
+ }
558
+ for (let beforeIndex = 0;beforeIndex < beforeSlides.length; beforeIndex++) {
559
+ if (!matchedBeforeIndices.has(beforeIndex)) {
560
+ matches.push({
561
+ beforeSlide: beforeSlides[beforeIndex],
562
+ beforeIndex,
563
+ matchedBy: "none"
564
+ });
565
+ }
566
+ }
567
+ for (let afterIndex = 0;afterIndex < afterSlides.length; afterIndex++) {
568
+ if (!matchedAfterIndices.has(afterIndex)) {
569
+ matches.push({
570
+ afterSlide: afterSlides[afterIndex],
571
+ afterIndex,
572
+ matchedBy: "none"
573
+ });
574
+ }
575
+ }
576
+ matches.sort((a, b) => {
577
+ const aIndex = a.beforeIndex ?? a.afterIndex ?? 0;
578
+ const bIndex = b.beforeIndex ?? b.afterIndex ?? 0;
579
+ return aIndex - bIndex;
580
+ });
581
+ return matches;
582
+ }
583
+ function slidesAreEqual(slide1, slide2) {
584
+ return normalizeSlideContent(slide1) === normalizeSlideContent(slide2);
585
+ }
586
+ function normalizeSlideContent(slide) {
587
+ return normalizeText(slide.location.content);
588
+ }
589
+ function normalizeTitleForMatching(title) {
590
+ return title.trim().replace(/\s+/g, " ").toLowerCase();
591
+ }
592
+
593
+ // src/diff/diffSummary.ts
594
+ function calculateDiffSummary(diff) {
595
+ const summary = {
596
+ totalSlidesBefore: diff.before.slides.length,
597
+ totalSlidesAfter: diff.after.slides.length,
598
+ added: 0,
599
+ removed: 0,
600
+ modified: 0,
601
+ unchanged: 0,
602
+ moved: 0
603
+ };
604
+ for (const slideDiff of diff.slideDiffs) {
605
+ switch (slideDiff.status) {
606
+ case "added":
607
+ summary.added++;
608
+ break;
609
+ case "removed":
610
+ summary.removed++;
611
+ break;
612
+ case "modified":
613
+ summary.modified++;
614
+ break;
615
+ case "unchanged":
616
+ summary.unchanged++;
617
+ break;
618
+ case "moved":
619
+ summary.moved++;
620
+ break;
621
+ }
622
+ }
623
+ return summary;
624
+ }
625
+ function hasChanges(diff) {
626
+ const { summary } = diff;
627
+ return summary.added > 0 || summary.removed > 0 || summary.modified > 0 || summary.moved > 0;
628
+ }
629
+ function getTotalChangedSlides(summary) {
630
+ return summary.added + summary.removed + summary.modified + summary.moved;
631
+ }
632
+ function formatDiffSummary(summary) {
633
+ const parts = [];
634
+ if (summary.added > 0) {
635
+ parts.push(`${summary.added} added`);
636
+ }
637
+ if (summary.removed > 0) {
638
+ parts.push(`${summary.removed} removed`);
639
+ }
640
+ if (summary.modified > 0) {
641
+ parts.push(`${summary.modified} modified`);
642
+ }
643
+ if (summary.moved > 0) {
644
+ parts.push(`${summary.moved} moved`);
645
+ }
646
+ if (parts.length === 0) {
647
+ return "No changes";
648
+ }
649
+ return parts.join(", ");
650
+ }
651
+
652
+ // src/diff/diffPresentations.ts
653
+ function diffPresentations(before, after) {
654
+ const matches = matchSlides(before.slides, after.slides);
655
+ const slideDiffs = matches.map((match) => createSlideDiff(match));
656
+ const presentationDiff = {
657
+ before,
658
+ after,
659
+ slideDiffs,
660
+ summary: {
661
+ totalSlidesBefore: 0,
662
+ totalSlidesAfter: 0,
663
+ added: 0,
664
+ removed: 0,
665
+ modified: 0,
666
+ unchanged: 0,
667
+ moved: 0
668
+ }
669
+ };
670
+ presentationDiff.summary = calculateDiffSummary(presentationDiff);
671
+ return presentationDiff;
672
+ }
673
+ function createSlideDiff(match) {
674
+ const { beforeSlide, afterSlide, beforeIndex, afterIndex, matchedBy } = match;
675
+ let status;
676
+ let contentChanges;
677
+ let titleChanged = false;
678
+ if (!beforeSlide && afterSlide) {
679
+ status = "added";
680
+ } else if (beforeSlide && !afterSlide) {
681
+ status = "removed";
682
+ } else if (beforeSlide && afterSlide) {
683
+ const contentEqual = slidesAreEqual(beforeSlide, afterSlide);
684
+ const positionChanged = beforeIndex !== afterIndex;
685
+ titleChanged = beforeSlide.title !== afterSlide.title;
686
+ if (contentEqual) {
687
+ if (positionChanged) {
688
+ status = "moved";
689
+ } else {
690
+ status = "unchanged";
691
+ }
692
+ } else {
693
+ status = "modified";
694
+ contentChanges = diffText(beforeSlide.location.content, afterSlide.location.content);
695
+ }
696
+ } else {
697
+ status = "unchanged";
698
+ }
699
+ return {
700
+ status,
701
+ beforeSlide,
702
+ afterSlide,
703
+ beforeIndex,
704
+ afterIndex,
705
+ contentChanges,
706
+ titleChanged
707
+ };
708
+ }
709
+ export {
710
+ updatePresentationSlide,
711
+ transformMarkdownImageUrls,
712
+ transformImageUrl,
713
+ slidesAreEqual,
714
+ serializePresentationToMarkdown,
715
+ parseMarkdownIntoPresentationFromSource,
716
+ parseMarkdownIntoPresentation,
717
+ parseMarkdownChunks,
718
+ parseBashCommands,
719
+ normalizedTextEquals,
720
+ normalizeText,
721
+ normalizeSlideContent,
722
+ matchSlides,
723
+ isRelativeUrl,
724
+ isMermaidChunk,
725
+ isMarkdownChunk,
726
+ isCodeChunk,
727
+ hasChanges,
728
+ getTotalChangedSlides,
729
+ getCommandDisplayName,
730
+ getAllSlideTitles,
731
+ formatDiffSummary,
732
+ findSlideIndexByTitle,
733
+ findSlideByTitle,
734
+ extractSlideTitle,
735
+ extractAllSlideTitles,
736
+ diffText,
737
+ diffPresentations,
738
+ calculateDiffSummary,
739
+ MarkdownSourceType,
740
+ CHUNK_TYPES
741
+ };