gitmaps 1.0.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/README.md +167 -0
- package/app/api/auth/favorites/route.ts +56 -0
- package/app/api/auth/github/callback/route.ts +103 -0
- package/app/api/auth/github/route.ts +32 -0
- package/app/api/auth/me/route.ts +52 -0
- package/app/api/auth/positions/route.ts +50 -0
- package/app/api/chat/route.ts +101 -0
- package/app/api/connections/route.ts +72 -0
- package/app/api/github/repos/route.ts +111 -0
- package/app/api/positions/route.ts +80 -0
- package/app/api/repo/branch-diff/route.ts +201 -0
- package/app/api/repo/branches/route.ts +53 -0
- package/app/api/repo/browse/route.ts +55 -0
- package/app/api/repo/clone/route.ts +78 -0
- package/app/api/repo/clone-stream/route.ts +131 -0
- package/app/api/repo/file-content/route.ts +28 -0
- package/app/api/repo/file-delete/route.ts +62 -0
- package/app/api/repo/file-history/route.ts +45 -0
- package/app/api/repo/file-rename/route.ts +83 -0
- package/app/api/repo/file-save/route.ts +45 -0
- package/app/api/repo/files/route.ts +169 -0
- package/app/api/repo/git-blame/route.ts +86 -0
- package/app/api/repo/git-commit/route.ts +40 -0
- package/app/api/repo/git-heatmap/route.ts +55 -0
- package/app/api/repo/imports/route.ts +154 -0
- package/app/api/repo/load/route.ts +56 -0
- package/app/api/repo/mode/route.ts +14 -0
- package/app/api/repo/search/route.ts +127 -0
- package/app/api/repo/tree/route.ts +104 -0
- package/app/api/repo/upload/route.ts +53 -0
- package/app/api/repo/validate-path.ts +53 -0
- package/app/canvas_users.db +0 -0
- package/app/canvas_users.db-shm +0 -0
- package/app/canvas_users.db-wal +0 -0
- package/app/globals.css +7899 -0
- package/app/layout.tsx +493 -0
- package/app/lib/auth.ts +193 -0
- package/app/lib/auto-save.ts +137 -0
- package/app/lib/branch-compare.ts +443 -0
- package/app/lib/breadcrumbs.ts +170 -0
- package/app/lib/canvas-export.ts +358 -0
- package/app/lib/canvas-text.ts +912 -0
- package/app/lib/canvas.ts +564 -0
- package/app/lib/card-arrangement.ts +188 -0
- package/app/lib/card-context-menu.tsx +453 -0
- package/app/lib/card-diff-markers.ts +270 -0
- package/app/lib/card-expand.ts +189 -0
- package/app/lib/card-groups.ts +246 -0
- package/app/lib/cards.tsx +914 -0
- package/app/lib/chat.tsx +308 -0
- package/app/lib/code-editor.ts +508 -0
- package/app/lib/command-palette.ts +262 -0
- package/app/lib/connections.tsx +1037 -0
- package/app/lib/context.ts +94 -0
- package/app/lib/cursor-sharing.ts +281 -0
- package/app/lib/dependency-graph.ts +438 -0
- package/app/lib/events.tsx +1747 -0
- package/app/lib/file-card-plugin.ts +134 -0
- package/app/lib/file-modal.tsx +849 -0
- package/app/lib/file-preview.ts +400 -0
- package/app/lib/file-tabs.ts +318 -0
- package/app/lib/galaxydraw-bridge.ts +477 -0
- package/app/lib/galaxydraw.test.ts +229 -0
- package/app/lib/global-search.ts +264 -0
- package/app/lib/goto-definition.ts +224 -0
- package/app/lib/heatmap.ts +178 -0
- package/app/lib/hidden-files.tsx +222 -0
- package/app/lib/layers.ts +0 -0
- package/app/lib/layers.tsx +365 -0
- package/app/lib/loading.tsx +45 -0
- package/app/lib/multi-repo.ts +286 -0
- package/app/lib/new-file-dialog.tsx +230 -0
- package/app/lib/onboarding.tsx +213 -0
- package/app/lib/perf-overlay.ts +360 -0
- package/app/lib/positions.ts +176 -0
- package/app/lib/pr-review.ts +374 -0
- package/app/lib/production-mode.ts +47 -0
- package/app/lib/repo.tsx +977 -0
- package/app/lib/settings-modal.tsx +374 -0
- package/app/lib/settings.ts +97 -0
- package/app/lib/shortcuts-panel.ts +141 -0
- package/app/lib/status-bar.ts +128 -0
- package/app/lib/symbol-outline.ts +212 -0
- package/app/lib/syntax.ts +177 -0
- package/app/lib/tab-diff.ts +238 -0
- package/app/lib/user.tsx +133 -0
- package/app/lib/utils.ts +78 -0
- package/app/lib/viewport-culling.ts +728 -0
- package/app/page.client.tsx +215 -0
- package/app/page.tsx +291 -0
- package/app/state/machine.js +196 -0
- package/app/styles/main.css +2168 -0
- package/banner.png +0 -0
- package/cli.ts +44 -0
- package/package.json +75 -0
- package/packages/galaxydraw/README.md +296 -0
- package/packages/galaxydraw/banner.png +0 -0
- package/packages/galaxydraw/demo/build-static.ts +100 -0
- package/packages/galaxydraw/demo/client.ts +154 -0
- package/packages/galaxydraw/demo/dist/client.js +8 -0
- package/packages/galaxydraw/demo/index.html +256 -0
- package/packages/galaxydraw/demo/server.ts +96 -0
- package/packages/galaxydraw/dist/index.js +984 -0
- package/packages/galaxydraw/dist/index.js.map +16 -0
- package/packages/galaxydraw/node_modules/.bin/tsc.bunx +0 -0
- package/packages/galaxydraw/node_modules/.bin/tsc.exe +0 -0
- package/packages/galaxydraw/node_modules/.bin/tsserver.bunx +0 -0
- package/packages/galaxydraw/node_modules/.bin/tsserver.exe +0 -0
- package/packages/galaxydraw/package.json +49 -0
- package/packages/galaxydraw/perf.test.ts +284 -0
- package/packages/galaxydraw/src/core/cards.ts +435 -0
- package/packages/galaxydraw/src/core/engine.ts +339 -0
- package/packages/galaxydraw/src/core/events.ts +81 -0
- package/packages/galaxydraw/src/core/layout.ts +136 -0
- package/packages/galaxydraw/src/core/minimap.ts +216 -0
- package/packages/galaxydraw/src/core/state.ts +177 -0
- package/packages/galaxydraw/src/core/viewport.ts +106 -0
- package/packages/galaxydraw/src/galaxydraw.css +166 -0
- package/packages/galaxydraw/src/index.ts +40 -0
- package/packages/galaxydraw/tsconfig.json +30 -0
- package/server.ts +62 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command Palette — Ctrl+K / Ctrl+P file search with fuzzy matching
|
|
3
|
+
*
|
|
4
|
+
* - Ctrl+K or Ctrl+P opens the palette overlay
|
|
5
|
+
* - Type to fuzzy-search file names
|
|
6
|
+
* - Arrow keys to navigate, Enter to jump, Escape to close
|
|
7
|
+
* - Supports both fileCards (DOM) and deferredCards (virtualized)
|
|
8
|
+
*/
|
|
9
|
+
import type { CanvasContext } from './context';
|
|
10
|
+
import { jumpToFile } from './canvas';
|
|
11
|
+
|
|
12
|
+
// ── Fuzzy scoring ────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
interface SearchResult {
|
|
15
|
+
path: string;
|
|
16
|
+
name: string;
|
|
17
|
+
dir: string;
|
|
18
|
+
score: number;
|
|
19
|
+
matchIndices: number[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function fuzzyMatch(query: string, target: string): { score: number; indices: number[] } | null {
|
|
23
|
+
const q = query.toLowerCase();
|
|
24
|
+
const t = target.toLowerCase();
|
|
25
|
+
|
|
26
|
+
let qi = 0;
|
|
27
|
+
let score = 0;
|
|
28
|
+
const indices: number[] = [];
|
|
29
|
+
let lastMatchIdx = -1;
|
|
30
|
+
|
|
31
|
+
for (let ti = 0; ti < t.length && qi < q.length; ti++) {
|
|
32
|
+
if (t[ti] === q[qi]) {
|
|
33
|
+
indices.push(ti);
|
|
34
|
+
// Consecutive match bonus
|
|
35
|
+
if (lastMatchIdx === ti - 1) score += 10;
|
|
36
|
+
// Start-of-word bonus (after / or . or -)
|
|
37
|
+
if (ti === 0 || '/.-_'.includes(t[ti - 1]!)) score += 8;
|
|
38
|
+
// Exact case match bonus
|
|
39
|
+
if (target[ti] === query[qi]) score += 2;
|
|
40
|
+
score += 1;
|
|
41
|
+
lastMatchIdx = ti;
|
|
42
|
+
qi++;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (qi !== q.length) return null;
|
|
47
|
+
|
|
48
|
+
// Prefer shorter targets (more specific matches)
|
|
49
|
+
score -= target.length * 0.1;
|
|
50
|
+
// Prefer matches in filename over full path
|
|
51
|
+
const nameStart = target.lastIndexOf('/') + 1;
|
|
52
|
+
const nameMatches = indices.filter(i => i >= nameStart).length;
|
|
53
|
+
score += nameMatches * 3;
|
|
54
|
+
|
|
55
|
+
return { score, indices };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ── Palette DOM ──────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
let overlay: HTMLElement | null = null;
|
|
61
|
+
let input: HTMLInputElement | null = null;
|
|
62
|
+
let resultsList: HTMLElement | null = null;
|
|
63
|
+
let selectedIdx = 0;
|
|
64
|
+
let currentResults: SearchResult[] = [];
|
|
65
|
+
let currentCtx: CanvasContext | null = null;
|
|
66
|
+
|
|
67
|
+
function getAllFiles(ctx: CanvasContext): { path: string; name: string; dir: string; isChanged: boolean }[] {
|
|
68
|
+
const files: { path: string; name: string; dir: string; isChanged: boolean }[] = [];
|
|
69
|
+
const seen = new Set<string>();
|
|
70
|
+
|
|
71
|
+
ctx.fileCards.forEach((_card, path) => {
|
|
72
|
+
seen.add(path);
|
|
73
|
+
const parts = path.split('/');
|
|
74
|
+
const name = parts.pop() || path;
|
|
75
|
+
const dir = parts.join('/');
|
|
76
|
+
files.push({ path, name, dir, isChanged: _card.dataset.changed === 'true' });
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
ctx.deferredCards?.forEach((entry, path) => {
|
|
80
|
+
if (seen.has(path)) return;
|
|
81
|
+
const parts = path.split('/');
|
|
82
|
+
const name = parts.pop() || path;
|
|
83
|
+
const dir = parts.join('/');
|
|
84
|
+
files.push({ path, name, dir, isChanged: !!entry.isChanged });
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return files;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function createOverlay(): void {
|
|
91
|
+
overlay = document.createElement('div');
|
|
92
|
+
overlay.id = 'command-palette-overlay';
|
|
93
|
+
overlay.innerHTML = `
|
|
94
|
+
<div id="command-palette">
|
|
95
|
+
<div class="cp-search">
|
|
96
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
97
|
+
<circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>
|
|
98
|
+
</svg>
|
|
99
|
+
<input type="text" id="cp-input" placeholder="Search files…" autocomplete="off" spellcheck="false" />
|
|
100
|
+
<kbd>esc</kbd>
|
|
101
|
+
</div>
|
|
102
|
+
<div id="cp-results"></div>
|
|
103
|
+
<div id="cp-footer">
|
|
104
|
+
<span><kbd>↑↓</kbd> navigate</span>
|
|
105
|
+
<span><kbd>↵</kbd> jump to file</span>
|
|
106
|
+
<span><kbd>esc</kbd> close</span>
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
`;
|
|
110
|
+
document.body.appendChild(overlay);
|
|
111
|
+
|
|
112
|
+
input = overlay.querySelector('#cp-input') as HTMLInputElement;
|
|
113
|
+
resultsList = overlay.querySelector('#cp-results') as HTMLElement;
|
|
114
|
+
|
|
115
|
+
// Close on backdrop click
|
|
116
|
+
overlay.addEventListener('mousedown', (e) => {
|
|
117
|
+
if (e.target === overlay) close();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
input.addEventListener('input', onInput);
|
|
121
|
+
input.addEventListener('keydown', onKeyDown);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function highlightText(text: string, indices: number[]): string {
|
|
125
|
+
const chars = text.split('');
|
|
126
|
+
const set = new Set(indices);
|
|
127
|
+
return chars.map((ch, i) => set.has(i) ? `<mark>${ch}</mark>` : ch).join('');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function renderResults(): void {
|
|
131
|
+
if (!resultsList) return;
|
|
132
|
+
|
|
133
|
+
if (currentResults.length === 0 && input?.value) {
|
|
134
|
+
resultsList.innerHTML = '<div class="cp-empty">No files found</div>';
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
resultsList.innerHTML = currentResults.map((r, i) => {
|
|
139
|
+
const isActive = i === selectedIdx;
|
|
140
|
+
const nameHighlighted = highlightText(r.name, r.matchIndices.filter(idx => idx >= r.path.length - r.name.length).map(idx => idx - (r.path.length - r.name.length)));
|
|
141
|
+
const dirHighlighted = r.dir ? highlightText(r.dir + '/', r.matchIndices.filter(idx => idx < r.path.length - r.name.length)) : '';
|
|
142
|
+
|
|
143
|
+
return `<div class="cp-result${isActive ? ' cp-result--active' : ''}" data-idx="${i}">
|
|
144
|
+
<span class="cp-result-name">${nameHighlighted}</span>
|
|
145
|
+
<span class="cp-result-dir">${dirHighlighted}</span>
|
|
146
|
+
</div>`;
|
|
147
|
+
}).join('');
|
|
148
|
+
|
|
149
|
+
// Scroll active item into view
|
|
150
|
+
const active = resultsList.querySelector('.cp-result--active') as HTMLElement;
|
|
151
|
+
active?.scrollIntoView({ block: 'nearest' });
|
|
152
|
+
|
|
153
|
+
// Click to select
|
|
154
|
+
resultsList.querySelectorAll('.cp-result').forEach(el => {
|
|
155
|
+
el.addEventListener('mousedown', (e) => {
|
|
156
|
+
e.preventDefault();
|
|
157
|
+
const idx = parseInt((el as HTMLElement).dataset.idx || '0');
|
|
158
|
+
selectResult(idx);
|
|
159
|
+
});
|
|
160
|
+
el.addEventListener('mouseenter', () => {
|
|
161
|
+
selectedIdx = parseInt((el as HTMLElement).dataset.idx || '0');
|
|
162
|
+
renderResults();
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function onInput(): void {
|
|
168
|
+
const query = input?.value || '';
|
|
169
|
+
if (!currentCtx) return;
|
|
170
|
+
|
|
171
|
+
const files = getAllFiles(currentCtx);
|
|
172
|
+
|
|
173
|
+
if (!query) {
|
|
174
|
+
// Show all files sorted by path
|
|
175
|
+
currentResults = files
|
|
176
|
+
.map(f => ({ path: f.path, name: f.name, dir: f.dir, score: 0, matchIndices: [] }))
|
|
177
|
+
.sort((a, b) => a.path.localeCompare(b.path))
|
|
178
|
+
.slice(0, 30);
|
|
179
|
+
} else {
|
|
180
|
+
currentResults = files
|
|
181
|
+
.map(f => {
|
|
182
|
+
const match = fuzzyMatch(query, f.path);
|
|
183
|
+
if (!match) return null;
|
|
184
|
+
return { path: f.path, name: f.name, dir: f.dir, score: match.score, matchIndices: match.indices };
|
|
185
|
+
})
|
|
186
|
+
.filter((r): r is SearchResult => r !== null)
|
|
187
|
+
.sort((a, b) => b.score - a.score)
|
|
188
|
+
.slice(0, 20);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
selectedIdx = 0;
|
|
192
|
+
renderResults();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function onKeyDown(e: KeyboardEvent): void {
|
|
196
|
+
switch (e.key) {
|
|
197
|
+
case 'ArrowDown':
|
|
198
|
+
e.preventDefault();
|
|
199
|
+
selectedIdx = Math.min(selectedIdx + 1, currentResults.length - 1);
|
|
200
|
+
renderResults();
|
|
201
|
+
break;
|
|
202
|
+
case 'ArrowUp':
|
|
203
|
+
e.preventDefault();
|
|
204
|
+
selectedIdx = Math.max(selectedIdx - 1, 0);
|
|
205
|
+
renderResults();
|
|
206
|
+
break;
|
|
207
|
+
case 'Enter':
|
|
208
|
+
e.preventDefault();
|
|
209
|
+
selectResult(selectedIdx);
|
|
210
|
+
break;
|
|
211
|
+
case 'Escape':
|
|
212
|
+
e.preventDefault();
|
|
213
|
+
close();
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function selectResult(idx: number): void {
|
|
219
|
+
const result = currentResults[idx];
|
|
220
|
+
if (result && currentCtx) {
|
|
221
|
+
close();
|
|
222
|
+
jumpToFile(currentCtx, result.path);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function open(ctx: CanvasContext): void {
|
|
227
|
+
currentCtx = ctx;
|
|
228
|
+
if (!overlay) createOverlay();
|
|
229
|
+
overlay!.style.display = 'flex';
|
|
230
|
+
input!.value = '';
|
|
231
|
+
selectedIdx = 0;
|
|
232
|
+
onInput(); // Show all files
|
|
233
|
+
|
|
234
|
+
// Focus after a tick to avoid the Ctrl+K keystroke appearing
|
|
235
|
+
requestAnimationFrame(() => input?.focus());
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function close(): void {
|
|
239
|
+
if (overlay) overlay.style.display = 'none';
|
|
240
|
+
currentCtx = null;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export function isCommandPaletteOpen(): boolean {
|
|
244
|
+
return overlay?.style.display === 'flex';
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// ── Init ─────────────────────────────────────────────────
|
|
248
|
+
|
|
249
|
+
export function initCommandPalette(ctx: CanvasContext): void {
|
|
250
|
+
document.addEventListener('keydown', (e) => {
|
|
251
|
+
// Ctrl+K, Ctrl+P, or Cmd+K/Cmd+P
|
|
252
|
+
if ((e.ctrlKey || e.metaKey) && (e.key === 'k' || e.key === 'p')) {
|
|
253
|
+
e.preventDefault();
|
|
254
|
+
e.stopPropagation();
|
|
255
|
+
if (isCommandPaletteOpen()) {
|
|
256
|
+
close();
|
|
257
|
+
} else {
|
|
258
|
+
open(ctx);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
}
|