@gallop.software/studio 1.5.9 → 2.0.0
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/app/api/studio/[...path]/route.ts +1 -0
- package/app/layout.tsx +20 -0
- package/app/page.tsx +82 -0
- package/bin/studio.mjs +110 -0
- package/dist/handlers/index.js +84 -63
- package/dist/handlers/index.js.map +1 -1
- package/dist/handlers/index.mjs +135 -114
- package/dist/handlers/index.mjs.map +1 -1
- package/dist/index.d.mts +14 -10
- package/dist/index.d.ts +14 -10
- package/dist/index.js +2 -177
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4 -179
- package/dist/index.mjs.map +1 -1
- package/next.config.mjs +22 -0
- package/package.json +18 -10
- package/src/components/AddNewModal.tsx +402 -0
- package/src/components/ErrorModal.tsx +89 -0
- package/src/components/R2SetupModal.tsx +400 -0
- package/src/components/StudioBreadcrumb.tsx +115 -0
- package/src/components/StudioButton.tsx +200 -0
- package/src/components/StudioContext.tsx +219 -0
- package/src/components/StudioDetailView.tsx +714 -0
- package/src/components/StudioFileGrid.tsx +704 -0
- package/src/components/StudioFileList.tsx +743 -0
- package/src/components/StudioFolderPicker.tsx +342 -0
- package/src/components/StudioModal.tsx +473 -0
- package/src/components/StudioPreview.tsx +399 -0
- package/src/components/StudioSettings.tsx +536 -0
- package/src/components/StudioToolbar.tsx +1448 -0
- package/src/components/StudioUI.tsx +731 -0
- package/src/components/styles/common.ts +236 -0
- package/src/components/tokens.ts +78 -0
- package/src/components/useStudioActions.tsx +497 -0
- package/src/config/index.ts +7 -0
- package/src/config/workspace.ts +52 -0
- package/src/handlers/favicon.ts +152 -0
- package/src/handlers/files.ts +784 -0
- package/src/handlers/images.ts +949 -0
- package/src/handlers/import.ts +190 -0
- package/src/handlers/index.ts +168 -0
- package/src/handlers/list.ts +627 -0
- package/src/handlers/scan.ts +311 -0
- package/src/handlers/utils/cdn.ts +234 -0
- package/src/handlers/utils/files.ts +64 -0
- package/src/handlers/utils/index.ts +4 -0
- package/src/handlers/utils/meta.ts +102 -0
- package/src/handlers/utils/thumbnails.ts +98 -0
- package/src/hooks/useFileList.ts +143 -0
- package/src/index.tsx +36 -0
- package/src/lib/api.ts +176 -0
- package/src/types.ts +119 -0
- package/dist/StudioUI-GJK45R3T.js +0 -6500
- package/dist/StudioUI-GJK45R3T.js.map +0 -1
- package/dist/StudioUI-QZ54STXE.mjs +0 -6500
- package/dist/StudioUI-QZ54STXE.mjs.map +0 -1
- package/dist/chunk-N6JYTJCB.js +0 -68
- package/dist/chunk-N6JYTJCB.js.map +0 -1
- package/dist/chunk-RHI3UROE.mjs +0 -68
- package/dist/chunk-RHI3UROE.mjs.map +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
import * as _emotion_react_jsx_runtime from '@emotion/react/jsx-runtime';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Floating button that opens the Studio modal.
|
|
5
|
-
* Fixed position in bottom-right corner.
|
|
6
|
-
* Only renders in development mode.
|
|
7
|
-
*/
|
|
8
|
-
declare function StudioButton(): _emotion_react_jsx_runtime.JSX.Element | null;
|
|
9
|
-
|
|
10
1
|
/**
|
|
11
2
|
* Dimensions object {w, h}
|
|
12
3
|
*/
|
|
@@ -28,6 +19,15 @@ interface MetaEntry {
|
|
|
28
19
|
f?: Dimensions;
|
|
29
20
|
c?: number;
|
|
30
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Full meta schema including special keys
|
|
24
|
+
* _cdns: Array of CDN base URLs
|
|
25
|
+
* Other keys: file paths from public folder
|
|
26
|
+
*/
|
|
27
|
+
interface FullMeta {
|
|
28
|
+
_cdns?: string[];
|
|
29
|
+
[key: string]: MetaEntry | string[] | undefined;
|
|
30
|
+
}
|
|
31
31
|
/**
|
|
32
32
|
* Meta schema - keyed by path from public folder
|
|
33
33
|
* Example: { "/portfolio/photo.jpg": { o: {w:2400,h:1600}, b: "...", sm: {w:300,h:200}, ... } }
|
|
@@ -82,5 +82,9 @@ declare function getThumbnailPath(originalPath: string, size: 'sm' | 'md' | 'lg'
|
|
|
82
82
|
* Get all thumbnail paths for an image
|
|
83
83
|
*/
|
|
84
84
|
declare function getAllThumbnailPaths(originalPath: string): string[];
|
|
85
|
+
/**
|
|
86
|
+
* Check if an image entry is processed (has any thumbnail dimensions)
|
|
87
|
+
*/
|
|
88
|
+
declare function isProcessed(entry: MetaEntry | undefined): boolean;
|
|
85
89
|
|
|
86
|
-
export { type FileItem, type LeanImageEntry, type LeanMeta,
|
|
90
|
+
export { type Dimensions, type FileItem, type FullMeta, type LeanImageEntry, type LeanMeta, type MetaEntry, type StudioConfig, getAllThumbnailPaths, getThumbnailPath, isProcessed };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
import * as _emotion_react_jsx_runtime from '@emotion/react/jsx-runtime';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Floating button that opens the Studio modal.
|
|
5
|
-
* Fixed position in bottom-right corner.
|
|
6
|
-
* Only renders in development mode.
|
|
7
|
-
*/
|
|
8
|
-
declare function StudioButton(): _emotion_react_jsx_runtime.JSX.Element | null;
|
|
9
|
-
|
|
10
1
|
/**
|
|
11
2
|
* Dimensions object {w, h}
|
|
12
3
|
*/
|
|
@@ -28,6 +19,15 @@ interface MetaEntry {
|
|
|
28
19
|
f?: Dimensions;
|
|
29
20
|
c?: number;
|
|
30
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Full meta schema including special keys
|
|
24
|
+
* _cdns: Array of CDN base URLs
|
|
25
|
+
* Other keys: file paths from public folder
|
|
26
|
+
*/
|
|
27
|
+
interface FullMeta {
|
|
28
|
+
_cdns?: string[];
|
|
29
|
+
[key: string]: MetaEntry | string[] | undefined;
|
|
30
|
+
}
|
|
31
31
|
/**
|
|
32
32
|
* Meta schema - keyed by path from public folder
|
|
33
33
|
* Example: { "/portfolio/photo.jpg": { o: {w:2400,h:1600}, b: "...", sm: {w:300,h:200}, ... } }
|
|
@@ -82,5 +82,9 @@ declare function getThumbnailPath(originalPath: string, size: 'sm' | 'md' | 'lg'
|
|
|
82
82
|
* Get all thumbnail paths for an image
|
|
83
83
|
*/
|
|
84
84
|
declare function getAllThumbnailPaths(originalPath: string): string[];
|
|
85
|
+
/**
|
|
86
|
+
* Check if an image entry is processed (has any thumbnail dimensions)
|
|
87
|
+
*/
|
|
88
|
+
declare function isProcessed(entry: MetaEntry | undefined): boolean;
|
|
85
89
|
|
|
86
|
-
export { type FileItem, type LeanImageEntry, type LeanMeta,
|
|
90
|
+
export { type Dimensions, type FileItem, type FullMeta, type LeanImageEntry, type LeanMeta, type MetaEntry, type StudioConfig, getAllThumbnailPaths, getThumbnailPath, isProcessed };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
@@ -7,180 +7,5 @@ var _chunkVI6QG6WTjs = require('./chunk-VI6QG6WT.js');
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
var _chunkN6JYTJCBjs = require('./chunk-N6JYTJCB.js');
|
|
12
|
-
|
|
13
|
-
// src/components/StudioButton.tsx
|
|
14
|
-
var _react = require('react');
|
|
15
|
-
var _react3 = require('@emotion/react');
|
|
16
|
-
var _jsxruntime = require('@emotion/react/jsx-runtime');
|
|
17
|
-
var StudioUI = _react.lazy.call(void 0, () => Promise.resolve().then(() => _interopRequireWildcard(require("./StudioUI-GJK45R3T.js"))));
|
|
18
|
-
var spin = _react3.keyframes`
|
|
19
|
-
to {
|
|
20
|
-
transform: rotate(360deg);
|
|
21
|
-
}
|
|
22
|
-
`;
|
|
23
|
-
var styles = {
|
|
24
|
-
button: _react3.css`
|
|
25
|
-
position: fixed;
|
|
26
|
-
bottom: 24px;
|
|
27
|
-
right: 24px;
|
|
28
|
-
z-index: 9998;
|
|
29
|
-
width: 52px;
|
|
30
|
-
height: 52px;
|
|
31
|
-
border-radius: 50%;
|
|
32
|
-
background: ${_chunkN6JYTJCBjs.colors.primary};
|
|
33
|
-
color: white;
|
|
34
|
-
box-shadow: 0 4px 12px ${_chunkN6JYTJCBjs.colors.shadowDark}, 0 1px 3px ${_chunkN6JYTJCBjs.colors.shadow};
|
|
35
|
-
display: flex;
|
|
36
|
-
align-items: center;
|
|
37
|
-
justify-content: center;
|
|
38
|
-
border: none;
|
|
39
|
-
cursor: pointer;
|
|
40
|
-
transition: all 0.15s ease;
|
|
41
|
-
font-family: ${_chunkN6JYTJCBjs.fontStack};
|
|
42
|
-
|
|
43
|
-
&:hover {
|
|
44
|
-
transform: translateY(-2px);
|
|
45
|
-
box-shadow: 0 8px 20px ${_chunkN6JYTJCBjs.colors.shadowDark}, 0 2px 6px ${_chunkN6JYTJCBjs.colors.shadow};
|
|
46
|
-
background: ${_chunkN6JYTJCBjs.colors.primaryHover};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
&:active {
|
|
50
|
-
transform: translateY(0);
|
|
51
|
-
}
|
|
52
|
-
`,
|
|
53
|
-
buttonIcon: _react3.css`
|
|
54
|
-
width: 24px;
|
|
55
|
-
height: 24px;
|
|
56
|
-
`,
|
|
57
|
-
overlay: _react3.css`
|
|
58
|
-
position: fixed;
|
|
59
|
-
top: 0;
|
|
60
|
-
right: 0;
|
|
61
|
-
bottom: 0;
|
|
62
|
-
left: 0;
|
|
63
|
-
z-index: 9999;
|
|
64
|
-
transition: opacity 0.2s ease, visibility 0.2s ease;
|
|
65
|
-
`,
|
|
66
|
-
overlayHidden: _react3.css`
|
|
67
|
-
opacity: 0;
|
|
68
|
-
visibility: hidden;
|
|
69
|
-
pointer-events: none;
|
|
70
|
-
`,
|
|
71
|
-
backdrop: _react3.css`
|
|
72
|
-
position: absolute;
|
|
73
|
-
top: 0;
|
|
74
|
-
right: 0;
|
|
75
|
-
bottom: 0;
|
|
76
|
-
left: 0;
|
|
77
|
-
background-color: rgba(26, 31, 54, 0.4);
|
|
78
|
-
backdrop-filter: blur(4px);
|
|
79
|
-
`,
|
|
80
|
-
modal: _react3.css`
|
|
81
|
-
${_chunkN6JYTJCBjs.baseReset}
|
|
82
|
-
position: absolute;
|
|
83
|
-
top: 24px;
|
|
84
|
-
right: 24px;
|
|
85
|
-
bottom: 24px;
|
|
86
|
-
left: 24px;
|
|
87
|
-
background-color: ${_chunkN6JYTJCBjs.colors.surface};
|
|
88
|
-
border-radius: 12px;
|
|
89
|
-
box-shadow: 0 30px 60px -12px rgba(50, 50, 93, 0.25), 0 18px 36px -18px rgba(0, 0, 0, 0.3);
|
|
90
|
-
display: flex;
|
|
91
|
-
flex-direction: column;
|
|
92
|
-
overflow: hidden;
|
|
93
|
-
`,
|
|
94
|
-
loading: _react3.css`
|
|
95
|
-
display: flex;
|
|
96
|
-
align-items: center;
|
|
97
|
-
justify-content: center;
|
|
98
|
-
height: 100%;
|
|
99
|
-
background: ${_chunkN6JYTJCBjs.colors.background};
|
|
100
|
-
font-family: ${_chunkN6JYTJCBjs.fontStack};
|
|
101
|
-
`,
|
|
102
|
-
loadingContent: _react3.css`
|
|
103
|
-
display: flex;
|
|
104
|
-
flex-direction: column;
|
|
105
|
-
align-items: center;
|
|
106
|
-
gap: 16px;
|
|
107
|
-
`,
|
|
108
|
-
spinner: _react3.css`
|
|
109
|
-
width: 36px;
|
|
110
|
-
height: 36px;
|
|
111
|
-
border-radius: 50%;
|
|
112
|
-
border: 3px solid ${_chunkN6JYTJCBjs.colors.border};
|
|
113
|
-
border-top-color: ${_chunkN6JYTJCBjs.colors.primary};
|
|
114
|
-
animation: ${spin} 0.8s linear infinite;
|
|
115
|
-
`,
|
|
116
|
-
loadingText: _react3.css`
|
|
117
|
-
color: ${_chunkN6JYTJCBjs.colors.textSecondary};
|
|
118
|
-
font-size: ${_chunkN6JYTJCBjs.fontSize.base};
|
|
119
|
-
font-weight: 500;
|
|
120
|
-
margin: 0;
|
|
121
|
-
letter-spacing: -0.01em;
|
|
122
|
-
`
|
|
123
|
-
};
|
|
124
|
-
function StudioButton() {
|
|
125
|
-
const [mounted, setMounted] = _react.useState.call(void 0, false);
|
|
126
|
-
const [isOpen, setIsOpen] = _react.useState.call(void 0, false);
|
|
127
|
-
const [hasBeenOpened, setHasBeenOpened] = _react.useState.call(void 0, false);
|
|
128
|
-
_react.useEffect.call(void 0, () => {
|
|
129
|
-
setMounted(true);
|
|
130
|
-
}, []);
|
|
131
|
-
const handleOpen = () => {
|
|
132
|
-
setIsOpen(true);
|
|
133
|
-
setHasBeenOpened(true);
|
|
134
|
-
};
|
|
135
|
-
if (!mounted || process.env.NODE_ENV !== "development") {
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
139
|
-
!isOpen && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
140
|
-
"button",
|
|
141
|
-
{
|
|
142
|
-
css: styles.button,
|
|
143
|
-
onClick: handleOpen,
|
|
144
|
-
title: "Open Studio",
|
|
145
|
-
"aria-label": "Open Studio media manager",
|
|
146
|
-
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, ImageIcon, {})
|
|
147
|
-
}
|
|
148
|
-
),
|
|
149
|
-
hasBeenOpened && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: [styles.overlay, !isOpen && styles.overlayHidden], children: [
|
|
150
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles.backdrop, onClick: () => setIsOpen(false) }),
|
|
151
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles.modal, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _react.Suspense, { fallback: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, LoadingState, {}), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, StudioUI, { onClose: () => setIsOpen(false), isVisible: isOpen }) }) })
|
|
152
|
-
] })
|
|
153
|
-
] });
|
|
154
|
-
}
|
|
155
|
-
function ImageIcon() {
|
|
156
|
-
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
157
|
-
"svg",
|
|
158
|
-
{
|
|
159
|
-
css: styles.buttonIcon,
|
|
160
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
161
|
-
viewBox: "0 0 24 24",
|
|
162
|
-
fill: "none",
|
|
163
|
-
stroke: "currentColor",
|
|
164
|
-
strokeWidth: 2,
|
|
165
|
-
strokeLinecap: "round",
|
|
166
|
-
strokeLinejoin: "round",
|
|
167
|
-
children: [
|
|
168
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
|
|
169
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
|
|
170
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "polyline", { points: "21 15 16 10 5 21" })
|
|
171
|
-
]
|
|
172
|
-
}
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
function LoadingState() {
|
|
176
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles.loading, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { css: styles.loadingContent, children: [
|
|
177
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { css: styles.spinner }),
|
|
178
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { css: styles.loadingText, children: "Loading Studio..." })
|
|
179
|
-
] }) });
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
exports.StudioButton = StudioButton; exports.getAllThumbnailPaths = _chunkVI6QG6WTjs.getAllThumbnailPaths; exports.getThumbnailPath = _chunkVI6QG6WTjs.getThumbnailPath;
|
|
10
|
+
exports.getAllThumbnailPaths = _chunkVI6QG6WTjs.getAllThumbnailPaths; exports.getThumbnailPath = _chunkVI6QG6WTjs.getThumbnailPath; exports.isProcessed = _chunkVI6QG6WTjs.isProcessed;
|
|
186
11
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/chrisb/Sites/studio/dist/index.js"
|
|
1
|
+
{"version":3,"sources":["/Users/chrisb/Sites/studio/dist/index.js"],"names":[],"mappings":"AAAA;AACE;AACA;AACA;AACF,sDAA4B;AAC5B;AACE;AACA;AACA;AACF,uLAAC","file":"/Users/chrisb/Sites/studio/dist/index.js"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,186 +1,11 @@
|
|
|
1
|
-
"use client";
|
|
2
1
|
import {
|
|
3
2
|
getAllThumbnailPaths,
|
|
4
|
-
getThumbnailPath
|
|
3
|
+
getThumbnailPath,
|
|
4
|
+
isProcessed
|
|
5
5
|
} from "./chunk-TRYWHLJ2.mjs";
|
|
6
|
-
import {
|
|
7
|
-
baseReset,
|
|
8
|
-
colors,
|
|
9
|
-
fontSize,
|
|
10
|
-
fontStack
|
|
11
|
-
} from "./chunk-RHI3UROE.mjs";
|
|
12
|
-
|
|
13
|
-
// src/components/StudioButton.tsx
|
|
14
|
-
import { useState, useEffect, lazy, Suspense } from "react";
|
|
15
|
-
import { css, keyframes } from "@emotion/react";
|
|
16
|
-
import { Fragment, jsx, jsxs } from "@emotion/react/jsx-runtime";
|
|
17
|
-
var StudioUI = lazy(() => import("./StudioUI-QZ54STXE.mjs"));
|
|
18
|
-
var spin = keyframes`
|
|
19
|
-
to {
|
|
20
|
-
transform: rotate(360deg);
|
|
21
|
-
}
|
|
22
|
-
`;
|
|
23
|
-
var styles = {
|
|
24
|
-
button: css`
|
|
25
|
-
position: fixed;
|
|
26
|
-
bottom: 24px;
|
|
27
|
-
right: 24px;
|
|
28
|
-
z-index: 9998;
|
|
29
|
-
width: 52px;
|
|
30
|
-
height: 52px;
|
|
31
|
-
border-radius: 50%;
|
|
32
|
-
background: ${colors.primary};
|
|
33
|
-
color: white;
|
|
34
|
-
box-shadow: 0 4px 12px ${colors.shadowDark}, 0 1px 3px ${colors.shadow};
|
|
35
|
-
display: flex;
|
|
36
|
-
align-items: center;
|
|
37
|
-
justify-content: center;
|
|
38
|
-
border: none;
|
|
39
|
-
cursor: pointer;
|
|
40
|
-
transition: all 0.15s ease;
|
|
41
|
-
font-family: ${fontStack};
|
|
42
|
-
|
|
43
|
-
&:hover {
|
|
44
|
-
transform: translateY(-2px);
|
|
45
|
-
box-shadow: 0 8px 20px ${colors.shadowDark}, 0 2px 6px ${colors.shadow};
|
|
46
|
-
background: ${colors.primaryHover};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
&:active {
|
|
50
|
-
transform: translateY(0);
|
|
51
|
-
}
|
|
52
|
-
`,
|
|
53
|
-
buttonIcon: css`
|
|
54
|
-
width: 24px;
|
|
55
|
-
height: 24px;
|
|
56
|
-
`,
|
|
57
|
-
overlay: css`
|
|
58
|
-
position: fixed;
|
|
59
|
-
top: 0;
|
|
60
|
-
right: 0;
|
|
61
|
-
bottom: 0;
|
|
62
|
-
left: 0;
|
|
63
|
-
z-index: 9999;
|
|
64
|
-
transition: opacity 0.2s ease, visibility 0.2s ease;
|
|
65
|
-
`,
|
|
66
|
-
overlayHidden: css`
|
|
67
|
-
opacity: 0;
|
|
68
|
-
visibility: hidden;
|
|
69
|
-
pointer-events: none;
|
|
70
|
-
`,
|
|
71
|
-
backdrop: css`
|
|
72
|
-
position: absolute;
|
|
73
|
-
top: 0;
|
|
74
|
-
right: 0;
|
|
75
|
-
bottom: 0;
|
|
76
|
-
left: 0;
|
|
77
|
-
background-color: rgba(26, 31, 54, 0.4);
|
|
78
|
-
backdrop-filter: blur(4px);
|
|
79
|
-
`,
|
|
80
|
-
modal: css`
|
|
81
|
-
${baseReset}
|
|
82
|
-
position: absolute;
|
|
83
|
-
top: 24px;
|
|
84
|
-
right: 24px;
|
|
85
|
-
bottom: 24px;
|
|
86
|
-
left: 24px;
|
|
87
|
-
background-color: ${colors.surface};
|
|
88
|
-
border-radius: 12px;
|
|
89
|
-
box-shadow: 0 30px 60px -12px rgba(50, 50, 93, 0.25), 0 18px 36px -18px rgba(0, 0, 0, 0.3);
|
|
90
|
-
display: flex;
|
|
91
|
-
flex-direction: column;
|
|
92
|
-
overflow: hidden;
|
|
93
|
-
`,
|
|
94
|
-
loading: css`
|
|
95
|
-
display: flex;
|
|
96
|
-
align-items: center;
|
|
97
|
-
justify-content: center;
|
|
98
|
-
height: 100%;
|
|
99
|
-
background: ${colors.background};
|
|
100
|
-
font-family: ${fontStack};
|
|
101
|
-
`,
|
|
102
|
-
loadingContent: css`
|
|
103
|
-
display: flex;
|
|
104
|
-
flex-direction: column;
|
|
105
|
-
align-items: center;
|
|
106
|
-
gap: 16px;
|
|
107
|
-
`,
|
|
108
|
-
spinner: css`
|
|
109
|
-
width: 36px;
|
|
110
|
-
height: 36px;
|
|
111
|
-
border-radius: 50%;
|
|
112
|
-
border: 3px solid ${colors.border};
|
|
113
|
-
border-top-color: ${colors.primary};
|
|
114
|
-
animation: ${spin} 0.8s linear infinite;
|
|
115
|
-
`,
|
|
116
|
-
loadingText: css`
|
|
117
|
-
color: ${colors.textSecondary};
|
|
118
|
-
font-size: ${fontSize.base};
|
|
119
|
-
font-weight: 500;
|
|
120
|
-
margin: 0;
|
|
121
|
-
letter-spacing: -0.01em;
|
|
122
|
-
`
|
|
123
|
-
};
|
|
124
|
-
function StudioButton() {
|
|
125
|
-
const [mounted, setMounted] = useState(false);
|
|
126
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
127
|
-
const [hasBeenOpened, setHasBeenOpened] = useState(false);
|
|
128
|
-
useEffect(() => {
|
|
129
|
-
setMounted(true);
|
|
130
|
-
}, []);
|
|
131
|
-
const handleOpen = () => {
|
|
132
|
-
setIsOpen(true);
|
|
133
|
-
setHasBeenOpened(true);
|
|
134
|
-
};
|
|
135
|
-
if (!mounted || process.env.NODE_ENV !== "development") {
|
|
136
|
-
return null;
|
|
137
|
-
}
|
|
138
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
139
|
-
!isOpen && /* @__PURE__ */ jsx(
|
|
140
|
-
"button",
|
|
141
|
-
{
|
|
142
|
-
css: styles.button,
|
|
143
|
-
onClick: handleOpen,
|
|
144
|
-
title: "Open Studio",
|
|
145
|
-
"aria-label": "Open Studio media manager",
|
|
146
|
-
children: /* @__PURE__ */ jsx(ImageIcon, {})
|
|
147
|
-
}
|
|
148
|
-
),
|
|
149
|
-
hasBeenOpened && /* @__PURE__ */ jsxs("div", { css: [styles.overlay, !isOpen && styles.overlayHidden], children: [
|
|
150
|
-
/* @__PURE__ */ jsx("div", { css: styles.backdrop, onClick: () => setIsOpen(false) }),
|
|
151
|
-
/* @__PURE__ */ jsx("div", { css: styles.modal, children: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(LoadingState, {}), children: /* @__PURE__ */ jsx(StudioUI, { onClose: () => setIsOpen(false), isVisible: isOpen }) }) })
|
|
152
|
-
] })
|
|
153
|
-
] });
|
|
154
|
-
}
|
|
155
|
-
function ImageIcon() {
|
|
156
|
-
return /* @__PURE__ */ jsxs(
|
|
157
|
-
"svg",
|
|
158
|
-
{
|
|
159
|
-
css: styles.buttonIcon,
|
|
160
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
161
|
-
viewBox: "0 0 24 24",
|
|
162
|
-
fill: "none",
|
|
163
|
-
stroke: "currentColor",
|
|
164
|
-
strokeWidth: 2,
|
|
165
|
-
strokeLinecap: "round",
|
|
166
|
-
strokeLinejoin: "round",
|
|
167
|
-
children: [
|
|
168
|
-
/* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
|
|
169
|
-
/* @__PURE__ */ jsx("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
|
|
170
|
-
/* @__PURE__ */ jsx("polyline", { points: "21 15 16 10 5 21" })
|
|
171
|
-
]
|
|
172
|
-
}
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
function LoadingState() {
|
|
176
|
-
return /* @__PURE__ */ jsx("div", { css: styles.loading, children: /* @__PURE__ */ jsxs("div", { css: styles.loadingContent, children: [
|
|
177
|
-
/* @__PURE__ */ jsx("div", { css: styles.spinner }),
|
|
178
|
-
/* @__PURE__ */ jsx("p", { css: styles.loadingText, children: "Loading Studio..." })
|
|
179
|
-
] }) });
|
|
180
|
-
}
|
|
181
6
|
export {
|
|
182
|
-
StudioButton,
|
|
183
7
|
getAllThumbnailPaths,
|
|
184
|
-
getThumbnailPath
|
|
8
|
+
getThumbnailPath,
|
|
9
|
+
isProcessed
|
|
185
10
|
};
|
|
186
11
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/next.config.mjs
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** @type {import('next').NextConfig} */
|
|
2
|
+
const nextConfig = {
|
|
3
|
+
// Enable standalone output for better CLI experience
|
|
4
|
+
output: 'standalone',
|
|
5
|
+
|
|
6
|
+
// Emotion requires this for the css prop to work
|
|
7
|
+
compiler: {
|
|
8
|
+
emotion: true,
|
|
9
|
+
},
|
|
10
|
+
|
|
11
|
+
// Disable static generation for this dev tool
|
|
12
|
+
experimental: {
|
|
13
|
+
// Allow importing from src/ in app/
|
|
14
|
+
},
|
|
15
|
+
|
|
16
|
+
// Environment variables to expose to the client
|
|
17
|
+
env: {
|
|
18
|
+
NEXT_PUBLIC_STUDIO_WORKSPACE: process.env.STUDIO_WORKSPACE || process.cwd(),
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export default nextConfig
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gallop.software/studio",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Standalone media manager for Gallop templates - upload, process, and sync images to CDN",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"studio": "./bin/studio.mjs"
|
|
10
|
+
},
|
|
8
11
|
"exports": {
|
|
9
12
|
".": {
|
|
10
13
|
"types": "./dist/index.d.ts",
|
|
@@ -18,11 +21,16 @@
|
|
|
18
21
|
}
|
|
19
22
|
},
|
|
20
23
|
"files": [
|
|
21
|
-
"dist"
|
|
24
|
+
"dist",
|
|
25
|
+
"bin",
|
|
26
|
+
"app",
|
|
27
|
+
"src",
|
|
28
|
+
"next.config.mjs"
|
|
22
29
|
],
|
|
23
30
|
"scripts": {
|
|
24
31
|
"build": "tsup",
|
|
25
|
-
"dev": "
|
|
32
|
+
"dev": "next dev -p 3001",
|
|
33
|
+
"start": "next dev -p 3001",
|
|
26
34
|
"lint": "eslint src",
|
|
27
35
|
"typecheck": "tsc --noEmit",
|
|
28
36
|
"prepublishOnly": "npm run build"
|
|
@@ -33,7 +41,8 @@
|
|
|
33
41
|
"cloudflare-r2",
|
|
34
42
|
"nextjs",
|
|
35
43
|
"gallop",
|
|
36
|
-
"cdn"
|
|
44
|
+
"cdn",
|
|
45
|
+
"cli"
|
|
37
46
|
],
|
|
38
47
|
"author": "Gallop Software",
|
|
39
48
|
"license": "MIT",
|
|
@@ -45,16 +54,15 @@
|
|
|
45
54
|
"bugs": {
|
|
46
55
|
"url": "https://github.com/gallop-software/studio/issues"
|
|
47
56
|
},
|
|
48
|
-
"peerDependencies": {
|
|
49
|
-
"next": ">=14.0.0",
|
|
50
|
-
"react": ">=18.0.0",
|
|
51
|
-
"react-dom": ">=18.0.0"
|
|
52
|
-
},
|
|
53
57
|
"dependencies": {
|
|
54
58
|
"@aws-sdk/client-s3": "^3.705.0",
|
|
55
59
|
"@emotion/react": "^11.14.0",
|
|
56
60
|
"blurhash": "^2.0.5",
|
|
57
61
|
"clsx": "^2.1.1",
|
|
62
|
+
"next": "^15.1.0",
|
|
63
|
+
"open": "^10.1.0",
|
|
64
|
+
"react": "^19.0.0",
|
|
65
|
+
"react-dom": "^19.0.0",
|
|
58
66
|
"react-dropzone": "^14.3.5",
|
|
59
67
|
"sharp": "^0.33.5"
|
|
60
68
|
},
|