@gallop.software/studio 2.0.7 → 2.1.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/bin/studio.mjs +17 -124
- package/dist/client/assets/index-TPS3nigu.js +74 -0
- package/dist/client/index.html +19 -0
- package/dist/{handlers/index.mjs → server/index.js} +242 -427
- package/dist/server/index.js.map +1 -0
- package/package.json +17 -32
- package/app/api/studio/[...path]/route.js +0 -1
- package/app/layout.jsx +0 -19
- package/app/page.jsx +0 -90
- package/dist/chunk-TRYWHLJ2.mjs +0 -32
- package/dist/chunk-TRYWHLJ2.mjs.map +0 -1
- package/dist/chunk-VI6QG6WT.js +0 -32
- package/dist/chunk-VI6QG6WT.js.map +0 -1
- package/dist/components/StudioUI.d.mts +0 -15
- package/dist/components/StudioUI.d.ts +0 -15
- package/dist/components/StudioUI.js +0 -6572
- package/dist/components/StudioUI.js.map +0 -1
- package/dist/components/StudioUI.mjs +0 -6572
- package/dist/components/StudioUI.mjs.map +0 -1
- package/dist/handlers/index.d.mts +0 -22
- package/dist/handlers/index.d.ts +0 -22
- package/dist/handlers/index.js +0 -2587
- package/dist/handlers/index.js.map +0 -1
- package/dist/handlers/index.mjs.map +0 -1
- package/dist/index.d.mts +0 -90
- package/dist/index.d.ts +0 -90
- package/dist/index.js +0 -11
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -11
- package/dist/index.mjs.map +0 -1
- package/next.config.mjs +0 -20
package/app/page.jsx
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import dynamic from 'next/dynamic'
|
|
4
|
-
import { css } from '@emotion/react'
|
|
5
|
-
|
|
6
|
-
const colors = {
|
|
7
|
-
background: '#0a0a0a',
|
|
8
|
-
primary: '#635bff',
|
|
9
|
-
border: '#2a2a2a',
|
|
10
|
-
textSecondary: '#888888',
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const fontStack = `-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif`
|
|
14
|
-
|
|
15
|
-
// Dynamically import StudioUI from compiled dist
|
|
16
|
-
const StudioUI = dynamic(() => import('../dist/components/StudioUI.mjs').then(m => m.default), {
|
|
17
|
-
ssr: false,
|
|
18
|
-
loading: () => <LoadingState />,
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
const styles = {
|
|
22
|
-
container: css`
|
|
23
|
-
position: fixed;
|
|
24
|
-
top: 0;
|
|
25
|
-
left: 0;
|
|
26
|
-
right: 0;
|
|
27
|
-
bottom: 0;
|
|
28
|
-
background: ${colors.background};
|
|
29
|
-
font-family: ${fontStack};
|
|
30
|
-
`,
|
|
31
|
-
loading: css`
|
|
32
|
-
display: flex;
|
|
33
|
-
align-items: center;
|
|
34
|
-
justify-content: center;
|
|
35
|
-
height: 100vh;
|
|
36
|
-
background: ${colors.background};
|
|
37
|
-
font-family: ${fontStack};
|
|
38
|
-
`,
|
|
39
|
-
loadingContent: css`
|
|
40
|
-
display: flex;
|
|
41
|
-
flex-direction: column;
|
|
42
|
-
align-items: center;
|
|
43
|
-
gap: 16px;
|
|
44
|
-
`,
|
|
45
|
-
spinner: css`
|
|
46
|
-
width: 36px;
|
|
47
|
-
height: 36px;
|
|
48
|
-
border-radius: 50%;
|
|
49
|
-
border: 3px solid ${colors.border};
|
|
50
|
-
border-top-color: ${colors.primary};
|
|
51
|
-
animation: spin 0.8s linear infinite;
|
|
52
|
-
|
|
53
|
-
@keyframes spin {
|
|
54
|
-
to {
|
|
55
|
-
transform: rotate(360deg);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
`,
|
|
59
|
-
loadingText: css`
|
|
60
|
-
color: ${colors.textSecondary};
|
|
61
|
-
font-size: 14px;
|
|
62
|
-
font-weight: 500;
|
|
63
|
-
margin: 0;
|
|
64
|
-
`,
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function LoadingState() {
|
|
68
|
-
return (
|
|
69
|
-
<div css={styles.loading}>
|
|
70
|
-
<div css={styles.loadingContent}>
|
|
71
|
-
<div css={styles.spinner} />
|
|
72
|
-
<p css={styles.loadingText}>Loading Studio...</p>
|
|
73
|
-
</div>
|
|
74
|
-
</div>
|
|
75
|
-
)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export default function StudioPage() {
|
|
79
|
-
const workspace = process.env.NEXT_PUBLIC_STUDIO_WORKSPACE || 'Unknown'
|
|
80
|
-
|
|
81
|
-
return (
|
|
82
|
-
<div css={styles.container}>
|
|
83
|
-
<StudioUI
|
|
84
|
-
isVisible={true}
|
|
85
|
-
standaloneMode={true}
|
|
86
|
-
workspacePath={workspace}
|
|
87
|
-
/>
|
|
88
|
-
</div>
|
|
89
|
-
)
|
|
90
|
-
}
|
package/dist/chunk-TRYWHLJ2.mjs
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
// src/types.ts
|
|
2
|
-
function getThumbnailPath(originalPath, size) {
|
|
3
|
-
if (size === "full") {
|
|
4
|
-
const ext2 = originalPath.match(/\.\w+$/)?.[0] || ".jpg";
|
|
5
|
-
const base2 = originalPath.replace(/\.\w+$/, "");
|
|
6
|
-
const outputExt2 = ext2.toLowerCase() === ".png" ? ".png" : ".jpg";
|
|
7
|
-
return `/images${base2}${outputExt2}`;
|
|
8
|
-
}
|
|
9
|
-
const ext = originalPath.match(/\.\w+$/)?.[0] || ".jpg";
|
|
10
|
-
const base = originalPath.replace(/\.\w+$/, "");
|
|
11
|
-
const outputExt = ext.toLowerCase() === ".png" ? ".png" : ".jpg";
|
|
12
|
-
return `/images${base}-${size}${outputExt}`;
|
|
13
|
-
}
|
|
14
|
-
function getAllThumbnailPaths(originalPath) {
|
|
15
|
-
return [
|
|
16
|
-
getThumbnailPath(originalPath, "full"),
|
|
17
|
-
getThumbnailPath(originalPath, "lg"),
|
|
18
|
-
getThumbnailPath(originalPath, "md"),
|
|
19
|
-
getThumbnailPath(originalPath, "sm")
|
|
20
|
-
];
|
|
21
|
-
}
|
|
22
|
-
function isProcessed(entry) {
|
|
23
|
-
if (!entry) return false;
|
|
24
|
-
return !!(entry.f || entry.lg || entry.md || entry.sm);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export {
|
|
28
|
-
getThumbnailPath,
|
|
29
|
-
getAllThumbnailPaths,
|
|
30
|
-
isProcessed
|
|
31
|
-
};
|
|
32
|
-
//# sourceMappingURL=chunk-TRYWHLJ2.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["/**\n * Dimensions object {w, h}\n */\nexport interface Dimensions {\n w: number\n h: number\n}\n\n/**\n * Meta entry - works for images and non-images\n * o: original dimensions, b: blurhash, c: CDN index\n * sm/md/lg/f: thumbnail dimensions (presence implies processed)\n */\nexport interface MetaEntry {\n o?: Dimensions // original dimensions {w, h}\n b?: string // blurhash\n sm?: Dimensions // small thumbnail (300px width)\n md?: Dimensions // medium thumbnail (700px width)\n lg?: Dimensions // large thumbnail (1400px width)\n f?: Dimensions // full size (capped at 2560px width)\n c?: number // CDN index - index into _cdns array\n}\n\n/**\n * Full meta schema including special keys\n * _cdns: Array of CDN base URLs\n * Other keys: file paths from public folder\n */\nexport interface FullMeta {\n _cdns?: string[] // Array of CDN base URLs\n [key: string]: MetaEntry | string[] | undefined\n}\n\n/**\n * Meta schema - keyed by path from public folder\n * Example: { \"/portfolio/photo.jpg\": { o: {w:2400,h:1600}, b: \"...\", sm: {w:300,h:200}, ... } }\n */\nexport type LeanMeta = Record<string, MetaEntry>\n\n// Alias for compatibility\nexport type LeanImageEntry = MetaEntry\n\n/**\n * File/folder item for browser\n */\nexport interface FileItem {\n name: string\n path: string\n type: 'file' | 'folder'\n size?: number\n dimensions?: { width: number; height: number }\n isProcessed?: boolean\n cdnPushed?: boolean\n cdnBaseUrl?: string // CDN base URL when pushed to cloud\n isRemote?: boolean // true if CDN URL doesn't match R2 (external import)\n isProtected?: boolean // true for images folder and its contents (cannot select/modify)\n // Folder-specific properties\n fileCount?: number\n totalSize?: number\n cloudCount?: number // Number of R2 cloud files in folder\n remoteCount?: number // Number of remote (imported URL) files in folder\n localCount?: number // Number of local files in folder\n // For showing thumbnails - path to -sm version if exists\n thumbnail?: string\n // Whether a processed thumbnail exists\n hasThumbnail?: boolean\n}\n\n/**\n * Studio configuration\n */\nexport interface StudioConfig {\n r2AccountId?: string\n r2AccessKeyId?: string\n r2SecretAccessKey?: string\n r2BucketName?: string\n r2PublicUrl?: string\n thumbnailSizes?: {\n small: number\n medium: number\n large: number\n }\n}\n\n/**\n * Get thumbnail path from original image path\n */\nexport function getThumbnailPath(originalPath: string, size: 'sm' | 'md' | 'lg' | 'full'): string {\n if (size === 'full') {\n const ext = originalPath.match(/\\.\\w+$/)?.[0] || '.jpg'\n const base = originalPath.replace(/\\.\\w+$/, '')\n const outputExt = ext.toLowerCase() === '.png' ? '.png' : '.jpg'\n return `/images${base}${outputExt}`\n }\n const ext = originalPath.match(/\\.\\w+$/)?.[0] || '.jpg'\n const base = originalPath.replace(/\\.\\w+$/, '')\n const outputExt = ext.toLowerCase() === '.png' ? '.png' : '.jpg'\n return `/images${base}-${size}${outputExt}`\n}\n\n/**\n * Get all thumbnail paths for an image\n */\nexport function getAllThumbnailPaths(originalPath: string): string[] {\n return [\n getThumbnailPath(originalPath, 'full'),\n getThumbnailPath(originalPath, 'lg'),\n getThumbnailPath(originalPath, 'md'),\n getThumbnailPath(originalPath, 'sm'),\n ]\n}\n\n/**\n * Check if an image entry is processed (has any thumbnail dimensions)\n */\nexport function isProcessed(entry: MetaEntry | undefined): boolean {\n if (!entry) return false\n return !!(entry.f || entry.lg || entry.md || entry.sm)\n}\n"],"mappings":";AAuFO,SAAS,iBAAiB,cAAsB,MAA2C;AAChG,MAAI,SAAS,QAAQ;AACnB,UAAMA,OAAM,aAAa,MAAM,QAAQ,IAAI,CAAC,KAAK;AACjD,UAAMC,QAAO,aAAa,QAAQ,UAAU,EAAE;AAC9C,UAAMC,aAAYF,KAAI,YAAY,MAAM,SAAS,SAAS;AAC1D,WAAO,UAAUC,KAAI,GAAGC,UAAS;AAAA,EACnC;AACA,QAAM,MAAM,aAAa,MAAM,QAAQ,IAAI,CAAC,KAAK;AACjD,QAAM,OAAO,aAAa,QAAQ,UAAU,EAAE;AAC9C,QAAM,YAAY,IAAI,YAAY,MAAM,SAAS,SAAS;AAC1D,SAAO,UAAU,IAAI,IAAI,IAAI,GAAG,SAAS;AAC3C;AAKO,SAAS,qBAAqB,cAAgC;AACnE,SAAO;AAAA,IACL,iBAAiB,cAAc,MAAM;AAAA,IACrC,iBAAiB,cAAc,IAAI;AAAA,IACnC,iBAAiB,cAAc,IAAI;AAAA,IACnC,iBAAiB,cAAc,IAAI;AAAA,EACrC;AACF;AAKO,SAAS,YAAY,OAAuC;AACjE,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,CAAC,EAAE,MAAM,KAAK,MAAM,MAAM,MAAM,MAAM,MAAM;AACrD;","names":["ext","base","outputExt"]}
|
package/dist/chunk-VI6QG6WT.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/types.ts
|
|
2
|
-
function getThumbnailPath(originalPath, size) {
|
|
3
|
-
if (size === "full") {
|
|
4
|
-
const ext2 = _optionalChain([originalPath, 'access', _ => _.match, 'call', _2 => _2(/\.\w+$/), 'optionalAccess', _3 => _3[0]]) || ".jpg";
|
|
5
|
-
const base2 = originalPath.replace(/\.\w+$/, "");
|
|
6
|
-
const outputExt2 = ext2.toLowerCase() === ".png" ? ".png" : ".jpg";
|
|
7
|
-
return `/images${base2}${outputExt2}`;
|
|
8
|
-
}
|
|
9
|
-
const ext = _optionalChain([originalPath, 'access', _4 => _4.match, 'call', _5 => _5(/\.\w+$/), 'optionalAccess', _6 => _6[0]]) || ".jpg";
|
|
10
|
-
const base = originalPath.replace(/\.\w+$/, "");
|
|
11
|
-
const outputExt = ext.toLowerCase() === ".png" ? ".png" : ".jpg";
|
|
12
|
-
return `/images${base}-${size}${outputExt}`;
|
|
13
|
-
}
|
|
14
|
-
function getAllThumbnailPaths(originalPath) {
|
|
15
|
-
return [
|
|
16
|
-
getThumbnailPath(originalPath, "full"),
|
|
17
|
-
getThumbnailPath(originalPath, "lg"),
|
|
18
|
-
getThumbnailPath(originalPath, "md"),
|
|
19
|
-
getThumbnailPath(originalPath, "sm")
|
|
20
|
-
];
|
|
21
|
-
}
|
|
22
|
-
function isProcessed(entry) {
|
|
23
|
-
if (!entry) return false;
|
|
24
|
-
return !!(entry.f || entry.lg || entry.md || entry.sm);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
exports.getThumbnailPath = getThumbnailPath; exports.getAllThumbnailPaths = getAllThumbnailPaths; exports.isProcessed = isProcessed;
|
|
32
|
-
//# sourceMappingURL=chunk-VI6QG6WT.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/chrisb/Sites/studio/dist/chunk-VI6QG6WT.js","../src/types.ts"],"names":["ext","base","outputExt"],"mappings":"AAAA;ACuFO,SAAS,gBAAA,CAAiB,YAAA,EAAsB,IAAA,EAA2C;AAChG,EAAA,GAAA,CAAI,KAAA,IAAS,MAAA,EAAQ;AACnB,IAAA,MAAMA,KAAAA,kBAAM,YAAA,mBAAa,KAAA,mBAAM,QAAQ,CAAA,4BAAA,CAAI,CAAC,IAAA,GAAK,MAAA;AACjD,IAAA,MAAMC,MAAAA,EAAO,YAAA,CAAa,OAAA,CAAQ,QAAA,EAAU,EAAE,CAAA;AAC9C,IAAA,MAAMC,WAAAA,EAAYF,IAAAA,CAAI,WAAA,CAAY,EAAA,IAAM,OAAA,EAAS,OAAA,EAAS,MAAA;AAC1D,IAAA,OAAO,CAAA,OAAA,EAAUC,KAAI,CAAA,EAAA;AACvB,EAAA;AACyB,EAAA;AACZ,EAAA;AACS,EAAA;AACG,EAAA;AAC3B;AAKgB;AACP,EAAA;AACY,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACnB,EAAA;AACF;AAK4B;AACP,EAAA;AACE,EAAA;AACvB;AD7F2B;AACA;AACA;AACA;AACA;AACA","file":"/Users/chrisb/Sites/studio/dist/chunk-VI6QG6WT.js","sourcesContent":[null,"/**\n * Dimensions object {w, h}\n */\nexport interface Dimensions {\n w: number\n h: number\n}\n\n/**\n * Meta entry - works for images and non-images\n * o: original dimensions, b: blurhash, c: CDN index\n * sm/md/lg/f: thumbnail dimensions (presence implies processed)\n */\nexport interface MetaEntry {\n o?: Dimensions // original dimensions {w, h}\n b?: string // blurhash\n sm?: Dimensions // small thumbnail (300px width)\n md?: Dimensions // medium thumbnail (700px width)\n lg?: Dimensions // large thumbnail (1400px width)\n f?: Dimensions // full size (capped at 2560px width)\n c?: number // CDN index - index into _cdns array\n}\n\n/**\n * Full meta schema including special keys\n * _cdns: Array of CDN base URLs\n * Other keys: file paths from public folder\n */\nexport interface FullMeta {\n _cdns?: string[] // Array of CDN base URLs\n [key: string]: MetaEntry | string[] | undefined\n}\n\n/**\n * Meta schema - keyed by path from public folder\n * Example: { \"/portfolio/photo.jpg\": { o: {w:2400,h:1600}, b: \"...\", sm: {w:300,h:200}, ... } }\n */\nexport type LeanMeta = Record<string, MetaEntry>\n\n// Alias for compatibility\nexport type LeanImageEntry = MetaEntry\n\n/**\n * File/folder item for browser\n */\nexport interface FileItem {\n name: string\n path: string\n type: 'file' | 'folder'\n size?: number\n dimensions?: { width: number; height: number }\n isProcessed?: boolean\n cdnPushed?: boolean\n cdnBaseUrl?: string // CDN base URL when pushed to cloud\n isRemote?: boolean // true if CDN URL doesn't match R2 (external import)\n isProtected?: boolean // true for images folder and its contents (cannot select/modify)\n // Folder-specific properties\n fileCount?: number\n totalSize?: number\n cloudCount?: number // Number of R2 cloud files in folder\n remoteCount?: number // Number of remote (imported URL) files in folder\n localCount?: number // Number of local files in folder\n // For showing thumbnails - path to -sm version if exists\n thumbnail?: string\n // Whether a processed thumbnail exists\n hasThumbnail?: boolean\n}\n\n/**\n * Studio configuration\n */\nexport interface StudioConfig {\n r2AccountId?: string\n r2AccessKeyId?: string\n r2SecretAccessKey?: string\n r2BucketName?: string\n r2PublicUrl?: string\n thumbnailSizes?: {\n small: number\n medium: number\n large: number\n }\n}\n\n/**\n * Get thumbnail path from original image path\n */\nexport function getThumbnailPath(originalPath: string, size: 'sm' | 'md' | 'lg' | 'full'): string {\n if (size === 'full') {\n const ext = originalPath.match(/\\.\\w+$/)?.[0] || '.jpg'\n const base = originalPath.replace(/\\.\\w+$/, '')\n const outputExt = ext.toLowerCase() === '.png' ? '.png' : '.jpg'\n return `/images${base}${outputExt}`\n }\n const ext = originalPath.match(/\\.\\w+$/)?.[0] || '.jpg'\n const base = originalPath.replace(/\\.\\w+$/, '')\n const outputExt = ext.toLowerCase() === '.png' ? '.png' : '.jpg'\n return `/images${base}-${size}${outputExt}`\n}\n\n/**\n * Get all thumbnail paths for an image\n */\nexport function getAllThumbnailPaths(originalPath: string): string[] {\n return [\n getThumbnailPath(originalPath, 'full'),\n getThumbnailPath(originalPath, 'lg'),\n getThumbnailPath(originalPath, 'md'),\n getThumbnailPath(originalPath, 'sm'),\n ]\n}\n\n/**\n * Check if an image entry is processed (has any thumbnail dimensions)\n */\nexport function isProcessed(entry: MetaEntry | undefined): boolean {\n if (!entry) return false\n return !!(entry.f || entry.lg || entry.md || entry.sm)\n}\n"]}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import * as _emotion_react_jsx_runtime from '@emotion/react/jsx-runtime';
|
|
2
|
-
|
|
3
|
-
interface StudioUIProps {
|
|
4
|
-
onClose?: () => void;
|
|
5
|
-
isVisible?: boolean;
|
|
6
|
-
standaloneMode?: boolean;
|
|
7
|
-
workspacePath?: string;
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* Main Studio UI - contains all panels and manages internal state
|
|
11
|
-
* Rendered inside the modal via lazy loading
|
|
12
|
-
*/
|
|
13
|
-
declare function StudioUI({ onClose, isVisible, standaloneMode, workspacePath }: StudioUIProps): _emotion_react_jsx_runtime.JSX.Element;
|
|
14
|
-
|
|
15
|
-
export { StudioUI, StudioUI as default };
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import * as _emotion_react_jsx_runtime from '@emotion/react/jsx-runtime';
|
|
2
|
-
|
|
3
|
-
interface StudioUIProps {
|
|
4
|
-
onClose?: () => void;
|
|
5
|
-
isVisible?: boolean;
|
|
6
|
-
standaloneMode?: boolean;
|
|
7
|
-
workspacePath?: string;
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* Main Studio UI - contains all panels and manages internal state
|
|
11
|
-
* Rendered inside the modal via lazy loading
|
|
12
|
-
*/
|
|
13
|
-
declare function StudioUI({ onClose, isVisible, standaloneMode, workspacePath }: StudioUIProps): _emotion_react_jsx_runtime.JSX.Element;
|
|
14
|
-
|
|
15
|
-
export { StudioUI, StudioUI as default };
|