File size: 10,469 Bytes
9575119
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# ColorRM Pro

A real-time collaborative PDF annotation and drawing application built with modern web technologies.

## Architecture Overview

```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         Frontend                                 β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚  β”‚                    color_rm.html                          β”‚   β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚   β”‚
β”‚  β”‚  β”‚ ColorRm     β”‚  β”‚ ColorRm     β”‚  β”‚ ColorRm         β”‚   β”‚   β”‚
β”‚  β”‚  β”‚ Session.js  │──│ Renderer.js │──│ LiveSync.js     β”‚   β”‚   β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚   β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         Backend                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚  Cloudflare Workers  β”‚    β”‚  HuggingFace Spaces         β”‚    β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚    β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚    β”‚
β”‚  β”‚  β”‚ worker.ts      β”‚  β”‚    β”‚  β”‚ Vite Dev Server       β”‚  β”‚    β”‚
β”‚  β”‚  β”‚ - API Routes   β”‚  β”‚    β”‚  β”‚ - Static files        β”‚  β”‚    β”‚
β”‚  β”‚  β”‚ - R2 Storage   β”‚  β”‚    β”‚  β”‚ - PDF Convert (7861)  β”‚  β”‚    β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚    β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚    β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β”‚  β”‚  β”‚ Durable Objectsβ”‚  β”‚                                        β”‚
β”‚  β”‚  β”‚ - ColorRm DO   β”‚  β”‚    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚    β”‚  Liveblocks                 β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚  - Real-time sync           β”‚    β”‚
β”‚                              β”‚  - Presence                  β”‚    β”‚
β”‚                              β”‚  - Room management           β”‚    β”‚
β”‚                              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

## Key Modules

### Frontend (`public/scripts/modules/`)

| Module | Description |
|--------|-------------|
| `ColorRmSession.js` | Main application controller. Handles state, tools, page management, import/export |
| `ColorRmRenderer.js` | Canvas rendering engine. Draws strokes, shapes, images, handles transformations |
| `ColorRmStorage.js` | IndexedDB and R2 storage. Manages local persistence and cloud sync |
| `ColorRmLiveSync.js` | Liveblocks integration. Real-time collaboration, cursors, history sync |
| `ColorRmSvgImporter.js` | SVG parsing and import. Converts SVG elements to ColorRM history items |
| `ColorRmSvgExporter.js` | SVG export. Renders history to vector SVG format |

### Backend (`worker/`)

| File | Description |
|------|-------------|
| `worker.ts` | Main Cloudflare Worker entry point. Routes API requests |
| `colorRmAssets.ts` | R2 storage handlers for pages, history, modifications |
| `pdfToSvg.ts` | PDF conversion job management (placeholder for CF Workers) |
| `ColorRmDurableObject.ts` | Durable Object for room state management |

### Commands (`cmd/`)

| Script | Description |
|--------|-------------|
| `pdf_convert_server.mjs` | Local PDF to SVG server using pdf2svg binary |
| `hf_init.mjs` | HuggingFace authentication initialization |
| `hf_backup.mjs` | Periodic backup to HuggingFace Hub |
| `hf_restore.mjs` | Restore from HuggingFace Hub on startup |

## Data Flow

### SVG Import Flow

```
1. User selects SVG file
   β”‚
2. ColorRmSvgImporter.importSvg() parses SVG
   β”‚
3. Elements are classified:
   β”œβ”€β”€ Images/Text β†’ Rasterized to background blob
   └── Strokes/Shapes β†’ Kept as vector history items
   β”‚
4. Page object created with blob + history
   β”‚
5. Sync to cloud:
   β”œβ”€β”€ Blob β†’ R2 via _uploadPageBlob()
   β”œβ”€β”€ History β†’ R2 via /api/color_rm/history/:sessionId/:pageId
   └── Structure β†’ Liveblocks via _syncPageStructureToLive()
   β”‚
6. Other users receive notification via Liveblocks
   β”‚
7. Other users fetch page blob from R2
```

### History Sync (Delta Architecture)

For pages with base history (SVG imports):

```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    R2 Storage (Base)                         β”‚
β”‚  /api/color_rm/history/:sessionId/:pageId                   β”‚
β”‚  - Original SVG items (large, stored once)                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              +
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                 Liveblocks (Deltas Only)                     β”‚
β”‚  - New strokes added by users                               β”‚
β”‚  - Modifications to base items (position, color, etc.)      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              =
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Final Rendered Page                       β”‚
β”‚  BaseHistory + Deltas + Modifications                       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

### Thresholds

| Condition | Threshold | Action |
|-----------|-----------|--------|
| SVG page history | > 400 items | Use R2 for base, Liveblocks for deltas |
| Regular page history | > 2000 items | Convert to hybrid R2/Liveblocks |
| Modifications | > 100 items | Store in R2, sync metadata via Liveblocks |

## API Endpoints

### Page Management

| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | `/api/color_rm/page/:sessionId/:pageId` | Upload page blob |
| GET | `/api/color_rm/page/:sessionId/:pageId` | Download page blob |
| DELETE | `/api/color_rm/page/:sessionId/:pageId` | Delete page |

### History

| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | `/api/color_rm/history/:sessionId/:pageId` | Upload base history |
| GET | `/api/color_rm/history/:sessionId/:pageId` | Get base history |

### Page Structure

| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/color_rm/page_structure/:sessionId` | Get page order |
| POST | `/api/color_rm/page_structure/:sessionId` | Update page order |

### PDF Conversion (Local Server)

| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | `/convert/pdf` | Upload PDF for conversion |
| GET | `/convert/status/:jobId` | Check conversion status |
| GET | `/convert/page/:jobId/:pageNum` | Download converted SVG page |

## Development

### Local Development

```bash
npm install
npm run dev
```

### HuggingFace Spaces Deployment

The Dockerfile runs:
1. `hf_init.mjs` - Authenticate with HF
2. `hf_restore.mjs` - Restore previous state
3. `npm run dev` - Vite dev server (port 7860)
4. `hf_backup.mjs` - Periodic backup worker
5. `pdf_convert_server.mjs` - PDF conversion (port 7861)

### Environment Variables

| Variable | Description |
|----------|-------------|
| `HF_TOKEN` | HuggingFace API token |
| `LIVEBLOCKS_SECRET_KEY` | Liveblocks API key |
| `PORT` | Dev server port (default: 7860) |

## Tools

| Tool | Shortcut | Description |
|------|----------|-------------|
| Move | V | Select and move objects |
| Hand | H | Pan the canvas |
| Lasso | L | Free-form selection |
| Pen | P | Freehand drawing |
| Shape | S | Draw shapes (rect, ellipse, arrow, etc.) |
| Text | T | Add text annotations |
| Eraser | E | Erase strokes |
| Box/Capture | B | Capture region to clipboard |

## Collaboration Features

- **Real-time cursors**: See other users' cursors
- **Live history sync**: Strokes appear instantly for all users
- **Page structure sync**: Page order synchronized across users
- **Presence indicators**: See who's online in the room