Skip to content

Commit cd422fe

Browse files
committed
feat(file) multi-platform file handlers
1 parent c6c54b2 commit cd422fe

File tree

13 files changed

+634
-180
lines changed

13 files changed

+634
-180
lines changed

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@
5050
- **Web-based text art drawing, also works offline as a PWA**
5151
- No install required!
5252
- But easily [installed as a Progressive Web Application](docs/install-pwa.md) to your device
53+
- **Multi-platform file opening**
54+
- Desktop: OS "Open with" integration (Chrome/Edge)
55+
- Android: Share sheet integration
56+
- iPad+iOS: Enhanced file picker
57+
- Drag-and-drop support for everyone!
5358
- **Comprehensive keyboard shortcuts and mouse controls**
5459
- Draw using the keyboard, mouse, or touch screen
5560
- **Classic and modern fonts**
@@ -86,6 +91,7 @@
8691
- `*.bin`: DOS-era BIN
8792
- `*.xbin`: Modern XBIN
8893
- `*.nfo`: Scene/release NFO
94+
- `*.diz`: FILE_ID.DIZ release files
8995
- `*.txt`: ASCII or other plain text
9096
- `*.png`: Image (export support only)
9197

@@ -326,7 +332,7 @@ bun www # or npm run www
326332
dist/
327333
├── index.html # Main entry point
328334
├── site.webmanifest # PWA manifest
329-
├── service.js # Service worker
335+
├── service.js # Service worker (injectManifest strategy)
330336
├── workbox-[hash].js # Workbox runtime for caching
331337
├── robots.txt # Search engine directives
332338
├── sitemap.xml # Site map

docs/architecture.md

Lines changed: 105 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -23,28 +23,34 @@ teXt0wnz is a Progressive Web Application (PWA) for creating and editing text-mo
2323
2. **Collaborative mode** - Real-time multi-user editing via WebSocket server
2424

2525
```
26-
┌─────────────────────────────────────────────────────────────┐
27-
│ Browser Client │
28-
│ ┌─────────────┐ ┌──────────────┐ ┌───────────────┐ │
29-
│ │ UI Layer │ │ Canvas Layer │ │ Storage Layer │ │
30-
│ │ (Controls) │ │ (Rendering) │ │ (IndexedDB) │ │
31-
│ └─────────────┘ └──────────────┘ └───────────────┘ │
32-
│ │ │ │ │
33-
│ └────────────────┴───────────────────┘ │
34-
│ │ │
35-
│ State Management │
36-
│ │ │
37-
└──────────────────────────┼──────────────────────────────────┘
26+
┌────────────────────────────────────────────────────────┐
27+
│ Browser Client │
28+
│ ┌─────────────┐ ┌──────────────┐ ┌───────────────┐ │
29+
│ │ UI Layer │ │ Canvas Layer │ │ Storage Layer │ │
30+
│ │ (Controls) │ │ (Rendering) │ │ (IndexedDB) │ │
31+
│ └─────────────┘ └──────────────┘ └───────────────┘ │
32+
│ │ │ │ │
33+
│ └────────────────┴───────────────────┘ │
34+
│ │ │
35+
│ State Management │
36+
│ │ │
37+
└──────────────────────────┼─────────────────────────────┘
38+
39+
┌────────┼────────┐
40+
│ │ │
41+
Service Worker │ File System APIs
42+
(Offline/Share) │ (File Handlers)
43+
3844
3945
Optional WebSocket
4046
41-
┌──────────────────────────┼──────────────────────────────────
42-
│ Collaboration Server
43-
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐
44-
│ │ WebSocket │ │ Session Mgmt │ │ File Storage │
45-
│ │ Handlers │ │ (Canvas) │ │ (Disk) │
46-
│ └─────────────┘ └──────────────┘ └──────────────┘
47-
└─────────────────────────────────────────────────────────────
47+
┌──────────────────────────┼────────────────────────────┐
48+
│ Collaboration Server │
49+
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ │
50+
│ │ WebSocket │ │ Session Mgmt │ │ File Storage │ │
51+
│ │ Handlers │ │ (Canvas) │ │ (Disk) │ │
52+
│ └─────────────┘ └──────────────┘ └──────────────┘ │
53+
└───────────────────────────────────────────────────────┘
4854
```
4955

5056
## Application Modes
@@ -106,10 +112,10 @@ User Action → State Update → Canvas Render → WebSocket Send → Server Bro
106112
│ Rendering Engine, Font Management, Dirty Tracking │
107113
└─────────────────┬────────────────────────────────────┘
108114
109-
┌─────────────────┴────────────────────────────────────┐
110-
│ Data Layer │
111-
│ Storage (IndexedDB), File I/O, Network (WebSocket) │
112-
└──────────────────────────────────────────────────────┘
115+
┌─────────────────┴────────────────────────────────────────────────────
116+
Data Layer
117+
│ Storage (IndexedDB), File I/O, Network (WebSocket), PWA (Caching)
118+
└──────────────────────────────────────────────────────────────────────
113119
```
114120

115121
### Core Modules
@@ -170,10 +176,35 @@ User Action → State Update → Canvas Render → WebSocket Send → Server Bro
170176
- ANSI format (.ans, .utf8.ans)
171177
- Binary format (.bin)
172178
- XBIN format (.xb)
173-
- NFO format (.nfo)
179+
- Scene release formats (.nfo, .diz)
174180
- Plain text (.txt)
181+
- Full SAUCE metadata support
175182
- PNG export
176-
- SAUCE metadata support
183+
184+
**File Opening Methods:**
185+
186+
- Traditional file picker (all platforms)
187+
- Drag-and-drop (all platforms)
188+
- OS "Open with" (Desktop Chrome/Edge via File Handlers API)
189+
- Share sheet (Android via Share Target API)
190+
- iOS workaround (`accept="*/*"` for broader file access)
191+
192+
**Service Worker** (`service.js`)
193+
194+
- Offline support and caching
195+
- Share Target API (Android file sharing)
196+
- Runtime caching strategies
197+
- Workbox-based precaching
198+
- Stale file cleanup
199+
200+
**PWA Capabilities** (`site.webmanifest`)
201+
202+
- File Handlers API (Desktop "Open with" support)
203+
- Share Target API (Mobile share sheet integration)
204+
- Multi-platform file opening:
205+
- Desktop: OS "Open with" → File Handlers API
206+
- Android: Share sheet → Share Target API
207+
- iOS: Manual file picker with `accept="*/*"` hack
177208

178209
**Color Management** (`palette.js`)
179210

@@ -490,24 +521,26 @@ When a drawing occurs:
490521
### Client Modules
491522

492523
```
493-
src/js/client/
494-
├── main.js # Application entry point
495-
├── state.js # Global state management
496-
├── canvas.js # Canvas rendering engine
497-
├── ui.js # User interface components
498-
├── toolbar.js # Toolbar management
499-
├── palette.js # Color palette
500-
├── keyboard.js # Keyboard mode and shortcuts
501-
├── freehandTools.js # Drawing tools
502-
├── file.js # File I/O operations
503-
├── network.js # Network communication
504-
├── websocket.js # WebSocket worker
505-
├── font.js # Font loading and rendering
506-
├── lazyFont.js # Lazy font loading
507-
├── fontCache.js # Font caching
508-
├── storage.js # IndexedDB persistence
509-
├── compression.js # Data compression
510-
└── magicNumbers.js # Constants and magic values
524+
src/
525+
├── js/client/
526+
│   ├── main.js # Application entry point
527+
│   ├── state.js # Global state management
528+
│   ├── canvas.js # Canvas rendering engine
529+
│   ├── ui.js # User interface components
530+
│   ├── toolbar.js # Toolbar management
531+
│   ├── palette.js # Color palette
532+
│   ├── keyboard.js # Keyboard mode and shortcuts
533+
│   ├── freehandTools.js # Drawing tools
534+
│   ├── file.js # File I/O operations
535+
│   ├── network.js # Network communication
536+
│   ├── websocket.js # WebSocket worker
537+
│   ├── font.js # Font loading and rendering
538+
│   ├── lazyFont.js # Lazy font loading
539+
│   ├── fontCache.js # Font caching
540+
│   ├── storage.js # IndexedDB persistence
541+
│   ├── compression.js # Data compression
542+
│   └── magicNumbers.js # Constants and magic values
543+
└── service.js # PWA service worker
511544
```
512545

513546
### Server Modules
@@ -655,6 +688,35 @@ const saveToIndexedDB = debounce(() => {
655688
}, 500);
656689
```
657690

691+
### Service Worker Cache (Cache API)
692+
693+
**Purpose:** Temporary storage for shared files and offline support
694+
695+
**Cache: `text0wnz-shared-files`**
696+
697+
**Usage:**
698+
699+
- Stores files shared via Share Target API (Android)
700+
- Temporary cache cleared after file is opened
701+
- Stale file cleanup on service worker activation
702+
703+
**Cache Strategy:**
704+
705+
```javascript
706+
// Service worker handles POST to /open
707+
self.addEventListener('fetch', event => {
708+
if (url.pathname === '/open' && event.request.method === 'POST') {
709+
// Cache shared file temporarily
710+
event.respondWith(handleShareTarget(event.request));
711+
}
712+
});
713+
```
714+
715+
**Cache Cleanup:**
716+
717+
- Files deleted immediately after opening in main app
718+
- Stale files cleaned on service worker activation
719+
658720
### Server Storage (File System)
659721

660722
**Session Files:**

docs/building-and-developing.md

Lines changed: 100 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,50 @@ dist/
9292

9393
### Vite Plugins
9494

95+
#### vite-plugin-pwa (PWA Support)
96+
97+
**Strategy:** `injectManifest` (custom service worker)
98+
99+
Generates Progressive Web App files:
100+
101+
- `service.js` - Custom service worker with Workbox manifest injection
102+
- `site.webmanifest` - PWA manifest with file handling capabilities
103+
- Workbox runtime for precaching and caching strategies
104+
105+
**Features:**
106+
107+
**Offline & Caching:**
108+
109+
- Custom runtime caching strategies (assets, scripts, styles, HTML)
110+
- Workbox-based precaching with `__WB_MANIFEST` injection
111+
- Cache-first strategy for static assets
112+
- Auto-update on new service worker versions
113+
114+
**File Handling (Multi-Platform):**
115+
116+
- **Desktop (Chrome/Edge):** File Handlers API for OS "Open with" integration
117+
- **Android:** Share Target API for share sheet integration
118+
- **iOS:** Manual file picker with `accept="*/*"` workaround
119+
120+
**Supported File Types:**
121+
122+
- Text art: `.ans`, `.ansi`, `.xb`, `.xbin`, `.bin`, `.nfo`, `.diz`, `.utf8ans`
123+
- Plain text: `.txt`
124+
- MIME types: `text/plain`, `text/*`, `application/octet-stream`
125+
126+
**Share Target Handler:**
127+
128+
- Receives files via POST to `/open`
129+
- Caches files temporarily in `text0wnz-shared-files` cache
130+
- Main app retrieves and deletes cached files after opening
131+
- Stale file cleanup on service worker activation
132+
133+
**Service Worker Location:**
134+
135+
- Source: `src/service.js` (custom, not auto-generated)
136+
- Build output: `dist/service.js`
137+
- Uses `injectManifest` strategy (not `generateSW`)
138+
95139
#### vite-plugin-static-copy
96140

97141
Copies static assets to the build directory:
@@ -101,21 +145,6 @@ Copies static assets to the build directory:
101145
- Manifest icons
102146
- `humans.txt`
103147

104-
#### vite-plugin-pwa (PWA Support)
105-
106-
Generates Progressive Web App files:
107-
108-
- `service.js` - Service worker for offline support
109-
- `site.webmanifest` - PWA manifest
110-
- Workbox runtime for caching strategies
111-
112-
**Features:**
113-
114-
- Offline support
115-
- Install to home screen
116-
- Auto-update on new versions
117-
- Cache-first strategy for assets
118-
119148
#### vite-plugin-sitemap
120149

121150
Generates SEO files:
@@ -558,18 +587,42 @@ docs/
558587

559588
### 2. PWA Generation
560589

561-
**Service Worker:**
562-
563-
- Generated by vite-plugin-pwa
564-
- Precaches critical assets
565-
- Implements offline support
566-
- Cache-first strategy for static assets
567-
568-
**Manifest:**
569-
570-
- `site.webmanifest` with app metadata
571-
- Icons, screenshots, theme colors
572-
- Install prompts and app info
590+
**Service Worker (`service.js`):**
591+
592+
- **Custom implementation** using `injectManifest` strategy
593+
- Source: `src/service.js` (manually written, not auto-generated)
594+
- Workbox manifest (`__WB_MANIFEST`) injected at build time
595+
- Custom runtime caching strategies:
596+
- Images: `CacheFirst``asset-cache`
597+
- Scripts: `CacheFirst``app-cache`
598+
- Styles: `CacheFirst``style-cache`
599+
- HTML: `CacheFirst``html-cache`
600+
- Share Target API handler for Android file sharing
601+
- File Handlers API support for Desktop file opening
602+
- Stale shared file cleanup on activation
603+
604+
**Manifest (`site.webmanifest`):**
605+
606+
- App metadata: name, description, theme colors
607+
- Icons and screenshots for install prompts
608+
- **Share Target configuration:**
609+
- Action: `/open` (POST endpoint)
610+
- Accepts: Text art files, plain text, any text MIME type
611+
- **File Handlers configuration:**
612+
- Desktop "Open with" integration
613+
- Registered file extensions and MIME types
614+
- Launches app when files are opened from OS
615+
616+
**Build Configuration:**
617+
618+
```javascript
619+
VitePWA({
620+
strategies: 'injectManifest', // Custom service worker
621+
srcDir: '.', // Source at root level
622+
filename: 'service.js', // Service worker filename
623+
// ...
624+
});
625+
```
573626

574627
### 3. SEO Files
575628

@@ -663,12 +716,28 @@ This logs:
663716
- Gzip/Brotli compression in nginx
664717
- Hashed filenames enable aggressive browser caching
665718

719+
**PWA Optimization:**
720+
721+
**Service Worker:**
722+
723+
- Custom `injectManifest` strategy for full control
724+
- Workbox precaching for critical assets
725+
- Runtime caching with cache-first strategy
726+
- Regex-safe UI directory path construction from env vars
727+
728+
**File Handling:**
729+
730+
- Temporary cache for shared files (auto-cleanup)
731+
- Stale file cleanup on service worker activation
732+
- Non-blocking file operations (Cache API)
733+
- Platform-specific optimizations (iOS `accept="*/*"`)
734+
666735
**Cache Strategy:**
667736

668-
- Service worker caching with cache-first strategy
669-
- Workbox runtime for efficient offline support
670-
- Font cache for instant font switching
671-
- Precaching of critical assets for offline use
737+
- Service worker cache-first for static assets
738+
- Font cache for instant font switching (client-side)
739+
- IndexedDB for canvas persistence
740+
- Aggressive browser caching with hashed filenames
672741

673742
## Troubleshooting
674743

0 commit comments

Comments
 (0)