gitmaps 1.1.0 → 1.1.2

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 (121) hide show
  1. package/README.md +267 -118
  2. package/app/[...slug]/page.client.tsx +1 -0
  3. package/app/[...slug]/page.tsx +6 -0
  4. package/app/analytics.db +0 -0
  5. package/app/api/analytics/route.ts +64 -0
  6. package/app/api/auth/positions/route.ts +95 -33
  7. package/app/api/build-info/route.ts +19 -0
  8. package/app/api/chat/route.ts +13 -2
  9. package/app/api/og-image/route.ts +14 -0
  10. package/app/api/repo/file-content/route.ts +73 -20
  11. package/app/api/repo/load/route.test.ts +62 -0
  12. package/app/api/repo/load/route.ts +41 -1
  13. package/app/api/repo/pdf-thumb/route.ts +127 -0
  14. package/app/api/repo/resolve-slug/route.ts +51 -0
  15. package/app/api/repo/tree/route.ts +188 -104
  16. package/app/api/version/route.ts +26 -0
  17. package/app/globals.css +5706 -4938
  18. package/app/layout.tsx +1279 -490
  19. package/app/lib/auto-arrange.test.ts +158 -0
  20. package/app/lib/auto-arrange.ts +147 -0
  21. package/app/lib/canvas-export.ts +358 -358
  22. package/app/lib/canvas.ts +625 -564
  23. package/app/lib/cards.tsx +1361 -916
  24. package/app/lib/chat.tsx +65 -9
  25. package/app/lib/code-editor.ts +86 -2
  26. package/app/lib/context.test.ts +32 -0
  27. package/app/lib/context.ts +19 -3
  28. package/app/lib/cursor-sharing.ts +34 -0
  29. package/app/lib/events.tsx +71 -93
  30. package/app/lib/export-canvas.ts +287 -0
  31. package/app/lib/file-card-plugin.ts +148 -148
  32. package/app/lib/file-modal.tsx +49 -0
  33. package/app/lib/file-preview.ts +486 -427
  34. package/app/lib/github-import.test.ts +424 -0
  35. package/app/lib/initial-route-hydration.test.ts +283 -0
  36. package/app/lib/initial-route-hydration.ts +202 -0
  37. package/app/lib/landing-reset.test.ts +99 -0
  38. package/app/lib/landing-reset.ts +106 -0
  39. package/app/lib/landing-shell.test.ts +75 -0
  40. package/app/lib/large-repo-optimization.ts +37 -0
  41. package/app/lib/layout-snapshots.ts +320 -0
  42. package/app/lib/loading.test.ts +69 -0
  43. package/app/lib/loading.tsx +160 -45
  44. package/app/lib/mount-cleanup.test.ts +52 -0
  45. package/app/lib/mount-cleanup.ts +34 -0
  46. package/app/lib/mount-init.test.ts +123 -0
  47. package/app/lib/mount-init.ts +107 -0
  48. package/app/lib/mount-lifecycle.test.ts +39 -0
  49. package/app/lib/mount-lifecycle.ts +12 -0
  50. package/app/lib/mount-route-wiring.test.ts +87 -0
  51. package/app/lib/mount-route-wiring.ts +84 -0
  52. package/app/lib/multi-repo.ts +14 -0
  53. package/app/lib/onboarding-tutorial.ts +278 -0
  54. package/app/lib/positions.ts +190 -121
  55. package/app/lib/recent-commits.test.ts +947 -0
  56. package/app/lib/recent-commits.ts +227 -0
  57. package/app/lib/repo-handoff.test.ts +23 -0
  58. package/app/lib/repo-handoff.ts +16 -0
  59. package/app/lib/repo-progressive.ts +119 -0
  60. package/app/lib/repo-select.test.ts +61 -0
  61. package/app/lib/repo-select.ts +74 -0
  62. package/app/lib/repo.tsx +1383 -987
  63. package/app/lib/role.ts +228 -0
  64. package/app/lib/route-catchall.test.ts +27 -0
  65. package/app/lib/route-repo-entry.test.ts +95 -0
  66. package/app/lib/route-repo-entry.ts +36 -0
  67. package/app/lib/router-contract.test.ts +22 -0
  68. package/app/lib/router-contract.ts +19 -0
  69. package/app/lib/shared-layout.test.ts +86 -0
  70. package/app/lib/shared-layout.ts +82 -0
  71. package/app/lib/status-bar.test.ts +118 -0
  72. package/app/lib/status-bar.ts +365 -128
  73. package/app/lib/sync-controls.test.ts +43 -0
  74. package/app/lib/sync-controls.tsx +303 -0
  75. package/app/lib/test-dom.ts +145 -0
  76. package/app/lib/test-fixtures/router-contract/[...slug]/page.tsx +3 -0
  77. package/app/lib/test-fixtures/router-contract/api/health/route.ts +3 -0
  78. package/app/lib/test-fixtures/router-contract/api/version/route.ts +3 -0
  79. package/app/lib/test-fixtures/router-contract/galaxy-canvas/page.tsx +3 -0
  80. package/app/lib/test-fixtures/router-contract/page.tsx +3 -0
  81. package/app/lib/transclusion-smoke.test.ts +163 -0
  82. package/app/lib/tutorial.ts +301 -0
  83. package/app/lib/version.ts +93 -0
  84. package/app/lib/viewport-culling.ts +740 -735
  85. package/app/lib/virtual-files.ts +456 -0
  86. package/app/lib/webgl-text.ts +189 -0
  87. package/app/lib/{galaxydraw-bridge.ts → xydraw-bridge.ts} +485 -482
  88. package/app/lib/{galaxydraw.test.ts → xydraw.test.ts} +228 -229
  89. package/app/og-image.png +0 -0
  90. package/app/page.client.tsx +70 -269
  91. package/app/page.tsx +15 -16
  92. package/app/state/machine.js +13 -0
  93. package/package.json +84 -75
  94. package/server.ts +10 -0
  95. package/app/[owner]/[repo]/page.tsx +0 -6
  96. package/app/[slug]/page.tsx +0 -6
  97. package/packages/galaxydraw/README.md +0 -296
  98. package/packages/galaxydraw/banner.png +0 -0
  99. package/packages/galaxydraw/demo/build-static.ts +0 -100
  100. package/packages/galaxydraw/demo/client.ts +0 -154
  101. package/packages/galaxydraw/demo/dist/client.js +0 -8
  102. package/packages/galaxydraw/demo/index.html +0 -256
  103. package/packages/galaxydraw/demo/server.ts +0 -96
  104. package/packages/galaxydraw/dist/index.js +0 -984
  105. package/packages/galaxydraw/dist/index.js.map +0 -16
  106. package/packages/galaxydraw/node_modules/.bin/tsc.bunx +0 -0
  107. package/packages/galaxydraw/node_modules/.bin/tsc.exe +0 -0
  108. package/packages/galaxydraw/node_modules/.bin/tsserver.bunx +0 -0
  109. package/packages/galaxydraw/node_modules/.bin/tsserver.exe +0 -0
  110. package/packages/galaxydraw/package.json +0 -49
  111. package/packages/galaxydraw/perf.test.ts +0 -284
  112. package/packages/galaxydraw/src/core/cards.ts +0 -435
  113. package/packages/galaxydraw/src/core/engine.ts +0 -339
  114. package/packages/galaxydraw/src/core/events.ts +0 -81
  115. package/packages/galaxydraw/src/core/layout.ts +0 -136
  116. package/packages/galaxydraw/src/core/minimap.ts +0 -216
  117. package/packages/galaxydraw/src/core/state.ts +0 -177
  118. package/packages/galaxydraw/src/core/viewport.ts +0 -106
  119. package/packages/galaxydraw/src/galaxydraw.css +0 -166
  120. package/packages/galaxydraw/src/index.ts +0 -40
  121. package/packages/galaxydraw/tsconfig.json +0 -30
@@ -1,104 +1,188 @@
1
- import { measure } from 'measure-fn';
2
- import simpleGit from 'simple-git';
3
- import { readFileSync, existsSync } from 'fs';
4
- import path from 'path';
5
- import { validateRepoPath } from '../validate-path';
6
-
7
- const BINARY_EXTS = new Set(['png', 'jpg', 'jpeg', 'gif', 'bmp', 'ico', 'svg', 'webp', 'mp3', 'mp4', 'wav', 'ogg', 'avi', 'mov', 'zip', 'tar', 'gz', 'rar', '7z', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'exe', 'dll', 'so', 'dylib', 'woff', 'woff2', 'ttf', 'eot', 'otf', 'lock']);
8
-
9
- export async function POST(req: Request) {
10
- return measure('api:repo:tree', async () => {
11
- try {
12
- const { path: repoPath } = await req.json();
13
-
14
- if (!repoPath) {
15
- return new Response('Repository path is required', { status: 400 });
16
- }
17
-
18
- const blocked = validateRepoPath(repoPath);
19
- if (blocked) return blocked;
20
-
21
- const git = simpleGit(repoPath);
22
-
23
- // Get tracked files only (respects .gitignore by definition)
24
- const result = await git.raw(['ls-files']);
25
- const ignoreDirs = ['node_modules', '.git', 'dist', 'build', '.next', '.cache', 'coverage', '.turbo', '__pycache__', '.tsbuildinfo'];
26
-
27
- // Also parse .gitignore for extra patterns
28
- const gitignorePatterns: string[] = [];
29
- const gitignorePath = path.join(repoPath, '.gitignore');
30
- if (existsSync(gitignorePath)) {
31
- try {
32
- const content = readFileSync(gitignorePath, 'utf-8');
33
- content.split('\n').forEach(line => {
34
- line = line.trim();
35
- if (line && !line.startsWith('#')) {
36
- // Normalize: remove trailing slashes for dir matching
37
- const clean = line.replace(/\/+$/, '');
38
- if (clean) gitignorePatterns.push(clean);
39
- }
40
- });
41
- } catch (e) { /* ignore */ }
42
- }
43
-
44
- const filePaths = result.trim().split('\n').filter(fp => {
45
- if (!fp) return false;
46
- // Filter out known heavy directories
47
- if (ignoreDirs.some(d => fp.startsWith(d + '/') || fp.startsWith(d + '\\'))) return false;
48
- // Filter out files matching gitignore patterns (extra safety)
49
- for (const pattern of gitignorePatterns) {
50
- if (fp.startsWith(pattern + '/') || fp.startsWith(pattern + '\\')) return false;
51
- if (fp === pattern) return false;
52
- // Simple glob: *.ext
53
- if (pattern.startsWith('*.')) {
54
- const ext = pattern.substring(1); // .ext
55
- if (fp.endsWith(ext)) return false;
56
- }
57
- }
58
- return true;
59
- });
60
-
61
- const files = filePaths.map(filePath => {
62
- const parts = filePath.split('/');
63
- const name = parts[parts.length - 1];
64
- const ext = name.includes('.') ? name.split('.').pop()!.toLowerCase() : '';
65
-
66
- let content = null;
67
- let lines = 0;
68
- let isBinary = BINARY_EXTS.has(ext);
69
-
70
- if (!isBinary) {
71
- try {
72
- const fullPath = path.join(repoPath, filePath);
73
- const raw = readFileSync(fullPath, 'utf-8');
74
- const allLines = raw.split('\n');
75
- lines = allLines.length;
76
- // Send full content for small/medium files, truncate very large ones
77
- if (allLines.length > 10000) {
78
- content = allLines.slice(0, 10000).join('\n');
79
- } else {
80
- content = raw;
81
- }
82
- } catch (e) {
83
- content = null;
84
- }
85
- }
86
-
87
- return {
88
- path: filePath,
89
- name,
90
- ext,
91
- type: 'file',
92
- content,
93
- lines,
94
- isBinary
95
- };
96
- });
97
-
98
- return Response.json({ files, total: files.length });
99
- } catch (error: any) {
100
- console.error('api:repo:tree:error', error);
101
- return new Response(`Error: ${error.message}`, { status: 500 });
102
- }
103
- });
104
- }
1
+ import { measure } from 'measure-fn';
2
+ import simpleGit from 'simple-git';
3
+ import { readFileSync, existsSync, readdirSync, statSync } from 'fs';
4
+ import path from 'path';
5
+ import { validateRepoPath } from '../validate-path';
6
+
7
+ const BINARY_EXTS = new Set(['png', 'jpg', 'jpeg', 'gif', 'bmp', 'ico', 'svg', 'webp', 'mp3', 'mp4', 'wav', 'ogg', 'avi', 'mov', 'zip', 'tar', 'gz', 'rar', '7z', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'exe', 'dll', 'so', 'dylib', 'woff', 'woff2', 'ttf', 'eot', 'otf', 'lock']);
8
+ const IMAGE_EXTS = new Set(['png', 'jpg', 'jpeg', 'gif', 'bmp', 'ico', 'svg', 'webp']);
9
+ const PDF_EXTS = new Set(['pdf']);
10
+ const MAX_READ_SIZE = 2 * 1024 * 1024;
11
+
12
+ export async function POST(req: Request) {
13
+ return measure('api:repo:tree', async () => {
14
+ try {
15
+ const body = await req.json();
16
+ const repoPath = body.path;
17
+ const stream = body.stream === true;
18
+ const includeAll = body.includeAll === true;
19
+
20
+ if (!repoPath) {
21
+ return new Response('Repository path is required', { status: 400 });
22
+ }
23
+
24
+ const blocked = validateRepoPath(repoPath);
25
+ if (blocked) return blocked;
26
+
27
+ const ignoreDirs = new Set(['node_modules', '.git', 'dist', 'build', '.next', '.cache', 'coverage', '.turbo', '__pycache__', '.tsbuildinfo']);
28
+
29
+ // Recursively scan filesystem for all files (ignoring standard dirs)
30
+ function scanDir(dir: string, prefix: string): string[] {
31
+ const results: string[] = [];
32
+ try {
33
+ const entries = readdirSync(dir);
34
+ for (const entry of entries) {
35
+ if (ignoreDirs.has(entry)) continue;
36
+ const fullPath = path.join(dir, entry);
37
+ const relativePath = prefix ? `${prefix}/${entry}` : entry;
38
+ try {
39
+ const stats = statSync(fullPath);
40
+ if (stats.isDirectory()) {
41
+ results.push(...scanDir(fullPath, relativePath));
42
+ } else if (stats.isFile()) {
43
+ results.push(relativePath);
44
+ }
45
+ } catch (e: any) {
46
+ console.warn(`[tree:scanDir] stat error: ${fullPath}: ${e.message}`);
47
+ }
48
+ }
49
+ } catch (e: any) {
50
+ console.warn(`[tree:scanDir] readdir error: ${dir}: ${e.message}`);
51
+ }
52
+ return results;
53
+ }
54
+
55
+ let filePaths: string[];
56
+
57
+ const git = simpleGit(repoPath);
58
+ const isRepo = await git.checkIsRepo().catch(() => false);
59
+
60
+ if (!isRepo || includeAll) {
61
+ // Not a git repo or explicit all-files mode: scan filesystem
62
+ filePaths = scanDir(repoPath, '');
63
+ } else {
64
+ // Get tracked files
65
+ const result = await git.raw(['ls-files']);
66
+ const trackedPaths = result.trim().split('\n').filter(fp => {
67
+ if (!fp) return false;
68
+ if (Array.from(ignoreDirs).some(d => fp.startsWith(d + '/') || fp.startsWith(d + '\\'))) return false;
69
+ return true;
70
+ });
71
+
72
+ // If very few tracked files, also scan filesystem for untracked content
73
+ // This catches repos where most content (PDFs, images) is gitignored
74
+ if (trackedPaths.length < 50) {
75
+ const allPaths = scanDir(repoPath, '');
76
+ console.log(`[tree] ${trackedPaths.length} tracked, ${allPaths.length} on disk`);
77
+ if (allPaths.length > trackedPaths.length * 5) {
78
+ // Lots of untracked content include everything
79
+ const trackedSet = new Set(trackedPaths);
80
+ filePaths = allPaths;
81
+ // But still filter out obvious junk from non-tracked scan
82
+ filePaths = filePaths.filter(fp => {
83
+ if (Array.from(ignoreDirs).some(d => fp.startsWith(d + '/') || fp.startsWith(d + '\\'))) return false;
84
+ return true;
85
+ });
86
+ console.log(`[tree] ${trackedPaths.length} tracked, ${allPaths.length} on disk → including all ${filePaths.length} files`);
87
+ } else {
88
+ filePaths = trackedPaths;
89
+ }
90
+ } else {
91
+ filePaths = trackedPaths;
92
+ }
93
+ }
94
+
95
+ function readFile(filePath: string) {
96
+ const parts = filePath.split('/');
97
+ const name = parts[parts.length - 1];
98
+ const ext = name.includes('.') ? name.split('.').pop()!.toLowerCase() : '';
99
+
100
+ let content = null;
101
+ let lines = 0;
102
+ let size = 0;
103
+ let isBinary = BINARY_EXTS.has(ext);
104
+ const isImage = IMAGE_EXTS.has(ext);
105
+ const isPdf = PDF_EXTS.has(ext);
106
+
107
+ if (!isBinary) {
108
+ try {
109
+ const fullPath = path.join(repoPath, filePath);
110
+ const file = Bun.file(fullPath);
111
+ size = file.size;
112
+
113
+ // Skip reading content for very large files
114
+ if (size > MAX_READ_SIZE) {
115
+ isBinary = true;
116
+ } else {
117
+ const raw = readFileSync(fullPath, 'utf-8');
118
+ size = raw.length;
119
+ const allLines = raw.split('\n');
120
+ lines = allLines.length;
121
+ if (allLines.length > 10000) {
122
+ content = allLines.slice(0, 10000).join('\n');
123
+ } else {
124
+ content = raw;
125
+ }
126
+ }
127
+ } catch (e) {
128
+ content = null;
129
+ }
130
+ } else {
131
+ // For binary files, at least get the file size
132
+ try {
133
+ const fullPath = path.join(repoPath, filePath);
134
+ size = Bun.file(fullPath).size;
135
+ } catch (_) {}
136
+ }
137
+
138
+ return { path: filePath, name, ext, type: 'file', content, lines, size, isBinary, isImage, isPdf };
139
+ }
140
+
141
+ // ── Streaming mode: NDJSON with total header ──
142
+ if (stream) {
143
+ const total = filePaths.length;
144
+ const BATCH_SIZE = 20;
145
+ const encoder = new TextEncoder();
146
+
147
+ const readable = new ReadableStream({
148
+ start(controller) {
149
+ // First line: total count
150
+ controller.enqueue(encoder.encode(JSON.stringify({ total }) + '\n'));
151
+
152
+ let i = 0;
153
+ function nextBatch() {
154
+ const end = Math.min(i + BATCH_SIZE, total);
155
+ const batch: any[] = [];
156
+ for (; i < end; i++) {
157
+ batch.push(readFile(filePaths[i]));
158
+ }
159
+ controller.enqueue(encoder.encode(JSON.stringify({ files: batch, loaded: i }) + '\n'));
160
+
161
+ if (i < total) {
162
+ // Yield to event loop between batches
163
+ setTimeout(nextBatch, 0);
164
+ } else {
165
+ controller.close();
166
+ }
167
+ }
168
+ nextBatch();
169
+ }
170
+ });
171
+
172
+ return new Response(readable, {
173
+ headers: {
174
+ 'Content-Type': 'application/x-ndjson',
175
+ 'Cache-Control': 'no-cache',
176
+ }
177
+ });
178
+ }
179
+
180
+ // ── Legacy non-streaming mode ──
181
+ const files = filePaths.map(readFile);
182
+ return Response.json({ files, total: files.length });
183
+ } catch (error: any) {
184
+ console.error('api:repo:tree:error', error);
185
+ return new Response(`Error: ${error.message}`, { status: 500 });
186
+ }
187
+ });
188
+ }
@@ -0,0 +1,26 @@
1
+ import path from 'path';
2
+
3
+ function runGit(args: string[]): string {
4
+ const repoRoot = path.resolve(import.meta.dir, '../../..');
5
+ const proc = Bun.spawnSync(['git', ...args], {
6
+ cwd: repoRoot,
7
+ stdout: 'pipe',
8
+ stderr: 'pipe',
9
+ });
10
+
11
+ if (proc.exitCode !== 0) {
12
+ return '';
13
+ }
14
+
15
+ return proc.stdout.toString().trim();
16
+ }
17
+
18
+ export async function GET() {
19
+ const commit = process.env.GIT_COMMIT_HASH || runGit(['rev-parse', '--short', 'HEAD']) || 'unknown';
20
+ const commitDate = process.env.GIT_COMMIT_DATE || runGit(['log', '-1', '--format=%cs']) || '';
21
+
22
+ return Response.json({
23
+ commit,
24
+ commitDate,
25
+ });
26
+ }