@gallop.software/studio 0.1.1 → 0.1.2

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/dist/index.mjs CHANGED
@@ -2,8 +2,98 @@
2
2
 
3
3
  // src/components/StudioButton.tsx
4
4
  import { useState, lazy, Suspense } from "react";
5
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
- var StudioUI = lazy(() => import("./StudioUI-4ST2P6R7.mjs"));
5
+ import { css, keyframes } from "@emotion/react";
6
+ import { Fragment, jsx, jsxs } from "@emotion/react/jsx-runtime";
7
+ var StudioUI = lazy(() => import("./StudioUI-BBXJCQ3O.mjs"));
8
+ var spin = keyframes`
9
+ to {
10
+ transform: rotate(360deg);
11
+ }
12
+ `;
13
+ var styles = {
14
+ button: css`
15
+ position: fixed;
16
+ bottom: 24px;
17
+ right: 24px;
18
+ z-index: 9998;
19
+ width: 48px;
20
+ height: 48px;
21
+ border-radius: 50%;
22
+ background: linear-gradient(to bottom right, #a855f7, #ec4899);
23
+ color: white;
24
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
25
+ display: flex;
26
+ align-items: center;
27
+ justify-content: center;
28
+ border: none;
29
+ cursor: pointer;
30
+ transition: all 0.2s;
31
+
32
+ &:hover {
33
+ transform: scale(1.05);
34
+ box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
35
+ }
36
+ `,
37
+ buttonIcon: css`
38
+ width: 24px;
39
+ height: 24px;
40
+ `,
41
+ overlay: css`
42
+ position: fixed;
43
+ top: 0;
44
+ right: 0;
45
+ bottom: 0;
46
+ left: 0;
47
+ z-index: 9999;
48
+ `,
49
+ backdrop: css`
50
+ position: absolute;
51
+ top: 0;
52
+ right: 0;
53
+ bottom: 0;
54
+ left: 0;
55
+ background-color: rgba(0, 0, 0, 0.5);
56
+ backdrop-filter: blur(4px);
57
+ `,
58
+ modal: css`
59
+ position: absolute;
60
+ top: 32px;
61
+ right: 32px;
62
+ bottom: 32px;
63
+ left: 32px;
64
+ background-color: white;
65
+ border-radius: 16px;
66
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
67
+ display: flex;
68
+ flex-direction: column;
69
+ overflow: hidden;
70
+ `,
71
+ loading: css`
72
+ display: flex;
73
+ align-items: center;
74
+ justify-content: center;
75
+ height: 100%;
76
+ `,
77
+ loadingContent: css`
78
+ display: flex;
79
+ flex-direction: column;
80
+ align-items: center;
81
+ gap: 16px;
82
+ `,
83
+ spinner: css`
84
+ width: 32px;
85
+ height: 32px;
86
+ border-radius: 50%;
87
+ border: 2px solid transparent;
88
+ border-bottom-color: #9333ea;
89
+ animation: ${spin} 1s linear infinite;
90
+ `,
91
+ loadingText: css`
92
+ color: #6b7280;
93
+ font-size: 14px;
94
+ margin: 0;
95
+ `
96
+ };
7
97
  function StudioButton() {
8
98
  const [isOpen, setIsOpen] = useState(false);
9
99
  if (process.env.NODE_ENV !== "development") {
@@ -13,85 +103,24 @@ function StudioButton() {
13
103
  !isOpen && /* @__PURE__ */ jsx(
14
104
  "button",
15
105
  {
106
+ css: styles.button,
16
107
  onClick: () => setIsOpen(true),
17
- style: {
18
- position: "fixed",
19
- bottom: "24px",
20
- right: "24px",
21
- zIndex: 9998,
22
- width: "48px",
23
- height: "48px",
24
- borderRadius: "50%",
25
- background: "linear-gradient(to bottom right, #a855f7, #ec4899)",
26
- color: "white",
27
- boxShadow: "0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)",
28
- display: "flex",
29
- alignItems: "center",
30
- justifyContent: "center",
31
- border: "none",
32
- cursor: "pointer",
33
- transition: "all 0.2s"
34
- },
35
108
  title: "Open Studio",
36
109
  "aria-label": "Open Studio media manager",
37
110
  children: /* @__PURE__ */ jsx(ImageIcon, {})
38
111
  }
39
112
  ),
40
- isOpen && /* @__PURE__ */ jsxs(
41
- "div",
42
- {
43
- style: {
44
- position: "fixed",
45
- top: 0,
46
- right: 0,
47
- bottom: 0,
48
- left: 0,
49
- zIndex: 9999
50
- },
51
- children: [
52
- /* @__PURE__ */ jsx(
53
- "div",
54
- {
55
- style: {
56
- position: "absolute",
57
- top: 0,
58
- right: 0,
59
- bottom: 0,
60
- left: 0,
61
- backgroundColor: "rgba(0, 0, 0, 0.5)",
62
- backdropFilter: "blur(4px)"
63
- },
64
- onClick: () => setIsOpen(false)
65
- }
66
- ),
67
- /* @__PURE__ */ jsx(
68
- "div",
69
- {
70
- style: {
71
- position: "absolute",
72
- top: "32px",
73
- right: "32px",
74
- bottom: "32px",
75
- left: "32px",
76
- backgroundColor: "white",
77
- borderRadius: "16px",
78
- boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
79
- display: "flex",
80
- flexDirection: "column",
81
- overflow: "hidden"
82
- },
83
- children: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(LoadingState, {}), children: /* @__PURE__ */ jsx(StudioUI, { onClose: () => setIsOpen(false) }) })
84
- }
85
- )
86
- ]
87
- }
88
- )
113
+ isOpen && /* @__PURE__ */ jsxs("div", { css: styles.overlay, children: [
114
+ /* @__PURE__ */ jsx("div", { css: styles.backdrop, onClick: () => setIsOpen(false) }),
115
+ /* @__PURE__ */ jsx("div", { css: styles.modal, children: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(LoadingState, {}), children: /* @__PURE__ */ jsx(StudioUI, { onClose: () => setIsOpen(false) }) }) })
116
+ ] })
89
117
  ] });
90
118
  }
91
119
  function ImageIcon() {
92
120
  return /* @__PURE__ */ jsxs(
93
121
  "svg",
94
122
  {
123
+ css: styles.buttonIcon,
95
124
  xmlns: "http://www.w3.org/2000/svg",
96
125
  viewBox: "0 0 24 24",
97
126
  fill: "none",
@@ -99,7 +128,6 @@ function ImageIcon() {
99
128
  strokeWidth: 2,
100
129
  strokeLinecap: "round",
101
130
  strokeLinejoin: "round",
102
- className: "w-6 h-6",
103
131
  children: [
104
132
  /* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
105
133
  /* @__PURE__ */ jsx("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
@@ -109,9 +137,9 @@ function ImageIcon() {
109
137
  );
110
138
  }
111
139
  function LoadingState() {
112
- return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-full", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-4", children: [
113
- /* @__PURE__ */ jsx("div", { className: "animate-spin rounded-full h-8 w-8 border-b-2 border-purple-600" }),
114
- /* @__PURE__ */ jsx("p", { className: "text-gray-500", children: "Loading Studio..." })
140
+ return /* @__PURE__ */ jsx("div", { css: styles.loading, children: /* @__PURE__ */ jsxs("div", { css: styles.loadingContent, children: [
141
+ /* @__PURE__ */ jsx("div", { css: styles.spinner }),
142
+ /* @__PURE__ */ jsx("p", { css: styles.loadingText, children: "Loading Studio..." })
115
143
  ] }) });
116
144
  }
117
145
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/components/StudioButton.tsx","../src/lib/meta.ts"],"sourcesContent":["'use client'\n\nimport { useState, lazy, Suspense } from 'react'\n\n// Lazy load the full Studio UI to avoid bundling in production\nconst StudioUI = lazy(() => import('./StudioUI'))\n\n/**\n * Floating button that opens the Studio modal.\n * Fixed position in bottom-right corner.\n * Only renders in development mode.\n * \n * Usage:\n * ```tsx\n * import { StudioButton } from '@gallop.software/studio'\n * \n * // In your layout\n * <StudioButton />\n * ```\n */\nexport function StudioButton() {\n const [isOpen, setIsOpen] = useState(false)\n\n // Only render in development\n if (process.env.NODE_ENV !== 'development') {\n return null\n }\n\n return (\n <>\n {/* Floating button - hidden when modal is open */}\n {!isOpen && (\n <button\n onClick={() => setIsOpen(true)}\n style={{\n position: 'fixed',\n bottom: '24px',\n right: '24px',\n zIndex: 9998,\n width: '48px',\n height: '48px',\n borderRadius: '50%',\n background: 'linear-gradient(to bottom right, #a855f7, #ec4899)',\n color: 'white',\n boxShadow: '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n border: 'none',\n cursor: 'pointer',\n transition: 'all 0.2s',\n }}\n title=\"Open Studio\"\n aria-label=\"Open Studio media manager\"\n >\n <ImageIcon />\n </button>\n )}\n\n {/* Modal overlay with lazy-loaded UI */}\n {isOpen && (\n <div\n style={{\n position: 'fixed',\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n zIndex: 9999,\n }}\n >\n {/* Backdrop */}\n <div\n style={{\n position: 'absolute',\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n backgroundColor: 'rgba(0, 0, 0, 0.5)',\n backdropFilter: 'blur(4px)',\n }}\n onClick={() => setIsOpen(false)}\n />\n\n {/* Modal */}\n <div\n style={{\n position: 'absolute',\n top: '32px',\n right: '32px',\n bottom: '32px',\n left: '32px',\n backgroundColor: 'white',\n borderRadius: '16px',\n boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.25)',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n }}\n >\n <Suspense fallback={<LoadingState />}>\n <StudioUI onClose={() => setIsOpen(false)} />\n </Suspense>\n </div>\n </div>\n )}\n </>\n )\n}\n\nfunction ImageIcon() {\n return (\n <svg\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 className=\"w-6 h-6\"\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 className=\"flex items-center justify-center h-full\">\n <div className=\"flex flex-col items-center gap-4\">\n <div className=\"animate-spin rounded-full h-8 w-8 border-b-2 border-purple-600\" />\n <p className=\"text-gray-500\">Loading Studio...</p>\n </div>\n </div>\n )\n}\n","import type { StudioMeta, ImageEntry, ImageSize, SizeEntry } from '../types'\n\n// Default empty meta - will be populated when reading from project\nlet _meta: StudioMeta = {\n $schema: 'https://gallop.software/schemas/studio-meta.json',\n version: 1,\n generatedAt: new Date().toISOString(),\n images: {},\n}\n\n/**\n * The meta object containing all image metadata.\n * This is read from _data/_meta.json in the consuming project.\n */\nexport const meta: StudioMeta = _meta\n\n/**\n * Initialize meta from a JSON object (called during build/runtime)\n */\nexport function initializeMeta(data: StudioMeta): void {\n _meta = data\n Object.assign(meta, data)\n}\n\n/**\n * Get the resolved URL for an image, handling CDN vs local paths\n */\nexport function getImageUrl(\n imageKey: string,\n size: ImageSize = 'medium'\n): string | undefined {\n const image = meta.images[imageKey]\n if (!image) return undefined\n\n const sizeData = image.sizes[size] || image.sizes.full\n if (!sizeData) return undefined\n\n // If synced to CDN, use CDN URL\n if (image.cdn?.synced && image.cdn.baseUrl) {\n return `${image.cdn.baseUrl}${sizeData.path}`\n }\n\n // Otherwise use local path\n return sizeData.path\n}\n\n/**\n * Get the full image entry for a key\n */\nexport function getStudioMeta(imageKey: string): ImageEntry | undefined {\n return meta.images[imageKey]\n}\n\n/**\n * Get size data for an image\n */\nexport function getImageSize(\n imageKey: string,\n size: ImageSize = 'medium'\n): SizeEntry | undefined {\n const image = meta.images[imageKey]\n if (!image) return undefined\n return image.sizes[size] || image.sizes.full\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/components/StudioButton.tsx","../src/lib/meta.ts"],"sourcesContent":["/** @jsxImportSource @emotion/react */\n'use client'\n\nimport { useState, lazy, Suspense } from 'react'\nimport { css, keyframes } from '@emotion/react'\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: 48px;\n height: 48px;\n border-radius: 50%;\n background: linear-gradient(to bottom right, #a855f7, #ec4899);\n color: white;\n box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n cursor: pointer;\n transition: all 0.2s;\n \n &:hover {\n transform: scale(1.05);\n box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);\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 `,\n backdrop: css`\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n background-color: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n `,\n modal: css`\n position: absolute;\n top: 32px;\n right: 32px;\n bottom: 32px;\n left: 32px;\n background-color: white;\n border-radius: 16px;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25);\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 `,\n loadingContent: css`\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 16px;\n `,\n spinner: css`\n width: 32px;\n height: 32px;\n border-radius: 50%;\n border: 2px solid transparent;\n border-bottom-color: #9333ea;\n animation: ${spin} 1s linear infinite;\n `,\n loadingText: css`\n color: #6b7280;\n font-size: 14px;\n margin: 0;\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 [isOpen, setIsOpen] = useState(false)\n\n // Only render in development\n if (process.env.NODE_ENV !== 'development') {\n return null\n }\n\n return (\n <>\n {!isOpen && (\n <button\n css={styles.button}\n onClick={() => setIsOpen(true)}\n title=\"Open Studio\"\n aria-label=\"Open Studio media manager\"\n >\n <ImageIcon />\n </button>\n )}\n\n {isOpen && (\n <div css={styles.overlay}>\n <div css={styles.backdrop} onClick={() => setIsOpen(false)} />\n <div css={styles.modal}>\n <Suspense fallback={<LoadingState />}>\n <StudioUI onClose={() => setIsOpen(false)} />\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","import type { StudioMeta, ImageEntry, ImageSize, SizeEntry } from '../types'\n\n// Default empty meta - will be populated when reading from project\nlet _meta: StudioMeta = {\n $schema: 'https://gallop.software/schemas/studio-meta.json',\n version: 1,\n generatedAt: new Date().toISOString(),\n images: {},\n}\n\n/**\n * The meta object containing all image metadata.\n * This is read from _data/_meta.json in the consuming project.\n */\nexport const meta: StudioMeta = _meta\n\n/**\n * Initialize meta from a JSON object (called during build/runtime)\n */\nexport function initializeMeta(data: StudioMeta): void {\n _meta = data\n Object.assign(meta, data)\n}\n\n/**\n * Get the resolved URL for an image, handling CDN vs local paths\n */\nexport function getImageUrl(\n imageKey: string,\n size: ImageSize = 'medium'\n): string | undefined {\n const image = meta.images[imageKey]\n if (!image) return undefined\n\n const sizeData = image.sizes[size] || image.sizes.full\n if (!sizeData) return undefined\n\n // If synced to CDN, use CDN URL\n if (image.cdn?.synced && image.cdn.baseUrl) {\n return `${image.cdn.baseUrl}${sizeData.path}`\n }\n\n // Otherwise use local path\n return sizeData.path\n}\n\n/**\n * Get the full image entry for a key\n */\nexport function getStudioMeta(imageKey: string): ImageEntry | undefined {\n return meta.images[imageKey]\n}\n\n/**\n * Get size data for an image\n */\nexport function getImageSize(\n imageKey: string,\n size: ImageSize = 'medium'\n): SizeEntry | undefined {\n const image = meta.images[imageKey]\n if (!image) return undefined\n return image.sizes[size] || image.sizes.full\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gallop.software/studio",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Media manager for Gallop templates - upload, process, and sync images to CDN",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -52,6 +52,7 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "@aws-sdk/client-s3": "^3.705.0",
55
+ "@emotion/react": "^11.14.0",
55
56
  "blurhash": "^2.0.5",
56
57
  "clsx": "^2.1.1",
57
58
  "react-dropzone": "^14.3.5",