@gallop.software/studio 1.5.10 → 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.
Files changed (60) hide show
  1. package/app/api/studio/[...path]/route.ts +1 -0
  2. package/app/layout.tsx +20 -0
  3. package/app/page.tsx +82 -0
  4. package/bin/studio.mjs +110 -0
  5. package/dist/handlers/index.js +77 -55
  6. package/dist/handlers/index.js.map +1 -1
  7. package/dist/handlers/index.mjs +128 -106
  8. package/dist/handlers/index.mjs.map +1 -1
  9. package/dist/index.d.mts +14 -10
  10. package/dist/index.d.ts +14 -10
  11. package/dist/index.js +2 -177
  12. package/dist/index.js.map +1 -1
  13. package/dist/index.mjs +4 -179
  14. package/dist/index.mjs.map +1 -1
  15. package/next.config.mjs +22 -0
  16. package/package.json +18 -10
  17. package/src/components/AddNewModal.tsx +402 -0
  18. package/src/components/ErrorModal.tsx +89 -0
  19. package/src/components/R2SetupModal.tsx +400 -0
  20. package/src/components/StudioBreadcrumb.tsx +115 -0
  21. package/src/components/StudioButton.tsx +200 -0
  22. package/src/components/StudioContext.tsx +219 -0
  23. package/src/components/StudioDetailView.tsx +714 -0
  24. package/src/components/StudioFileGrid.tsx +704 -0
  25. package/src/components/StudioFileList.tsx +743 -0
  26. package/src/components/StudioFolderPicker.tsx +342 -0
  27. package/src/components/StudioModal.tsx +473 -0
  28. package/src/components/StudioPreview.tsx +399 -0
  29. package/src/components/StudioSettings.tsx +536 -0
  30. package/src/components/StudioToolbar.tsx +1448 -0
  31. package/src/components/StudioUI.tsx +731 -0
  32. package/src/components/styles/common.ts +236 -0
  33. package/src/components/tokens.ts +78 -0
  34. package/src/components/useStudioActions.tsx +497 -0
  35. package/src/config/index.ts +7 -0
  36. package/src/config/workspace.ts +52 -0
  37. package/src/handlers/favicon.ts +152 -0
  38. package/src/handlers/files.ts +784 -0
  39. package/src/handlers/images.ts +949 -0
  40. package/src/handlers/import.ts +190 -0
  41. package/src/handlers/index.ts +168 -0
  42. package/src/handlers/list.ts +627 -0
  43. package/src/handlers/scan.ts +311 -0
  44. package/src/handlers/utils/cdn.ts +234 -0
  45. package/src/handlers/utils/files.ts +64 -0
  46. package/src/handlers/utils/index.ts +4 -0
  47. package/src/handlers/utils/meta.ts +102 -0
  48. package/src/handlers/utils/thumbnails.ts +98 -0
  49. package/src/hooks/useFileList.ts +143 -0
  50. package/src/index.tsx +36 -0
  51. package/src/lib/api.ts +176 -0
  52. package/src/types.ts +119 -0
  53. package/dist/StudioUI-GJK45R3T.js +0 -6500
  54. package/dist/StudioUI-GJK45R3T.js.map +0 -1
  55. package/dist/StudioUI-QZ54STXE.mjs +0 -6500
  56. package/dist/StudioUI-QZ54STXE.mjs.map +0 -1
  57. package/dist/chunk-N6JYTJCB.js +0 -68
  58. package/dist/chunk-N6JYTJCB.js.map +0 -1
  59. package/dist/chunk-RHI3UROE.mjs +0 -68
  60. 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, StudioButton, type StudioConfig, getAllThumbnailPaths, getThumbnailPath };
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, StudioButton, type StudioConfig, getAllThumbnailPaths, getThumbnailPath };
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}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } }"use client";
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","../src/components/StudioButton.tsx"],"names":[],"mappings":"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","file":"/Users/chrisb/Sites/studio/dist/index.js","sourcesContent":[null,"/** @jsxImportSource @emotion/react */\n'use client'\n\nimport { useState, useEffect, lazy, Suspense } from 'react'\nimport { css, keyframes } from '@emotion/react'\nimport { colors, fontStack, fontSize, baseReset } from './tokens'\n\n// Lazy load the full Studio UI to avoid bundling in production\nconst StudioUI = lazy(() => import('./StudioUI'))\n\nconst spin = keyframes`\n to {\n transform: rotate(360deg);\n }\n`\n\nconst styles = {\n button: css`\n position: fixed;\n bottom: 24px;\n right: 24px;\n z-index: 9998;\n width: 52px;\n height: 52px;\n border-radius: 50%;\n background: ${colors.primary};\n color: white;\n box-shadow: 0 4px 12px ${colors.shadowDark}, 0 1px 3px ${colors.shadow};\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n cursor: pointer;\n transition: all 0.15s ease;\n font-family: ${fontStack};\n \n &:hover {\n transform: translateY(-2px);\n box-shadow: 0 8px 20px ${colors.shadowDark}, 0 2px 6px ${colors.shadow};\n background: ${colors.primaryHover};\n }\n \n &:active {\n transform: translateY(0);\n }\n `,\n buttonIcon: css`\n width: 24px;\n height: 24px;\n `,\n overlay: css`\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 9999;\n transition: opacity 0.2s ease, visibility 0.2s ease;\n `,\n overlayHidden: css`\n opacity: 0;\n visibility: hidden;\n pointer-events: none;\n `,\n backdrop: css`\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n background-color: rgba(26, 31, 54, 0.4);\n backdrop-filter: blur(4px);\n `,\n modal: css`\n ${baseReset}\n position: absolute;\n top: 24px;\n right: 24px;\n bottom: 24px;\n left: 24px;\n background-color: ${colors.surface};\n border-radius: 12px;\n box-shadow: 0 30px 60px -12px rgba(50, 50, 93, 0.25), 0 18px 36px -18px rgba(0, 0, 0, 0.3);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n `,\n loading: css`\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n background: ${colors.background};\n font-family: ${fontStack};\n `,\n loadingContent: css`\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 16px;\n `,\n spinner: css`\n width: 36px;\n height: 36px;\n border-radius: 50%;\n border: 3px solid ${colors.border};\n border-top-color: ${colors.primary};\n animation: ${spin} 0.8s linear infinite;\n `,\n loadingText: css`\n color: ${colors.textSecondary};\n font-size: ${fontSize.base};\n font-weight: 500;\n margin: 0;\n letter-spacing: -0.01em;\n `,\n}\n\n/**\n * Floating button that opens the Studio modal.\n * Fixed position in bottom-right corner.\n * Only renders in development mode.\n */\nexport function StudioButton() {\n const [mounted, setMounted] = useState(false)\n const [isOpen, setIsOpen] = useState(false)\n const [hasBeenOpened, setHasBeenOpened] = useState(false)\n\n // Only render on client to avoid hydration mismatch\n useEffect(() => {\n setMounted(true)\n }, [])\n\n const handleOpen = () => {\n setIsOpen(true)\n setHasBeenOpened(true)\n }\n\n // Only render in development and on client\n if (!mounted || process.env.NODE_ENV !== 'development') {\n return null\n }\n\n return (\n <>\n {!isOpen && (\n <button\n css={styles.button}\n onClick={handleOpen}\n title=\"Open Studio\"\n aria-label=\"Open Studio media manager\"\n >\n <ImageIcon />\n </button>\n )}\n\n {/* Keep mounted once opened to preserve state */}\n {hasBeenOpened && (\n <div css={[styles.overlay, !isOpen && styles.overlayHidden]}>\n <div css={styles.backdrop} onClick={() => setIsOpen(false)} />\n <div css={styles.modal}>\n <Suspense fallback={<LoadingState />}>\n <StudioUI onClose={() => setIsOpen(false)} isVisible={isOpen} />\n </Suspense>\n </div>\n </div>\n )}\n </>\n )\n}\n\nfunction ImageIcon() {\n return (\n <svg\n css={styles.buttonIcon}\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\" />\n <circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\" />\n <polyline points=\"21 15 16 10 5 21\" />\n </svg>\n )\n}\n\nfunction LoadingState() {\n return (\n <div css={styles.loading}>\n <div css={styles.loadingContent}>\n <div css={styles.spinner} />\n <p css={styles.loadingText}>Loading Studio...</p>\n </div>\n </div>\n )\n}\n"]}
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
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/StudioButton.tsx"],"sourcesContent":["/** @jsxImportSource @emotion/react */\n'use client'\n\nimport { useState, useEffect, lazy, Suspense } from 'react'\nimport { css, keyframes } from '@emotion/react'\nimport { colors, fontStack, fontSize, baseReset } from './tokens'\n\n// Lazy load the full Studio UI to avoid bundling in production\nconst StudioUI = lazy(() => import('./StudioUI'))\n\nconst spin = keyframes`\n to {\n transform: rotate(360deg);\n }\n`\n\nconst styles = {\n button: css`\n position: fixed;\n bottom: 24px;\n right: 24px;\n z-index: 9998;\n width: 52px;\n height: 52px;\n border-radius: 50%;\n background: ${colors.primary};\n color: white;\n box-shadow: 0 4px 12px ${colors.shadowDark}, 0 1px 3px ${colors.shadow};\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n cursor: pointer;\n transition: all 0.15s ease;\n font-family: ${fontStack};\n \n &:hover {\n transform: translateY(-2px);\n box-shadow: 0 8px 20px ${colors.shadowDark}, 0 2px 6px ${colors.shadow};\n background: ${colors.primaryHover};\n }\n \n &:active {\n transform: translateY(0);\n }\n `,\n buttonIcon: css`\n width: 24px;\n height: 24px;\n `,\n overlay: css`\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n z-index: 9999;\n transition: opacity 0.2s ease, visibility 0.2s ease;\n `,\n overlayHidden: css`\n opacity: 0;\n visibility: hidden;\n pointer-events: none;\n `,\n backdrop: css`\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n background-color: rgba(26, 31, 54, 0.4);\n backdrop-filter: blur(4px);\n `,\n modal: css`\n ${baseReset}\n position: absolute;\n top: 24px;\n right: 24px;\n bottom: 24px;\n left: 24px;\n background-color: ${colors.surface};\n border-radius: 12px;\n box-shadow: 0 30px 60px -12px rgba(50, 50, 93, 0.25), 0 18px 36px -18px rgba(0, 0, 0, 0.3);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n `,\n loading: css`\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n background: ${colors.background};\n font-family: ${fontStack};\n `,\n loadingContent: css`\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 16px;\n `,\n spinner: css`\n width: 36px;\n height: 36px;\n border-radius: 50%;\n border: 3px solid ${colors.border};\n border-top-color: ${colors.primary};\n animation: ${spin} 0.8s linear infinite;\n `,\n loadingText: css`\n color: ${colors.textSecondary};\n font-size: ${fontSize.base};\n font-weight: 500;\n margin: 0;\n letter-spacing: -0.01em;\n `,\n}\n\n/**\n * Floating button that opens the Studio modal.\n * Fixed position in bottom-right corner.\n * Only renders in development mode.\n */\nexport function StudioButton() {\n const [mounted, setMounted] = useState(false)\n const [isOpen, setIsOpen] = useState(false)\n const [hasBeenOpened, setHasBeenOpened] = useState(false)\n\n // Only render on client to avoid hydration mismatch\n useEffect(() => {\n setMounted(true)\n }, [])\n\n const handleOpen = () => {\n setIsOpen(true)\n setHasBeenOpened(true)\n }\n\n // Only render in development and on client\n if (!mounted || process.env.NODE_ENV !== 'development') {\n return null\n }\n\n return (\n <>\n {!isOpen && (\n <button\n css={styles.button}\n onClick={handleOpen}\n title=\"Open Studio\"\n aria-label=\"Open Studio media manager\"\n >\n <ImageIcon />\n </button>\n )}\n\n {/* Keep mounted once opened to preserve state */}\n {hasBeenOpened && (\n <div css={[styles.overlay, !isOpen && styles.overlayHidden]}>\n <div css={styles.backdrop} onClick={() => setIsOpen(false)} />\n <div css={styles.modal}>\n <Suspense fallback={<LoadingState />}>\n <StudioUI onClose={() => setIsOpen(false)} isVisible={isOpen} />\n </Suspense>\n </div>\n </div>\n )}\n </>\n )\n}\n\nfunction ImageIcon() {\n return (\n <svg\n css={styles.buttonIcon}\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\" />\n <circle cx=\"8.5\" cy=\"8.5\" r=\"1.5\" />\n <polyline points=\"21 15 16 10 5 21\" />\n </svg>\n )\n}\n\nfunction LoadingState() {\n return (\n <div css={styles.loading}>\n <div css={styles.loadingContent}>\n <div css={styles.spinner} />\n <p css={styles.loadingText}>Loading Studio...</p>\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -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": "1.5.10",
4
- "description": "Media manager for Gallop templates - upload, process, and sync images to CDN",
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": "tsup --watch",
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
  },