@spicemod/creator 0.0.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.
Files changed (101) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +35 -0
  3. package/dist/bin.d.mts +1 -0
  4. package/dist/bin.mjs +1739 -0
  5. package/dist/client/index.d.mts +2175 -0
  6. package/dist/client/index.mjs +7 -0
  7. package/dist/templates/extension/js/react/eslint.config.js +29 -0
  8. package/dist/templates/extension/js/react/src/app.jsx +27 -0
  9. package/dist/templates/extension/js/react/src/components/Onboarding.jsx +72 -0
  10. package/dist/templates/extension/js/vanilla/components/Onboarding.js +71 -0
  11. package/dist/templates/extension/js/vanilla/eslint.config.js +16 -0
  12. package/dist/templates/extension/js/vanilla/src/app.js +12 -0
  13. package/dist/templates/extension/meta.json +4 -0
  14. package/dist/templates/extension/shared/.oxlintrc.json +36 -0
  15. package/dist/templates/extension/shared/README.template.md +53 -0
  16. package/dist/templates/extension/shared/app.css +163 -0
  17. package/dist/templates/extension/shared/biome.json +36 -0
  18. package/dist/templates/extension/shared/css.d.ts +44 -0
  19. package/dist/templates/extension/shared/jsconfig.json +32 -0
  20. package/dist/templates/extension/shared/spice.config.js +9 -0
  21. package/dist/templates/extension/shared/spice.config.ts +9 -0
  22. package/dist/templates/extension/shared/tsconfig.json +32 -0
  23. package/dist/templates/extension/ts/react/eslint.config.ts +29 -0
  24. package/dist/templates/extension/ts/react/src/app.tsx +27 -0
  25. package/dist/templates/extension/ts/react/src/components/Onboarding.tsx +83 -0
  26. package/dist/templates/extension/ts/vanilla/biome.json +36 -0
  27. package/dist/templates/extension/ts/vanilla/eslint.config.ts +16 -0
  28. package/dist/templates/extension/ts/vanilla/src/app.ts +12 -0
  29. package/dist/templates/extension/ts/vanilla/src/components/Onboarding.ts +79 -0
  30. package/dist/templates/liveReload.js +70 -0
  31. package/dist/templates/theme/js/react/eslint.config.js +29 -0
  32. package/dist/templates/theme/js/react/src/app.jsx +25 -0
  33. package/dist/templates/theme/js/react/src/components/Onboarding.jsx +72 -0
  34. package/dist/templates/theme/js/vanilla/eslint.config.js +16 -0
  35. package/dist/templates/theme/js/vanilla/src/app.js +11 -0
  36. package/dist/templates/theme/js/vanilla/src/components/Onboarding.js +71 -0
  37. package/dist/templates/theme/meta.json +4 -0
  38. package/dist/templates/theme/shared/.oxlintrc.json +36 -0
  39. package/dist/templates/theme/shared/README.template.md +53 -0
  40. package/dist/templates/theme/shared/app.css +163 -0
  41. package/dist/templates/theme/shared/biome.json +36 -0
  42. package/dist/templates/theme/shared/css.d.ts +44 -0
  43. package/dist/templates/theme/shared/jsconfig.json +31 -0
  44. package/dist/templates/theme/shared/spice.config.js +9 -0
  45. package/dist/templates/theme/shared/spice.config.ts +9 -0
  46. package/dist/templates/theme/shared/tsconfig.json +32 -0
  47. package/dist/templates/theme/ts/react/eslint.config.ts +29 -0
  48. package/dist/templates/theme/ts/react/src/app.tsx +26 -0
  49. package/dist/templates/theme/ts/react/src/components/Onboarding.tsx +83 -0
  50. package/dist/templates/theme/ts/vanilla/eslint.config.ts +16 -0
  51. package/dist/templates/theme/ts/vanilla/src/app.ts +11 -0
  52. package/dist/templates/theme/ts/vanilla/src/components/Onboarding.ts +79 -0
  53. package/dist/templates/wrapper.js +48 -0
  54. package/package.json +80 -0
  55. package/templates/extension/js/react/eslint.config.js +29 -0
  56. package/templates/extension/js/react/src/app.jsx +27 -0
  57. package/templates/extension/js/react/src/components/Onboarding.jsx +72 -0
  58. package/templates/extension/js/vanilla/components/Onboarding.js +71 -0
  59. package/templates/extension/js/vanilla/eslint.config.js +16 -0
  60. package/templates/extension/js/vanilla/src/app.js +12 -0
  61. package/templates/extension/meta.json +4 -0
  62. package/templates/extension/shared/.oxlintrc.json +36 -0
  63. package/templates/extension/shared/README.template.md +53 -0
  64. package/templates/extension/shared/app.css +163 -0
  65. package/templates/extension/shared/biome.json +36 -0
  66. package/templates/extension/shared/css.d.ts +44 -0
  67. package/templates/extension/shared/jsconfig.json +32 -0
  68. package/templates/extension/shared/spice.config.js +9 -0
  69. package/templates/extension/shared/spice.config.ts +9 -0
  70. package/templates/extension/shared/tsconfig.json +32 -0
  71. package/templates/extension/ts/react/eslint.config.ts +29 -0
  72. package/templates/extension/ts/react/src/app.tsx +27 -0
  73. package/templates/extension/ts/react/src/components/Onboarding.tsx +83 -0
  74. package/templates/extension/ts/vanilla/biome.json +36 -0
  75. package/templates/extension/ts/vanilla/eslint.config.ts +16 -0
  76. package/templates/extension/ts/vanilla/src/app.ts +12 -0
  77. package/templates/extension/ts/vanilla/src/components/Onboarding.ts +79 -0
  78. package/templates/liveReload.js +70 -0
  79. package/templates/theme/js/react/eslint.config.js +29 -0
  80. package/templates/theme/js/react/src/app.jsx +25 -0
  81. package/templates/theme/js/react/src/components/Onboarding.jsx +72 -0
  82. package/templates/theme/js/vanilla/eslint.config.js +16 -0
  83. package/templates/theme/js/vanilla/src/app.js +11 -0
  84. package/templates/theme/js/vanilla/src/components/Onboarding.js +71 -0
  85. package/templates/theme/meta.json +4 -0
  86. package/templates/theme/shared/.oxlintrc.json +36 -0
  87. package/templates/theme/shared/README.template.md +53 -0
  88. package/templates/theme/shared/app.css +163 -0
  89. package/templates/theme/shared/biome.json +36 -0
  90. package/templates/theme/shared/css.d.ts +44 -0
  91. package/templates/theme/shared/jsconfig.json +31 -0
  92. package/templates/theme/shared/spice.config.js +9 -0
  93. package/templates/theme/shared/spice.config.ts +9 -0
  94. package/templates/theme/shared/tsconfig.json +32 -0
  95. package/templates/theme/ts/react/eslint.config.ts +29 -0
  96. package/templates/theme/ts/react/src/app.tsx +26 -0
  97. package/templates/theme/ts/react/src/components/Onboarding.tsx +83 -0
  98. package/templates/theme/ts/vanilla/eslint.config.ts +16 -0
  99. package/templates/theme/ts/vanilla/src/app.ts +11 -0
  100. package/templates/theme/ts/vanilla/src/components/Onboarding.ts +79 -0
  101. package/templates/wrapper.js +48 -0
@@ -0,0 +1,29 @@
1
+ import js from "@eslint/js";
2
+ import globals from "globals";
3
+ import tseslint from "typescript-eslint";
4
+ import react from "eslint-plugin-react";
5
+ import css from "@eslint/css";
6
+ import { defineConfig } from "eslint/config";
7
+
8
+ export default defineConfig([
9
+ {
10
+ files: ["**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
11
+ plugins: { js, react },
12
+ extends: ["js/recommended"],
13
+ languageOptions: {
14
+ parserOptions: {
15
+ ecmaFeatures: {
16
+ jsx: true,
17
+ },
18
+ },
19
+ globals: globals.browser,
20
+ },
21
+ },
22
+ tseslint.configs.recommended,
23
+ {
24
+ files: ["**/*.css"],
25
+ plugins: { css },
26
+ language: "css/css",
27
+ extends: ["css/recommended"],
28
+ },
29
+ ]);
@@ -0,0 +1,27 @@
1
+ import "@/app.css";
2
+ import { createRoot } from "react-dom/client";
3
+ // you can use aliases too ! (just add them to tsconfig.json)
4
+ import Onboarding from "@/components/Onboarding";
5
+
6
+ const config = {
7
+ projectName: "{{project-name}}",
8
+ framework: "{{framework}}",
9
+ language: "{{language}}",
10
+ packageManager: "{{package-manager}}",
11
+ linter: "{{linter}}",
12
+ };
13
+
14
+ function main() {
15
+ // Create a container for the React application
16
+ // We append this to document.body to ensure it sits above the Spotify UI
17
+ const container = document.createElement("div");
18
+ container.id = "spicetify-onboarding-root";
19
+ document.body.appendChild(container);
20
+
21
+ const root = createRoot(container);
22
+
23
+ // Render the Onboarding UI
24
+ root.render(<Onboarding config={config} />);
25
+ }
26
+
27
+ main();
@@ -0,0 +1,83 @@
1
+ import { useState } from "react";
2
+
3
+ interface OnboardingProps {
4
+ config: {
5
+ projectName: string;
6
+ framework: string;
7
+ language: string;
8
+ packageManager: string;
9
+ linter: string;
10
+ };
11
+ }
12
+
13
+ const Onboarding: React.FC<OnboardingProps> = ({ config }) => {
14
+ const [isVisible, setIsVisible] = useState(true);
15
+ const [isFading, setIsFading] = useState(false);
16
+
17
+ const handleDismiss = () => {
18
+ setIsFading(true);
19
+ setTimeout(() => setIsVisible(false), 250);
20
+ };
21
+
22
+ if (!isVisible) return null;
23
+
24
+ return (
25
+ <div className={`onboarding-overlay ${isFading ? "fade-out" : ""}`} onClick={handleDismiss}>
26
+ <div className="onboarding-card" onClick={(e) => e.stopPropagation()}>
27
+ <button className="close-icon-btn" onClick={handleDismiss} aria-label="Close">
28
+ <svg width="12" height="12" viewBox="0 0 12 12" fill="none">
29
+ <path
30
+ d="M1 1L11 11M1 11L11 1"
31
+ stroke="currentColor"
32
+ strokeWidth="2"
33
+ strokeLinecap="round"
34
+ />
35
+ </svg>
36
+ </button>
37
+
38
+ <div className="status-badge">🚀 {config.projectName} Ready</div>
39
+
40
+ <div className="config-grid">
41
+ <span className="label">Framework</span>
42
+ <span className="value">{config.framework}</span>
43
+
44
+ <span className="label">Language</span>
45
+ <span className="value">{config.language}</span>
46
+
47
+ <span className="label">Manager</span>
48
+ <span className="value">{config.packageManager}</span>
49
+
50
+ <span className="label">Linter</span>
51
+ <span className="value">{config.linter}</span>
52
+ </div>
53
+
54
+ <div className="footer-tip">
55
+ Next Step: Edit <code>src/app.tsx</code>
56
+ </div>
57
+
58
+ <div className="onboarding-actions">
59
+ <button className="get-started-btn" onClick={() => openLink("{{get-started-link}}")}>
60
+ Get Started
61
+ </button>
62
+ <button className="discord-btn" onClick={() => openLink("{{discord-link}}")}>
63
+ Join Discord
64
+ </button>
65
+ <button className="dismiss-btn" onClick={handleDismiss}>
66
+ Dismiss
67
+ </button>
68
+ </div>
69
+ </div>
70
+ </div>
71
+ );
72
+ };
73
+
74
+ export default Onboarding;
75
+
76
+ function openLink(url: string, newTab = true) {
77
+ if (!url) return;
78
+ if (newTab) {
79
+ window.open(url, "_blank", "noopener,noreferrer");
80
+ } else {
81
+ window.location.href = url;
82
+ }
83
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "$schema": "https://biomejs.dev/schemas/2.3.13/schema.json",
3
+ "vcs": {
4
+ "enabled": true,
5
+ "clientKind": "git",
6
+ "useIgnoreFile": true
7
+ },
8
+ "files": {
9
+ "ignoreUnknown": true,
10
+ "includes": ["**", "!node_modules", "!dist"]
11
+ },
12
+ "formatter": {
13
+ "enabled": true,
14
+ "indentStyle": "space",
15
+ "indentWidth": 2
16
+ },
17
+ "linter": {
18
+ "enabled": true,
19
+ "rules": {
20
+ "recommended": true,
21
+ "suspicious": {
22
+ "noUnknownAtRules": "off"
23
+ }
24
+ },
25
+ "domains": {
26
+ "project": "recommended"
27
+ }
28
+ },
29
+ "assist": {
30
+ "actions": {
31
+ "source": {
32
+ "organizeImports": "on"
33
+ }
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,16 @@
1
+ import js from "@eslint/js";
2
+ import globals from "globals";
3
+ import tseslint from "typescript-eslint";
4
+ import css from "@eslint/css";
5
+ import { defineConfig } from "eslint/config";
6
+
7
+ export default defineConfig([
8
+ {
9
+ files: ["**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
10
+ plugins: { js },
11
+ extends: ["js/recommended"],
12
+ languageOptions: { globals: globals.browser },
13
+ },
14
+ tseslint.configs.recommended,
15
+ { files: ["**/*.css"], plugins: { css }, language: "css/css", extends: ["css/recommended"] },
16
+ ]);
@@ -0,0 +1,12 @@
1
+ import "@/app.css";
2
+ import { initOnboarding } from "@/components/Onboarding";
3
+
4
+ const config = {
5
+ projectName: "{{project-name}}",
6
+ framework: "{{framework}}",
7
+ language: "{{language}}",
8
+ packageManager: "{{package-manager}}",
9
+ linter: "{{linter}}",
10
+ };
11
+
12
+ initOnboarding(config);
@@ -0,0 +1,79 @@
1
+ interface OnboardingConfig {
2
+ projectName: string;
3
+ framework: string;
4
+ language: string;
5
+ packageManager: string;
6
+ linter: string;
7
+ }
8
+
9
+ export function initOnboarding(config: OnboardingConfig): void {
10
+ const overlay = document.createElement("div");
11
+ overlay.className = "onboarding-overlay";
12
+
13
+ overlay.innerHTML = `
14
+ <div class="onboarding-card">
15
+ <button class="close-icon-btn" aria-label="Close" id="close-x">
16
+ <svg width="12" height="12" viewBox="0 0 12 12" fill="none">
17
+ <path d="M1 1L11 11M1 11L11 1" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
18
+ </svg>
19
+ </button>
20
+
21
+ <div>
22
+ <span class="status-dot"></span>
23
+ 🚀 ${config.projectName} Ready
24
+ </div>
25
+
26
+ <div class="config-grid">
27
+ <span class="label">Framework</span>
28
+ <span class="value">${config.framework}</span>
29
+
30
+ <span class="label">Language</span>
31
+ <span class="value">${config.language}</span>
32
+
33
+ <span class="label">Manager</span>
34
+ <span class="value">${config.packageManager}</span>
35
+
36
+ <span class="label">Linter</span>
37
+ <span class="value">${config.linter}</span>
38
+ </div>
39
+
40
+ <div class="footer-tip">
41
+ Next Step: Edit <code>src/app.tsx</code>
42
+ </div>
43
+ <div class="onboarding-actions">
44
+ <button class="dismiss-btn" id="dismiss-btn">Dismiss</button>
45
+ <button class="discord-btn" id="discord-btn">Join Discord</button>
46
+ <button class="get-started-btn" id="get-started-btn">Get Started</button>
47
+ </div>
48
+ </div>
49
+ `;
50
+
51
+ const dismiss = (): void => {
52
+ overlay.classList.add("fade-out");
53
+ overlay.addEventListener("transitionend", () => overlay.remove(), {
54
+ once: true,
55
+ });
56
+ };
57
+
58
+ overlay.addEventListener("click", (e: MouseEvent) => {
59
+ if (e.target === overlay) dismiss();
60
+ });
61
+
62
+ function openLink(url: string, newTab = true) {
63
+ if (!url) return;
64
+ if (newTab) {
65
+ window.open(url, "_blank", "noopener,noreferrer");
66
+ } else {
67
+ window.location.href = url;
68
+ }
69
+ }
70
+
71
+ const getStarted = () => openLink("{{get-started-link}}");
72
+ const openDiscord = () => openLink("{{discord-link}}");
73
+ overlay.querySelector("#close-x")?.addEventListener("click", dismiss);
74
+ overlay.querySelector("#dismiss-btn")?.addEventListener("click", dismiss);
75
+ overlay.querySelector("#discord-btn")?.addEventListener("click", openDiscord);
76
+ overlay.querySelector("#get-started-btn")?.addEventListener("click", getStarted);
77
+
78
+ document.body.prepend(overlay);
79
+ }
@@ -0,0 +1,70 @@
1
+ (() => {
2
+ const WS_URL = _HOT_RELOAD_LINK;
3
+ const SERVER = _SERVER_URL;
4
+ const CSS_PATH = _CSS_PATH;
5
+ const JS_PATH = _JS_PATH;
6
+
7
+ const CSS_ID = "sc-css-injected";
8
+ const JS_ID = "sc-js-injected";
9
+
10
+ let socket;
11
+
12
+ const connect = () => {
13
+ socket = new WebSocket(WS_URL);
14
+
15
+ socket.addEventListener("open", () => {
16
+ console.log("[SC] Live reload connected");
17
+ });
18
+
19
+ socket.addEventListener("message", (event) => {
20
+ let updated;
21
+
22
+ try {
23
+ updated = JSON.parse(event.data);
24
+ } catch {
25
+ return;
26
+ }
27
+
28
+ if (!Array.isArray(updated) || updated.length === 0) return;
29
+
30
+ const isOnlyCSS = updated.every((file) => file.endsWith(".css"));
31
+
32
+ if (isOnlyCSS && CSS_PATH) {
33
+ const link = document.getElementById(CSS_ID);
34
+ if (!link || !link.parentNode) return;
35
+
36
+ const next = link.cloneNode(false);
37
+ next.href = `${SERVER}${CSS_PATH}?t=${Date.now()}`;
38
+ next.onload = () => link.remove();
39
+
40
+ link.parentNode.insertBefore(next, link.nextSibling);
41
+ } else {
42
+ window.location.reload();
43
+ }
44
+ });
45
+
46
+ socket.addEventListener("close", () => {
47
+ setTimeout(connect, 1000);
48
+ });
49
+
50
+ socket.addEventListener("error", () => {
51
+ socket.close();
52
+ });
53
+ };
54
+
55
+ if (CSS_PATH) {
56
+ const link = document.createElement("link");
57
+ link.id = CSS_ID;
58
+ link.rel = "stylesheet";
59
+ link.href = SERVER + CSS_PATH;
60
+ document.head.appendChild(link);
61
+ }
62
+
63
+ const script = document.createElement("script");
64
+ script.id = JS_ID;
65
+ script.src = SERVER + JS_PATH;
66
+ script.type = "module";
67
+ document.body.appendChild(script);
68
+
69
+ connect();
70
+ })();
@@ -0,0 +1,29 @@
1
+ import js from "@eslint/js";
2
+ import globals from "globals";
3
+ import tseslint from "typescript-eslint";
4
+ import react from "eslint-plugin-react";
5
+ import css from "@eslint/css";
6
+ import { defineConfig } from "eslint/config";
7
+
8
+ export default defineConfig([
9
+ {
10
+ files: ["**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
11
+ plugins: { js, react },
12
+ extends: ["js/recommended"],
13
+ languageOptions: {
14
+ parserOptions: {
15
+ ecmaFeatures: {
16
+ jsx: true,
17
+ },
18
+ },
19
+ globals: globals.browser,
20
+ },
21
+ },
22
+ tseslint.configs.recommended,
23
+ {
24
+ files: ["**/*.css"],
25
+ plugins: { css },
26
+ language: "css/css",
27
+ extends: ["css/recommended"],
28
+ },
29
+ ]);
@@ -0,0 +1,25 @@
1
+ import { createRoot } from "react-dom/client";
2
+ // you can use aliases too ! (just add them to tsconfig.json)
3
+ import OnboardingCard from "@/components/Onboarding";
4
+
5
+ const config = {
6
+ projectName: "{{project-name}}",
7
+ framework: "{{framework}}",
8
+ language: "{{language}}",
9
+ packageManager: "{{package-manager}}",
10
+ linter: "{{linter}}",
11
+ };
12
+
13
+ function main() {
14
+ // Create a container for the React application
15
+ // We append this to document.body to ensure it sits above the Spotify UI
16
+ const container = document.createElement("div");
17
+ container.id = "spicetify-onboarding-root";
18
+ document.body.appendChild(container);
19
+
20
+ const root = createRoot(container);
21
+
22
+ // Render the Onboarding UI
23
+ root.render(<OnboardingCard config={config} />);
24
+ }
25
+ main();
@@ -0,0 +1,72 @@
1
+ import { useState } from "react";
2
+ const Onboarding = ({ config }) => {
3
+ const [isVisible, setIsVisible] = useState(true);
4
+ const [isFading, setIsFading] = useState(false);
5
+
6
+ const handleDismiss = () => {
7
+ setIsFading(true);
8
+ setTimeout(() => setIsVisible(false), 250);
9
+ };
10
+
11
+ if (!isVisible) return null;
12
+
13
+ return (
14
+ <div className={`onboarding-overlay ${isFading ? "fade-out" : ""}`} onClick={handleDismiss}>
15
+ <div className="onboarding-card" onClick={(e) => e.stopPropagation()}>
16
+ <button className="close-icon-btn" onClick={handleDismiss} aria-label="Close">
17
+ <svg width="12" height="12" viewBox="0 0 12 12" fill="none">
18
+ <path
19
+ d="M1 1L11 11M1 11L11 1"
20
+ stroke="currentColor"
21
+ strokeWidth="2"
22
+ strokeLinecap="round"
23
+ />
24
+ </svg>
25
+ </button>
26
+
27
+ <div className="status-badge">🚀 {config.projectName} Ready</div>
28
+
29
+ <div className="config-grid">
30
+ <span className="label">Framework</span>
31
+ <span className="value">{config.framework}</span>
32
+
33
+ <span className="label">Language</span>
34
+ <span className="value">{config.language}</span>
35
+
36
+ <span className="label">Manager</span>
37
+ <span className="value">{config.packageManager}</span>
38
+
39
+ <span className="label">Linter</span>
40
+ <span className="value">{config.linter}</span>
41
+ </div>
42
+
43
+ <div className="footer-tip">
44
+ Next Step: Edit <code>src/app.tsx</code>
45
+ </div>
46
+
47
+ <div className="onboarding-actions">
48
+ <button className="get-started-btn" onClick={() => openLink("{{get-started-link}}")}>
49
+ Get Started
50
+ </button>
51
+ <button className="discord-btn" onClick={() => openLink("{{discord-link}}")}>
52
+ Join Discord
53
+ </button>
54
+ <button className="dismiss-btn" onClick={handleDismiss}>
55
+ Dismiss
56
+ </button>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ );
61
+ };
62
+
63
+ export default Onboarding;
64
+
65
+ function openLink(url, newTab = true) {
66
+ if (!url) return;
67
+ if (newTab) {
68
+ window.open(url, "_blank", "noopener,noreferrer");
69
+ } else {
70
+ window.location.href = url;
71
+ }
72
+ }
@@ -0,0 +1,16 @@
1
+ import js from "@eslint/js";
2
+ import globals from "globals";
3
+ import tseslint from "typescript-eslint";
4
+ import css from "@eslint/css";
5
+ import { defineConfig } from "eslint/config";
6
+
7
+ export default defineConfig([
8
+ {
9
+ files: ["**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"],
10
+ plugins: { js },
11
+ extends: ["js/recommended"],
12
+ languageOptions: { globals: globals.browser },
13
+ },
14
+ tseslint.configs.recommended,
15
+ { files: ["**/*.css"], plugins: { css }, language: "css/css", extends: ["css/recommended"] },
16
+ ]);
@@ -0,0 +1,11 @@
1
+ import { initOnboarding } from "@/components/Onboarding";
2
+
3
+ const config = {
4
+ projectName: "{{project-name}}",
5
+ framework: "{{framework}}",
6
+ language: "{{language}}",
7
+ packageManager: "{{package-manager}}",
8
+ linter: "{{linter}}",
9
+ };
10
+
11
+ initOnboarding(config);
@@ -0,0 +1,71 @@
1
+ export function initOnboarding(config) {
2
+ const overlay = document.createElement("div");
3
+ overlay.className = "onboarding-overlay";
4
+
5
+ overlay.innerHTML = `
6
+ <div class="onboarding-card">
7
+ <button class="close-icon-btn" aria-label="Close" id="close-x">
8
+ <svg width="12" height="12" viewBox="0 0 12 12" fill="none">
9
+ <path d="M1 1L11 11M1 11L11 1" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
10
+ </svg>
11
+ </button>
12
+
13
+ <div>
14
+ <span class="status-dot"></span>
15
+ 🚀 ${config.projectName} Ready
16
+ </div>
17
+
18
+ <div class="config-grid">
19
+ <span class="label">Framework</span>
20
+ <span class="value">${config.framework}</span>
21
+
22
+ <span class="label">Language</span>
23
+ <span class="value">${config.language}</span>
24
+
25
+ <span class="label">Manager</span>
26
+ <span class="value">${config.packageManager}</span>
27
+
28
+ <span class="label">Linter</span>
29
+ <span class="value">${config.linter}</span>
30
+ </div>
31
+
32
+ <div class="footer-tip">
33
+ Next Step: Edit <code>src/app.tsx</code>
34
+ </div>
35
+ <div class="onboarding-actions">
36
+ <button class="dismiss-btn" id="dismiss-btn">Dismiss</button>
37
+ <button class="discord-btn" id="discord-btn">Join Discord</button>
38
+ <button class="get-started-btn" id="get-started-btn">Get Started</button>
39
+ </div>
40
+ </div>
41
+ `;
42
+
43
+ const dismiss = () => {
44
+ overlay.classList.add("fade-out");
45
+ overlay.addEventListener("transitionend", () => overlay.remove(), {
46
+ once: true,
47
+ });
48
+ };
49
+
50
+ overlay.addEventListener("click", (e) => {
51
+ if (e.target === overlay) dismiss();
52
+ });
53
+
54
+ function openLink(url, newTab = true) {
55
+ if (!url) return;
56
+ if (newTab) {
57
+ window.open(url, "_blank", "noopener,noreferrer");
58
+ } else {
59
+ window.location.href = url;
60
+ }
61
+ }
62
+
63
+ const getStarted = () => openLink("{{get-started-link}}");
64
+ const openDiscord = () => openLink("{{discord-link}}");
65
+ overlay.querySelector("#close-x")?.addEventListener("click", dismiss);
66
+ overlay.querySelector("#dismiss-btn")?.addEventListener("click", dismiss);
67
+ overlay.querySelector("#discord-btn")?.addEventListener("click", openDiscord);
68
+ overlay.querySelector("#get-started-btn")?.addEventListener("click", getStarted);
69
+
70
+ document.body.prepend(overlay);
71
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "Theme",
3
+ "description": "scaffolding for your spicetify theme"
4
+ }
@@ -0,0 +1,36 @@
1
+ {
2
+ "$schema": "./node_modules/oxlint/configuration_schema.json",
3
+ "plugins": null,
4
+ "categories": {},
5
+ "rules": {
6
+ "no-await-in-loop": "error",
7
+ "unicorn/prefer-array-find": "error",
8
+ "unicorn/prefer-array-flat-map": "error",
9
+ "unicorn/prefer-set-has": "error"
10
+ },
11
+ "settings": {
12
+ "jsx-a11y": {
13
+ "polymorphicPropName": null,
14
+ "components": {},
15
+ "attributes": {}
16
+ },
17
+ "react": {
18
+ "formComponents": [],
19
+ "linkComponents": [],
20
+ "version": null,
21
+ "componentWrapperFunctions": []
22
+ },
23
+ "jsdoc": {
24
+ "ignorePrivate": false,
25
+ "ignoreInternal": false,
26
+ "ignoreReplacesDocs": true,
27
+ "overrideReplacesDocs": true,
28
+ "augmentsExtendsReplacesDocs": false,
29
+ "implementsReplacesDocs": false,
30
+ "exemptDestructuredRootsFromChecks": false,
31
+ "tagNamePreference": {}
32
+ }
33
+ },
34
+ "globals": {},
35
+ "ignorePatterns": ["dist/**"]
36
+ }
@@ -0,0 +1,53 @@
1
+ # {{project-name}}
2
+
3
+ Welcome to your new **Spicetify {{template}}** project! This project was
4
+ bootstrapped using [@spicetify/creator]({{github-link}}).
5
+
6
+ ## Getting Started
7
+
8
+ First, install the dependencies using your package manager:
9
+
10
+ ```bash
11
+ {{package-manager}} install
12
+ ```
13
+
14
+ Then, run the development server:
15
+
16
+ ```bash
17
+ {{package-manager}} run dev
18
+ ```
19
+
20
+ The development server will watch for changes and automatically rebuild your
21
+ project.
22
+
23
+ ## Available Scripts
24
+
25
+ ### `dev`
26
+
27
+ Runs the project in development mode with hot-reloading.
28
+
29
+ ### `build`
30
+
31
+ Optimizes and bundles your {{template}} for production. The output will be
32
+ located in the `dist` directory.
33
+
34
+ ### `lint`
35
+
36
+ Checks your code for errors and formatting issues using {{linter}}.
37
+
38
+ ## Project Structure
39
+
40
+ ```text
41
+ .
42
+ ├── src/
43
+ │ └── app.{{entry-ext}} # Main entry point for your {{template}}
44
+ └── spice.config.{{language}} # Configuration for @spicetify/creator
45
+ ```
46
+
47
+ ## Learn More
48
+
49
+ To learn more about Spicetify and how to customize your Spotify client, check
50
+ out the following resources:
51
+
52
+ - [Spicetify]({{spicetify-link}})
53
+ - [@spicetify/creator]({{docs-link}})