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.
- package/README.md +267 -118
- package/app/[...slug]/page.client.tsx +1 -0
- package/app/[...slug]/page.tsx +6 -0
- package/app/analytics.db +0 -0
- package/app/api/analytics/route.ts +64 -0
- package/app/api/auth/positions/route.ts +95 -33
- package/app/api/build-info/route.ts +19 -0
- package/app/api/chat/route.ts +13 -2
- package/app/api/og-image/route.ts +14 -0
- package/app/api/repo/file-content/route.ts +73 -20
- package/app/api/repo/load/route.test.ts +62 -0
- package/app/api/repo/load/route.ts +41 -1
- package/app/api/repo/pdf-thumb/route.ts +127 -0
- package/app/api/repo/resolve-slug/route.ts +51 -0
- package/app/api/repo/tree/route.ts +188 -104
- package/app/api/version/route.ts +26 -0
- package/app/globals.css +5706 -4938
- package/app/layout.tsx +1279 -490
- package/app/lib/auto-arrange.test.ts +158 -0
- package/app/lib/auto-arrange.ts +147 -0
- package/app/lib/canvas-export.ts +358 -358
- package/app/lib/canvas.ts +625 -564
- package/app/lib/cards.tsx +1361 -916
- package/app/lib/chat.tsx +65 -9
- package/app/lib/code-editor.ts +86 -2
- package/app/lib/context.test.ts +32 -0
- package/app/lib/context.ts +19 -3
- package/app/lib/cursor-sharing.ts +34 -0
- package/app/lib/events.tsx +71 -93
- package/app/lib/export-canvas.ts +287 -0
- package/app/lib/file-card-plugin.ts +148 -148
- package/app/lib/file-modal.tsx +49 -0
- package/app/lib/file-preview.ts +486 -427
- package/app/lib/github-import.test.ts +424 -0
- package/app/lib/initial-route-hydration.test.ts +283 -0
- package/app/lib/initial-route-hydration.ts +202 -0
- package/app/lib/landing-reset.test.ts +99 -0
- package/app/lib/landing-reset.ts +106 -0
- package/app/lib/landing-shell.test.ts +75 -0
- package/app/lib/large-repo-optimization.ts +37 -0
- package/app/lib/layout-snapshots.ts +320 -0
- package/app/lib/loading.test.ts +69 -0
- package/app/lib/loading.tsx +160 -45
- package/app/lib/mount-cleanup.test.ts +52 -0
- package/app/lib/mount-cleanup.ts +34 -0
- package/app/lib/mount-init.test.ts +123 -0
- package/app/lib/mount-init.ts +107 -0
- package/app/lib/mount-lifecycle.test.ts +39 -0
- package/app/lib/mount-lifecycle.ts +12 -0
- package/app/lib/mount-route-wiring.test.ts +87 -0
- package/app/lib/mount-route-wiring.ts +84 -0
- package/app/lib/multi-repo.ts +14 -0
- package/app/lib/onboarding-tutorial.ts +278 -0
- package/app/lib/positions.ts +190 -121
- package/app/lib/recent-commits.test.ts +947 -0
- package/app/lib/recent-commits.ts +227 -0
- package/app/lib/repo-handoff.test.ts +23 -0
- package/app/lib/repo-handoff.ts +16 -0
- package/app/lib/repo-progressive.ts +119 -0
- package/app/lib/repo-select.test.ts +61 -0
- package/app/lib/repo-select.ts +74 -0
- package/app/lib/repo.tsx +1383 -987
- package/app/lib/role.ts +228 -0
- package/app/lib/route-catchall.test.ts +27 -0
- package/app/lib/route-repo-entry.test.ts +95 -0
- package/app/lib/route-repo-entry.ts +36 -0
- package/app/lib/router-contract.test.ts +22 -0
- package/app/lib/router-contract.ts +19 -0
- package/app/lib/shared-layout.test.ts +86 -0
- package/app/lib/shared-layout.ts +82 -0
- package/app/lib/status-bar.test.ts +118 -0
- package/app/lib/status-bar.ts +365 -128
- package/app/lib/sync-controls.test.ts +43 -0
- package/app/lib/sync-controls.tsx +303 -0
- package/app/lib/test-dom.ts +145 -0
- package/app/lib/test-fixtures/router-contract/[...slug]/page.tsx +3 -0
- package/app/lib/test-fixtures/router-contract/api/health/route.ts +3 -0
- package/app/lib/test-fixtures/router-contract/api/version/route.ts +3 -0
- package/app/lib/test-fixtures/router-contract/galaxy-canvas/page.tsx +3 -0
- package/app/lib/test-fixtures/router-contract/page.tsx +3 -0
- package/app/lib/transclusion-smoke.test.ts +163 -0
- package/app/lib/tutorial.ts +301 -0
- package/app/lib/version.ts +93 -0
- package/app/lib/viewport-culling.ts +740 -735
- package/app/lib/virtual-files.ts +456 -0
- package/app/lib/webgl-text.ts +189 -0
- package/app/lib/{galaxydraw-bridge.ts → xydraw-bridge.ts} +485 -482
- package/app/lib/{galaxydraw.test.ts → xydraw.test.ts} +228 -229
- package/app/og-image.png +0 -0
- package/app/page.client.tsx +70 -269
- package/app/page.tsx +15 -16
- package/app/state/machine.js +13 -0
- package/package.json +84 -75
- package/server.ts +10 -0
- package/app/[owner]/[repo]/page.tsx +0 -6
- package/app/[slug]/page.tsx +0 -6
- package/packages/galaxydraw/README.md +0 -296
- package/packages/galaxydraw/banner.png +0 -0
- package/packages/galaxydraw/demo/build-static.ts +0 -100
- package/packages/galaxydraw/demo/client.ts +0 -154
- package/packages/galaxydraw/demo/dist/client.js +0 -8
- package/packages/galaxydraw/demo/index.html +0 -256
- package/packages/galaxydraw/demo/server.ts +0 -96
- package/packages/galaxydraw/dist/index.js +0 -984
- package/packages/galaxydraw/dist/index.js.map +0 -16
- 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 +0 -49
- package/packages/galaxydraw/perf.test.ts +0 -284
- package/packages/galaxydraw/src/core/cards.ts +0 -435
- package/packages/galaxydraw/src/core/engine.ts +0 -339
- package/packages/galaxydraw/src/core/events.ts +0 -81
- package/packages/galaxydraw/src/core/layout.ts +0 -136
- package/packages/galaxydraw/src/core/minimap.ts +0 -216
- package/packages/galaxydraw/src/core/state.ts +0 -177
- package/packages/galaxydraw/src/core/viewport.ts +0 -106
- package/packages/galaxydraw/src/galaxydraw.css +0 -166
- package/packages/galaxydraw/src/index.ts +0 -40
- package/packages/galaxydraw/tsconfig.json +0 -30
package/README.md
CHANGED
|
@@ -1,161 +1,310 @@
|
|
|
1
|
-
|
|
2
|
-
<img src="banner.png" alt="GitMaps" width="100%" />
|
|
3
|
-
</p>
|
|
1
|
+
# 🗺️ GitMaps — Spatial Code Explorer
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
<img src="https://img.shields.io/badge/runtime-Bun-f472b6?style=flat-square" alt="Bun">
|
|
7
|
-
<img src="https://img.shields.io/badge/framework-Melina-7c3aed?style=flat-square" alt="Melina">
|
|
8
|
-
<img src="https://img.shields.io/badge/engine-GalaxyDraw-38bdf8?style=flat-square" alt="GalaxyDraw">
|
|
9
|
-
<img src="https://img.shields.io/badge/license-ISC-4ade80?style=flat-square" alt="License">
|
|
10
|
-
</p>
|
|
3
|
+
**Explore codebases on an infinite canvas instead of file trees.**
|
|
11
4
|
|
|
12
|
-
|
|
5
|
+
[](https://gitmaps.xyz)
|
|
6
|
+
[](https://npmjs.com/package/gitmaps)
|
|
7
|
+
[](LICENSE)
|
|
13
8
|
|
|
14
|
-
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 🎯 What is GitMaps?
|
|
12
|
+
|
|
13
|
+
GitMaps renders your entire repository on an infinite canvas with layers, git time-travel, and a minimap to never lose context.
|
|
14
|
+
|
|
15
|
+
**Traditional file trees are 1-dimensional. GitMaps gives you 5 dimensions:**
|
|
16
|
+
|
|
17
|
+
1. **1D — Lines** — Individual lines of code
|
|
18
|
+
2. **2D — Canvas** — Spatial arrangement of files
|
|
19
|
+
3. **3D — Layers** — Focus areas (Auth, API, UI)
|
|
20
|
+
4. **4D — Time** — Git history with persistent layout
|
|
21
|
+
5. **5D — Connections** — Links between related code
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 🚀 Quick Start
|
|
26
|
+
|
|
27
|
+
### Try Online (No Install)
|
|
28
|
+
Visit **[https://gitmaps.xyz](https://gitmaps.xyz)** and explore popular repos like React, Deno, or Svelte.
|
|
29
|
+
|
|
30
|
+
### Run Locally
|
|
31
|
+
```bash
|
|
32
|
+
git clone https://github.com/7flash/gitmaps.git
|
|
33
|
+
cd gitmaps
|
|
34
|
+
bun install
|
|
35
|
+
bun run dev
|
|
36
|
+
# Open http://localhost:3335
|
|
37
|
+
```
|
|
15
38
|
|
|
16
|
-
|
|
17
|
-
|
|
39
|
+
### Install Globally
|
|
40
|
+
```bash
|
|
41
|
+
bun install -g gitmaps
|
|
42
|
+
gitmaps /path/to/your/repo
|
|
43
|
+
```
|
|
18
44
|
|
|
19
45
|
---
|
|
20
46
|
|
|
21
47
|
## ✨ Features
|
|
22
48
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
### Core Experience
|
|
50
|
+
- 🗺️ **Infinite Canvas** — Pan, zoom, arrange files freely
|
|
51
|
+
- 📄 **File Cards** — Interactive cards with code preview and inline diffs
|
|
52
|
+
- 🧭 **Minimap** — Bird's eye view with click-to-navigate
|
|
53
|
+
- 📚 **Layers** — Organize files into focus layers
|
|
54
|
+
|
|
55
|
+
### Git Integration
|
|
56
|
+
- ⏳ **Commit Timeline** — Browse history in sidebar
|
|
57
|
+
- 📊 **Inline Diffs** — Green/red markers for changes
|
|
58
|
+
- 🔄 **Time Travel** — Navigate commits while layout persists
|
|
59
|
+
- 🔀 **Branch Comparison** — Compare branches side-by-side
|
|
60
|
+
|
|
61
|
+
### Performance
|
|
62
|
+
- ⚡ **WebGL Renderer** — Pixi.js GPU acceleration for 1000+ cards at 60fps
|
|
63
|
+
- 🎯 **Viewport Culling** — Only render visible cards
|
|
64
|
+
- 📦 **Progressive Loading** — Load large repos in batches
|
|
65
|
+
- ⏱️ **30s Timeout** — Large repo support (up from 5s)
|
|
66
|
+
|
|
67
|
+
### Collaboration
|
|
68
|
+
- 👑 **Leader Mode** — Full control when running locally
|
|
69
|
+
- 👁️ **Follower Mode** — Read-only view on production
|
|
70
|
+
- 🔄 **Sync Controls** — Push/pull canvas state to servers
|
|
71
|
+
- ⚡ **Auto-Sync** — Automatic position sync on changes
|
|
72
|
+
|
|
73
|
+
### UX Features
|
|
74
|
+
- 📊 **Progress Bar** — Visual feedback during loading
|
|
75
|
+
- 🖼️ **Image/Video Rendering** — Media files display inline
|
|
76
|
+
- 📷 **Canvas Export** — Save layouts as PNG/JPEG/WebP
|
|
77
|
+
- 🎓 **Onboarding Tutorial** — 10-step interactive guide
|
|
78
|
+
- 🔗 **Shareable URLs** — `/owner/repo#commit` format
|
|
79
|
+
- 📑 **Multi-Repo Tabs** — Load multiple repos simultaneously
|
|
80
|
+
- ⌨️ **Keyboard Shortcuts** — Power user shortcuts
|
|
45
81
|
|
|
46
|
-
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## 🎯 Use Cases
|
|
85
|
+
|
|
86
|
+
### AI Code Review
|
|
87
|
+
AI agents generate thousands of lines across dozens of files. GitMaps renders every file simultaneously so you can see the full picture, spot patterns, and review changes spatially instead of one-file-at-a-time.
|
|
88
|
+
|
|
89
|
+
### Architecture Exploration
|
|
90
|
+
Understand unfamiliar codebases faster by seeing how files relate spatially. Draw connections between related code across layers.
|
|
91
|
+
|
|
92
|
+
### Onboarding
|
|
93
|
+
New team members explore the codebase spatially. Senior devs create canvas arrangements showing key files and their relationships.
|
|
94
|
+
|
|
95
|
+
### Pair Programming
|
|
96
|
+
Driver runs locally (leader), navigator visits URL (follower). Both see same spatial arrangement.
|
|
97
|
+
|
|
98
|
+
### Legacy Code Understanding
|
|
99
|
+
Visualize connections and dependencies in complex legacy systems. Layers help focus on specific concerns.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 📊 Performance
|
|
104
|
+
|
|
105
|
+
| Repo Size | Renderer | Load Time | FPS |
|
|
106
|
+
|-----------|----------|-----------|-----|
|
|
107
|
+
| <100 files | DOM | <1s | 60 |
|
|
108
|
+
| 100-500 files | DOM | 1-3s | 60 |
|
|
109
|
+
| 500-1000 files | WebGL | 3-5s | 60 |
|
|
110
|
+
| 1000+ files | WebGL | 5-10s | 60 |
|
|
111
|
+
| 10000+ files | WebGL | 10-30s | 30-40 |
|
|
112
|
+
|
|
113
|
+
### Tested Repositories
|
|
114
|
+
- ✅ [7flash/gitmaps](https://gitmaps.xyz/7flash/gitmaps) — 9 files, <1s
|
|
115
|
+
- ✅ [facebook/react](https://gitmaps.xyz/facebook/react) — 100 commits, 2.8s
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## ⌨️ Keyboard Shortcuts
|
|
47
120
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
121
|
+
| Shortcut | Action |
|
|
122
|
+
|----------|--------|
|
|
123
|
+
| `Scroll` | Zoom in/out |
|
|
124
|
+
| `Space + Drag` | Pan canvas |
|
|
125
|
+
| `Click` | Select card |
|
|
126
|
+
| `Shift + Click` | Multi-select |
|
|
127
|
+
| `Drag Canvas` | Rect select |
|
|
128
|
+
| `Drag Card` | Move card |
|
|
129
|
+
| `Del` | Hide file |
|
|
130
|
+
| `H` | Arrange in row |
|
|
131
|
+
| `V` | Arrange in column |
|
|
132
|
+
| `G` | Arrange in grid |
|
|
133
|
+
| `W` | Fit to screen |
|
|
134
|
+
| `Ctrl + F` | Search across files |
|
|
135
|
+
| `Ctrl + O` | Find file |
|
|
136
|
+
| `Ctrl + G` | Toggle dependency graph |
|
|
137
|
+
| `Ctrl + +/-` | Text zoom |
|
|
138
|
+
| `Dbl-click` | Open editor |
|
|
139
|
+
| `Alt + Click` | Connect lines |
|
|
140
|
+
| `Arrow Keys` | Prev/next commit |
|
|
141
|
+
| `Ctrl + N` | New file |
|
|
142
|
+
| `Ctrl + S` | Save (in editor) |
|
|
143
|
+
| `?` | Show keyboard shortcuts |
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## 🏗️ Architecture
|
|
148
|
+
|
|
149
|
+
### Tech Stack
|
|
150
|
+
- **Runtime:** Bun / Node.js 18+
|
|
151
|
+
- **Framework:** Melina (full-stack TypeScript)
|
|
152
|
+
- **State:** XState for canvas state machine
|
|
153
|
+
- **Rendering:** DOM + Pixi.js (WebGL)
|
|
154
|
+
- **Editor:** CodeMirror 6
|
|
155
|
+
- **Database:** SQLite with Zod ORM
|
|
156
|
+
|
|
157
|
+
### Project Structure
|
|
52
158
|
```
|
|
159
|
+
gitmaps/
|
|
160
|
+
├── app/ # Main application
|
|
161
|
+
│ ├── api/ # API routes
|
|
162
|
+
│ ├── lib/ # Shared modules
|
|
163
|
+
│ ├── [owner]/[repo]/ # Dynamic routes
|
|
164
|
+
│ └── page.tsx # Main pages
|
|
165
|
+
├── packages/
|
|
166
|
+
│ └── galaxydraw/ # Rendering engine
|
|
167
|
+
│ ├── src/
|
|
168
|
+
│ │ ├── core/ # Core engine
|
|
169
|
+
│ │ └── webgl-renderer.ts # WebGL renderer
|
|
170
|
+
│ └── demo/ # Performance benchmarks
|
|
171
|
+
└── docs/ # Documentation
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
53
175
|
|
|
54
|
-
|
|
176
|
+
## 🚀 Deployment
|
|
55
177
|
|
|
56
|
-
|
|
178
|
+
### Production Server
|
|
179
|
+
```bash
|
|
180
|
+
# Clone repo
|
|
57
181
|
git clone https://github.com/7flash/gitmaps.git
|
|
58
|
-
cd
|
|
182
|
+
cd gitmaps
|
|
183
|
+
|
|
184
|
+
# Install dependencies
|
|
59
185
|
bun install
|
|
60
|
-
bun run dev
|
|
61
|
-
```
|
|
62
186
|
|
|
63
|
-
|
|
187
|
+
# Build
|
|
188
|
+
bun run build
|
|
64
189
|
|
|
190
|
+
# Start server
|
|
191
|
+
bun run server.ts
|
|
192
|
+
# Or use bgrun for production
|
|
193
|
+
bgrun start
|
|
65
194
|
```
|
|
66
|
-
|
|
195
|
+
|
|
196
|
+
### Environment Variables
|
|
197
|
+
```bash
|
|
198
|
+
# Server configuration
|
|
199
|
+
PORT=3335
|
|
200
|
+
NODE_ENV=production
|
|
201
|
+
|
|
202
|
+
# Database
|
|
203
|
+
DATABASE_PATH=./data/canvas_users.db
|
|
204
|
+
|
|
205
|
+
# GitHub OAuth (optional)
|
|
206
|
+
GITHUB_CLIENT_ID=your_client_id
|
|
207
|
+
GITHUB_CLIENT_SECRET=your_client_secret
|
|
67
208
|
```
|
|
68
209
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
##
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
| `Shift+J` / `Shift+K` | Next / previous changed file |
|
|
80
|
-
| `W` | Fit selected cards to screen |
|
|
81
|
-
| `H` | Arrange selected in a row |
|
|
82
|
-
| `V` | Arrange selected in a column |
|
|
83
|
-
| `G` | Arrange selected in a grid |
|
|
84
|
-
| `Ctrl+A` | Select all cards |
|
|
85
|
-
| `Del` / `Backspace` | Hide selected files |
|
|
86
|
-
| `Space+Drag` | Pan canvas |
|
|
87
|
-
| `Scroll` | Zoom in/out |
|
|
88
|
-
| `Ctrl+`/`Ctrl-` | Increase/decrease card font size |
|
|
89
|
-
| `I` | Toggle AI chat sidebar |
|
|
90
|
-
| `Alt+Click` | Start connection from clicked line |
|
|
91
|
-
| `Esc` | Cancel / deselect all |
|
|
92
|
-
| Double-click | Zoom to file |
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## 🧪 Testing
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
# Run tests
|
|
216
|
+
bun test
|
|
217
|
+
|
|
218
|
+
# Test specific module
|
|
219
|
+
bun test app/lib/
|
|
93
220
|
|
|
94
|
-
|
|
221
|
+
# Performance benchmarks
|
|
222
|
+
open packages/galaxydraw/demo/webgl-demo.html
|
|
223
|
+
```
|
|
95
224
|
|
|
96
|
-
|
|
225
|
+
---
|
|
97
226
|
|
|
98
|
-
|
|
99
|
-
2. Use the sidebar dropdown to load a second repo
|
|
100
|
-
3. Repos appear side-by-side with **zone labels** (color-coded floating badges)
|
|
101
|
-
4. **Sidebar tabs** switch commit timelines between repos
|
|
102
|
-
5. Each repo auto-offsets horizontally with an 800px gap
|
|
227
|
+
## 📚 Documentation
|
|
103
228
|
|
|
104
|
-
|
|
229
|
+
- [Getting Started Guide](../GETTING-STARTED.md)
|
|
230
|
+
- [Deployment Checklist](../DEPLOYMENT-CHECKLIST.md)
|
|
231
|
+
- [Launch Kit](../LAUNCH-KIT.md)
|
|
232
|
+
- [Session Report](../SESSION-FINAL-REPORT.md)
|
|
105
233
|
|
|
106
|
-
|
|
234
|
+
---
|
|
107
235
|
|
|
108
|
-
|
|
109
|
-
2. A **file picker** appears — search and select the target file
|
|
110
|
-
3. **Click a line** in the target file to complete the connection
|
|
236
|
+
## 🤝 Contributing
|
|
111
237
|
|
|
112
|
-
|
|
238
|
+
1. Fork the repo
|
|
239
|
+
2. Create a feature branch (`git checkout -b feat/amazing-feature`)
|
|
240
|
+
3. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
241
|
+
4. Push to the branch (`git push origin feat/amazing-feature`)
|
|
242
|
+
5. Open a Pull Request
|
|
113
243
|
|
|
114
|
-
|
|
244
|
+
### Development Setup
|
|
245
|
+
```bash
|
|
246
|
+
git clone https://github.com/7flash/gitmaps.git
|
|
247
|
+
cd gitmaps
|
|
248
|
+
bun install
|
|
249
|
+
bun run dev
|
|
250
|
+
```
|
|
115
251
|
|
|
116
|
-
|
|
252
|
+
---
|
|
117
253
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
-
|
|
254
|
+
## 📈 Roadmap
|
|
255
|
+
|
|
256
|
+
### v1.0.0 (Current) ✅
|
|
257
|
+
- ✅ Infinite canvas with pan/zoom
|
|
258
|
+
- ✅ Git integration with timeline
|
|
259
|
+
- ✅ Layers system
|
|
260
|
+
- ✅ WebGL rendering
|
|
261
|
+
- ✅ Leader/Follower mode
|
|
262
|
+
- ✅ Canvas export
|
|
263
|
+
- ✅ Onboarding tutorial
|
|
264
|
+
|
|
265
|
+
### v1.1.0 (Planned)
|
|
266
|
+
- [ ] Real-time collaboration (WebSocket cursors)
|
|
267
|
+
- [ ] Canvas snapshots (save/restore layouts)
|
|
268
|
+
- [ ] Advanced search with filters
|
|
269
|
+
- [ ] Plugin system
|
|
270
|
+
- [ ] Custom themes
|
|
271
|
+
|
|
272
|
+
### v2.0.0 (Future)
|
|
273
|
+
- [ ] AI-powered code analysis
|
|
274
|
+
- [ ] Automated architecture diagrams
|
|
275
|
+
- [ ] Integration with VS Code
|
|
276
|
+
- [ ] Team workspaces
|
|
277
|
+
- [ ] Comments and annotations
|
|
122
278
|
|
|
123
|
-
|
|
279
|
+
---
|
|
124
280
|
|
|
125
|
-
##
|
|
281
|
+
## 🙏 Acknowledgments
|
|
126
282
|
|
|
127
|
-
|
|
283
|
+
Inspired by **Ted Nelson's vision of intertwingularity** — the idea that everything is connected and hierarchical structures hide more relationships than they reveal.
|
|
128
284
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
| **Touch support** | Single-finger pan + pinch-to-zoom on tablets. |
|
|
136
|
-
| **Minimap** | Shows all files including deferred cards. Click to navigate. |
|
|
285
|
+
Built with:
|
|
286
|
+
- [Bun](https://bun.sh/) — Fast JavaScript runtime
|
|
287
|
+
- [Melina](https://github.com/7flash/melina) — Full-stack framework
|
|
288
|
+
- [Pixi.js](https://pixijs.com/) — WebGL rendering
|
|
289
|
+
- [CodeMirror](https://codemirror.net/) — Code editor
|
|
290
|
+
- [XState](https://xstate.js.org/) — State management
|
|
137
291
|
|
|
138
|
-
|
|
292
|
+
---
|
|
139
293
|
|
|
140
|
-
##
|
|
294
|
+
## 📄 License
|
|
141
295
|
|
|
142
|
-
|
|
296
|
+
ISC © [7flash](https://github.com/7flash)
|
|
143
297
|
|
|
144
|
-
|
|
145
|
-
- Folder browser endpoint completely blocked
|
|
146
|
-
- All 7 repo API routes validate paths via `validateRepoPath()`
|
|
298
|
+
---
|
|
147
299
|
|
|
148
|
-
##
|
|
300
|
+
## 🔗 Links
|
|
149
301
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
| Database | [sqlite-zod-orm](https://github.com/7flash/measure-fn) (positions, connections, layers) |
|
|
156
|
-
| Git | [simple-git](https://github.com/steveukx/git-js) |
|
|
157
|
-
| Profiling | [measure-fn](https://github.com/7flash/measure-fn) |
|
|
302
|
+
- **Live Demo:** https://gitmaps.xyz
|
|
303
|
+
- **GitHub:** https://github.com/7flash/gitmaps
|
|
304
|
+
- **npm:** https://npmjs.com/package/gitmaps
|
|
305
|
+
- **jsx-ai:** https://npmjs.com/package/jsx-ai
|
|
306
|
+
- **Twitter:** https://twitter.com/7flash
|
|
158
307
|
|
|
159
|
-
|
|
308
|
+
---
|
|
160
309
|
|
|
161
|
-
|
|
310
|
+
**Built with ❤️ on
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '../page.client';
|
package/app/analytics.db
ADDED
|
Binary file
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Database } from 'bun:sqlite';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
const DB_PATH = path.join(process.cwd(), 'app', 'analytics.db');
|
|
5
|
+
const db = new Database(DB_PATH);
|
|
6
|
+
|
|
7
|
+
db.run(`CREATE TABLE IF NOT EXISTS hits (
|
|
8
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
9
|
+
path TEXT NOT NULL,
|
|
10
|
+
referrer TEXT,
|
|
11
|
+
ua TEXT,
|
|
12
|
+
ts INTEGER NOT NULL DEFAULT (unixepoch())
|
|
13
|
+
)`);
|
|
14
|
+
|
|
15
|
+
db.run(`CREATE INDEX IF NOT EXISTS idx_hits_ts ON hits(ts)`);
|
|
16
|
+
db.run(`CREATE INDEX IF NOT EXISTS idx_hits_path ON hits(path)`);
|
|
17
|
+
|
|
18
|
+
const insertStmt = db.prepare(`INSERT INTO hits (path, referrer, ua) VALUES (?, ?, ?)`);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* POST /api/analytics — log a page hit
|
|
22
|
+
* Body: { path: string }
|
|
23
|
+
*/
|
|
24
|
+
export async function POST(req: Request) {
|
|
25
|
+
try {
|
|
26
|
+
const { path: pagePath } = await req.json() as { path: string };
|
|
27
|
+
const referrer = req.headers.get('referer') || '';
|
|
28
|
+
const ua = req.headers.get('user-agent') || '';
|
|
29
|
+
insertStmt.run(pagePath || '/', referrer, ua.slice(0, 200));
|
|
30
|
+
return Response.json({ ok: true });
|
|
31
|
+
} catch {
|
|
32
|
+
return Response.json({ ok: false }, { status: 400 });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* GET /api/analytics — return stats
|
|
38
|
+
* Query: ?hours=24 (default 24)
|
|
39
|
+
*/
|
|
40
|
+
export function GET(req: Request) {
|
|
41
|
+
const url = new URL(req.url);
|
|
42
|
+
const hours = parseInt(url.searchParams.get('hours') || '24', 10);
|
|
43
|
+
const since = Math.floor(Date.now() / 1000) - hours * 3600;
|
|
44
|
+
|
|
45
|
+
const total = db.prepare(`SELECT COUNT(*) as count FROM hits WHERE ts > ?`).get(since) as any;
|
|
46
|
+
const byPath = db.prepare(`SELECT path, COUNT(*) as count FROM hits WHERE ts > ? GROUP BY path ORDER BY count DESC LIMIT 20`).all(since);
|
|
47
|
+
const byHour = db.prepare(`
|
|
48
|
+
SELECT strftime('%Y-%m-%d %H:00', ts, 'unixepoch') as hour, COUNT(*) as count
|
|
49
|
+
FROM hits WHERE ts > ? GROUP BY hour ORDER BY hour
|
|
50
|
+
`).all(since);
|
|
51
|
+
const byReferrer = db.prepare(`
|
|
52
|
+
SELECT referrer, COUNT(*) as count FROM hits
|
|
53
|
+
WHERE ts > ? AND referrer != ''
|
|
54
|
+
GROUP BY referrer ORDER BY count DESC LIMIT 10
|
|
55
|
+
`).all(since);
|
|
56
|
+
|
|
57
|
+
return Response.json({
|
|
58
|
+
total: (total as any).count,
|
|
59
|
+
hours,
|
|
60
|
+
byPath,
|
|
61
|
+
byHour,
|
|
62
|
+
byReferrer,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
@@ -1,50 +1,112 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* GET /api/auth/positions?repo=<url> — Load saved positions for a repo
|
|
3
|
-
* POST /api/auth/positions — Save positions for a repo
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
3
|
+
* POST /api/auth/positions — Save positions for a repo (Leader only)
|
|
4
|
+
*
|
|
5
|
+
* Leader/Follower enforcement:
|
|
6
|
+
* - Leaders (localhost/local network): Can read/write positions
|
|
7
|
+
* - Followers (remote/production): Read-only access
|
|
8
|
+
*
|
|
9
|
+
* This prevents unauthorized canvas modifications on production servers.
|
|
7
10
|
*/
|
|
8
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
getSessionFromRequest,
|
|
13
|
+
loadRepoPositions,
|
|
14
|
+
saveRepoPositions,
|
|
15
|
+
} from "../../../lib/auth";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Detect if request is from a leader (local) or follower (remote)
|
|
19
|
+
* Based on IP address - localhost and local network = leader
|
|
20
|
+
*/
|
|
21
|
+
function isLeaderRequest(req: Request): boolean {
|
|
22
|
+
const forwarded = req.headers.get("x-forwarded-for");
|
|
23
|
+
const ip =
|
|
24
|
+
forwarded?.split(",")[0]?.trim() ||
|
|
25
|
+
req.headers.get("x-real-ip") ||
|
|
26
|
+
"unknown";
|
|
27
|
+
|
|
28
|
+
// Leader: localhost, local network IPs
|
|
29
|
+
const leaderPatterns = [
|
|
30
|
+
/^127\./, // 127.0.0.1
|
|
31
|
+
/^::1$/, // IPv6 localhost
|
|
32
|
+
/^192\.168\./, // Private network
|
|
33
|
+
/^10\./, // Private network
|
|
34
|
+
/^172\.(1[6-9]|2[0-9]|3[0-1])\./, // Private network
|
|
35
|
+
/^localhost$/i,
|
|
36
|
+
/^unknown$/i, // No IP header = likely local dev
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
return leaderPatterns.some((pattern) => pattern.test(ip));
|
|
40
|
+
}
|
|
9
41
|
|
|
10
42
|
export async function GET(req: Request) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
43
|
+
const url = new URL(req.url);
|
|
44
|
+
const repoUrl = url.searchParams.get("repo");
|
|
45
|
+
if (!repoUrl) {
|
|
46
|
+
return Response.json({ error: "repo param required" }, { status: 400 });
|
|
47
|
+
}
|
|
15
48
|
|
|
16
|
-
|
|
17
|
-
const repoUrl = url.searchParams.get('repo');
|
|
18
|
-
if (!repoUrl) {
|
|
19
|
-
return Response.json({ error: 'repo param required' }, { status: 400 });
|
|
20
|
-
}
|
|
49
|
+
const user = getSessionFromRequest(req);
|
|
21
50
|
|
|
22
|
-
|
|
51
|
+
// Allow unauthenticated reads (for public repos)
|
|
52
|
+
if (!user) {
|
|
53
|
+
// Try to load from guest account (userId 0) or return empty
|
|
54
|
+
const positionsJson = loadRepoPositions(0, repoUrl);
|
|
23
55
|
return Response.json({
|
|
24
|
-
|
|
25
|
-
|
|
56
|
+
positions: positionsJson ? JSON.parse(positionsJson) : null,
|
|
57
|
+
repoUrl,
|
|
58
|
+
authenticated: false,
|
|
26
59
|
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const positionsJson = loadRepoPositions(user.id, repoUrl);
|
|
63
|
+
return Response.json({
|
|
64
|
+
positions: positionsJson ? JSON.parse(positionsJson) : null,
|
|
65
|
+
repoUrl,
|
|
66
|
+
authenticated: true,
|
|
67
|
+
});
|
|
27
68
|
}
|
|
28
69
|
|
|
29
70
|
export async function POST(req: Request) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
71
|
+
// Enforce leader-only writes
|
|
72
|
+
const isLeader = isLeaderRequest(req);
|
|
73
|
+
if (!isLeader) {
|
|
74
|
+
return Response.json(
|
|
75
|
+
{
|
|
76
|
+
error:
|
|
77
|
+
"Write access denied: Follower mode (read-only). Run GitMaps locally to edit canvas.",
|
|
78
|
+
code: "FOLLOWER_READ_ONLY",
|
|
79
|
+
},
|
|
80
|
+
{ status: 403 },
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const user = getSessionFromRequest(req);
|
|
34
85
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
repoUrl: string;
|
|
38
|
-
positions: Record<string, any>;
|
|
39
|
-
};
|
|
86
|
+
// Allow local dev without auth (guest mode - use userId 0)
|
|
87
|
+
const userId = user?.id ?? 0;
|
|
40
88
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
89
|
+
try {
|
|
90
|
+
const body = (await req.json()) as {
|
|
91
|
+
repoUrl: string;
|
|
92
|
+
positions: Record<string, any>;
|
|
93
|
+
};
|
|
44
94
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
} catch (err: any) {
|
|
48
|
-
return Response.json({ error: err.message }, { status: 400 });
|
|
95
|
+
if (!body.repoUrl) {
|
|
96
|
+
return Response.json({ error: "repoUrl required" }, { status: 400 });
|
|
49
97
|
}
|
|
98
|
+
|
|
99
|
+
saveRepoPositions(
|
|
100
|
+
userId,
|
|
101
|
+
body.repoUrl,
|
|
102
|
+
JSON.stringify(body.positions || {}),
|
|
103
|
+
);
|
|
104
|
+
return Response.json({
|
|
105
|
+
ok: true,
|
|
106
|
+
mode: "leader",
|
|
107
|
+
syncedAt: new Date().toISOString(),
|
|
108
|
+
});
|
|
109
|
+
} catch (err: any) {
|
|
110
|
+
return Response.json({ error: err.message }, { status: 400 });
|
|
111
|
+
}
|
|
50
112
|
}
|