difit 2.0.8 → 2.0.10

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.
package/README.md CHANGED
@@ -111,7 +111,7 @@ difit supports syntax highlighting for multiple programming languages with dynam
111
111
  - **Web Technologies**: HTML, CSS, JSON, XML, Markdown
112
112
  - **Shell Scripts**: `.sh`, `.bash`, `.zsh`, `.fish` files
113
113
  - **Backend Languages**: PHP, SQL, Ruby, Java, Scala
114
- - **Systems Languages**: C, C++, Rust, Go
114
+ - **Systems Languages**: C, C++, C#, Rust, Go
115
115
  - **Mobile Languages**: Swift, Kotlin, Dart
116
116
  - **Others**: Python, YAML, Solidity, Vim script
117
117
 
package/dist/cli/index.js CHANGED
@@ -21,6 +21,7 @@ program
21
21
  .option('--mode <mode>', 'diff mode (side-by-side or inline)', 'side-by-side')
22
22
  .option('--tui', 'use terminal UI instead of web interface')
23
23
  .option('--pr <url>', 'GitHub PR URL to review (e.g., https://github.com/owner/repo/pull/123)')
24
+ .option('--clean', 'start with a clean slate by clearing all existing comments')
24
25
  .action(async (commitish, compareWith, options) => {
25
26
  try {
26
27
  // Determine target and base commitish
@@ -94,9 +95,13 @@ program
94
95
  host: options.host,
95
96
  openBrowser: options.open,
96
97
  mode: options.mode,
98
+ clearComments: options.clean,
97
99
  });
98
100
  console.log(`\n🚀 difit server started on ${url}`);
99
101
  console.log(`📋 Reviewing: ${targetCommitish}`);
102
+ if (options.clean) {
103
+ console.log('🧹 Starting with a clean slate - all existing comments will be cleared');
104
+ }
100
105
  if (isEmpty) {
101
106
  console.log('\n! \x1b[33mNo differences found. Browser will not open automatically.\x1b[0m');
102
107
  console.log(` Server is running at ${url} if you want to check manually.\n`);
@@ -177,6 +177,11 @@ describe('CLI index.ts', () => {
177
177
  args: ['--mode', 'inline'],
178
178
  expectedOptions: { mode: 'inline' },
179
179
  },
180
+ {
181
+ name: '--clean option',
182
+ args: ['--clean'],
183
+ expectedOptions: { clean: true },
184
+ },
180
185
  ])('$name', async ({ args, expectedOptions }) => {
181
186
  mockFindUntrackedFiles.mockResolvedValue([]);
182
187
  const program = new Command();
@@ -189,6 +194,7 @@ describe('CLI index.ts', () => {
189
194
  .option('--mode <mode>', 'mode', 'side-by-side')
190
195
  .option('--tui', 'tui')
191
196
  .option('--pr <url>', 'pr')
197
+ .option('--clean', 'start with a clean slate by clearing all existing comments')
192
198
  .action(async (commitish, _compareWith, options) => {
193
199
  let targetCommitish = commitish;
194
200
  let baseCommitish = commitish + '^';
@@ -199,6 +205,7 @@ describe('CLI index.ts', () => {
199
205
  host: options.host,
200
206
  openBrowser: options.open,
201
207
  mode: options.mode,
208
+ clearComments: options.clean,
202
209
  });
203
210
  });
204
211
  await program.parseAsync([...args], { from: 'user' });
@@ -209,6 +216,7 @@ describe('CLI index.ts', () => {
209
216
  host: expectedOptions.host || '',
210
217
  openBrowser: expectedOptions.open !== false,
211
218
  mode: expectedOptions.mode || 'side-by-side',
219
+ clearComments: expectedOptions.clean,
212
220
  };
213
221
  expect(mockStartServer).toHaveBeenCalledWith(expectedCall);
214
222
  });
@@ -356,6 +364,140 @@ describe('CLI index.ts', () => {
356
364
  expect(console.error).toHaveBeenCalledWith('Error: --pr option cannot be used with positional arguments');
357
365
  expect(process.exit).toHaveBeenCalledWith(1);
358
366
  });
367
+ it('resolves GitHub Enterprise PR commits correctly', async () => {
368
+ const prUrl = 'https://github.enterprise.com/owner/repo/pull/456';
369
+ const prCommits = {
370
+ targetCommitish: 'xyz789',
371
+ baseCommitish: 'uvw012',
372
+ };
373
+ mockResolvePrCommits.mockResolvedValue(prCommits);
374
+ const program = new Command();
375
+ program
376
+ .argument('[commit-ish]', 'commit-ish', 'HEAD')
377
+ .argument('[compare-with]', 'compare-with')
378
+ .option('--port <port>', 'port', parseInt)
379
+ .option('--host <host>', 'host', '')
380
+ .option('--no-open', 'no-open')
381
+ .option('--mode <mode>', 'mode', 'side-by-side')
382
+ .option('--tui', 'tui')
383
+ .option('--pr <url>', 'pr')
384
+ .action(async (commitish, _compareWith, options) => {
385
+ let targetCommitish = commitish;
386
+ let baseCommitish;
387
+ if (options.pr) {
388
+ if (commitish !== 'HEAD' || _compareWith) {
389
+ console.error('Error: --pr option cannot be used with positional arguments');
390
+ process.exit(1);
391
+ }
392
+ const prCommits = await resolvePrCommits(options.pr);
393
+ targetCommitish = prCommits.targetCommitish;
394
+ baseCommitish = prCommits.baseCommitish;
395
+ }
396
+ else {
397
+ baseCommitish = commitish + '^';
398
+ }
399
+ await startServer({
400
+ targetCommitish,
401
+ baseCommitish,
402
+ preferredPort: options.port,
403
+ host: options.host,
404
+ openBrowser: options.open,
405
+ mode: options.mode,
406
+ });
407
+ });
408
+ await program.parseAsync(['--pr', prUrl], { from: 'user' });
409
+ expect(mockResolvePrCommits).toHaveBeenCalledWith(prUrl);
410
+ expect(mockStartServer).toHaveBeenCalledWith({
411
+ targetCommitish: 'xyz789',
412
+ baseCommitish: 'uvw012',
413
+ preferredPort: undefined,
414
+ host: '',
415
+ openBrowser: true,
416
+ mode: 'side-by-side',
417
+ });
418
+ });
419
+ });
420
+ describe('Clean flag functionality', () => {
421
+ it('displays clean message when flag is used', async () => {
422
+ mockFindUntrackedFiles.mockResolvedValue([]);
423
+ mockStartServer.mockResolvedValue({
424
+ port: 3000,
425
+ url: 'http://localhost:3000',
426
+ isEmpty: false,
427
+ });
428
+ const program = new Command();
429
+ program
430
+ .argument('[commit-ish]', 'commit-ish', 'HEAD')
431
+ .argument('[compare-with]', 'compare-with')
432
+ .option('--port <port>', 'port', parseInt)
433
+ .option('--host <host>', 'host', '')
434
+ .option('--no-open', 'no-open')
435
+ .option('--mode <mode>', 'mode', 'side-by-side')
436
+ .option('--tui', 'tui')
437
+ .option('--pr <url>', 'pr')
438
+ .option('--clean', 'start with a clean slate by clearing all existing comments')
439
+ .action(async (commitish, _compareWith, options) => {
440
+ const { url } = await startServer({
441
+ targetCommitish: commitish,
442
+ baseCommitish: commitish + '^',
443
+ preferredPort: options.port,
444
+ host: options.host,
445
+ openBrowser: options.open,
446
+ mode: options.mode,
447
+ clearComments: options.clean,
448
+ });
449
+ console.log(`\n🚀 difit server started on ${url}`);
450
+ console.log(`📋 Reviewing: ${commitish}`);
451
+ if (options.clean) {
452
+ console.log('🧹 Starting with a clean slate - all existing comments will be cleared');
453
+ }
454
+ });
455
+ await program.parseAsync(['--clean'], { from: 'user' });
456
+ expect(mockStartServer).toHaveBeenCalledWith(expect.objectContaining({
457
+ clearComments: true,
458
+ }));
459
+ expect(console.log).toHaveBeenCalledWith('🧹 Starting with a clean slate - all existing comments will be cleared');
460
+ });
461
+ it('does not display clean message when flag is not used', async () => {
462
+ mockFindUntrackedFiles.mockResolvedValue([]);
463
+ mockStartServer.mockResolvedValue({
464
+ port: 3000,
465
+ url: 'http://localhost:3000',
466
+ isEmpty: false,
467
+ });
468
+ const program = new Command();
469
+ program
470
+ .argument('[commit-ish]', 'commit-ish', 'HEAD')
471
+ .argument('[compare-with]', 'compare-with')
472
+ .option('--port <port>', 'port', parseInt)
473
+ .option('--host <host>', 'host', '')
474
+ .option('--no-open', 'no-open')
475
+ .option('--mode <mode>', 'mode', 'side-by-side')
476
+ .option('--tui', 'tui')
477
+ .option('--pr <url>', 'pr')
478
+ .option('--clean', 'start with a clean slate by clearing all existing comments')
479
+ .action(async (commitish, _compareWith, options) => {
480
+ const { url } = await startServer({
481
+ targetCommitish: commitish,
482
+ baseCommitish: commitish + '^',
483
+ preferredPort: options.port,
484
+ host: options.host,
485
+ openBrowser: options.open,
486
+ mode: options.mode,
487
+ clearComments: options.clean,
488
+ });
489
+ console.log(`\n🚀 difit server started on ${url}`);
490
+ console.log(`📋 Reviewing: ${commitish}`);
491
+ if (options.clean) {
492
+ console.log('🧹 Starting with a clean slate - all existing comments will be cleared');
493
+ }
494
+ });
495
+ await program.parseAsync([], { from: 'user' });
496
+ expect(mockStartServer).toHaveBeenCalledWith(expect.objectContaining({
497
+ clearComments: undefined,
498
+ }));
499
+ expect(console.log).not.toHaveBeenCalledWith('🧹 Starting with a clean slate - all existing comments will be cleared');
500
+ });
359
501
  });
360
502
  describe('Console output', () => {
361
503
  it('displays server startup message with correct URL', async () => {
@@ -6,6 +6,7 @@ export interface PullRequestInfo {
6
6
  owner: string;
7
7
  repo: string;
8
8
  pullNumber: number;
9
+ hostname: string;
9
10
  }
10
11
  export interface PullRequestDetails {
11
12
  baseSha: string;
package/dist/cli/utils.js CHANGED
@@ -72,9 +72,8 @@ export function createCommitRangeString(baseHash, targetHash) {
72
72
  export function parseGitHubPrUrl(url) {
73
73
  try {
74
74
  const urlObj = new URL(url);
75
- if (urlObj.hostname !== 'github.com') {
76
- return null;
77
- }
75
+ // Allow any hostname for GitHub Enterprise support
76
+ // Just validate the path structure
78
77
  const pathParts = urlObj.pathname.split('/').filter(Boolean);
79
78
  if (pathParts.length < 4 || pathParts[2] !== 'pull') {
80
79
  return null;
@@ -85,7 +84,7 @@ export function parseGitHubPrUrl(url) {
85
84
  if (isNaN(pullNumber)) {
86
85
  return null;
87
86
  }
88
- return { owner, repo, pullNumber };
87
+ return { owner, repo, pullNumber, hostname: urlObj.hostname };
89
88
  }
90
89
  catch {
91
90
  return null;
@@ -108,9 +107,14 @@ function getGitHubToken() {
108
107
  }
109
108
  export async function fetchPrDetails(prInfo) {
110
109
  const token = getGitHubToken();
111
- const octokit = new Octokit({
110
+ const octokitOptions = {
112
111
  auth: token,
113
- });
112
+ };
113
+ // For GitHub Enterprise, set the base URL
114
+ if (prInfo.hostname !== 'github.com') {
115
+ octokitOptions.baseUrl = `https://${prInfo.hostname}/api/v3`;
116
+ }
117
+ const octokit = new Octokit(octokitOptions);
114
118
  try {
115
119
  const { data: pr } = await octokit.rest.pulls.get({
116
120
  owner: prInfo.owner,
@@ -165,7 +169,7 @@ export function resolveCommitInLocalRepo(sha, context) {
165
169
  export async function resolvePrCommits(prUrl) {
166
170
  const prInfo = parseGitHubPrUrl(prUrl);
167
171
  if (!prInfo) {
168
- throw new Error('Invalid GitHub PR URL format. Expected: https://github.com/owner/repo/pull/123');
172
+ throw new Error('Invalid GitHub PR URL format. Expected: https://github.com/owner/repo/pull/123 or https://github.enterprise.com/owner/repo/pull/123');
169
173
  }
170
174
  const prDetails = await fetchPrDetails(prInfo);
171
175
  const context = { owner: prInfo.owner, repo: prInfo.repo };
@@ -204,6 +204,7 @@ describe('CLI Utils', () => {
204
204
  owner: 'owner',
205
205
  repo: 'repo',
206
206
  pullNumber: 123,
207
+ hostname: 'github.com',
207
208
  });
208
209
  });
209
210
  it('should parse GitHub PR URLs with additional path segments', () => {
@@ -212,6 +213,7 @@ describe('CLI Utils', () => {
212
213
  owner: 'owner',
213
214
  repo: 'repo',
214
215
  pullNumber: 456,
216
+ hostname: 'github.com',
215
217
  });
216
218
  });
217
219
  it('should parse GitHub PR URLs with query parameters', () => {
@@ -220,6 +222,7 @@ describe('CLI Utils', () => {
220
222
  owner: 'owner',
221
223
  repo: 'repo',
222
224
  pullNumber: 789,
225
+ hostname: 'github.com',
223
226
  });
224
227
  });
225
228
  it('should handle URLs with hyphens and underscores in owner/repo names', () => {
@@ -228,11 +231,27 @@ describe('CLI Utils', () => {
228
231
  owner: 'owner-name',
229
232
  repo: 'repo_name',
230
233
  pullNumber: 123,
234
+ hostname: 'github.com',
235
+ });
236
+ });
237
+ it('should parse GitHub Enterprise PR URLs', () => {
238
+ const result1 = parseGitHubPrUrl('https://github.enterprise.com/owner/repo/pull/123');
239
+ expect(result1).toEqual({
240
+ owner: 'owner',
241
+ repo: 'repo',
242
+ pullNumber: 123,
243
+ hostname: 'github.enterprise.com',
244
+ });
245
+ const result2 = parseGitHubPrUrl('https://git.company.io/team/project/pull/456');
246
+ expect(result2).toEqual({
247
+ owner: 'team',
248
+ repo: 'project',
249
+ pullNumber: 456,
250
+ hostname: 'git.company.io',
231
251
  });
232
252
  });
233
253
  it('should return null for invalid URLs', () => {
234
254
  expect(parseGitHubPrUrl('not-a-url')).toBe(null);
235
- expect(parseGitHubPrUrl('https://example.com/owner/repo/pull/123')).toBe(null);
236
255
  expect(parseGitHubPrUrl('https://github.com/owner/repo/issues/123')).toBe(null);
237
256
  expect(parseGitHubPrUrl('https://github.com/owner/repo')).toBe(null);
238
257
  expect(parseGitHubPrUrl('https://github.com/owner/repo/pull/abc')).toBe(null);
@@ -0,0 +1 @@
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-white:#fff;--spacing:.25rem;--container-md:28rem;--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);--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)}.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}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.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)}.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-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,)}.cursor-col-resize{cursor:col-resize}.cursor-pointer{cursor:pointer}.resize{resize:both}.resize-none{resize:none}.resize-y{resize:vertical}.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)}: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-\[var\(--border-muted\)\]{border-color:var(--border-muted)}.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-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-blue-600{background-color:var(--color-blue-600)}.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-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)}.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)}.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)}.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-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-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)}.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}}.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 (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}@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}