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.
Files changed (48) hide show
  1. package/.tracerc.example +38 -0
  2. package/README.md +136 -0
  3. package/bun.lock +511 -0
  4. package/bunchee.config.ts +11 -0
  5. package/cli/index.ts +251 -0
  6. package/cli/parser.ts +76 -0
  7. package/cli/tsconfig.json +6 -0
  8. package/dist/cli/index.d.ts +1 -0
  9. package/dist/cli/index.js +858 -0
  10. package/dist/config.cjs +66 -0
  11. package/dist/config.d.ts +15 -0
  12. package/dist/config.js +63 -0
  13. package/dist/highlight/index.cjs +770 -0
  14. package/dist/highlight/index.d.ts +26 -0
  15. package/dist/highlight/index.js +766 -0
  16. package/dist/index.cjs +849 -0
  17. package/dist/index.d.ts +52 -0
  18. package/dist/index.js +845 -0
  19. package/examples/demo/App.tsx +78 -0
  20. package/examples/demo/index.html +12 -0
  21. package/examples/demo/main.tsx +10 -0
  22. package/examples/demo/mockData.ts +170 -0
  23. package/examples/demo/styles.css +103 -0
  24. package/examples/demo/tsconfig.json +21 -0
  25. package/examples/demo/tsconfig.node.json +10 -0
  26. package/examples/demo/vite.config.ts +20 -0
  27. package/package.json +58 -0
  28. package/src/Trace.tsx +717 -0
  29. package/src/cache.ts +118 -0
  30. package/src/config.ts +51 -0
  31. package/src/entries/config.ts +7 -0
  32. package/src/entries/gitea.ts +4 -0
  33. package/src/entries/github.ts +5 -0
  34. package/src/entries/gitlab.ts +4 -0
  35. package/src/gitea.ts +58 -0
  36. package/src/github.ts +100 -0
  37. package/src/gitlab.ts +65 -0
  38. package/src/highlight/highlight.ts +119 -0
  39. package/src/highlight/index.ts +4 -0
  40. package/src/host.ts +32 -0
  41. package/src/index.ts +6 -0
  42. package/src/patterns.ts +6 -0
  43. package/src/shared.ts +108 -0
  44. package/src/themes.ts +98 -0
  45. package/src/types.ts +72 -0
  46. package/test/e2e.html +424 -0
  47. package/tsconfig.json +18 -0
  48. 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, '&amp;')
104
+ .replace(/</g, '&lt;')
105
+ .replace(/>/g, '&gt;')
106
+ .replace(/"/g, '&quot;')
107
+ .replace(/'/g, '&#039;')
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
+ }