git-trace 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.tracerc.example +38 -0
- package/README.md +136 -0
- package/bun.lock +511 -0
- package/bunchee.config.ts +11 -0
- package/cli/index.ts +251 -0
- package/cli/parser.ts +76 -0
- package/cli/tsconfig.json +6 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +858 -0
- package/dist/config.cjs +66 -0
- package/dist/config.d.ts +15 -0
- package/dist/config.js +63 -0
- package/dist/highlight/index.cjs +770 -0
- package/dist/highlight/index.d.ts +26 -0
- package/dist/highlight/index.js +766 -0
- package/dist/index.cjs +849 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.js +845 -0
- package/examples/demo/App.tsx +78 -0
- package/examples/demo/index.html +12 -0
- package/examples/demo/main.tsx +10 -0
- package/examples/demo/mockData.ts +170 -0
- package/examples/demo/styles.css +103 -0
- package/examples/demo/tsconfig.json +21 -0
- package/examples/demo/tsconfig.node.json +10 -0
- package/examples/demo/vite.config.ts +20 -0
- package/package.json +58 -0
- package/src/Trace.tsx +717 -0
- package/src/cache.ts +118 -0
- package/src/config.ts +51 -0
- package/src/entries/config.ts +7 -0
- package/src/entries/gitea.ts +4 -0
- package/src/entries/github.ts +5 -0
- package/src/entries/gitlab.ts +4 -0
- package/src/gitea.ts +58 -0
- package/src/github.ts +100 -0
- package/src/gitlab.ts +65 -0
- package/src/highlight/highlight.ts +119 -0
- package/src/highlight/index.ts +4 -0
- package/src/host.ts +32 -0
- package/src/index.ts +6 -0
- package/src/patterns.ts +6 -0
- package/src/shared.ts +108 -0
- package/src/themes.ts +98 -0
- package/src/types.ts +72 -0
- package/test/e2e.html +424 -0
- package/tsconfig.json +18 -0
- package/vercel.json +4 -0
package/src/shared.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// Shared utilities for git adapters — parse diffs, detect AI authors, format dates
|
|
2
|
+
|
|
3
|
+
import { DEFAULT_PATTERNS } from './patterns'
|
|
4
|
+
import type { Commit } from './types'
|
|
5
|
+
|
|
6
|
+
// Parse unified diff format into lines
|
|
7
|
+
export function parseDiff(diffText: string): Commit['lines'] {
|
|
8
|
+
const lines: Commit['lines'] = []
|
|
9
|
+
|
|
10
|
+
for (const line of diffText.split('\n')) {
|
|
11
|
+
// Skip diff headers
|
|
12
|
+
if (line.startsWith('@@')) continue
|
|
13
|
+
if (line.startsWith('+++') || line.startsWith('---')) continue
|
|
14
|
+
if (line.startsWith('index') || line.startsWith('diff')) continue
|
|
15
|
+
if (line.startsWith('new file') || line.startsWith('deleted file')) continue
|
|
16
|
+
|
|
17
|
+
if (line.startsWith('+')) {
|
|
18
|
+
lines.push({ type: 'add', content: line.slice(1) })
|
|
19
|
+
} else if (line.startsWith('-')) {
|
|
20
|
+
lines.push({ type: 'remove', content: line.slice(1) })
|
|
21
|
+
} else {
|
|
22
|
+
lines.push({ type: 'ctx', content: line.slice(1) || '' })
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return lines
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Compile string arrays to lowercase Sets for fast O(1) lookup
|
|
30
|
+
function compilePatterns(patterns: typeof DEFAULT_PATTERNS) {
|
|
31
|
+
return {
|
|
32
|
+
emails: new Set(patterns.emails.map(p => p.toLowerCase())),
|
|
33
|
+
messages: new Set(patterns.messages.map(p => p.toLowerCase()))
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Cached compiled defaults
|
|
38
|
+
const CACHED_DEFAULTS = compilePatterns(DEFAULT_PATTERNS)
|
|
39
|
+
|
|
40
|
+
// Detect if author is AI based on patterns
|
|
41
|
+
export function detectAI(
|
|
42
|
+
login?: string,
|
|
43
|
+
email?: string,
|
|
44
|
+
message?: string,
|
|
45
|
+
patterns?: typeof DEFAULT_PATTERNS
|
|
46
|
+
): 'human' | 'ai' {
|
|
47
|
+
const compiled = patterns ? compilePatterns(patterns) : CACHED_DEFAULTS
|
|
48
|
+
const toCheck = [login, email, message].filter(Boolean)
|
|
49
|
+
|
|
50
|
+
for (const str of toCheck) {
|
|
51
|
+
const lower = str!.toLowerCase()
|
|
52
|
+
for (const pattern of compiled.emails) {
|
|
53
|
+
if (lower === pattern || lower.includes(pattern)) return 'ai'
|
|
54
|
+
}
|
|
55
|
+
for (const pattern of compiled.messages) {
|
|
56
|
+
if (lower.includes(pattern)) return 'ai'
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return 'human'
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Format date to relative time string
|
|
64
|
+
export function formatRelativeTime(dateStr: string): string {
|
|
65
|
+
const date = new Date(dateStr)
|
|
66
|
+
const diff = Date.now() - date.getTime()
|
|
67
|
+
const days = Math.floor(diff / 86400000)
|
|
68
|
+
|
|
69
|
+
if (days > 30) {
|
|
70
|
+
return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })
|
|
71
|
+
}
|
|
72
|
+
if (days > 0) return `${days}d ago`
|
|
73
|
+
|
|
74
|
+
const hours = Math.floor(diff / 3600000)
|
|
75
|
+
if (hours > 0) return `${hours}h ago`
|
|
76
|
+
|
|
77
|
+
const minutes = Math.floor(diff / 60000)
|
|
78
|
+
if (minutes > 0) return `${minutes}m ago`
|
|
79
|
+
|
|
80
|
+
return 'just now'
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Extract short hash from full SHA (7 characters)
|
|
84
|
+
export function shortHash(sha: string): string {
|
|
85
|
+
return sha.slice(0, 7)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Get first line of commit message
|
|
89
|
+
export function firstLine(message: string): string {
|
|
90
|
+
return message.split('\n')[0]
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Filter Promise.allSettled results to non-null Commit array
|
|
94
|
+
export function filterSettledCommits(results: PromiseSettledResult<Commit | null>[]): Commit[] {
|
|
95
|
+
return results
|
|
96
|
+
.map(r => r.status === 'fulfilled' ? r.value : null)
|
|
97
|
+
.filter((c): c is Commit => c !== null)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Escape HTML to prevent XSS attacks
|
|
101
|
+
export function escapeHtml(unsafe: string): string {
|
|
102
|
+
return unsafe
|
|
103
|
+
.replace(/&/g, '&')
|
|
104
|
+
.replace(/</g, '<')
|
|
105
|
+
.replace(/>/g, '>')
|
|
106
|
+
.replace(/"/g, '"')
|
|
107
|
+
.replace(/'/g, ''')
|
|
108
|
+
}
|
package/src/themes.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// 2026 preset themes — modern, accessible, trend-aligned color schemes
|
|
2
|
+
|
|
3
|
+
import type { Theme, ThemeColors } from './types'
|
|
4
|
+
|
|
5
|
+
export const themes: Record<Theme, ThemeColors> = {
|
|
6
|
+
// Dark theme (default) — classic developer dark mode
|
|
7
|
+
dark: {
|
|
8
|
+
bg: '#09090b',
|
|
9
|
+
fg: '#e4e4e7',
|
|
10
|
+
dim: '#71717a',
|
|
11
|
+
human: '#22c55e',
|
|
12
|
+
ai: '#a855f7',
|
|
13
|
+
add: 'rgba(34, 102, 68, 0.25)',
|
|
14
|
+
remove: 'rgba(185, 28, 28, 0.2)',
|
|
15
|
+
border: '#27272a',
|
|
16
|
+
borderSubtle: '#18181b'
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
// Light theme — clean, minimal
|
|
20
|
+
light: {
|
|
21
|
+
bg: '#ffffff',
|
|
22
|
+
fg: '#18181b',
|
|
23
|
+
dim: '#71717a',
|
|
24
|
+
human: '#16a34a',
|
|
25
|
+
ai: '#9333ea',
|
|
26
|
+
add: 'rgba(22, 163, 74, 0.15)',
|
|
27
|
+
remove: 'rgba(220, 38, 38, 0.12)',
|
|
28
|
+
border: '#e4e4e7',
|
|
29
|
+
borderSubtle: '#f4f4f5'
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
// Midnight — deep blue-black, cyberpunk adjacent
|
|
33
|
+
midnight: {
|
|
34
|
+
bg: '#030712',
|
|
35
|
+
fg: '#e2e8f0',
|
|
36
|
+
dim: '#64748b',
|
|
37
|
+
human: '#06b6d4',
|
|
38
|
+
ai: '#f472b6',
|
|
39
|
+
add: 'rgba(6, 182, 212, 0.2)',
|
|
40
|
+
remove: 'rgba(244, 114, 182, 0.15)',
|
|
41
|
+
border: '#1e293b',
|
|
42
|
+
borderSubtle: '#0f172a'
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
// Cyber — neon accents on near-black
|
|
46
|
+
cyber: {
|
|
47
|
+
bg: '#050505',
|
|
48
|
+
fg: '#f0f0f0',
|
|
49
|
+
dim: '#6b6b6b',
|
|
50
|
+
human: '#00ff9f',
|
|
51
|
+
ai: '#ff00ff',
|
|
52
|
+
add: 'rgba(0, 255, 159, 0.2)',
|
|
53
|
+
remove: 'rgba(255, 0, 255, 0.15)',
|
|
54
|
+
border: '#2a2a2a',
|
|
55
|
+
borderSubtle: '#0a0a0a'
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
// Forest — nature-inspired greens
|
|
59
|
+
forest: {
|
|
60
|
+
bg: '#0a1208',
|
|
61
|
+
fg: '#d4e5d4',
|
|
62
|
+
dim: '#5a6a5a',
|
|
63
|
+
human: '#4ade80',
|
|
64
|
+
ai: '#2dd4bf',
|
|
65
|
+
add: 'rgba(74, 222, 128, 0.2)',
|
|
66
|
+
remove: 'rgba(248, 113, 113, 0.15)',
|
|
67
|
+
border: '#1a2a1a',
|
|
68
|
+
borderSubtle: '#0a180a'
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
// Sunset — warm tones, easy on eyes
|
|
72
|
+
sunset: {
|
|
73
|
+
bg: '#0f0808',
|
|
74
|
+
fg: '#f5e6e6',
|
|
75
|
+
dim: '#7a5a5a',
|
|
76
|
+
human: '#fb923c',
|
|
77
|
+
ai: '#f43f5e',
|
|
78
|
+
add: 'rgba(251, 146, 60, 0.2)',
|
|
79
|
+
remove: 'rgba(244, 63, 94, 0.15)',
|
|
80
|
+
border: '#2a1a1a',
|
|
81
|
+
borderSubtle: '#1a0a0a'
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Generate CSS variables from theme colors
|
|
86
|
+
export function themeToVars(theme: ThemeColors): Record<string, string> {
|
|
87
|
+
return {
|
|
88
|
+
'--trace-bg': theme.bg,
|
|
89
|
+
'--trace-fg': theme.fg,
|
|
90
|
+
'--trace-dim': theme.dim,
|
|
91
|
+
'--trace-human': theme.human,
|
|
92
|
+
'--trace-ai': theme.ai,
|
|
93
|
+
'--trace-add': theme.add,
|
|
94
|
+
'--trace-remove': theme.remove,
|
|
95
|
+
'--trace-border': theme.border,
|
|
96
|
+
'--trace-border-subtle': theme.borderSubtle
|
|
97
|
+
}
|
|
98
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Type definitions for Trace component and commit data
|
|
2
|
+
|
|
3
|
+
export type Theme = 'dark' | 'light' | 'midnight' | 'cyber' | 'forest' | 'sunset'
|
|
4
|
+
|
|
5
|
+
export interface ThemeColors {
|
|
6
|
+
bg: string
|
|
7
|
+
fg: string
|
|
8
|
+
dim: string
|
|
9
|
+
human: string
|
|
10
|
+
ai: string
|
|
11
|
+
add: string
|
|
12
|
+
remove: string
|
|
13
|
+
border: string
|
|
14
|
+
borderSubtle: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type Commit = {
|
|
18
|
+
hash: string
|
|
19
|
+
message: string
|
|
20
|
+
author: string
|
|
21
|
+
authorType: 'human' | 'ai'
|
|
22
|
+
time: string
|
|
23
|
+
lines: DiffLine[]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type DiffLine = {
|
|
27
|
+
type: 'add' | 'remove' | 'ctx'
|
|
28
|
+
content: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type AuthorTypeFilter = 'all' | 'human' | 'ai'
|
|
32
|
+
|
|
33
|
+
export type FilterOptions = {
|
|
34
|
+
search?: string
|
|
35
|
+
authorType?: AuthorTypeFilter
|
|
36
|
+
dateFrom?: string
|
|
37
|
+
dateTo?: string
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export type TraceProps = {
|
|
41
|
+
commits?: Commit[]
|
|
42
|
+
autoPlay?: boolean
|
|
43
|
+
interval?: number
|
|
44
|
+
onCommit?: (commit: Commit) => void
|
|
45
|
+
className?: string
|
|
46
|
+
theme?: Theme
|
|
47
|
+
filterable?: boolean
|
|
48
|
+
defaultFilter?: FilterOptions
|
|
49
|
+
onFilterChange?: (filter: FilterOptions) => void
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// GitHub API response types
|
|
53
|
+
export interface GitHubCommit {
|
|
54
|
+
sha: string
|
|
55
|
+
commit: {
|
|
56
|
+
message: string
|
|
57
|
+
author: {
|
|
58
|
+
name: string
|
|
59
|
+
email: string
|
|
60
|
+
date: string
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
author?: {
|
|
64
|
+
login: string
|
|
65
|
+
}
|
|
66
|
+
files?: GitHubFile[]
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface GitHubFile {
|
|
70
|
+
patch?: string
|
|
71
|
+
filename: string
|
|
72
|
+
}
|