difit 1.0.8 → 1.1.1
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 +56 -35
- package/dist/cli/index.js +44 -7
- package/dist/cli/utils.d.ts +5 -0
- package/dist/cli/utils.js +35 -2
- package/dist/cli/utils.test.js +1 -85
- package/dist/client/assets/index-BHrhjAlZ.css +1 -0
- package/dist/client/index.html +2 -2
- package/dist/server/git-diff-tui.js +3 -10
- package/dist/server/git-diff.d.ts +1 -1
- package/dist/server/git-diff.js +32 -15
- package/dist/server/server.d.ts +2 -1
- package/dist/server/server.js +4 -4
- package/dist/tui/App.d.ts +0 -1
- package/dist/tui/App.js +4 -4
- package/dist/tui/components/StatusBar.d.ts +1 -1
- package/dist/tui/components/StatusBar.js +1 -1
- package/dist/types/diff.d.ts +11 -1
- package/package.json +6 -5
- package/dist/client/assets/index-CpclbaYk.css +0 -1
- /package/dist/client/assets/{index-DpDAJan6.js → index-B67TY4_1.js} +0 -0
package/README.md
CHANGED
|
@@ -11,52 +11,71 @@ A lightweight command-line tool that spins up a local web server to display Git
|
|
|
11
11
|
- 🌙 **GitHub-like UI**: Familiar dark theme file list and diff interface
|
|
12
12
|
- 💬 **Inline Comments**: Add comments to specific lines and generate Claude Code prompts
|
|
13
13
|
- 🔄 **Side-by-Side & Inline Views**: Choose your preferred diff viewing mode
|
|
14
|
+
- 🖥️ **Terminal UI Mode**: View diffs directly in your terminal with `--tui` flag
|
|
14
15
|
- ⚡ **Zero Config**: Just run `npx reviewit <commit>` and it works
|
|
15
16
|
- 🔐 **Local Only**: Never exposes data over network - runs on localhost only
|
|
16
17
|
- 🛠️ **Modern Stack**: React 18 + TypeScript + Tailwind CSS
|
|
17
18
|
- 🎨 **Syntax Highlighting**: Dynamic language loading for Bash, PHP, SQL, Ruby, Java, and more
|
|
18
19
|
- ✨ **100% vibe coding**: Built with pure coding energy and good vibes
|
|
19
20
|
|
|
20
|
-
##
|
|
21
|
+
## ⚡ Quick Start
|
|
21
22
|
|
|
22
23
|
```bash
|
|
23
|
-
#
|
|
24
|
-
npm install -g reviewit
|
|
25
|
-
|
|
26
|
-
# Or use npx (no installation needed)
|
|
27
|
-
npx reviewit <commit-ish>
|
|
24
|
+
npx reviewit # View HEAD commit changes in a beautiful diff viewer
|
|
28
25
|
```
|
|
29
26
|
|
|
30
27
|
## 🚀 Usage
|
|
31
28
|
|
|
29
|
+
### Basic Usage
|
|
30
|
+
|
|
32
31
|
```bash
|
|
33
|
-
#
|
|
34
|
-
reviewit
|
|
32
|
+
npx reviewit <commit-ish> # View single commit diff
|
|
33
|
+
npx reviewit <commit-ish> [compare-with] # Compare two commits/branches
|
|
34
|
+
```
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
reviewit HEAD~3
|
|
36
|
+
### Single commit review
|
|
38
37
|
|
|
39
|
-
|
|
40
|
-
reviewit
|
|
38
|
+
```bash
|
|
39
|
+
npx reviewit 6f4a9b7 # Specific commit
|
|
40
|
+
npx reviewit HEAD^ # Previous commit
|
|
41
|
+
npx reviewit feature # Latest commit on branch
|
|
42
|
+
```
|
|
41
43
|
|
|
42
|
-
|
|
43
|
-
reviewit 6f4a9b7 --port 4300 --no-open
|
|
44
|
+
### Compare two commits
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
npx reviewit main
|
|
47
|
-
npx reviewit
|
|
46
|
+
```bash
|
|
47
|
+
npx reviewit HEAD main # Compare HEAD with main branch
|
|
48
|
+
npx reviewit feature main # Compare branches
|
|
49
|
+
npx reviewit . origin/main # Compare working directory with remote main
|
|
48
50
|
```
|
|
49
51
|
|
|
50
|
-
###
|
|
52
|
+
### Special Arguments
|
|
53
|
+
|
|
54
|
+
ReviewIt supports special keywords for common diff scenarios:
|
|
51
55
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
```bash
|
|
57
|
+
npx reviewit # HEAD commit changes
|
|
58
|
+
npx reviewit . # All uncommitted changes (staged + unstaged)
|
|
59
|
+
npx reviewit staged # Staged changes ready for commit
|
|
60
|
+
npx reviewit working # Unstaged changes only (cannot use compare-with)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
| Keyword | Description | Compare-with Support |
|
|
64
|
+
| --------- | ------------------------------------------------------ | -------------------- |
|
|
65
|
+
| `.` | Shows all uncommitted changes (both staged & unstaged) | ✅ Yes |
|
|
66
|
+
| `staged` | Shows staged changes ready to be committed | ✅ Yes |
|
|
67
|
+
| `working` | Shows unstaged changes in your working directory | ❌ No |
|
|
68
|
+
|
|
69
|
+
### ⚙️ CLI Options
|
|
58
70
|
|
|
59
|
-
|
|
71
|
+
| Flag | Default | Description |
|
|
72
|
+
| ---------------- | ------------ | ------------------------------------------------------------------- |
|
|
73
|
+
| `<commit-ish>` | HEAD | Any Git reference: hash, tag, HEAD~n, branch, or Special Arguments |
|
|
74
|
+
| `[compare-with]` | (optional) | Optional second commit to compare with (shows diff between the two) |
|
|
75
|
+
| `--port` | auto | Preferred port; falls back if occupied |
|
|
76
|
+
| `--no-open` | false | Don't automatically open browser |
|
|
77
|
+
| `--mode` | side-by-side | Diff mode: `inline` or `side-by-side` |
|
|
78
|
+
| `--tui` | false | Use terminal UI mode instead of web interface |
|
|
60
79
|
|
|
61
80
|
## 💬 Comment System
|
|
62
81
|
|
|
@@ -104,15 +123,10 @@ pnpm install
|
|
|
104
123
|
|
|
105
124
|
# Start development server (with hot reload)
|
|
106
125
|
# This runs both Vite dev server and CLI with NODE_ENV=development
|
|
107
|
-
pnpm run dev
|
|
108
|
-
pnpm run dev HEAD~3 # review HEAD~3
|
|
109
|
-
pnpm run dev main # review main branch
|
|
110
|
-
|
|
111
|
-
# For development CLI only (connects to separate Vite server)
|
|
112
|
-
pnpm run dev:cli <commit-ish>
|
|
126
|
+
pnpm run dev
|
|
113
127
|
|
|
114
128
|
# Build and start production server
|
|
115
|
-
pnpm run start
|
|
129
|
+
pnpm run start <commit-ish>
|
|
116
130
|
|
|
117
131
|
# Build for production
|
|
118
132
|
pnpm run build
|
|
@@ -129,20 +143,27 @@ pnpm run typecheck
|
|
|
129
143
|
### Development Workflow
|
|
130
144
|
|
|
131
145
|
- **`pnpm run dev`**: Starts both Vite dev server (with hot reload) and CLI server simultaneously
|
|
132
|
-
- **`pnpm run start <commit>`**: Builds everything and starts production server (for testing final build)
|
|
146
|
+
- **`pnpm run start <commit-ish>`**: Builds everything and starts production server (for testing final build)
|
|
133
147
|
- **Development mode**: Uses Vite's dev server for hot reload and fast development
|
|
134
148
|
- **Production mode**: Serves built static files (used by npx and production builds)
|
|
135
149
|
|
|
136
150
|
## 🏗️ Architecture
|
|
137
151
|
|
|
138
|
-
- **CLI**: Commander.js for argument parsing
|
|
152
|
+
- **CLI**: Commander.js for argument parsing with comprehensive validation
|
|
139
153
|
- **Backend**: Express server with simple-git for diff processing
|
|
140
154
|
- **Frontend**: React 18 + TypeScript + Vite
|
|
141
155
|
- **Styling**: Tailwind CSS v4 with GitHub-like dark theme
|
|
142
156
|
- **Syntax Highlighting**: Prism.js with dynamic language loading
|
|
143
|
-
- **Testing**: Vitest for unit tests
|
|
157
|
+
- **Testing**: Vitest for unit tests with co-located test files
|
|
144
158
|
- **Quality**: ESLint, Prettier, lefthook pre-commit hooks
|
|
145
159
|
|
|
160
|
+
### Key Components
|
|
161
|
+
|
|
162
|
+
- **Validation System**: Unified validation logic for CLI arguments with comprehensive error handling
|
|
163
|
+
- **Dual Parameter System**: Internal refactoring splits commitish into targetCommitish and baseCommitish for flexibility
|
|
164
|
+
- **Special Argument Support**: Working directory, staging area, and uncommitted changes detection
|
|
165
|
+
- **Hash Utilities**: Consistent short hash generation for commit display
|
|
166
|
+
|
|
146
167
|
## 📋 Requirements
|
|
147
168
|
|
|
148
169
|
- Node.js ≥ 18.0.0
|
package/dist/cli/index.js
CHANGED
|
@@ -1,31 +1,68 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
|
+
import React from 'react';
|
|
3
4
|
import pkg from '../../package.json' with { type: 'json' };
|
|
4
5
|
import { startServer } from '../server/server.js';
|
|
5
|
-
import {
|
|
6
|
+
import { validateDiffArguments } from './utils.js';
|
|
7
|
+
function isSpecialArg(arg) {
|
|
8
|
+
return arg === 'working' || arg === 'staged' || arg === '.';
|
|
9
|
+
}
|
|
6
10
|
const program = new Command();
|
|
7
11
|
program
|
|
8
12
|
.name('reviewit')
|
|
9
13
|
.description('A lightweight Git diff viewer with GitHub-like interface')
|
|
10
14
|
.version(pkg.version)
|
|
11
|
-
.argument('[commit-ish]', 'Git commit, tag, branch,
|
|
15
|
+
.argument('[commit-ish]', 'Git commit, tag, branch, HEAD~n reference, or "working"/"staged"/"." (default: HEAD)', 'HEAD')
|
|
16
|
+
.argument('[compare-with]', 'Optional: Compare with this commit/branch (shows diff between commit-ish and compare-with)')
|
|
12
17
|
.option('--port <port>', 'preferred port (auto-assigned if occupied)', parseInt)
|
|
13
18
|
.option('--no-open', 'do not automatically open browser')
|
|
14
19
|
.option('--mode <mode>', 'diff mode (inline only for now)', 'inline')
|
|
15
|
-
.
|
|
20
|
+
.option('--tui', 'use terminal UI instead of web interface')
|
|
21
|
+
.action(async (commitish, compareWith, options) => {
|
|
16
22
|
try {
|
|
17
|
-
|
|
18
|
-
|
|
23
|
+
// Determine target and base commitish
|
|
24
|
+
let targetCommitish = commitish;
|
|
25
|
+
let baseCommitish;
|
|
26
|
+
if (compareWith) {
|
|
27
|
+
// If compareWith is provided, use it as base
|
|
28
|
+
baseCommitish = compareWith;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
// Handle special arguments
|
|
32
|
+
if (isSpecialArg(commitish)) {
|
|
33
|
+
baseCommitish = 'HEAD';
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
baseCommitish = commitish + '^';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (options.tui) {
|
|
40
|
+
// Check if we're in a TTY environment
|
|
41
|
+
if (!process.stdin.isTTY) {
|
|
42
|
+
console.error('Error: TUI mode requires an interactive terminal (TTY).');
|
|
43
|
+
console.error('Try running the command directly in your terminal without piping.');
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
// Dynamic import for TUI mode
|
|
47
|
+
const { render } = await import('ink');
|
|
48
|
+
const { default: TuiApp } = await import('../tui/App.js');
|
|
49
|
+
render(React.createElement(TuiApp, { targetCommitish, baseCommitish }));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const validation = validateDiffArguments(targetCommitish, compareWith);
|
|
53
|
+
if (!validation.valid) {
|
|
54
|
+
console.error(`Error: ${validation.error}`);
|
|
19
55
|
process.exit(1);
|
|
20
56
|
}
|
|
21
57
|
const { url } = await startServer({
|
|
22
|
-
|
|
58
|
+
targetCommitish,
|
|
59
|
+
baseCommitish,
|
|
23
60
|
preferredPort: options.port,
|
|
24
61
|
openBrowser: options.open,
|
|
25
62
|
mode: options.mode,
|
|
26
63
|
});
|
|
27
64
|
console.log(`\n🚀 ReviewIt server started on ${url}`);
|
|
28
|
-
console.log(`📋 Reviewing: ${
|
|
65
|
+
console.log(`📋 Reviewing: ${targetCommitish}`);
|
|
29
66
|
if (options.open) {
|
|
30
67
|
console.log('🌐 Opening browser...\n');
|
|
31
68
|
}
|
package/dist/cli/utils.d.ts
CHANGED
package/dist/cli/utils.js
CHANGED
|
@@ -10,8 +10,8 @@ export function validateCommitish(commitish) {
|
|
|
10
10
|
if (trimmed === 'HEAD~') {
|
|
11
11
|
return false;
|
|
12
12
|
}
|
|
13
|
-
if (trimmed === '.') {
|
|
14
|
-
return true; // Allow
|
|
13
|
+
if (trimmed === '.' || trimmed === 'working' || trimmed === 'staged') {
|
|
14
|
+
return true; // Allow special keywords for working directory and staging area diff
|
|
15
15
|
}
|
|
16
16
|
const validPatterns = [
|
|
17
17
|
/^[a-f0-9]{4,40}$/i, // SHA hashes
|
|
@@ -20,3 +20,36 @@ export function validateCommitish(commitish) {
|
|
|
20
20
|
];
|
|
21
21
|
return validPatterns.some((pattern) => pattern.test(trimmed));
|
|
22
22
|
}
|
|
23
|
+
export function shortHash(hash) {
|
|
24
|
+
return hash.substring(0, 7);
|
|
25
|
+
}
|
|
26
|
+
export function validateDiffArguments(targetCommitish, baseCommitish) {
|
|
27
|
+
// Validate target commitish format
|
|
28
|
+
if (!validateCommitish(targetCommitish)) {
|
|
29
|
+
return { valid: false, error: 'Invalid target commit-ish format' };
|
|
30
|
+
}
|
|
31
|
+
// Validate base commitish format if provided
|
|
32
|
+
if (baseCommitish !== undefined && !validateCommitish(baseCommitish)) {
|
|
33
|
+
return { valid: false, error: 'Invalid base commit-ish format' };
|
|
34
|
+
}
|
|
35
|
+
// Special arguments are only allowed in target, not base
|
|
36
|
+
const specialArgs = ['working', 'staged', '.'];
|
|
37
|
+
if (baseCommitish && specialArgs.includes(baseCommitish)) {
|
|
38
|
+
return {
|
|
39
|
+
valid: false,
|
|
40
|
+
error: `Special arguments (working, staged, .) are only allowed as target, not base. Got base: ${baseCommitish}`,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// Cannot compare same values
|
|
44
|
+
if (targetCommitish === baseCommitish) {
|
|
45
|
+
return { valid: false, error: `Cannot compare ${targetCommitish} with itself` };
|
|
46
|
+
}
|
|
47
|
+
// "working" shows unstaged changes and cannot be compared with another commit
|
|
48
|
+
if (targetCommitish === 'working' && baseCommitish) {
|
|
49
|
+
return {
|
|
50
|
+
valid: false,
|
|
51
|
+
error: '"working" shows unstaged changes and cannot be compared with another commit. Use "." instead to compare all uncommitted changes with a specific commit.',
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return { valid: true };
|
|
55
|
+
}
|
package/dist/cli/utils.test.js
CHANGED
|
@@ -1,23 +1,11 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { validateCommitish, validateDiffArguments, shortHash
|
|
2
|
+
import { validateCommitish, validateDiffArguments, shortHash } from './utils';
|
|
3
3
|
describe('CLI Utils', () => {
|
|
4
4
|
describe('validateCommitish', () => {
|
|
5
5
|
it('should validate full SHA hashes', () => {
|
|
6
6
|
expect(validateCommitish('a1b2c3d4e5f6789012345678901234567890abcd')).toBe(true);
|
|
7
7
|
expect(validateCommitish('abc123')).toBe(true);
|
|
8
8
|
});
|
|
9
|
-
it('should validate SHA hashes with parent references', () => {
|
|
10
|
-
expect(validateCommitish('a1b2c3d4e5f6789012345678901234567890abcd^')).toBe(true);
|
|
11
|
-
expect(validateCommitish('abc123^')).toBe(true);
|
|
12
|
-
expect(validateCommitish('abc123^^')).toBe(true);
|
|
13
|
-
expect(validateCommitish('bd4b7513e075b5b245284c38fd23427b9bd0f42e^')).toBe(true);
|
|
14
|
-
});
|
|
15
|
-
it('should validate SHA hashes with ancestor references', () => {
|
|
16
|
-
expect(validateCommitish('a1b2c3d4e5f6789012345678901234567890abcd~1')).toBe(true);
|
|
17
|
-
expect(validateCommitish('abc123~5')).toBe(true);
|
|
18
|
-
expect(validateCommitish('abc123~10')).toBe(true);
|
|
19
|
-
expect(validateCommitish('bd4b7513e075b5b245284c38fd23427b9bd0f42e~2')).toBe(true);
|
|
20
|
-
});
|
|
21
9
|
it('should validate HEAD references', () => {
|
|
22
10
|
expect(validateCommitish('HEAD')).toBe(true);
|
|
23
11
|
expect(validateCommitish('HEAD~1')).toBe(true);
|
|
@@ -85,12 +73,6 @@ describe('CLI Utils', () => {
|
|
|
85
73
|
expect(validateDiffArguments('staged', 'HEAD')).toEqual({ valid: true });
|
|
86
74
|
expect(validateDiffArguments('.', 'main')).toEqual({ valid: true });
|
|
87
75
|
});
|
|
88
|
-
it('should allow staged as base only with working target', () => {
|
|
89
|
-
expect(validateDiffArguments('working', 'staged')).toEqual({ valid: true });
|
|
90
|
-
const result = validateDiffArguments('HEAD', 'staged');
|
|
91
|
-
expect(result.valid).toBe(false);
|
|
92
|
-
expect(result.error).toBe('Special arguments (working, staged, .) are only allowed as target, not base. Got base: staged');
|
|
93
|
-
});
|
|
94
76
|
});
|
|
95
77
|
describe('same value comparison', () => {
|
|
96
78
|
it('should reject same target and base values', () => {
|
|
@@ -115,17 +97,6 @@ describe('CLI Utils', () => {
|
|
|
115
97
|
it('should allow working without compareWith', () => {
|
|
116
98
|
expect(validateDiffArguments('working')).toEqual({ valid: true });
|
|
117
99
|
});
|
|
118
|
-
it('should allow working with staged', () => {
|
|
119
|
-
expect(validateDiffArguments('working', 'staged')).toEqual({ valid: true });
|
|
120
|
-
});
|
|
121
|
-
it('should reject working with other commits', () => {
|
|
122
|
-
const result1 = validateDiffArguments('working', 'main');
|
|
123
|
-
expect(result1.valid).toBe(false);
|
|
124
|
-
expect(result1.error).toBe('"working" shows unstaged changes and cannot be compared with another commit. Use "." instead to compare all uncommitted changes with a specific commit.');
|
|
125
|
-
const result2 = validateDiffArguments('working', 'abc123');
|
|
126
|
-
expect(result2.valid).toBe(false);
|
|
127
|
-
expect(result2.error).toBe('"working" shows unstaged changes and cannot be compared with another commit. Use "." instead to compare all uncommitted changes with a specific commit.');
|
|
128
|
-
});
|
|
129
100
|
it('should allow other special args with compareWith', () => {
|
|
130
101
|
expect(validateDiffArguments('staged', 'HEAD')).toEqual({ valid: true });
|
|
131
102
|
expect(validateDiffArguments('.', 'main')).toEqual({ valid: true });
|
|
@@ -143,14 +114,6 @@ describe('CLI Utils', () => {
|
|
|
143
114
|
valid: true,
|
|
144
115
|
});
|
|
145
116
|
});
|
|
146
|
-
it('should handle SHA hashes with parent/ancestor references', () => {
|
|
147
|
-
expect(validateDiffArguments('bd4b7513e075b5b245284c38fd23427b9bd0f42e^', 'abc123')).toEqual({ valid: true });
|
|
148
|
-
expect(validateDiffArguments('abc123', 'def456^')).toEqual({ valid: true });
|
|
149
|
-
expect(validateDiffArguments('abc123~1', 'def456~2')).toEqual({ valid: true });
|
|
150
|
-
expect(validateDiffArguments('a1b2c3d4e5f6789012345678901234567890abcd^', 'HEAD')).toEqual({
|
|
151
|
-
valid: true,
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
117
|
});
|
|
155
118
|
});
|
|
156
119
|
describe('shortHash', () => {
|
|
@@ -164,51 +127,4 @@ describe('CLI Utils', () => {
|
|
|
164
127
|
expect(shortHash('')).toBe('');
|
|
165
128
|
});
|
|
166
129
|
});
|
|
167
|
-
describe('parseGitHubPrUrl', () => {
|
|
168
|
-
it('should parse valid GitHub PR URLs', () => {
|
|
169
|
-
const result = parseGitHubPrUrl('https://github.com/owner/repo/pull/123');
|
|
170
|
-
expect(result).toEqual({
|
|
171
|
-
owner: 'owner',
|
|
172
|
-
repo: 'repo',
|
|
173
|
-
pullNumber: 123,
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
it('should parse GitHub PR URLs with additional path segments', () => {
|
|
177
|
-
const result = parseGitHubPrUrl('https://github.com/owner/repo/pull/456/files');
|
|
178
|
-
expect(result).toEqual({
|
|
179
|
-
owner: 'owner',
|
|
180
|
-
repo: 'repo',
|
|
181
|
-
pullNumber: 456,
|
|
182
|
-
});
|
|
183
|
-
});
|
|
184
|
-
it('should parse GitHub PR URLs with query parameters', () => {
|
|
185
|
-
const result = parseGitHubPrUrl('https://github.com/owner/repo/pull/789?tab=files');
|
|
186
|
-
expect(result).toEqual({
|
|
187
|
-
owner: 'owner',
|
|
188
|
-
repo: 'repo',
|
|
189
|
-
pullNumber: 789,
|
|
190
|
-
});
|
|
191
|
-
});
|
|
192
|
-
it('should handle URLs with hyphens and underscores in owner/repo names', () => {
|
|
193
|
-
const result = parseGitHubPrUrl('https://github.com/owner-name/repo_name/pull/123');
|
|
194
|
-
expect(result).toEqual({
|
|
195
|
-
owner: 'owner-name',
|
|
196
|
-
repo: 'repo_name',
|
|
197
|
-
pullNumber: 123,
|
|
198
|
-
});
|
|
199
|
-
});
|
|
200
|
-
it('should return null for invalid URLs', () => {
|
|
201
|
-
expect(parseGitHubPrUrl('not-a-url')).toBe(null);
|
|
202
|
-
expect(parseGitHubPrUrl('https://example.com/owner/repo/pull/123')).toBe(null);
|
|
203
|
-
expect(parseGitHubPrUrl('https://github.com/owner/repo/issues/123')).toBe(null);
|
|
204
|
-
expect(parseGitHubPrUrl('https://github.com/owner/repo')).toBe(null);
|
|
205
|
-
expect(parseGitHubPrUrl('https://github.com/owner/repo/pull/abc')).toBe(null);
|
|
206
|
-
});
|
|
207
|
-
it('should handle malformed URLs gracefully', () => {
|
|
208
|
-
expect(parseGitHubPrUrl('')).toBe(null);
|
|
209
|
-
expect(parseGitHubPrUrl('https://github.com')).toBe(null);
|
|
210
|
-
expect(parseGitHubPrUrl('https://github.com/owner')).toBe(null);
|
|
211
|
-
expect(parseGitHubPrUrl('https://github.com/owner/repo/pull')).toBe(null);
|
|
212
|
-
});
|
|
213
|
-
});
|
|
214
130
|
});
|
|
@@ -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-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}}}@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-red-200:oklch(88.5% .062 18.334);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-yellow-100:oklch(97.3% .071 103.193);--color-yellow-200:oklch(94.5% .129 101.54);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-500:oklch(79.5% .184 86.047);--color-yellow-600:oklch(68.1% .162 75.834);--color-yellow-700:oklch(55.4% .135 66.442);--color-yellow-800:oklch(47.6% .114 61.907);--color-yellow-900:oklch(42.1% .095 57.708);--color-green-100:oklch(96.2% .044 156.743);--color-green-200:oklch(92.5% .084 155.995);--color-green-500:oklch(72.3% .219 149.579);--color-green-600:oklch(62.7% .194 149.214);--color-green-700:oklch(52.7% .154 150.069);--color-blue-200:oklch(88.2% .059 254.128);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-white:#fff;--spacing:.25rem;--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;--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-deletion-bg:#67060c}}@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{.absolute{position:absolute}.relative{position:relative}.static{position:static}.sticky{position:sticky}.top-0{top:calc(var(--spacing)*0)}.top-1\/2{top:50%}.left-3{left:calc(var(--spacing)*3)}.z-10{z-index:10}.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)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-6{margin-bottom:calc(var(--spacing)*6)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.hidden{display:none}.inline{display:inline}.h-4{height:calc(var(--spacing)*4)}.h-full{height:100%}.h-screen{height:100vh}.min-h-\[20px\]{min-height:20px}.min-h-\[60px\]{min-height:60px}.w-1{width:calc(var(--spacing)*1)}.w-1\/2{width:50%}.w-4{width:calc(var(--spacing)*4)}.w-5{width:calc(var(--spacing)*5)}.w-\[50px\]{width:50px}.w-\[60px\]{width:60px}.w-full{width:100%}.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)}.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}.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}.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)}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.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-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-blue-600\/50{border-color:#155dfc80}@supports (color:color-mix(in lab,red,red)){.border-blue-600\/50{border-color:color-mix(in oklab,var(--color-blue-600)50%,transparent)}}.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-gray-500{border-color:var(--color-gray-500)}.border-gray-600\/50{border-color:#4a556580}@supports (color:color-mix(in lab,red,red)){.border-gray-600\/50{border-color:color-mix(in oklab,var(--color-gray-600)50%,transparent)}}.border-green-600\/50{border-color:#00a54480}@supports (color:color-mix(in lab,red,red)){.border-green-600\/50{border-color:color-mix(in oklab,var(--color-green-600)50%,transparent)}}.border-red-600\/50{border-color:#e4001480}@supports (color:color-mix(in lab,red,red)){.border-red-600\/50{border-color:color-mix(in oklab,var(--color-red-600)50%,transparent)}}.border-yellow-400\/30{border-color:#fac8004d}@supports (color:color-mix(in lab,red,red)){.border-yellow-400\/30{border-color:color-mix(in oklab,var(--color-yellow-400)30%,transparent)}}.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-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-blue-700\/40{background-color:#1447e666}@supports (color:color-mix(in lab,red,red)){.bg-blue-700\/40{background-color:color-mix(in oklab,var(--color-blue-700)40%,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-warning\/20{background-color:#d2992233}@supports (color:color-mix(in lab,red,red)){.bg-github-warning\/20{background-color:color-mix(in oklab,var(--color-github-warning)20%,transparent)}}.bg-gray-600{background-color:var(--color-gray-600)}.bg-gray-700\/40{background-color:#36415366}@supports (color:color-mix(in lab,red,red)){.bg-gray-700\/40{background-color:color-mix(in oklab,var(--color-gray-700)40%,transparent)}}.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-green-700\/40{background-color:#00813866}@supports (color:color-mix(in lab,red,red)){.bg-green-700\/40{background-color:color-mix(in oklab,var(--color-green-700)40%,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-red-700\/40{background-color:#bf000f66}@supports (color:color-mix(in lab,red,red)){.bg-red-700\/40{background-color:color-mix(in oklab,var(--color-red-700)40%,transparent)}}.bg-transparent{background-color:#0000}.bg-yellow-500\/10{background-color:#edb2001a}@supports (color:color-mix(in lab,red,red)){.bg-yellow-500\/10{background-color:color-mix(in oklab,var(--color-yellow-500)10%,transparent)}}.bg-yellow-700\/40{background-color:#a3610066}@supports (color:color-mix(in lab,red,red)){.bg-yellow-700\/40{background-color:color-mix(in oklab,var(--color-yellow-700)40%,transparent)}}.bg-yellow-800\/30{background-color:#874b004d}@supports (color:color-mix(in lab,red,red)){.bg-yellow-800\/30{background-color:color-mix(in oklab,var(--color-yellow-800)30%,transparent)}}.bg-yellow-900\/20{background-color:#733e0a33}@supports (color:color-mix(in lab,red,red)){.bg-yellow-900\/20{background-color:color-mix(in oklab,var(--color-yellow-900)20%,transparent)}}.p-0{padding:calc(var(--spacing)*0)}.p-1{padding:calc(var(--spacing)*1)}.p-1\.5{padding:calc(var(--spacing)*1.5)}.p-3{padding:calc(var(--spacing)*3)}.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-3{padding-right:calc(var(--spacing)*3)}.pl-9{padding-left:calc(var(--spacing)*9)}.text-center{text-align:center}.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-blue-200{color:var(--color-blue-200)}.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-gray-200{color:var(--color-gray-200)}.text-gray-300{color:var(--color-gray-300)}.text-gray-400{color:var(--color-gray-400)}.text-green-200{color:var(--color-green-200)}.text-red-200{color:var(--color-red-200)}.text-white{color:var(--color-white)}.text-yellow-100{color:var(--color-yellow-100)}.text-yellow-200{color:var(--color-yellow-200)}.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)}.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;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))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}@media (hover:hover){.hover\:border-blue-500:hover{border-color:var(--color-blue-500)}.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-gray-500:hover{border-color:var(--color-gray-500)}.hover\:border-green-500:hover{border-color:var(--color-green-500)}.hover\:border-red-500:hover{border-color:var(--color-red-500)}.hover\:border-yellow-500:hover{border-color:var(--color-yellow-500)}.hover\:bg-blue-600\/50:hover{background-color:#155dfc80}@supports (color:color-mix(in lab,red,red)){.hover\:bg-blue-600\/50:hover{background-color:color-mix(in oklab,var(--color-blue-600)50%,transparent)}}.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-gray-500:hover{background-color:var(--color-gray-500)}.hover\:bg-gray-600\/50:hover{background-color:#4a556580}@supports (color:color-mix(in lab,red,red)){.hover\:bg-gray-600\/50:hover{background-color:color-mix(in oklab,var(--color-gray-600)50%,transparent)}}.hover\:bg-green-600\/50:hover{background-color:#00a54480}@supports (color:color-mix(in lab,red,red)){.hover\:bg-green-600\/50:hover{background-color:color-mix(in oklab,var(--color-green-600)50%,transparent)}}.hover\:bg-red-600\/50:hover{background-color:#e4001480}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-600\/50:hover{background-color:color-mix(in oklab,var(--color-red-600)50%,transparent)}}.hover\:bg-yellow-600\/50:hover{background-color:#cd890080}@supports (color:color-mix(in lab,red,red)){.hover\:bg-yellow-600\/50:hover{background-color:color-mix(in oklab,var(--color-yellow-600)50%,transparent)}}.hover\:bg-yellow-800\/30:hover{background-color:#874b004d}@supports (color:color-mix(in lab,red,red)){.hover\:bg-yellow-800\/30:hover{background-color:color-mix(in oklab,var(--color-yellow-800)30%,transparent)}}.hover\:text-github-text-primary:hover{color:var(--color-github-text-primary)}.hover\:text-white:hover{color:var(--color-white)}.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\:border-yellow-500:focus{border-color:var(--color-yellow-500)}.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\:ring-yellow-500:focus{--tw-ring-color:var(--color-yellow-500)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.disabled\:opacity-50:disabled{opacity:.5}.\[\&_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}}html,body{color:#f0f6fc;background-color:#0d1117;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Noto Sans,Helvetica,Arial,sans-serif;font-size:14px;line-height:1.5}button{cursor:pointer}@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-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}
|
package/dist/client/index.html
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
<link rel="icon" type="image/png" sizes="512x512" href="/icon-512x512.png" />
|
|
11
11
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
12
12
|
<title>ReviewIt - Git Diff Viewer</title>
|
|
13
|
-
<script type="module" crossorigin src="/assets/index-
|
|
14
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
13
|
+
<script type="module" crossorigin src="/assets/index-B67TY4_1.js"></script>
|
|
14
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BHrhjAlZ.css">
|
|
15
15
|
</head>
|
|
16
16
|
<body>
|
|
17
17
|
<div id="root"></div>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import simpleGit from 'simple-git';
|
|
2
|
-
import { validateDiffArguments
|
|
2
|
+
import { validateDiffArguments } from '../cli/utils.js';
|
|
3
3
|
export async function loadGitDiff(targetCommitish, baseCommitish) {
|
|
4
4
|
// Validate arguments
|
|
5
5
|
const validation = validateDiffArguments(targetCommitish, baseCommitish);
|
|
@@ -23,10 +23,7 @@ export async function loadGitDiff(targetCommitish, baseCommitish) {
|
|
|
23
23
|
}
|
|
24
24
|
else {
|
|
25
25
|
// Both are regular commits: standard commit-to-commit comparison
|
|
26
|
-
diff = await git.diff([
|
|
27
|
-
createCommitRangeString(baseCommitish, targetCommitish),
|
|
28
|
-
'--name-status',
|
|
29
|
-
]);
|
|
26
|
+
diff = await git.diff([`${baseCommitish}..${targetCommitish}`, '--name-status']);
|
|
30
27
|
if (!diff.trim()) {
|
|
31
28
|
// Try without parent (for initial commit)
|
|
32
29
|
const diffInitial = await git.diff([targetCommitish, '--name-status']);
|
|
@@ -63,11 +60,7 @@ export async function loadGitDiff(targetCommitish, baseCommitish) {
|
|
|
63
60
|
else {
|
|
64
61
|
try {
|
|
65
62
|
// Both are regular commits: standard commit-to-commit comparison
|
|
66
|
-
fileDiff = await git.diff([
|
|
67
|
-
createCommitRangeString(baseCommitish, targetCommitish),
|
|
68
|
-
'--',
|
|
69
|
-
path,
|
|
70
|
-
]);
|
|
63
|
+
fileDiff = await git.diff([`${baseCommitish}..${targetCommitish}`, '--', path]);
|
|
71
64
|
}
|
|
72
65
|
catch {
|
|
73
66
|
// For new files or if parent doesn't exist
|
|
@@ -2,7 +2,7 @@ import { type DiffResponse } from '../types/diff.js';
|
|
|
2
2
|
export declare class GitDiffParser {
|
|
3
3
|
private git;
|
|
4
4
|
constructor(repoPath?: string);
|
|
5
|
-
parseDiff(
|
|
5
|
+
parseDiff(targetCommitish: string, baseCommitish: string, ignoreWhitespace?: boolean): Promise<DiffResponse>;
|
|
6
6
|
private parseUnifiedDiff;
|
|
7
7
|
private parseFileBlock;
|
|
8
8
|
private parseChunks;
|
package/dist/server/git-diff.js
CHANGED
|
@@ -1,25 +1,42 @@
|
|
|
1
1
|
import { simpleGit } from 'simple-git';
|
|
2
|
+
import { validateDiffArguments, shortHash } from '../cli/utils.js';
|
|
2
3
|
export class GitDiffParser {
|
|
3
4
|
constructor(repoPath = process.cwd()) {
|
|
4
5
|
this.git = simpleGit(repoPath);
|
|
5
6
|
}
|
|
6
|
-
async parseDiff(
|
|
7
|
+
async parseDiff(targetCommitish, baseCommitish, ignoreWhitespace = false) {
|
|
7
8
|
try {
|
|
9
|
+
// Validate arguments
|
|
10
|
+
const validation = validateDiffArguments(targetCommitish, baseCommitish);
|
|
11
|
+
if (!validation.valid) {
|
|
12
|
+
throw new Error(validation.error);
|
|
13
|
+
}
|
|
8
14
|
let resolvedCommit;
|
|
9
15
|
let diffArgs;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
16
|
+
// Handle target special chars (base is always a regular commit)
|
|
17
|
+
if (targetCommitish === 'working') {
|
|
18
|
+
// Show unstaged changes (working vs staged)
|
|
19
|
+
resolvedCommit = 'Working Directory (unstaged changes)';
|
|
20
|
+
diffArgs = [];
|
|
21
|
+
}
|
|
22
|
+
else if (targetCommitish === 'staged') {
|
|
23
|
+
// Show staged changes against base commit
|
|
24
|
+
const baseHash = await this.git.revparse([baseCommitish]);
|
|
25
|
+
resolvedCommit = `${shortHash(baseHash)} vs Staging Area (staged changes)`;
|
|
26
|
+
diffArgs = ['--cached', baseCommitish];
|
|
27
|
+
}
|
|
28
|
+
else if (targetCommitish === '.') {
|
|
29
|
+
// Show all uncommitted changes against base commit
|
|
30
|
+
const baseHash = await this.git.revparse([baseCommitish]);
|
|
31
|
+
resolvedCommit = `${shortHash(baseHash)} vs Working Directory (all uncommitted changes)`;
|
|
32
|
+
diffArgs = [baseCommitish];
|
|
14
33
|
}
|
|
15
34
|
else {
|
|
16
|
-
//
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
resolvedCommit = `${shortParentHash}..${shortHash}`;
|
|
22
|
-
diffArgs = [`${commitish}^`, commitish];
|
|
35
|
+
// Both are regular commits: standard commit-to-commit comparison
|
|
36
|
+
const targetHash = await this.git.revparse([targetCommitish]);
|
|
37
|
+
const baseHash = await this.git.revparse([baseCommitish]);
|
|
38
|
+
resolvedCommit = `${shortHash(baseHash)}..${shortHash(targetHash)}`;
|
|
39
|
+
diffArgs = [baseCommitish, targetCommitish];
|
|
23
40
|
}
|
|
24
41
|
if (ignoreWhitespace) {
|
|
25
42
|
diffArgs.push('-w');
|
|
@@ -34,7 +51,7 @@ export class GitDiffParser {
|
|
|
34
51
|
};
|
|
35
52
|
}
|
|
36
53
|
catch (error) {
|
|
37
|
-
throw new Error(`Failed to parse diff for ${
|
|
54
|
+
throw new Error(`Failed to parse diff for ${targetCommitish} vs ${baseCommitish}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
38
55
|
}
|
|
39
56
|
}
|
|
40
57
|
parseUnifiedDiff(diffText, summary) {
|
|
@@ -134,8 +151,8 @@ export class GitDiffParser {
|
|
|
134
151
|
}
|
|
135
152
|
async validateCommit(commitish) {
|
|
136
153
|
try {
|
|
137
|
-
if (commitish === '.') {
|
|
138
|
-
// For working directory, just check if we're in a git repo
|
|
154
|
+
if (commitish === '.' || commitish === 'working' || commitish === 'staged') {
|
|
155
|
+
// For working directory or staging area, just check if we're in a git repo
|
|
139
156
|
await this.git.status();
|
|
140
157
|
return true;
|
|
141
158
|
}
|
package/dist/server/server.d.ts
CHANGED
package/dist/server/server.js
CHANGED
|
@@ -17,16 +17,16 @@ export async function startServer(options) {
|
|
|
17
17
|
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
|
|
18
18
|
next();
|
|
19
19
|
});
|
|
20
|
-
const isValidCommit = await parser.validateCommit(options.
|
|
20
|
+
const isValidCommit = await parser.validateCommit(options.targetCommitish);
|
|
21
21
|
if (!isValidCommit) {
|
|
22
|
-
throw new Error(`Invalid or non-existent commit: ${options.
|
|
22
|
+
throw new Error(`Invalid or non-existent commit: ${options.targetCommitish}`);
|
|
23
23
|
}
|
|
24
|
-
diffData = await parser.parseDiff(options.
|
|
24
|
+
diffData = await parser.parseDiff(options.targetCommitish, options.baseCommitish, currentIgnoreWhitespace);
|
|
25
25
|
app.get('/api/diff', async (req, res) => {
|
|
26
26
|
const ignoreWhitespace = req.query.ignoreWhitespace === 'true';
|
|
27
27
|
if (ignoreWhitespace !== currentIgnoreWhitespace) {
|
|
28
28
|
currentIgnoreWhitespace = ignoreWhitespace;
|
|
29
|
-
diffData = await parser.parseDiff(options.
|
|
29
|
+
diffData = await parser.parseDiff(options.targetCommitish, options.baseCommitish, ignoreWhitespace);
|
|
30
30
|
}
|
|
31
31
|
res.json({ ...diffData, ignoreWhitespace });
|
|
32
32
|
});
|
package/dist/tui/App.d.ts
CHANGED
package/dist/tui/App.js
CHANGED
|
@@ -5,12 +5,12 @@ import FileList from './components/FileList.js';
|
|
|
5
5
|
import DiffViewer from './components/DiffViewer.js';
|
|
6
6
|
import SideBySideDiffViewer from './components/SideBySideDiffViewer.js';
|
|
7
7
|
import StatusBar from './components/StatusBar.js';
|
|
8
|
-
const App = ({ targetCommitish, baseCommitish
|
|
8
|
+
const App = ({ targetCommitish, baseCommitish }) => {
|
|
9
9
|
const [files, setFiles] = useState([]);
|
|
10
10
|
const [selectedFileIndex, setSelectedFileIndex] = useState(0);
|
|
11
11
|
const [loading, setLoading] = useState(true);
|
|
12
12
|
const [error, setError] = useState(null);
|
|
13
|
-
const [viewMode, setViewMode] = useState(
|
|
13
|
+
const [viewMode, setViewMode] = useState('side-by-side');
|
|
14
14
|
const { exit } = useApp();
|
|
15
15
|
const loadDiff = async () => {
|
|
16
16
|
setLoading(true);
|
|
@@ -49,7 +49,7 @@ const App = ({ targetCommitish, baseCommitish, mode }) => {
|
|
|
49
49
|
setViewMode('side-by-side');
|
|
50
50
|
}
|
|
51
51
|
if (input === 'd') {
|
|
52
|
-
setViewMode('
|
|
52
|
+
setViewMode('diff');
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
else {
|
|
@@ -84,7 +84,7 @@ const App = ({ targetCommitish, baseCommitish, mode }) => {
|
|
|
84
84
|
React.createElement(Box, { flexGrow: 1, flexDirection: "column" }, viewMode === 'list' ? (React.createElement(FileList, { files: files, selectedIndex: selectedFileIndex })) : viewMode === 'side-by-side' ? (React.createElement(SideBySideDiffViewer, { files: files, initialFileIndex: selectedFileIndex, onBack: () => setViewMode('list') })) : (React.createElement(DiffViewer, { files: files, initialFileIndex: selectedFileIndex, onBack: () => setViewMode('list') }))),
|
|
85
85
|
React.createElement(Box, { borderStyle: "single", paddingX: 1 },
|
|
86
86
|
React.createElement(Text, { dimColor: true }, viewMode === 'list'
|
|
87
|
-
? '↑/↓ or j/k: navigate | Enter/Space: side-by-side | d:
|
|
87
|
+
? '↑/↓ or j/k: navigate | Enter/Space: side-by-side | d: unified diff | r: reload | q: quit'
|
|
88
88
|
: viewMode === 'side-by-side'
|
|
89
89
|
? 'Tab: next file | Shift+Tab: prev | ↑/↓ or j/k: scroll | ESC/b: list | r: reload | q: quit'
|
|
90
90
|
: 'Tab: next | Shift+Tab: prev | ↑/↓ or j/k: scroll | ESC/b: list | r: reload | q: quit'))));
|
|
@@ -2,7 +2,7 @@ import React from 'react';
|
|
|
2
2
|
interface StatusBarProps {
|
|
3
3
|
commitish: string;
|
|
4
4
|
totalFiles: number;
|
|
5
|
-
currentMode: 'list' | '
|
|
5
|
+
currentMode: 'list' | 'diff' | 'side-by-side';
|
|
6
6
|
}
|
|
7
7
|
declare const StatusBar: React.FC<StatusBarProps>;
|
|
8
8
|
export default StatusBar;
|
package/dist/types/diff.d.ts
CHANGED
|
@@ -6,6 +6,13 @@ export interface DiffFile {
|
|
|
6
6
|
deletions: number;
|
|
7
7
|
chunks: DiffChunk[];
|
|
8
8
|
}
|
|
9
|
+
export interface FileDiff {
|
|
10
|
+
path: string;
|
|
11
|
+
status: 'A' | 'M' | 'D';
|
|
12
|
+
diff: string;
|
|
13
|
+
additions: number;
|
|
14
|
+
deletions: number;
|
|
15
|
+
}
|
|
9
16
|
export interface DiffChunk {
|
|
10
17
|
header: string;
|
|
11
18
|
oldStart: number;
|
|
@@ -15,11 +22,14 @@ export interface DiffChunk {
|
|
|
15
22
|
lines: DiffLine[];
|
|
16
23
|
}
|
|
17
24
|
export interface DiffLine {
|
|
18
|
-
type: 'add' | 'delete' | 'normal' | 'hunk';
|
|
25
|
+
type: 'add' | 'delete' | 'normal' | 'hunk' | 'remove' | 'context' | 'header';
|
|
19
26
|
content: string;
|
|
20
27
|
oldLineNumber?: number;
|
|
21
28
|
newLineNumber?: number;
|
|
22
29
|
}
|
|
30
|
+
export interface ParsedDiff {
|
|
31
|
+
chunks: DiffChunk[];
|
|
32
|
+
}
|
|
23
33
|
export interface DiffResponse {
|
|
24
34
|
commit: string;
|
|
25
35
|
files: DiffFile[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "difit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
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": {
|
|
@@ -21,21 +21,22 @@
|
|
|
21
21
|
"scripts": {
|
|
22
22
|
"dev": "node scripts/dev.js",
|
|
23
23
|
"dev:cli": "tsc --project tsconfig.cli.json && NODE_ENV=development node dist/cli/index.js",
|
|
24
|
-
"build": "tsc && vite build",
|
|
24
|
+
"build": "tsc -b && vite build",
|
|
25
25
|
"build:cli": "tsc --project tsconfig.cli.json",
|
|
26
|
-
"start": "pnpm run build &&
|
|
26
|
+
"start": "pnpm run build && node dist/cli/index.js",
|
|
27
27
|
"lint": "eslint .",
|
|
28
28
|
"lint:fix": "eslint . --fix",
|
|
29
29
|
"format": "prettier --write .",
|
|
30
|
-
"typecheck": "tsc
|
|
30
|
+
"typecheck": "tsc -b",
|
|
31
31
|
"test": "vitest run",
|
|
32
32
|
"test:watch": "vitest",
|
|
33
33
|
"prepare": "lefthook install",
|
|
34
|
-
"prepublishOnly": "NODE_ENV=production pnpm run build
|
|
34
|
+
"prepublishOnly": "NODE_ENV=production pnpm run build"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"commander": "^11.1.0",
|
|
38
38
|
"express": "^4.18.2",
|
|
39
|
+
"ink": "^5.2.1",
|
|
39
40
|
"lucide-react": "^0.525.0",
|
|
40
41
|
"open": "^10.0.3",
|
|
41
42
|
"prism-react-renderer": "^2.4.1",
|
|
@@ -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-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}}}@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-red-200:oklch(88.5% .062 18.334);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-700:oklch(50.5% .213 27.518);--color-yellow-100:oklch(97.3% .071 103.193);--color-yellow-200:oklch(94.5% .129 101.54);--color-yellow-400:oklch(85.2% .199 91.936);--color-yellow-500:oklch(79.5% .184 86.047);--color-yellow-600:oklch(68.1% .162 75.834);--color-yellow-700:oklch(55.4% .135 66.442);--color-yellow-800:oklch(47.6% .114 61.907);--color-yellow-900:oklch(42.1% .095 57.708);--color-green-100:oklch(96.2% .044 156.743);--color-green-200:oklch(92.5% .084 155.995);--color-green-500:oklch(72.3% .219 149.579);--color-green-600:oklch(62.7% .194 149.214);--color-green-700:oklch(52.7% .154 150.069);--color-blue-200:oklch(88.2% .059 254.128);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-blue-700:oklch(48.8% .243 264.376);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-300:oklch(87.2% .01 258.338);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-white:#fff;--spacing:.25rem;--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;--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-deletion-bg:#67060c}}@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{.absolute{position:absolute}.relative{position:relative}.static{position:static}.sticky{position:sticky}.top-0{top:calc(var(--spacing)*0)}.top-1\/2{top:50%}.left-3{left:calc(var(--spacing)*3)}.z-10{z-index:10}.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)}.mb-2{margin-bottom:calc(var(--spacing)*2)}.mb-3{margin-bottom:calc(var(--spacing)*3)}.mb-6{margin-bottom:calc(var(--spacing)*6)}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.inline{display:inline}.h-4{height:calc(var(--spacing)*4)}.h-full{height:100%}.h-screen{height:100vh}.min-h-\[20px\]{min-height:20px}.min-h-\[60px\]{min-height:60px}.w-1{width:calc(var(--spacing)*1)}.w-1\/2{width:50%}.w-4{width:calc(var(--spacing)*4)}.w-5{width:calc(var(--spacing)*5)}.w-\[50px\]{width:50px}.w-\[60px\]{width:60px}.w-full{width:100%}.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)}.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}.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}.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)}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.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-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-blue-600\/50{border-color:#155dfc80}@supports (color:color-mix(in lab,red,red)){.border-blue-600\/50{border-color:color-mix(in oklab,var(--color-blue-600)50%,transparent)}}.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-gray-500{border-color:var(--color-gray-500)}.border-gray-600\/50{border-color:#4a556580}@supports (color:color-mix(in lab,red,red)){.border-gray-600\/50{border-color:color-mix(in oklab,var(--color-gray-600)50%,transparent)}}.border-green-600\/50{border-color:#00a54480}@supports (color:color-mix(in lab,red,red)){.border-green-600\/50{border-color:color-mix(in oklab,var(--color-green-600)50%,transparent)}}.border-red-600\/50{border-color:#e4001480}@supports (color:color-mix(in lab,red,red)){.border-red-600\/50{border-color:color-mix(in oklab,var(--color-red-600)50%,transparent)}}.border-yellow-400\/30{border-color:#fac8004d}@supports (color:color-mix(in lab,red,red)){.border-yellow-400\/30{border-color:color-mix(in oklab,var(--color-yellow-400)30%,transparent)}}.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-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-blue-700\/40{background-color:#1447e666}@supports (color:color-mix(in lab,red,red)){.bg-blue-700\/40{background-color:color-mix(in oklab,var(--color-blue-700)40%,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-warning\/20{background-color:#d2992233}@supports (color:color-mix(in lab,red,red)){.bg-github-warning\/20{background-color:color-mix(in oklab,var(--color-github-warning)20%,transparent)}}.bg-gray-600{background-color:var(--color-gray-600)}.bg-gray-700\/40{background-color:#36415366}@supports (color:color-mix(in lab,red,red)){.bg-gray-700\/40{background-color:color-mix(in oklab,var(--color-gray-700)40%,transparent)}}.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-green-700\/40{background-color:#00813866}@supports (color:color-mix(in lab,red,red)){.bg-green-700\/40{background-color:color-mix(in oklab,var(--color-green-700)40%,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-red-700\/40{background-color:#bf000f66}@supports (color:color-mix(in lab,red,red)){.bg-red-700\/40{background-color:color-mix(in oklab,var(--color-red-700)40%,transparent)}}.bg-transparent{background-color:#0000}.bg-yellow-500\/10{background-color:#edb2001a}@supports (color:color-mix(in lab,red,red)){.bg-yellow-500\/10{background-color:color-mix(in oklab,var(--color-yellow-500)10%,transparent)}}.bg-yellow-700\/40{background-color:#a3610066}@supports (color:color-mix(in lab,red,red)){.bg-yellow-700\/40{background-color:color-mix(in oklab,var(--color-yellow-700)40%,transparent)}}.bg-yellow-800\/30{background-color:#874b004d}@supports (color:color-mix(in lab,red,red)){.bg-yellow-800\/30{background-color:color-mix(in oklab,var(--color-yellow-800)30%,transparent)}}.bg-yellow-900\/20{background-color:#733e0a33}@supports (color:color-mix(in lab,red,red)){.bg-yellow-900\/20{background-color:color-mix(in oklab,var(--color-yellow-900)20%,transparent)}}.p-0{padding:calc(var(--spacing)*0)}.p-1{padding:calc(var(--spacing)*1)}.p-1\.5{padding:calc(var(--spacing)*1.5)}.p-3{padding:calc(var(--spacing)*3)}.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-3{padding-right:calc(var(--spacing)*3)}.pl-9{padding-left:calc(var(--spacing)*9)}.text-center{text-align:center}.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-blue-200{color:var(--color-blue-200)}.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-gray-200{color:var(--color-gray-200)}.text-gray-300{color:var(--color-gray-300)}.text-gray-400{color:var(--color-gray-400)}.text-green-200{color:var(--color-green-200)}.text-red-200{color:var(--color-red-200)}.text-white{color:var(--color-white)}.text-yellow-100{color:var(--color-yellow-100)}.text-yellow-200{color:var(--color-yellow-200)}.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)}.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;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))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}@media (hover:hover){.hover\:border-blue-500:hover{border-color:var(--color-blue-500)}.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-gray-500:hover{border-color:var(--color-gray-500)}.hover\:border-green-500:hover{border-color:var(--color-green-500)}.hover\:border-red-500:hover{border-color:var(--color-red-500)}.hover\:border-yellow-500:hover{border-color:var(--color-yellow-500)}.hover\:bg-blue-600\/50:hover{background-color:#155dfc80}@supports (color:color-mix(in lab,red,red)){.hover\:bg-blue-600\/50:hover{background-color:color-mix(in oklab,var(--color-blue-600)50%,transparent)}}.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-gray-500:hover{background-color:var(--color-gray-500)}.hover\:bg-gray-600\/50:hover{background-color:#4a556580}@supports (color:color-mix(in lab,red,red)){.hover\:bg-gray-600\/50:hover{background-color:color-mix(in oklab,var(--color-gray-600)50%,transparent)}}.hover\:bg-green-600\/50:hover{background-color:#00a54480}@supports (color:color-mix(in lab,red,red)){.hover\:bg-green-600\/50:hover{background-color:color-mix(in oklab,var(--color-green-600)50%,transparent)}}.hover\:bg-red-600\/50:hover{background-color:#e4001480}@supports (color:color-mix(in lab,red,red)){.hover\:bg-red-600\/50:hover{background-color:color-mix(in oklab,var(--color-red-600)50%,transparent)}}.hover\:bg-yellow-600\/50:hover{background-color:#cd890080}@supports (color:color-mix(in lab,red,red)){.hover\:bg-yellow-600\/50:hover{background-color:color-mix(in oklab,var(--color-yellow-600)50%,transparent)}}.hover\:bg-yellow-800\/30:hover{background-color:#874b004d}@supports (color:color-mix(in lab,red,red)){.hover\:bg-yellow-800\/30:hover{background-color:color-mix(in oklab,var(--color-yellow-800)30%,transparent)}}.hover\:text-github-text-primary:hover{color:var(--color-github-text-primary)}.hover\:text-white:hover{color:var(--color-white)}.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\:border-yellow-500:focus{border-color:var(--color-yellow-500)}.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\:ring-yellow-500:focus{--tw-ring-color:var(--color-yellow-500)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.disabled\:opacity-50:disabled{opacity:.5}.\[\&_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}}html,body{color:#f0f6fc;background-color:#0d1117;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Noto Sans,Helvetica,Arial,sans-serif;font-size:14px;line-height:1.5}button{cursor:pointer}@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-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}
|
|
File without changes
|