difit 2.2.1 → 2.2.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 (31) hide show
  1. package/LICENSE +2 -2
  2. package/README.ja.md +4 -1
  3. package/README.ko.md +4 -1
  4. package/README.md +4 -1
  5. package/README.zh.md +4 -1
  6. package/dist/cli/index.js +0 -0
  7. package/dist/client/assets/index-D4o04IuQ.css +1 -0
  8. package/dist/client/assets/{index-KydrgTN-.js → index-DdgLWfl6.js} +70 -70
  9. package/dist/client/assets/{prism-csharp-R5PE7dGT.js → prism-csharp-DNLu89K5.js} +1 -1
  10. package/dist/client/assets/{prism-java-BvQgUZlL.js → prism-java-DPmqUWjf.js} +1 -1
  11. package/dist/client/assets/{prism-php-sGBBrTD-.js → prism-php-BERkcwmm.js} +1 -1
  12. package/dist/client/assets/{prism-ruby-CBz5QFG3.js → prism-ruby-BWOjhenp.js} +1 -1
  13. package/dist/client/assets/{prism-solidity-Ck6Q6Nkr.js → prism-solidity-CD2S3KYq.js} +1 -1
  14. package/dist/client/index.html +2 -2
  15. package/dist/server/git-diff.d.ts +4 -0
  16. package/dist/server/git-diff.js +131 -7
  17. package/dist/server/git-diff.test.js +318 -0
  18. package/dist/server/server.js +2 -1
  19. package/dist/types/diff.d.ts +2 -1
  20. package/package.json +4 -3
  21. package/dist/client/assets/index-Bz9yRRZ7.css +0 -1
  22. package/dist/server/schemas/commentSchema.d.ts +0 -22
  23. package/dist/server/schemas/commentSchema.js +0 -16
  24. package/dist/server/schemas/commentSchema.test.d.ts +0 -1
  25. package/dist/server/schemas/commentSchema.test.js +0 -229
  26. package/dist/utils/commentFormatter.d.ts +0 -26
  27. package/dist/utils/commentFormatter.js +0 -50
  28. package/dist/utils/gitUtils.d.ts +0 -4
  29. package/dist/utils/gitUtils.js +0 -29
  30. package/dist/utils/gitUtils.test.d.ts +0 -1
  31. package/dist/utils/gitUtils.test.js +0 -63
@@ -114,8 +114,10 @@ describe('GitDiffParser', () => {
114
114
  'Binary files /dev/null and b/image.jpg differ',
115
115
  ];
116
116
  const summary = {
117
+ file: 'image.jpg',
117
118
  insertions: 0,
118
119
  deletions: 0,
120
+ binary: true,
119
121
  };
120
122
  // Access private method for testing
121
123
  const result = parser.parseFileBlock(diffLines.join('\n'), summary);
@@ -138,8 +140,10 @@ describe('GitDiffParser', () => {
138
140
  'Binary files a/old-image.png and /dev/null differ',
139
141
  ];
140
142
  const summary = {
143
+ file: 'old-image.png',
141
144
  insertions: 0,
142
145
  deletions: 0,
146
+ binary: true,
143
147
  };
144
148
  const result = parser.parseFileBlock(diffLines.join('\n'), summary);
145
149
  expect(result).toEqual({
@@ -160,8 +164,10 @@ describe('GitDiffParser', () => {
160
164
  'Binary files a/photo.jpg and b/photo.jpg differ',
161
165
  ];
162
166
  const summary = {
167
+ file: 'photo.jpg',
163
168
  insertions: 0,
164
169
  deletions: 0,
170
+ binary: true,
165
171
  };
166
172
  const result = parser.parseFileBlock(diffLines.join('\n'), summary);
167
173
  expect(result).toEqual({
@@ -181,8 +187,11 @@ describe('GitDiffParser', () => {
181
187
  'rename to new-name.gif',
182
188
  ];
183
189
  const summary = {
190
+ file: 'new-name.gif',
191
+ from: 'old-name.gif',
184
192
  insertions: 0,
185
193
  deletions: 0,
194
+ binary: true,
186
195
  };
187
196
  const result = parser.parseFileBlock(diffLines.join('\n'), summary);
188
197
  expect(result).toEqual({
@@ -206,8 +215,10 @@ describe('GitDiffParser', () => {
206
215
  ' // end',
207
216
  ];
208
217
  const summary = {
218
+ file: 'script.js',
209
219
  insertions: 1,
210
220
  deletions: 0,
221
+ binary: false,
211
222
  };
212
223
  const result = parser.parseFileBlock(diffLines.join('\n'), summary);
213
224
  expect(result).toEqual({
@@ -234,8 +245,10 @@ describe('GitDiffParser', () => {
234
245
  ' // end',
235
246
  ];
236
247
  const summary = {
248
+ file: 'script.js',
237
249
  insertions: 0,
238
250
  deletions: 1,
251
+ binary: false,
239
252
  };
240
253
  const result = parser.parseFileBlock(diffLines.join('\n'), summary);
241
254
  expect(result).toEqual({
@@ -258,8 +271,10 @@ describe('GitDiffParser', () => {
258
271
  '+line 2',
259
272
  ];
260
273
  const summary = {
274
+ file: 'new-file.txt',
261
275
  insertions: 2,
262
276
  deletions: 0,
277
+ binary: false,
263
278
  };
264
279
  const result = parser.parseFileBlock(diffLines.join('\n'), summary);
265
280
  expect(result.status).toBe('added');
@@ -275,13 +290,312 @@ describe('GitDiffParser', () => {
275
290
  '-line 2',
276
291
  ];
277
292
  const summary = {
293
+ file: 'deleted-file.txt',
278
294
  insertions: 0,
279
295
  deletions: 2,
296
+ binary: false,
280
297
  };
281
298
  const result = parser.parseFileBlock(diffLines.join('\n'), summary);
282
299
  expect(result.status).toBe('deleted');
283
300
  });
284
301
  });
302
+ describe('parseFileBlock with quoted paths', () => {
303
+ it('parses file paths with spaces correctly', () => {
304
+ const diffLines = [
305
+ 'diff --git "a/test with spaces/file name.txt" "b/test with spaces/file name.txt"',
306
+ 'new file mode 100644',
307
+ 'index 0000000..257cc56',
308
+ '--- /dev/null',
309
+ '+++ "b/test with spaces/file name.txt"',
310
+ '@@ -0,0 +1 @@',
311
+ '+foo',
312
+ ];
313
+ const result = parser.parseFileBlock(diffLines.join('\n'), null);
314
+ expect(result).toBeDefined();
315
+ expect(result.path).toBe('test with spaces/file name.txt');
316
+ expect(result.status).toBe('added');
317
+ expect(result.additions).toBe(1);
318
+ expect(result.deletions).toBe(0);
319
+ });
320
+ it('parses summary-provided filenames with escaped spaces', () => {
321
+ const diffLines = [
322
+ 'diff --git "a/templates/test file.py" "b/templates/test file.py"',
323
+ 'index abc123..def456 100644',
324
+ '--- "a/templates/test file.py"',
325
+ '+++ "b/templates/test file.py"',
326
+ '@@ -1 +1 @@',
327
+ '-old',
328
+ '+new',
329
+ ];
330
+ const summary = {
331
+ file: 'templates/test\\040file.py',
332
+ insertions: 1,
333
+ deletions: 1,
334
+ binary: false,
335
+ };
336
+ const result = parser.parseFileBlock(diffLines.join('\n'), summary);
337
+ expect(result?.path).toBe('templates/test file.py');
338
+ expect(result?.oldPath).toBeUndefined();
339
+ });
340
+ it('parses file paths with Jinja template brackets correctly', () => {
341
+ const diffLines = [
342
+ 'diff --git "a/templates/test_000_{{ package_name }}/__.py" "b/templates/test_000_{{ package_name }}/__.py"',
343
+ 'new file mode 100644',
344
+ 'index 0000000..257cc56',
345
+ '--- /dev/null',
346
+ '+++ "b/templates/test_000_{{ package_name }}/__.py"',
347
+ '@@ -0,0 +1 @@',
348
+ '+test',
349
+ ];
350
+ const result = parser.parseFileBlock(diffLines.join('\n'), null);
351
+ expect(result).toBeDefined();
352
+ expect(result.path).toBe('templates/test_000_{{ package_name }}/__.py');
353
+ expect(result.status).toBe('added');
354
+ });
355
+ it('parses file paths with escaped characters correctly', () => {
356
+ const diffLines = [
357
+ 'diff --git "a/file\\twith\\ttabs.txt" "b/file\\twith\\ttabs.txt"',
358
+ 'new file mode 100644',
359
+ 'index 0000000..257cc56',
360
+ '--- /dev/null',
361
+ '+++ "b/file\\twith\\ttabs.txt"',
362
+ '@@ -0,0 +1 @@',
363
+ '+content',
364
+ ];
365
+ const result = parser.parseFileBlock(diffLines.join('\n'), null);
366
+ expect(result).toBeDefined();
367
+ expect(result.path).toBe('file\twith\ttabs.txt');
368
+ expect(result.status).toBe('added');
369
+ });
370
+ it('parses renamed files with spaces correctly', () => {
371
+ const diffLines = [
372
+ 'diff --git "a/old folder/old name.txt" "b/new folder/new name.txt"',
373
+ 'similarity index 100%',
374
+ 'rename from old folder/old name.txt',
375
+ 'rename to new folder/new name.txt',
376
+ ];
377
+ const result = parser.parseFileBlock(diffLines.join('\n'), null);
378
+ expect(result).toBeDefined();
379
+ expect(result.path).toBe('new folder/new name.txt');
380
+ expect(result.oldPath).toBe('old folder/old name.txt');
381
+ expect(result.status).toBe('renamed');
382
+ });
383
+ it('still handles unquoted paths correctly', () => {
384
+ const diffLines = [
385
+ 'diff --git a/src/file.js b/src/file.js',
386
+ 'index 1234567..8901234 100644',
387
+ '--- a/src/file.js',
388
+ '+++ b/src/file.js',
389
+ '@@ -1,3 +1,3 @@',
390
+ ' line1',
391
+ '-old',
392
+ '+new',
393
+ ' line3',
394
+ ];
395
+ const result = parser.parseFileBlock(diffLines.join('\n'), null);
396
+ expect(result).toBeDefined();
397
+ expect(result.path).toBe('src/file.js');
398
+ expect(result.status).toBe('modified');
399
+ expect(result.additions).toBe(1);
400
+ expect(result.deletions).toBe(1);
401
+ });
402
+ it('handles unquoted paths with spaces when core.quotePath=false', () => {
403
+ const diffLines = [
404
+ 'diff --git a/path with spaces/file.txt b/path with spaces/file.txt',
405
+ 'index 1234567..8901234 100644',
406
+ '--- a/path with spaces/file.txt',
407
+ '+++ b/path with spaces/file.txt',
408
+ '@@ -1 +1 @@',
409
+ '-old content',
410
+ '+new content',
411
+ ];
412
+ const result = parser.parseFileBlock(diffLines.join('\n'), null);
413
+ expect(result).toBeDefined();
414
+ expect(result.path).toBe('path with spaces/file.txt');
415
+ expect(result.status).toBe('modified');
416
+ expect(result.additions).toBe(1);
417
+ expect(result.deletions).toBe(1);
418
+ });
419
+ it('decodes unquoted octal escapes in diff headers', () => {
420
+ const diffLines = [
421
+ 'diff --git a/some\\040folder/file\\040name.ts b/some\\040folder/file\\040name.ts',
422
+ 'index 3333333..4444444 100644',
423
+ '--- a/some\\040folder/file\\040name.ts',
424
+ '+++ b/some\\040folder/file\\040name.ts',
425
+ '@@ -1 +1 @@',
426
+ '-old',
427
+ '+new',
428
+ ];
429
+ const result = parser.parseFileBlock(diffLines.join('\n'), null);
430
+ expect(result?.path).toBe('some folder/file name.ts');
431
+ expect(result?.oldPath).toBeUndefined();
432
+ });
433
+ it('handles unquoted paths containing "b/" in filename', () => {
434
+ const diffLines = [
435
+ 'diff --git a/dir b/sub/file b/dir b/sub/file',
436
+ 'index 1234567..8901234 100644',
437
+ '--- a/dir b/sub/file',
438
+ '+++ b/dir b/sub/file',
439
+ '@@ -1 +1 @@',
440
+ '-old',
441
+ '+new',
442
+ ];
443
+ const result = parser.parseFileBlock(diffLines.join('\n'), null);
444
+ expect(result).toBeDefined();
445
+ expect(result.path).toBe('dir b/sub/file');
446
+ expect(result.oldPath).toBeUndefined();
447
+ expect(result.status).toBe('modified');
448
+ });
449
+ it('handles renamed files with "b/" in the path', () => {
450
+ const diffLines = [
451
+ 'diff --git a/old b/path/file b/new b/path/file',
452
+ 'similarity index 100%',
453
+ 'rename from old b/path/file',
454
+ 'rename to new b/path/file',
455
+ ];
456
+ const result = parser.parseFileBlock(diffLines.join('\n'), null);
457
+ expect(result).toBeDefined();
458
+ expect(result.path).toBe('new b/path/file');
459
+ expect(result.oldPath).toBe('old b/path/file');
460
+ expect(result.status).toBe('renamed');
461
+ });
462
+ it('handles alternative git diff prefixes for working tree comparisons', () => {
463
+ const diffLines = [
464
+ 'diff --git c/a/test.txt w/a/test.txt',
465
+ 'index 1234567..8901234 100644',
466
+ '--- c/a/test.txt',
467
+ '+++ w/a/test.txt\t',
468
+ '@@ -1 +1 @@',
469
+ '-old',
470
+ '+new',
471
+ ];
472
+ const summary = {
473
+ file: 'a/test.txt',
474
+ insertions: 1,
475
+ deletions: 1,
476
+ binary: false,
477
+ };
478
+ const result = parser.parseFileBlock(diffLines.join('\n'), summary);
479
+ expect(result).toBeDefined();
480
+ expect(result.path).toBe('a/test.txt');
481
+ expect(result.oldPath).toBeUndefined();
482
+ expect(result.status).toBe('modified');
483
+ });
484
+ it('handles rename metadata with alternative git prefixes', () => {
485
+ const diffLines = [
486
+ 'diff --git c/old/name.txt w/new/name.txt',
487
+ 'similarity index 100%',
488
+ 'rename from c/old/name.txt\t',
489
+ 'rename to w/new/name.txt\t',
490
+ ];
491
+ const summary = {
492
+ file: 'new/name.txt',
493
+ from: 'old/name.txt',
494
+ insertions: 0,
495
+ deletions: 0,
496
+ binary: false,
497
+ };
498
+ const result = parser.parseFileBlock(diffLines.join('\n'), summary);
499
+ expect(result).toBeDefined();
500
+ expect(result.path).toBe('new/name.txt');
501
+ expect(result.oldPath).toBe('old/name.txt');
502
+ expect(result.status).toBe('renamed');
503
+ });
504
+ it('ignores trailing metadata separators in diff path lines', () => {
505
+ const diffLines = [
506
+ 'diff --git c/foo bar.txt w/foo bar.txt',
507
+ 'index 1234567..8901234 100644',
508
+ '--- c/foo bar.txt\t',
509
+ '+++ w/foo bar.txt\t',
510
+ '@@ -1 +1 @@',
511
+ '-old',
512
+ '+new',
513
+ ];
514
+ const summary = {
515
+ file: 'foo bar.txt',
516
+ insertions: 1,
517
+ deletions: 1,
518
+ binary: false,
519
+ };
520
+ const result = parser.parseFileBlock(diffLines.join('\n'), summary);
521
+ expect(result).toBeDefined();
522
+ expect(result.path).toBe('foo bar.txt');
523
+ expect(result.status).toBe('modified');
524
+ });
525
+ it('prefers header paths over summary paths when they differ', () => {
526
+ const diffLines = [
527
+ 'diff --git a/a/test.txt b/a/test.txt',
528
+ 'new file mode 100644',
529
+ 'index 0000000..257cc56',
530
+ '--- /dev/null',
531
+ '+++ b/a/test.txt',
532
+ '@@ -0,0 +1 @@',
533
+ '+content',
534
+ ];
535
+ const summary = {
536
+ file: 'a/test.txt',
537
+ insertions: 1,
538
+ deletions: 0,
539
+ binary: false,
540
+ };
541
+ const result = parser.parseFileBlock(diffLines.join('\n'), summary);
542
+ expect(result).toBeDefined();
543
+ expect(result?.path).toBe('a/test.txt');
544
+ expect(result?.status).toBe('added');
545
+ });
546
+ it('does not treat added files as renamed even if summary includes from path', () => {
547
+ const diffLines = [
548
+ 'diff --git a/test.js b/test.js',
549
+ 'new file mode 100644',
550
+ 'index 0000000..257cc56',
551
+ '--- /dev/null',
552
+ '+++ b/test.js',
553
+ '@@ -0,0 +1 @@',
554
+ '+console.log("test");',
555
+ ];
556
+ const summary = {
557
+ file: 'test.js',
558
+ from: 'c/test.js',
559
+ insertions: 1,
560
+ deletions: 0,
561
+ binary: false,
562
+ };
563
+ const result = parser.parseFileBlock(diffLines.join('\n'), summary);
564
+ expect(result).toBeDefined();
565
+ expect(result?.status).toBe('added');
566
+ expect(result?.oldPath).toBeUndefined();
567
+ });
568
+ it('parses file paths with octal escape sequences correctly', () => {
569
+ const diffLines = [
570
+ 'diff --git "a/file\\303\\244.txt" "b/file\\303\\244.txt"',
571
+ 'new file mode 100644',
572
+ 'index 0000000..257cc56',
573
+ '--- /dev/null',
574
+ '+++ "b/file\\303\\244.txt"',
575
+ '@@ -0,0 +1 @@',
576
+ '+content',
577
+ ];
578
+ const result = parser.parseFileBlock(diffLines.join('\n'), null);
579
+ expect(result).toBeDefined();
580
+ expect(result.path).toBe('fileä.txt');
581
+ expect(result.status).toBe('added');
582
+ });
583
+ it('parses file paths with mixed escape sequences correctly', () => {
584
+ const diffLines = [
585
+ 'diff --git "a/dir\\303\\251/file\\twith\\nmixed.txt" "b/dir\\303\\251/file\\twith\\nmixed.txt"',
586
+ 'new file mode 100644',
587
+ 'index 0000000..257cc56',
588
+ '--- /dev/null',
589
+ '+++ "b/dir\\303\\251/file\\twith\\nmixed.txt"',
590
+ '@@ -0,0 +1 @@',
591
+ '+test',
592
+ ];
593
+ const result = parser.parseFileBlock(diffLines.join('\n'), null);
594
+ expect(result).toBeDefined();
595
+ expect(result.path).toBe('diré/file\twith\nmixed.txt');
596
+ expect(result.status).toBe('added');
597
+ });
598
+ });
285
599
  describe('File status detection improvements', () => {
286
600
  it('prioritizes new file mode over other indicators', () => {
287
601
  const diffLines = [
@@ -292,8 +606,10 @@ describe('GitDiffParser', () => {
292
606
  '+++ b/test.txt',
293
607
  ];
294
608
  const summary = {
609
+ file: 'test.txt',
295
610
  insertions: 5,
296
611
  deletions: 0,
612
+ binary: false,
297
613
  };
298
614
  const result = parser.parseFileBlock(diffLines.join('\n'), summary);
299
615
  expect(result.status).toBe('added');
@@ -307,8 +623,10 @@ describe('GitDiffParser', () => {
307
623
  '+++ b/test.txt', // This might confuse simple parsers
308
624
  ];
309
625
  const summary = {
626
+ file: 'test.txt',
310
627
  insertions: 0,
311
628
  deletions: 5,
629
+ binary: false,
312
630
  };
313
631
  const result = parser.parseFileBlock(diffLines.join('\n'), summary);
314
632
  expect(result.status).toBe('deleted');
@@ -14,7 +14,8 @@ export async function startServer(options) {
14
14
  const fileWatcher = new FileWatcherService();
15
15
  let diffDataCache = null;
16
16
  let currentIgnoreWhitespace = options.ignoreWhitespace || false;
17
- const diffMode = options.mode || 'side-by-side';
17
+ const normalizeDiffMode = (mode) => mode === 'inline' ? 'inline' : 'side-by-side';
18
+ const diffMode = normalizeDiffMode(options.mode);
18
19
  app.use(express.json());
19
20
  app.use(express.text()); // For sendBeacon text/plain requests
20
21
  app.use((_req, res, next) => {
@@ -30,12 +30,13 @@ export interface DiffLine {
30
30
  export interface ParsedDiff {
31
31
  chunks: DiffChunk[];
32
32
  }
33
+ export type DiffViewMode = 'side-by-side' | 'inline';
33
34
  export interface DiffResponse {
34
35
  commit: string;
35
36
  files: DiffFile[];
36
37
  ignoreWhitespace?: boolean;
37
38
  isEmpty?: boolean;
38
- mode?: string;
39
+ mode?: DiffViewMode;
39
40
  baseCommitish?: string;
40
41
  targetCommitish?: string;
41
42
  clearComments?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "difit",
3
- "version": "2.2.1",
3
+ "version": "2.2.3",
4
4
  "description": "A lightweight command-line tool that spins up a local web server to display Git commit diffs in a GitHub-like Files changed view",
5
5
  "type": "module",
6
6
  "engines": {
@@ -46,7 +46,7 @@
46
46
  "dracula-prism": "^2.1.16",
47
47
  "express": "^5.1.0",
48
48
  "ink": "^6.0.1",
49
- "lucide-react": "^0.525.0",
49
+ "lucide-react": "^0.544.0",
50
50
  "open": "^10.1.2",
51
51
  "prism-react-renderer": "^2.4.1",
52
52
  "prism-themes": "^1.9.0",
@@ -84,6 +84,7 @@
84
84
  "globals": "^16.3.0",
85
85
  "happy-dom": "^18.0.1",
86
86
  "lefthook": "^1.11.14",
87
+ "mri": "^1.2.0",
87
88
  "playwright": "^1.54.1",
88
89
  "postcss": "^8.5.6",
89
90
  "prettier": "^3.6.2",
@@ -113,5 +114,5 @@
113
114
  "publishConfig": {
114
115
  "access": "public"
115
116
  },
116
- "packageManager": "pnpm@10.10.0"
117
+ "packageManager": "pnpm@10.18.1"
117
118
  }
@@ -1 +0,0 @@
1
- /*! tailwindcss v4.1.11 | MIT License | https://tailwindcss.com */@layer properties{@supports ((-webkit-hyphens:none) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial;--tw-ease:initial;--tw-content:"";--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-100:oklch(93.6% .032 17.717);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-600:oklch(68.1% .162 75.834);--color-green-100:oklch(96.2% .044 156.743);--color-green-500:oklch(72.3% .219 149.579);--color-green-600:oklch(62.7% .194 149.214);--color-blue-100:oklch(93.2% .032 255.585);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-slate-400:oklch(70.4% .04 256.788);--color-slate-500:oklch(55.4% .046 257.417);--color-slate-600:oklch(44.6% .043 257.281);--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-md:28rem;--container-4xl:56rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-base:1rem;--text-base--line-height: 1.5 ;--text-lg:1.125rem;--text-lg--line-height:calc(1.75/1.125);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--font-weight-medium:500;--font-weight-semibold:600;--radius-md:.375rem;--radius-lg:.5rem;--ease-out:cubic-bezier(0,0,.2,1);--ease-in-out:cubic-bezier(.4,0,.2,1);--animate-spin:spin 1s linear infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-github-bg-primary:#0d1117;--color-github-bg-secondary:#161b22;--color-github-bg-tertiary:#21262d;--color-github-border:#30363d;--color-github-text-primary:#f0f6fc;--color-github-text-secondary:#8b949e;--color-github-text-muted:#6e7681;--color-github-accent:#238636;--color-github-danger:#da3633;--color-github-warning:#d29922;--color-diff-addition-bg:#0d4429;--color-diff-addition-border:#1b7c3d;--color-diff-deletion-bg:#67060c;--color-diff-deletion-border:#da3633;--color-diff-neutral-bg:#21262d;--color-diff-selected-bg:#ae7c1426;--color-diff-selected-border:#ae7c1466;--color-comment-bg:#1c2128;--color-comment-border:#373e47;--color-comment-text:#e6edf3}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::-moz-placeholder{opacity:1}::placeholder{opacity:1}@supports (not (-webkit-appearance:-apple-pay-button)) or (contain-intrinsic-size:1px){::-moz-placeholder{color:currentColor}::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::-moz-placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing)*0)}.top-0{top:calc(var(--spacing)*0)}.top-1\/2{top:50%}.-right-2{right:calc(var(--spacing)*-2)}.right-0{right:calc(var(--spacing)*0)}.left-0{left:calc(var(--spacing)*0)}.left-3{left:calc(var(--spacing)*3)}.z-10{z-index:10}.z-50{z-index:50}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.m-0{margin:calc(var(--spacing)*0)}.m-2{margin:calc(var(--spacing)*2)}.mx-3{margin-inline:calc(var(--spacing)*3)}.mx-4{margin-inline:calc(var(--spacing)*4)}.mx-auto{margin-inline:auto}.mt-2{margin-top:calc(var(--spacing)*2)}.mt-4{margin-top:calc(var(--spacing)*4)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-4{margin-bottom:calc(var(--spacing)*4)}.mb-6{margin-bottom:calc(var(--spacing)*6)}.ml-auto{margin-left:auto}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.table{display:table}.h-2{height:calc(var(--spacing)*2)}.h-4{height:calc(var(--spacing)*4)}.h-7{height:calc(var(--spacing)*7)}.h-full{height:100%}.h-screen{height:100vh}.max-h-96{max-height:calc(var(--spacing)*96)}.max-h-\[80vh\]{max-height:80vh}.max-h-\[calc\(80vh-120px\)\]{max-height:calc(80vh - 120px)}.min-h-\[16px\]{min-height:16px}.min-h-\[20px\]{min-height:20px}.min-h-\[60px\]{min-height:60px}.w-1\/2{width:50%}.w-4{width:calc(var(--spacing)*4)}.w-5{width:calc(var(--spacing)*5)}.w-7{width:calc(var(--spacing)*7)}.w-8{width:calc(var(--spacing)*8)}.w-\[50px\]{width:50px}.w-\[60px\]{width:60px}.w-full{width:100%}.max-w-4xl{max-width:var(--container-4xl)}.max-w-full{max-width:100%}.max-w-md{max-width:var(--container-md)}.min-w-0{min-width:calc(var(--spacing)*0)}.flex-1{flex:1}.flex-shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.-translate-y-1\/2{--tw-translate-y: -50% ;translate:var(--tw-translate-x)var(--tw-translate-y)}.rotate-180{rotate:180deg}.transform{transform:var(--tw-rotate-x,)var(--tw-rotate-y,)var(--tw-rotate-z,)var(--tw-skew-x,)var(--tw-skew-y,)}.animate-spin{animation:var(--animate-spin)}.cursor-col-resize{cursor:col-resize}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.resize{resize:both}.resize-none{resize:none}.resize-y{resize:vertical}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:calc(var(--spacing)*1)}.gap-1\.5{gap:calc(var(--spacing)*1.5)}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}.gap-6{gap:calc(var(--spacing)*6)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*1)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*1)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*6)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*6)*calc(1 - var(--tw-space-y-reverse)))}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-l{border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.rounded-r{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.rounded-b{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-t-2{border-top-style:var(--tw-border-style);border-top-width:2px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-4{border-left-style:var(--tw-border-style);border-left-width:4px}.border-none{--tw-border-style:none;border-style:none}.border-github-accent{border-color:var(--color-github-accent)}.border-github-border{border-color:var(--color-github-border)}.border-github-text-muted{border-color:var(--color-github-text-muted)}.border-github-text-primary{border-color:var(--color-github-text-primary)}.border-yellow-600\/50{border-color:#cd890080}@supports (color:color-mix(in lab,red,red)){.border-yellow-600\/50{border-color:color-mix(in oklab,var(--color-yellow-600)50%,transparent)}}.border-t-github-accent{border-top-color:var(--color-github-accent)}.border-l-yellow-400{border-left-color:var(--color-yellow-400)}.bg-\[var\(--bg-secondary\)\]{background-color:var(--bg-secondary)}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab,red,red)){.bg-black\/50{background-color:color-mix(in oklab,var(--color-black)50%,transparent)}}.bg-diff-addition-bg{background-color:var(--color-diff-addition-bg)}.bg-diff-deletion-bg{background-color:var(--color-diff-deletion-bg)}.bg-github-accent{background-color:var(--color-github-accent)}.bg-github-bg-primary{background-color:var(--color-github-bg-primary)}.bg-github-bg-secondary{background-color:var(--color-github-bg-secondary)}.bg-github-bg-tertiary{background-color:var(--color-github-bg-tertiary)}.bg-github-border{background-color:var(--color-github-border)}.bg-github-text-primary{background-color:var(--color-github-text-primary)}.bg-green-100\/10{background-color:#dcfce71a}@supports (color:color-mix(in lab,red,red)){.bg-green-100\/10{background-color:color-mix(in oklab,var(--color-green-100)10%,transparent)}}.bg-red-100\/10{background-color:#ffe2e21a}@supports (color:color-mix(in lab,red,red)){.bg-red-100\/10{background-color:color-mix(in oklab,var(--color-red-100)10%,transparent)}}.bg-transparent{background-color:#0000}.p-0{padding:calc(var(--spacing)*0)}.p-1{padding:calc(var(--spacing)*1)}.p-1\.5{padding:calc(var(--spacing)*1.5)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.p-6{padding:calc(var(--spacing)*6)}.px-1{padding-inline:calc(var(--spacing)*1)}.px-1\.5{padding-inline:calc(var(--spacing)*1.5)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-3{padding-inline:calc(var(--spacing)*3)}.px-4{padding-inline:calc(var(--spacing)*4)}.px-5{padding-inline:calc(var(--spacing)*5)}.px-6{padding-inline:calc(var(--spacing)*6)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-1{padding-block:calc(var(--spacing)*1)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-2{padding-block:calc(var(--spacing)*2)}.py-3{padding-block:calc(var(--spacing)*3)}.py-4{padding-block:calc(var(--spacing)*4)}.pt-4{padding-top:calc(var(--spacing)*4)}.pr-2{padding-right:calc(var(--spacing)*2)}.pr-3{padding-right:calc(var(--spacing)*3)}.pr-5{padding-right:calc(var(--spacing)*5)}.pb-px{padding-bottom:1px}.pl-9{padding-left:calc(var(--spacing)*9)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.align-top{vertical-align:top}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.leading-5{--tw-leading:calc(var(--spacing)*5);line-height:calc(var(--spacing)*5)}.leading-6{--tw-leading:calc(var(--spacing)*6);line-height:calc(var(--spacing)*6)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.break-all{word-break:break-all}.text-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-github-accent{color:var(--color-github-accent)}.text-github-bg-primary{color:var(--color-github-bg-primary)}.text-github-danger{color:var(--color-github-danger)}.text-github-text-muted{color:var(--color-github-text-muted)}.text-github-text-primary{color:var(--color-github-text-primary)}.text-github-text-secondary{color:var(--color-github-text-secondary)}.text-github-warning{color:var(--color-github-warning)}.text-green-600{color:var(--color-green-600)}.text-white{color:var(--color-white)}.lowercase{text-transform:lowercase}.italic{font-style:italic}.line-through{text-decoration-line:line-through}.placeholder-github-text-muted::-moz-placeholder{color:var(--color-github-text-muted)}.placeholder-github-text-muted::placeholder{color:var(--color-github-text-muted)}.accent-github-accent{accent-color:var(--color-github-accent)}.opacity-70{opacity:.7}.shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a),0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-2{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.ring-blue-500{--tw-ring-color:var(--color-blue-500)}.filter{filter:var(--tw-blur,)var(--tw-brightness,)var(--tw-contrast,)var(--tw-grayscale,)var(--tw-hue-rotate,)var(--tw-invert,)var(--tw-saturate,)var(--tw-sepia,)var(--tw-drop-shadow,)}.\!transition-all{transition-property:all!important;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function))!important;transition-duration:var(--tw-duration,var(--default-transition-duration))!important}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.\!duration-300{--tw-duration:.3s!important;transition-duration:.3s!important}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}.\!ease-in-out{--tw-ease:var(--ease-in-out)!important;transition-timing-function:var(--ease-in-out)!important}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.select-text{-webkit-user-select:text;-moz-user-select:text;user-select:text}.after\:pointer-events-none:after{content:var(--tw-content);pointer-events:none}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:inset-0:after{content:var(--tw-content);inset:calc(var(--spacing)*0)}.after\:border-t-2:after{content:var(--tw-content);border-top-style:var(--tw-border-style);border-top-width:2px}.after\:border-b-2:after{content:var(--tw-content);border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.after\:border-l-4:after{content:var(--tw-content);border-left-style:var(--tw-border-style);border-left-width:4px}.after\:border-l-5:after{content:var(--tw-content);border-left-style:var(--tw-border-style);border-left-width:5px}.after\:border-blue-500:after{content:var(--tw-content);border-color:var(--color-blue-500)}.after\:border-l-diff-selected-border:after{content:var(--tw-content);border-left-color:var(--color-diff-selected-border)}.after\:bg-blue-100:after{content:var(--tw-content);background-color:var(--color-blue-100)}.after\:bg-diff-selected-bg:after{content:var(--tw-content);background-color:var(--color-diff-selected-bg)}.after\:opacity-30:after{content:var(--tw-content);opacity:.3}@media (hover:hover){.hover\:scale-110:hover{--tw-scale-x:110%;--tw-scale-y:110%;--tw-scale-z:110%;scale:var(--tw-scale-x)var(--tw-scale-y)}.hover\:border-github-accent\/50:hover{border-color:#23863680}@supports (color:color-mix(in lab,red,red)){.hover\:border-github-accent\/50:hover{border-color:color-mix(in oklab,var(--color-github-accent)50%,transparent)}}.hover\:border-github-text-muted:hover{border-color:var(--color-github-text-muted)}.hover\:border-green-600:hover{border-color:var(--color-green-600)}.hover\:bg-github-bg-primary:hover{background-color:var(--color-github-bg-primary)}.hover\:bg-github-bg-tertiary:hover{background-color:var(--color-github-bg-tertiary)}.hover\:bg-github-text-muted:hover{background-color:var(--color-github-text-muted)}.hover\:bg-green-500\/10:hover{background-color:#00c7581a}@supports (color:color-mix(in lab,red,red)){.hover\:bg-green-500\/10:hover{background-color:color-mix(in oklab,var(--color-green-500)10%,transparent)}}.hover\:bg-green-600:hover{background-color:var(--color-green-600)}.hover\:text-github-text-primary:hover{color:var(--color-github-text-primary)}.hover\:opacity-80:hover{opacity:.8}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px var(--tw-shadow-color,#0000001a),0 2px 4px -2px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}}.focus\:min-h-\[80px\]:focus{min-height:80px}.focus\:border-blue-600:focus{border-color:var(--color-blue-600)}.focus\:border-github-accent:focus{border-color:var(--color-github-accent)}.focus\:ring-1:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(1px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,)0 0 0 calc(2px + var(--tw-ring-offset-width))var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.focus\:ring-blue-600\/30:focus{--tw-ring-color:#155dfc4d}@supports (color:color-mix(in lab,red,red)){.focus\:ring-blue-600\/30:focus{--tw-ring-color:color-mix(in oklab,var(--color-blue-600)30%,transparent)}}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.disabled\:opacity-50:disabled{opacity:.5}@media (min-width:64rem){.lg\:col-span-2{grid-column:span 2/span 2}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (prefers-color-scheme:dark){.dark\:border-slate-500{border-color:var(--color-slate-500)}.dark\:bg-slate-600{background-color:var(--color-slate-600)}.dark\:text-white{color:var(--color-white)}@media (hover:hover){.dark\:hover\:border-slate-400:hover{border-color:var(--color-slate-400)}.dark\:hover\:bg-slate-500:hover{background-color:var(--color-slate-500)}}}.\[\&_code\]\:\!bg-transparent code{background-color:#0000!important}.\[\&_code\]\:text-inherit code{color:inherit}.\[\&_pre\]\:m-0 pre{margin:calc(var(--spacing)*0)}.\[\&_pre\]\:\!bg-transparent pre{background-color:#0000!important}.\[\&_pre\]\:p-0 pre{padding:calc(var(--spacing)*0)}.\[\&_pre\]\:text-inherit pre{color:inherit}}:root{--app-font-size:14px;--app-font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif}html,body{background-color:var(--color-github-bg-primary);color:var(--color-github-text-primary);font-family:var(--app-font-family);line-height:1.5;font-size:var(--app-font-size)}:root{interpolate-size:allow-keywords}button{cursor:pointer}@keyframes sparkle-rise{0%{opacity:0;transform:translateY(20px)scale(.5)}20%{opacity:1;transform:translateY(10px)scale(1)}80%{opacity:1;transform:translateY(-30px)scale(1)}to{opacity:0;transform:translateY(-40px)scale(.8)}}.animate-sparkle-rise{animation:.8s ease-out both sparkle-rise}html,body,.bg-github-bg-primary,.bg-github-bg-secondary,.bg-github-bg-tertiary,[class*=bg-github],[class*=text-github],[class*=border-github],[class*=bg-diff],[class*=border-diff]{transition:background-color .3s,color .3s,border-color .3s}tr.keyboard-cursor td{position:relative}tr.keyboard-cursor td:before{content:"";pointer-events:none;z-index:1;background-color:#fffd5433;position:absolute;inset:0}tr.keyboard-cursor td:first-child:before{box-shadow:inset 1px 0 #fffd5466,inset 0 1px #fffd5466,inset 0 -1px #fffd5466}tr.keyboard-cursor td:last-child:before{box-shadow:inset -1px 0 #fffd5466,inset 0 1px #fffd5466,inset 0 -1px #fffd5466}tr.keyboard-cursor td:not(:first-child):not(:last-child):before{box-shadow:inset 0 1px #fffd5466,inset 0 -1px #fffd5466}td.keyboard-cursor{position:relative}td.keyboard-cursor:before{content:"";pointer-events:none;z-index:1;background-color:#fffd5433;position:absolute;inset:0;box-shadow:inset 0 0 0 1px #fffd5466}.word-token{cursor:pointer;transition:background-color .15s;position:relative}.word-highlight{background-color:var(--word-highlight-color,#fffd54);border-radius:2px}[data-theme=dark]{--word-highlight-color:#fffd544d}[data-theme=light]{--word-highlight-color:#fffd54}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@property --tw-scale-x{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-y{syntax:"*";inherits:false;initial-value:1}@property --tw-scale-z{syntax:"*";inherits:false;initial-value:1}@keyframes spin{to{transform:rotate(360deg)}}
@@ -1,22 +0,0 @@
1
- import { z } from 'zod';
2
- export declare const LineNumberSchema: z.ZodUnion<readonly [z.ZodNumber, z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>]>;
3
- export declare const CommentSchema: z.ZodObject<{
4
- id: z.ZodString;
5
- file: z.ZodString;
6
- line: z.ZodUnion<readonly [z.ZodNumber, z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>]>;
7
- body: z.ZodString;
8
- timestamp: z.ZodString;
9
- codeContent: z.ZodOptional<z.ZodString>;
10
- }, z.core.$strip>;
11
- export declare const CommentsPayloadSchema: z.ZodObject<{
12
- comments: z.ZodOptional<z.ZodArray<z.ZodObject<{
13
- id: z.ZodString;
14
- file: z.ZodString;
15
- line: z.ZodUnion<readonly [z.ZodNumber, z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>]>;
16
- body: z.ZodString;
17
- timestamp: z.ZodString;
18
- codeContent: z.ZodOptional<z.ZodString>;
19
- }, z.core.$strip>>>;
20
- }, z.core.$strip>;
21
- export type ValidatedComment = z.infer<typeof CommentSchema>;
22
- export type ValidatedCommentsPayload = z.infer<typeof CommentsPayloadSchema>;
@@ -1,16 +0,0 @@
1
- import { z } from 'zod';
2
- // Schema for line number: either a single number or a tuple of two numbers
3
- export const LineNumberSchema = z.union([z.number(), z.tuple([z.number(), z.number()])]);
4
- // Schema for a single comment
5
- export const CommentSchema = z.object({
6
- id: z.string(),
7
- file: z.string(),
8
- line: LineNumberSchema,
9
- body: z.string(),
10
- timestamp: z.string(),
11
- codeContent: z.string().optional(),
12
- });
13
- // Schema for the comments payload sent from client
14
- export const CommentsPayloadSchema = z.object({
15
- comments: z.array(CommentSchema).optional(),
16
- });
@@ -1 +0,0 @@
1
- export {};