@gallop.software/studio 2.3.173 → 2.4.1
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 +307 -30
- package/bin/studio.mjs +65 -20
- package/dist/cli/index.js +1355 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/client/assets/index-2UG4789Y.js +136 -0
- package/dist/client/assets/index-CdEhFsGP.js +136 -0
- package/dist/client/assets/index-Df8S-Sk8.js +138 -0
- package/dist/client/assets/index-SbUQtkcp.js +136 -0
- package/dist/client/index.html +1 -1
- package/dist/server/index.js +2512 -2446
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @gallop.software/studio
|
|
2
2
|
|
|
3
|
-
Standalone media manager for Gallop templates. Upload, process, and sync images to Cloudflare R2 CDN. Manage fonts with visual tools.
|
|
3
|
+
Standalone media manager for Gallop templates. Upload, process, and sync images to Cloudflare R2 CDN. Manage fonts with visual tools. Run headless via CLI.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
@@ -8,13 +8,15 @@ Standalone media manager for Gallop templates. Upload, process, and sync images
|
|
|
8
8
|
|
|
9
9
|
- **Standalone dev server** - runs on its own port, doesn't affect your app
|
|
10
10
|
- **Upload any file type** with drag-and-drop support and progress tracking
|
|
11
|
-
- **Automatic thumbnail generation** for images
|
|
11
|
+
- **Automatic thumbnail generation** for images (sm, md, lg, full)
|
|
12
12
|
- **Browse folders** with grid and list views
|
|
13
13
|
- **Multi-select** for batch operations (delete, move, download)
|
|
14
14
|
- **Push to CDN** (Cloudflare R2) with automatic local cleanup
|
|
15
15
|
- **Cache purge** for custom CDN domains
|
|
16
16
|
- **Blurhash** generation for image placeholders
|
|
17
17
|
- **Image editing** - crop, resize, rotate, and adjust quality
|
|
18
|
+
- **Favicon generation** from any image
|
|
19
|
+
- **Featured image** generation with customizable options
|
|
18
20
|
|
|
19
21
|
### Font Management
|
|
20
22
|
|
|
@@ -26,6 +28,13 @@ Standalone media manager for Gallop templates. Upload, process, and sync images
|
|
|
26
28
|
- **Folder status badges** - visual indicators for TTF-only, WOFF2-ready, and assigned folders
|
|
27
29
|
- **Batch operations** - rename folders, delete files, with streaming progress
|
|
28
30
|
|
|
31
|
+
### CLI Commands
|
|
32
|
+
|
|
33
|
+
- **Headless operation** - run scan, process, push, and download without the UI
|
|
34
|
+
- **Prefix filtering** - target specific folders (e.g. `studio process portfolio`)
|
|
35
|
+
- **Font conversion** - convert TTF/OTF to WOFF2 from the command line
|
|
36
|
+
- **Font assignment** - generate font config files without the UI
|
|
37
|
+
|
|
29
38
|
## Installation
|
|
30
39
|
|
|
31
40
|
```bash
|
|
@@ -34,25 +43,7 @@ npm install @gallop.software/studio --save-dev
|
|
|
34
43
|
|
|
35
44
|
## Quick Start
|
|
36
45
|
|
|
37
|
-
### 1.
|
|
38
|
-
|
|
39
|
-
Create a `.env.studio` file in your project root:
|
|
40
|
-
|
|
41
|
-
```bash
|
|
42
|
-
# Dev site link (opens in new tab from Studio header)
|
|
43
|
-
STUDIO_DEV_SITE_URL=http://localhost:3000
|
|
44
|
-
|
|
45
|
-
# Cloudflare R2 Storage
|
|
46
|
-
CLOUDFLARE_R2_ACCOUNT_ID=your_account_id
|
|
47
|
-
CLOUDFLARE_R2_ACCESS_KEY_ID=your_access_key
|
|
48
|
-
CLOUDFLARE_R2_SECRET_ACCESS_KEY=your_secret_key
|
|
49
|
-
CLOUDFLARE_R2_BUCKET_NAME=your_bucket
|
|
50
|
-
CLOUDFLARE_R2_PUBLIC_URL=https://your-cdn.example.com
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
Add `.env.studio` to your `.gitignore`.
|
|
54
|
-
|
|
55
|
-
### 2. Add script to package.json
|
|
46
|
+
### 1. Add script to package.json
|
|
56
47
|
|
|
57
48
|
```json
|
|
58
49
|
{
|
|
@@ -62,7 +53,7 @@ Add `.env.studio` to your `.gitignore`.
|
|
|
62
53
|
}
|
|
63
54
|
```
|
|
64
55
|
|
|
65
|
-
###
|
|
56
|
+
### 2. Run Studio
|
|
66
57
|
|
|
67
58
|
```bash
|
|
68
59
|
npm run studio
|
|
@@ -70,11 +61,82 @@ npm run studio
|
|
|
70
61
|
|
|
71
62
|
Studio opens in your browser on an available port (default 3001).
|
|
72
63
|
|
|
64
|
+
R2 credentials and other environment variables are loaded from `.env.local` in your project root.
|
|
65
|
+
|
|
66
|
+
## CLI Commands
|
|
67
|
+
|
|
68
|
+
All commands accept `--workspace <path>` to target a specific project (defaults to current directory).
|
|
69
|
+
|
|
70
|
+
### `studio`
|
|
71
|
+
|
|
72
|
+
Start the web UI server.
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
studio # Start on default port 3001
|
|
76
|
+
studio --port 4000 # Custom port
|
|
77
|
+
studio --open # Auto-open browser
|
|
78
|
+
studio --workspace ~/my-project # Target specific project
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### `studio scan`
|
|
82
|
+
|
|
83
|
+
Scan the filesystem for new files not yet tracked in `_data/_studio.json`. Detects orphaned thumbnails in `public/images/`.
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
studio scan
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### `studio process [prefix]`
|
|
90
|
+
|
|
91
|
+
Generate thumbnails (sm, md, lg, full) for unprocessed images. Optionally filter by path prefix.
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
studio process # Process all unprocessed images
|
|
95
|
+
studio process portfolio # Process only images in /portfolio/
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### `studio push [prefix]`
|
|
99
|
+
|
|
100
|
+
Upload local images and thumbnails to Cloudflare R2 CDN. Optionally filter by path prefix.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
studio push # Push all local images to CDN
|
|
104
|
+
studio push portfolio # Push only /portfolio/ images
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### `studio download [prefix]`
|
|
108
|
+
|
|
109
|
+
Download cloud images from R2 to local storage. Optionally filter by path prefix.
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
studio download # Download all cloud images
|
|
113
|
+
studio download portfolio # Download only /portfolio/ images
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### `studio fonts woff2 <folder>`
|
|
117
|
+
|
|
118
|
+
Convert TTF/OTF fonts in `_fonts/<folder>/` to WOFF2 format.
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
studio fonts woff2 inter # Convert _fonts/inter/ to woff2
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### `studio fonts assign <folder> --name <name>`
|
|
125
|
+
|
|
126
|
+
Generate a `src/fonts/<name>.ts` configuration file from a font folder's WOFF2 files.
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
studio fonts assign inter --name heading # Generate src/fonts/heading.ts
|
|
130
|
+
```
|
|
131
|
+
|
|
73
132
|
## Environment Variables
|
|
74
133
|
|
|
134
|
+
Variables are loaded from `.env.local` in your project root.
|
|
135
|
+
|
|
75
136
|
| Variable | Required | Description |
|
|
76
137
|
|----------|----------|-------------|
|
|
77
138
|
| `STUDIO_DEV_SITE_URL` | No | URL to your dev site (shown as link in header) |
|
|
139
|
+
| `NEXT_PUBLIC_PRODUCTION_URL` | No | Fallback for `STUDIO_DEV_SITE_URL` if not set |
|
|
78
140
|
| `CLOUDFLARE_R2_ACCOUNT_ID` | For CDN | Your Cloudflare account ID |
|
|
79
141
|
| `CLOUDFLARE_R2_ACCESS_KEY_ID` | For CDN | R2 API access key |
|
|
80
142
|
| `CLOUDFLARE_R2_SECRET_ACCESS_KEY` | For CDN | R2 API secret key |
|
|
@@ -132,7 +194,120 @@ export const body = localFont({
|
|
|
132
194
|
})
|
|
133
195
|
```
|
|
134
196
|
|
|
135
|
-
##
|
|
197
|
+
## Architecture
|
|
198
|
+
|
|
199
|
+
### Directory Structure
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
studio/
|
|
203
|
+
├── bin/
|
|
204
|
+
│ └── studio.mjs # CLI entry point (parses args, routes to server or CLI)
|
|
205
|
+
├── client/
|
|
206
|
+
│ └── main.tsx # React app entry (standalone mode)
|
|
207
|
+
├── src/
|
|
208
|
+
│ ├── cli/ # CLI subcommands
|
|
209
|
+
│ │ ├── index.ts # Command dispatcher + progress helpers
|
|
210
|
+
│ │ ├── scan.ts # runScan()
|
|
211
|
+
│ │ ├── process.ts # runProcess()
|
|
212
|
+
│ │ ├── push.ts # runPush()
|
|
213
|
+
│ │ ├── download.ts # runDownload()
|
|
214
|
+
│ │ └── fonts.ts # runFonts() → woff2, assign
|
|
215
|
+
│ ├── components/ # React UI
|
|
216
|
+
│ │ ├── StudioUI.tsx # Main container
|
|
217
|
+
│ │ ├── StudioContext.tsx # State provider
|
|
218
|
+
│ │ ├── useStudioActions.tsx # Action dispatchers
|
|
219
|
+
│ │ ├── useStreamingOperation.ts # SSE consumption hook
|
|
220
|
+
│ │ ├── tokens.ts # Design tokens (colors, fonts, sizes)
|
|
221
|
+
│ │ └── ... # Toolbar, FileGrid, DetailView, Modals, Fonts
|
|
222
|
+
│ ├── config/
|
|
223
|
+
│ │ └── workspace.ts # Path resolution (getPublicPath, getDataPath, etc.)
|
|
224
|
+
│ ├── handlers/ # HTTP handlers (Fetch API Request/Response)
|
|
225
|
+
│ │ ├── files.ts # Upload, delete, rename, move, create folder
|
|
226
|
+
│ │ ├── images.ts # Sync, reprocess, unprocess, download, push
|
|
227
|
+
│ │ ├── list.ts # Browse, search, folder listing
|
|
228
|
+
│ │ ├── scan.ts # Filesystem scan, orphan detection
|
|
229
|
+
│ │ ├── import.ts # URL import, CDN management
|
|
230
|
+
│ │ ├── fonts.ts # Font CRUD, woff2 conversion, assignment
|
|
231
|
+
│ │ ├── edit-image.ts # Crop, resize, rotate
|
|
232
|
+
│ │ ├── favicon.ts # Favicon generation
|
|
233
|
+
│ │ ├── featured-image.ts # Featured image generation
|
|
234
|
+
│ │ └── utils/ # Shared utilities
|
|
235
|
+
│ │ ├── meta.ts # loadMeta/saveMeta (atomic writes)
|
|
236
|
+
│ │ ├── response.ts # jsonResponse, streamResponse, createSSEStream
|
|
237
|
+
│ │ ├── cancellation.ts # cancelOperation, isOperationCancelled
|
|
238
|
+
│ │ ├── cdn.ts # R2 upload/download/delete/copy
|
|
239
|
+
│ │ ├── thumbnails.ts # Image processing with sharp
|
|
240
|
+
│ │ ├── files.ts # Slugify, file type detection
|
|
241
|
+
│ │ ├── folders.ts # Empty folder cleanup
|
|
242
|
+
│ │ └── errors.ts # isFileNotFound helper
|
|
243
|
+
│ ├── lib/
|
|
244
|
+
│ │ └── api.ts # Typed API client (used by React components)
|
|
245
|
+
│ ├── server/
|
|
246
|
+
│ │ └── index.ts # Express 5 server, route registration
|
|
247
|
+
│ └── types.ts # TypeScript interfaces (MetaEntry, FileItem, etc.)
|
|
248
|
+
└── package.json
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Server
|
|
252
|
+
|
|
253
|
+
Express 5 app (`src/server/index.ts`) that:
|
|
254
|
+
|
|
255
|
+
1. Sets `STUDIO_WORKSPACE` env var from `--workspace` flag
|
|
256
|
+
2. Loads `.env.local` from the workspace
|
|
257
|
+
3. Registers API routes at `/api/studio/*`
|
|
258
|
+
4. Serves static files from `<workspace>/public/`
|
|
259
|
+
5. Serves the React client with injected globals (`__STUDIO_WORKSPACE__`, `__STUDIO_SITE_URL__`)
|
|
260
|
+
|
|
261
|
+
Handlers use **Web API `Request`/`Response`** (not Express req/res). The server wraps them with `wrapHandler()` which converts between Express and Fetch API conventions.
|
|
262
|
+
|
|
263
|
+
### Client
|
|
264
|
+
|
|
265
|
+
React 19 + Emotion (CSS-in-JS via `css` prop). Component hierarchy:
|
|
266
|
+
|
|
267
|
+
```
|
|
268
|
+
StudioUI
|
|
269
|
+
├── StudioToolbar (search, view toggle, actions)
|
|
270
|
+
├── StudioFileGrid / StudioFileList (browsing)
|
|
271
|
+
├── StudioDetailView (file info, thumbnails, CDN status)
|
|
272
|
+
├── FontsSection
|
|
273
|
+
│ ├── FontsToolbar
|
|
274
|
+
│ └── FontsGrid / FontsList
|
|
275
|
+
└── Modals (Confirm, Input, Alert, Progress)
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
State is managed via `StudioContext` (React context) with `useStudioActions` for dispatch.
|
|
279
|
+
|
|
280
|
+
### CLI
|
|
281
|
+
|
|
282
|
+
`bin/studio.mjs` parses args and either starts the server or dispatches to `src/cli/index.ts`. CLI commands reuse the same utility functions as HTTP handlers — no code duplication.
|
|
283
|
+
|
|
284
|
+
### Streaming
|
|
285
|
+
|
|
286
|
+
Batch operations use **Server-Sent Events (SSE)** via `createSSEStream()`. Events follow this protocol:
|
|
287
|
+
|
|
288
|
+
```json
|
|
289
|
+
{ "type": "start", "total": 10 }
|
|
290
|
+
{ "type": "progress", "current": 1, "total": 10, "message": "Processing photo.jpg" }
|
|
291
|
+
{ "type": "cleanup", "message": "Removing temp files..." }
|
|
292
|
+
{ "type": "complete", "processed": 10, "errors": 0 }
|
|
293
|
+
{ "type": "error", "message": "Failed to process image" }
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
The client consumes these via `useStreamingOperation` hook, which manages progress UI and cancellation.
|
|
297
|
+
|
|
298
|
+
### Configuration
|
|
299
|
+
|
|
300
|
+
Workspace resolution in `src/config/workspace.ts`:
|
|
301
|
+
|
|
302
|
+
| Function | Returns |
|
|
303
|
+
|----------|---------|
|
|
304
|
+
| `getWorkspace()` | `STUDIO_WORKSPACE` env var or `cwd()` |
|
|
305
|
+
| `getPublicPath(...)` | `<workspace>/public/<segments>` |
|
|
306
|
+
| `getDataPath(...)` | `<workspace>/_data/<segments>` |
|
|
307
|
+
| `getSrcAppPath(...)` | `<workspace>/src/app/<segments>` |
|
|
308
|
+
| `getWorkspacePath(...)` | `<workspace>/<segments>` |
|
|
309
|
+
|
|
310
|
+
## Metadata Schema
|
|
136
311
|
|
|
137
312
|
Studio stores image metadata in `_data/_studio.json`:
|
|
138
313
|
|
|
@@ -151,13 +326,115 @@ Studio stores image metadata in `_data/_studio.json`:
|
|
|
151
326
|
}
|
|
152
327
|
```
|
|
153
328
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
|
157
|
-
|
|
158
|
-
| `
|
|
159
|
-
| `
|
|
160
|
-
| `
|
|
329
|
+
### Property Reference
|
|
330
|
+
|
|
331
|
+
| Property | Type | Description |
|
|
332
|
+
|----------|------|-------------|
|
|
333
|
+
| `_cdns` | `string[]` | Array of CDN base URLs |
|
|
334
|
+
| `o` | `{ w, h }` | Original dimensions |
|
|
335
|
+
| `b` | `string` | Blurhash string for placeholder |
|
|
336
|
+
| `sm` | `{ w, h }` | Small thumbnail dimensions (300px width) |
|
|
337
|
+
| `md` | `{ w, h }` | Medium thumbnail dimensions (700px width) |
|
|
338
|
+
| `lg` | `{ w, h }` | Large thumbnail dimensions (1400px width) |
|
|
339
|
+
| `f` | `{ w, h }` | Full size dimensions (capped at 2560px width) |
|
|
340
|
+
| `c` | `number` | CDN index — references `_cdns` array |
|
|
341
|
+
| `u` | `1` | Update pending — local file overrides cloud version |
|
|
342
|
+
|
|
343
|
+
### File Lifecycle
|
|
344
|
+
|
|
345
|
+
1. **Upload** — file added to `public/`, entry created with `o` (dimensions)
|
|
346
|
+
2. **Process** — thumbnails generated in `public/images/`, entry gains `sm`/`md`/`lg`/`f`
|
|
347
|
+
3. **Push** — thumbnails uploaded to R2, entry gains `c` (CDN index)
|
|
348
|
+
4. **Cloud** — local thumbnails can be removed; CDN serves the images
|
|
349
|
+
|
|
350
|
+
Files with `u: 1` have local changes that haven't been pushed to CDN yet.
|
|
351
|
+
|
|
352
|
+
## Thumbnail Sizes
|
|
353
|
+
|
|
354
|
+
| Size | Max Width | Suffix | Path Convention |
|
|
355
|
+
|------|-----------|--------|-----------------|
|
|
356
|
+
| sm | 300px | `-sm` | `/images/{path}-sm.{ext}` |
|
|
357
|
+
| md | 700px | `-md` | `/images/{path}-md.{ext}` |
|
|
358
|
+
| lg | 1400px | `-lg` | `/images/{path}-lg.{ext}` |
|
|
359
|
+
| full | 2560px | *(none)* | `/images/{path}.{ext}` |
|
|
360
|
+
|
|
361
|
+
- PNG inputs produce PNG thumbnails; all others produce JPEG (quality 85)
|
|
362
|
+
- Thumbnails are stored in `public/images/` mirroring the original path structure
|
|
363
|
+
|
|
364
|
+
## API Reference
|
|
365
|
+
|
|
366
|
+
All endpoints are prefixed with `/api/studio/`.
|
|
367
|
+
|
|
368
|
+
### Browsing
|
|
369
|
+
|
|
370
|
+
| Method | Path | Description |
|
|
371
|
+
|--------|------|-------------|
|
|
372
|
+
| GET | `/list` | List files/folders at a path |
|
|
373
|
+
| GET | `/search` | Search files by query |
|
|
374
|
+
| GET | `/list-folders` | List all folders |
|
|
375
|
+
| GET | `/count-images` | Count images by status |
|
|
376
|
+
| GET | `/folder-images` | Count images per folder |
|
|
377
|
+
|
|
378
|
+
### File Operations
|
|
379
|
+
|
|
380
|
+
| Method | Path | Streaming | Description |
|
|
381
|
+
|--------|------|-----------|-------------|
|
|
382
|
+
| POST | `/upload` | | Upload file (FormData) |
|
|
383
|
+
| POST | `/create-folder` | | Create new folder |
|
|
384
|
+
| POST | `/rename` | | Rename file/folder |
|
|
385
|
+
| POST | `/rename-stream` | SSE | Rename with progress |
|
|
386
|
+
| POST | `/delete` | | Delete files |
|
|
387
|
+
| POST | `/delete-stream` | SSE | Delete with progress |
|
|
388
|
+
| POST | `/move` | SSE | Move files to folder |
|
|
389
|
+
|
|
390
|
+
### Image Processing
|
|
391
|
+
|
|
392
|
+
| Method | Path | Streaming | Description |
|
|
393
|
+
|--------|------|-----------|-------------|
|
|
394
|
+
| POST | `/sync` | | Process single image |
|
|
395
|
+
| POST | `/sync-stream` | SSE | Process with progress |
|
|
396
|
+
| POST | `/reprocess-stream` | SSE | Regenerate thumbnails |
|
|
397
|
+
| POST | `/unprocess-stream` | SSE | Remove local thumbnails |
|
|
398
|
+
| POST | `/edit-image` | | Crop/resize/rotate |
|
|
399
|
+
| POST | `/scan` | SSE | Scan filesystem for changes |
|
|
400
|
+
| POST | `/delete-orphans` | | Remove orphaned thumbnails |
|
|
401
|
+
|
|
402
|
+
### CDN Operations
|
|
403
|
+
|
|
404
|
+
| Method | Path | Streaming | Description |
|
|
405
|
+
|--------|------|-----------|-------------|
|
|
406
|
+
| POST | `/push-updates-stream` | SSE | Push to R2 CDN |
|
|
407
|
+
| POST | `/download-stream` | SSE | Download from R2 |
|
|
408
|
+
| POST | `/cancel-stream` | | Cancel running operation |
|
|
409
|
+
| POST | `/cancel-updates` | | Cancel pending updates |
|
|
410
|
+
| GET | `/cdns` | | Get CDN URLs |
|
|
411
|
+
| POST | `/cdns` | | Update CDN URLs |
|
|
412
|
+
| POST | `/import` | SSE | Import from external URLs |
|
|
413
|
+
|
|
414
|
+
### Fonts
|
|
415
|
+
|
|
416
|
+
| Method | Path | Streaming | Description |
|
|
417
|
+
|--------|------|-----------|-------------|
|
|
418
|
+
| GET | `/fonts/list` | | List font folders |
|
|
419
|
+
| POST | `/fonts/upload` | | Upload font file (FormData) |
|
|
420
|
+
| POST | `/fonts/create-folder` | | Create font folder |
|
|
421
|
+
| POST | `/fonts/delete` | | Delete font file |
|
|
422
|
+
| POST | `/fonts/delete-stream` | SSE | Delete with progress |
|
|
423
|
+
| POST | `/fonts/rename` | | Rename font file/folder |
|
|
424
|
+
| POST | `/fonts/rename-stream` | SSE | Rename with progress |
|
|
425
|
+
| POST | `/fonts/scan` | | Scan font folders |
|
|
426
|
+
| GET | `/fonts/assignments` | | List font assignments |
|
|
427
|
+
| POST | `/fonts/assign-stream` | SSE | Generate font config |
|
|
428
|
+
| POST | `/fonts/delete-assignment` | | Delete font assignment |
|
|
429
|
+
|
|
430
|
+
### Other
|
|
431
|
+
|
|
432
|
+
| Method | Path | Streaming | Description |
|
|
433
|
+
|--------|------|-----------|-------------|
|
|
434
|
+
| POST | `/generate-favicon` | SSE | Generate favicon from image |
|
|
435
|
+
| POST | `/generate-featured-image` | SSE | Generate featured image |
|
|
436
|
+
| GET | `/check-featured-image` | | Check featured image status |
|
|
437
|
+
| GET | `/featured-image-options` | | Get featured image options |
|
|
161
438
|
|
|
162
439
|
## License
|
|
163
440
|
|
package/bin/studio.mjs
CHANGED
|
@@ -24,6 +24,15 @@ Studio - Media Manager
|
|
|
24
24
|
|
|
25
25
|
Usage:
|
|
26
26
|
studio [options]
|
|
27
|
+
studio <command> [options]
|
|
28
|
+
|
|
29
|
+
Commands:
|
|
30
|
+
scan Scan for new media files and update metadata
|
|
31
|
+
process [prefix] Generate thumbnails for unprocessed images
|
|
32
|
+
push [prefix] Upload local images to CDN (R2)
|
|
33
|
+
download [prefix] Download cloud images to local storage
|
|
34
|
+
fonts woff2 <folder> Convert TTF/OTF to woff2
|
|
35
|
+
fonts assign <folder> --name <n> Generate src/fonts/<n>.ts
|
|
27
36
|
|
|
28
37
|
Options:
|
|
29
38
|
--workspace <path> Path to the project workspace (default: current directory)
|
|
@@ -32,9 +41,15 @@ Options:
|
|
|
32
41
|
--help, -h Show this help message
|
|
33
42
|
|
|
34
43
|
Examples:
|
|
35
|
-
studio
|
|
36
|
-
studio --workspace ~/my-project
|
|
37
|
-
studio
|
|
44
|
+
studio # Start the web UI
|
|
45
|
+
studio --workspace ~/my-project # Start for specific project
|
|
46
|
+
studio scan # Scan for new files
|
|
47
|
+
studio process # Process all unprocessed images
|
|
48
|
+
studio process portfolio # Process images in /portfolio/
|
|
49
|
+
studio push # Push all local images to CDN
|
|
50
|
+
studio download # Download all cloud images
|
|
51
|
+
studio fonts woff2 inter # Convert _fonts/inter/ to woff2
|
|
52
|
+
studio fonts assign inter --name heading # Generate src/fonts/heading.ts
|
|
38
53
|
`)
|
|
39
54
|
process.exit(0)
|
|
40
55
|
}
|
|
@@ -46,22 +61,52 @@ if (!existsSync(workspace)) {
|
|
|
46
61
|
process.exit(1)
|
|
47
62
|
}
|
|
48
63
|
|
|
49
|
-
// Check for
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
64
|
+
// Check for CLI subcommands
|
|
65
|
+
const knownCommands = ['scan', 'process', 'push', 'download', 'fonts']
|
|
66
|
+
const command = args.find(a => !a.startsWith('-') && knownCommands.includes(a))
|
|
67
|
+
|
|
68
|
+
if (command) {
|
|
69
|
+
// Remove command from args, also remove --workspace and its value, --port and its value
|
|
70
|
+
const subArgs = []
|
|
71
|
+
let skipNext = false
|
|
72
|
+
for (const a of args) {
|
|
73
|
+
if (skipNext) {
|
|
74
|
+
skipNext = false
|
|
75
|
+
continue
|
|
76
|
+
}
|
|
77
|
+
if (a === command) continue
|
|
78
|
+
if (a === '--workspace' || a === '--port') {
|
|
79
|
+
skipNext = true
|
|
80
|
+
continue
|
|
81
|
+
}
|
|
82
|
+
if (a === '--open' || a === '-o') continue
|
|
83
|
+
subArgs.push(a)
|
|
84
|
+
}
|
|
56
85
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
open: shouldOpen,
|
|
86
|
+
import('../dist/cli/index.js').then(mod => {
|
|
87
|
+
mod.run(command, workspace, subArgs)
|
|
88
|
+
}).catch(error => {
|
|
89
|
+
console.error('CLI command failed:', error)
|
|
90
|
+
process.exit(1)
|
|
63
91
|
})
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
92
|
+
} else {
|
|
93
|
+
// Check for public folder (only needed for server mode)
|
|
94
|
+
const publicPath = resolve(workspace, 'public')
|
|
95
|
+
if (!existsSync(publicPath)) {
|
|
96
|
+
console.error(`Error: No 'public' folder found in workspace: ${workspace}`)
|
|
97
|
+
console.error('Studio requires a public folder to manage media files.')
|
|
98
|
+
process.exit(1)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Start the server
|
|
102
|
+
import('../dist/server/index.js').then((mod) => {
|
|
103
|
+
mod.startServer({
|
|
104
|
+
port,
|
|
105
|
+
workspace,
|
|
106
|
+
open: shouldOpen,
|
|
107
|
+
})
|
|
108
|
+
}).catch((error) => {
|
|
109
|
+
console.error('Failed to start Studio server:', error)
|
|
110
|
+
process.exit(1)
|
|
111
|
+
})
|
|
112
|
+
}
|