@spicemod/creator 0.0.26 → 0.0.28

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 (40) hide show
  1. package/dist/bin.mjs +188 -150
  2. package/dist/templates/custom-app/shared/spice.config.js +6 -0
  3. package/dist/templates/custom-app/shared/spice.config.ts +6 -0
  4. package/dist/templates/custom-app/shared/tsconfig.app.json +33 -0
  5. package/dist/templates/custom-app/shared/tsconfig.node.json +25 -0
  6. package/dist/templates/custom-app/ts/react/src/extension/index.tsx +1 -1
  7. package/dist/templates/extension/js/react/src/app.jsx +1 -1
  8. package/dist/templates/extension/shared/spice.config.js +6 -0
  9. package/dist/templates/extension/shared/spice.config.ts +6 -0
  10. package/dist/templates/extension/shared/tsconfig.app.json +33 -0
  11. package/dist/templates/extension/shared/tsconfig.node.json +25 -0
  12. package/dist/templates/extension/ts/react/src/app.tsx +1 -1
  13. package/dist/templates/hmrCustomApp.js +284 -0
  14. package/dist/templates/liveReload.js +298 -45
  15. package/dist/templates/theme/js/react/src/app.jsx +1 -1
  16. package/dist/templates/theme/shared/spice.config.js +6 -0
  17. package/dist/templates/theme/shared/spice.config.ts +6 -0
  18. package/dist/templates/theme/shared/tsconfig.app.json +33 -0
  19. package/dist/templates/theme/shared/tsconfig.node.json +25 -0
  20. package/dist/templates/theme/ts/react/src/app.tsx +1 -1
  21. package/package.json +1 -1
  22. package/templates/custom-app/shared/spice.config.js +6 -0
  23. package/templates/custom-app/shared/spice.config.ts +6 -0
  24. package/templates/custom-app/shared/tsconfig.app.json +33 -0
  25. package/templates/custom-app/shared/tsconfig.node.json +25 -0
  26. package/templates/custom-app/ts/react/src/extension/index.tsx +1 -1
  27. package/templates/extension/js/react/src/app.jsx +1 -1
  28. package/templates/extension/shared/spice.config.js +6 -0
  29. package/templates/extension/shared/spice.config.ts +6 -0
  30. package/templates/extension/shared/tsconfig.app.json +33 -0
  31. package/templates/extension/shared/tsconfig.node.json +25 -0
  32. package/templates/extension/ts/react/src/app.tsx +1 -1
  33. package/templates/hmrCustomApp.js +284 -0
  34. package/templates/liveReload.js +298 -45
  35. package/templates/theme/js/react/src/app.jsx +1 -1
  36. package/templates/theme/shared/spice.config.js +6 -0
  37. package/templates/theme/shared/spice.config.ts +6 -0
  38. package/templates/theme/shared/tsconfig.app.json +33 -0
  39. package/templates/theme/shared/tsconfig.node.json +25 -0
  40. package/templates/theme/ts/react/src/app.tsx +1 -1
@@ -0,0 +1,33 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "types": ["@spicetify/creator/client"],
8
+ "skipLibCheck": true,
9
+
10
+ /* Bundler mode */
11
+ "moduleResolution": "bundler",
12
+ "allowImportingTsExtensions": true,
13
+ "verbatimModuleSyntax": true,
14
+ "moduleDetection": "force",
15
+ "noEmit": true,
16
+ "jsx": "react-jsx",
17
+
18
+ /* Linting */
19
+ "strict": true,
20
+ "noUnusedLocals": true,
21
+ "noUnusedParameters": true,
22
+ "erasableSyntaxOnly": true,
23
+ "noFallthroughCasesInSwitch": true,
24
+ "noUncheckedSideEffectImports": true,
25
+
26
+ // Paths
27
+ "baseUrl": ".",
28
+ "paths": {
29
+ "@/*": ["./src/*"]
30
+ }
31
+ },
32
+ "include": ["src"]
33
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2023",
4
+ "lib": ["ES2023"],
5
+ "module": "ESNext",
6
+ "types": ["node"],
7
+ "skipLibCheck": true,
8
+
9
+ /* Bundler mode */
10
+ "moduleResolution": "bundler",
11
+ "allowImportingTsExtensions": true,
12
+ "verbatimModuleSyntax": true,
13
+ "moduleDetection": "force",
14
+ "noEmit": true,
15
+
16
+ /* Linting */
17
+ "strict": true,
18
+ "noUnusedLocals": true,
19
+ "noUnusedParameters": true,
20
+ "erasableSyntaxOnly": true,
21
+ "noFallthroughCasesInSwitch": true,
22
+ "noUncheckedSideEffectImports": true
23
+ },
24
+ "include": ["spice.config.ts"]
25
+ }
@@ -1,6 +1,6 @@
1
1
  import "@/app.css";
2
2
  import { createRoot } from "react-dom/client";
3
- // you can use aliases too ! (just add them to tsconfig.json)
3
+ // you can use aliases too ! (checkout tsconfig.app.json)
4
4
  import Onboarding from "@/components/Onboarding";
5
5
 
6
6
  const config = {
@@ -0,0 +1,284 @@
1
+ const React = Spicetify.React;
2
+ const rc = React.createElement;
3
+
4
+ const waitForImport = async (retryCount = 0) => {
5
+ try {
6
+ const mod = await import(_IMPORT_LINK);
7
+ return mod.default || mod.render;
8
+ } catch (err) {
9
+ console.error("Failed to import app:", err);
10
+ if (retryCount < 3) {
11
+ await new Promise((resolve) => setTimeout(resolve, 1000));
12
+ return waitForImport(retryCount + 1);
13
+ }
14
+ return null;
15
+ }
16
+ };
17
+
18
+ const styles = {
19
+ container: {
20
+ display: "flex",
21
+ flexDirection: "column",
22
+ alignItems: "center",
23
+ justifyContent: "center",
24
+ height: "100%",
25
+ width: "100%",
26
+ background:
27
+ "radial-gradient(circle at top left, rgba(29, 185, 84, 0.15), transparent 50%), var(--spice-main, #121212)",
28
+ color: "var(--spice-text, #ffffff)",
29
+ fontFamily: "var(--font-family, Circular, Helvetica, Arial, sans-serif)",
30
+ },
31
+ loadingText: {
32
+ marginTop: "24px",
33
+ fontSize: "14px",
34
+ fontWeight: "600",
35
+ color: "rgba(255, 255, 255, 0.8)",
36
+ letterSpacing: "0.05em",
37
+ textTransform: "uppercase",
38
+ },
39
+ errorContainer: {
40
+ display: "flex",
41
+ flexDirection: "column",
42
+ alignItems: "center",
43
+ padding: "48px",
44
+ backgroundColor: "rgba(255, 255, 255, 0.03)",
45
+ backdropFilter: "blur(16px)",
46
+ border: "1px solid rgba(255, 255, 255, 0.08)",
47
+ borderRadius: "24px",
48
+ boxShadow: "0 16px 40px rgba(0, 0, 0, 0.4)",
49
+ maxWidth: "800px",
50
+ width: "90%",
51
+ textAlign: "center",
52
+ },
53
+ iconContainer: {
54
+ display: "flex",
55
+ alignItems: "center",
56
+ justifyContent: "center",
57
+ width: "64px",
58
+ height: "64px",
59
+ borderRadius: "50%",
60
+ backgroundColor: "rgba(241, 94, 108, 0.1)",
61
+ border: "1px solid rgba(241, 94, 108, 0.2)",
62
+ marginBottom: "24px",
63
+ boxShadow: "0 0 20px rgba(241, 94, 108, 0.15)",
64
+ },
65
+ errorTitle: {
66
+ fontSize: "28px",
67
+ fontWeight: "800",
68
+ marginBottom: "16px",
69
+ color: "#ffffff",
70
+ letterSpacing: "-0.04em",
71
+ },
72
+ errorTraceBox: {
73
+ width: "100%",
74
+ backgroundColor: "rgba(0, 0, 0, 0.25)",
75
+ border: "1px solid rgba(255, 255, 255, 0.05)",
76
+ boxShadow: "inset 0 4px 10px rgba(0,0,0,0.3)",
77
+ borderRadius: "12px",
78
+ padding: "16px",
79
+ marginBottom: "32px",
80
+ textAlign: "left",
81
+ overflowX: "auto",
82
+ },
83
+ errorMessage: {
84
+ fontSize: "13px",
85
+ lineHeight: "1.6",
86
+ fontFamily: "monospace",
87
+ color: "rgba(255, 255, 255, 0.7)",
88
+ wordBreak: "break-word",
89
+ margin: 0,
90
+ },
91
+ buttonBase: {
92
+ padding: "14px 36px",
93
+ fontSize: "14px",
94
+ fontWeight: "600",
95
+ color: "#ffffff",
96
+ backgroundColor: "rgba(255, 255, 255, 0.05)",
97
+ backdropFilter: "blur(8px)",
98
+ WebkitBackdropFilter: "blur(8px)",
99
+ border: "1px solid rgba(255, 255, 255, 0.1)",
100
+ borderRadius: "500px",
101
+ cursor: "pointer",
102
+ textTransform: "uppercase",
103
+ letterSpacing: "0.1em",
104
+ transition: "all 0.2s ease",
105
+ },
106
+ };
107
+
108
+ const AlertIcon = () =>
109
+ rc(
110
+ "svg",
111
+ {
112
+ width: "32",
113
+ height: "32",
114
+ viewBox: "0 0 24 24",
115
+ fill: "none",
116
+ stroke: "rgba(241, 94, 108, 0.9)",
117
+ strokeWidth: "2",
118
+ strokeLinecap: "round",
119
+ strokeLinejoin: "round",
120
+ },
121
+ rc("path", {
122
+ d: "M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z",
123
+ }),
124
+ rc("line", { x1: "12", y1: "9", x2: "12", y2: "13" }),
125
+ rc("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" }),
126
+ );
127
+
128
+ const Spinner = () =>
129
+ rc(
130
+ "svg",
131
+ { width: "48", height: "48", viewBox: "0 0 50 50" },
132
+ rc("circle", {
133
+ cx: "25",
134
+ cy: "25",
135
+ r: "20",
136
+ fill: "none",
137
+ stroke: "rgba(255,255,255,0.1)",
138
+ strokeWidth: "4",
139
+ }),
140
+ rc(
141
+ "circle",
142
+ {
143
+ cx: "25",
144
+ cy: "25",
145
+ r: "20",
146
+ fill: "none",
147
+ stroke: "rgba(255,255,255,0.8)",
148
+ strokeWidth: "4",
149
+ strokeDasharray: "30 100",
150
+ strokeLinecap: "round",
151
+ },
152
+ rc("animateTransform", {
153
+ attributeName: "transform",
154
+ type: "rotate",
155
+ from: "0 25 25",
156
+ to: "360 25 25",
157
+ dur: "1s",
158
+ repeatCount: "indefinite",
159
+ }),
160
+ ),
161
+ );
162
+
163
+ const RetryButton = ({ onClick }) => {
164
+ const [isHovered, setIsHovered] = React.useState(false);
165
+ const [isActive, setIsActive] = React.useState(false);
166
+
167
+ const dynamicStyle = {
168
+ ...styles.buttonBase,
169
+ backgroundColor: isActive
170
+ ? "rgba(255, 255, 255, 0.15)"
171
+ : isHovered
172
+ ? "rgba(255, 255, 255, 0.1)"
173
+ : "rgba(255, 255, 255, 0.05)",
174
+ borderColor: isHovered ? "rgba(255, 255, 255, 0.3)" : "rgba(255, 255, 255, 0.1)",
175
+ transform: isActive ? "scale(0.96)" : isHovered ? "scale(1.02)" : "scale(1)",
176
+ boxShadow: isHovered ? "0 4px 12px rgba(0,0,0,0.2)" : "none",
177
+ };
178
+
179
+ return rc(
180
+ "button",
181
+ {
182
+ style: dynamicStyle,
183
+ onClick,
184
+ onMouseEnter: () => setIsHovered(true),
185
+ onMouseLeave: () => {
186
+ setIsHovered(false);
187
+ setIsActive(false);
188
+ },
189
+ onMouseDown: () => setIsActive(true),
190
+ onMouseUp: () => setIsActive(false),
191
+ },
192
+ "Try Again",
193
+ );
194
+ };
195
+
196
+ const Loading = () =>
197
+ rc(
198
+ "div",
199
+ { style: styles.container },
200
+ rc(Spinner),
201
+ rc("div", { style: styles.loadingText }, "Loading app..."),
202
+ );
203
+
204
+ const ErrorDisplay = ({ message, onRetry }) =>
205
+ rc(
206
+ "div",
207
+ { style: styles.container },
208
+ rc(
209
+ "div",
210
+ { style: styles.errorContainer },
211
+ rc("div", { style: styles.iconContainer }, rc(AlertIcon)),
212
+ rc("div", { style: styles.errorTitle }, "Failed to load app"),
213
+ rc("div", { style: styles.errorTraceBox }, rc("p", { style: styles.errorMessage }, message)),
214
+ rc(RetryButton, { onClick: onRetry }),
215
+ ),
216
+ );
217
+
218
+ class ErrorBoundary extends React.Component {
219
+ constructor(props) {
220
+ super(props);
221
+ this.state = { hasError: false, errorMessage: null };
222
+ }
223
+
224
+ static getDerivedStateFromError(error) {
225
+ return {
226
+ hasError: true,
227
+ errorMessage:
228
+ error.stack || error.message || "A runtime error occurred in the app component.",
229
+ };
230
+ }
231
+
232
+ componentDidCatch(error, errorInfo) {
233
+ console.error("Caught inside ErrorBoundary:", error, errorInfo);
234
+ }
235
+
236
+ render() {
237
+ if (this.state.hasError) {
238
+ return rc(ErrorDisplay, {
239
+ message: this.state.errorMessage,
240
+ onRetry: () => {
241
+ this.setState({ hasError: false, errorMessage: null });
242
+ if (this.props.onRetry) this.props.onRetry();
243
+ },
244
+ });
245
+ }
246
+ return this.props.children;
247
+ }
248
+ }
249
+
250
+ const AppWrapper = () => {
251
+ const [App, setApp] = React.useState(null);
252
+ const [error, setError] = React.useState(null);
253
+ const [isLoading, setIsLoading] = React.useState(true);
254
+
255
+ const loadApp = React.useCallback(() => {
256
+ setIsLoading(true);
257
+ setError(null);
258
+ waitForImport()
259
+ .then((app) => {
260
+ if (app) {
261
+ setApp(() => app);
262
+ } else {
263
+ setError("Unable to load the app. Please check if the development server is running.");
264
+ }
265
+ })
266
+ .catch((err) => {
267
+ setError(err.stack || err.message || "An unexpected error occurred during import.");
268
+ })
269
+ .finally(() => {
270
+ setIsLoading(false);
271
+ });
272
+ }, []);
273
+
274
+ React.useEffect(() => {
275
+ loadApp();
276
+ }, [loadApp]);
277
+
278
+ if (isLoading) return rc(Loading);
279
+ if (error) return rc(ErrorDisplay, { message: error, onRetry: loadApp });
280
+ if (!App) return rc(ErrorDisplay, { message: "App component not found.", onRetry: loadApp });
281
+
282
+ return rc(ErrorBoundary, { onRetry: loadApp }, rc(App));
283
+ };
284
+ var render = () => rc(AppWrapper);