@spicemod/creator 0.0.22 → 0.0.23

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 (70) hide show
  1. package/client.d.ts +47 -0
  2. package/dist/bin.mjs +684 -245
  3. package/dist/index.d.mts +697 -0
  4. package/dist/{client/index.mjs → index.mjs} +1 -1
  5. package/dist/templates/custom-app/js/react/eslint.config.ts +29 -0
  6. package/dist/templates/custom-app/js/react/src/app.jsx +22 -0
  7. package/dist/templates/custom-app/js/react/src/components/Onboarding.jsx +82 -0
  8. package/dist/templates/custom-app/js/react/src/extension/index.jsx +23 -0
  9. package/dist/templates/custom-app/meta.json +4 -0
  10. package/dist/templates/custom-app/shared/DOT-gitignore +34 -0
  11. package/dist/templates/custom-app/shared/DOT-oxlintrc.json +36 -0
  12. package/dist/templates/custom-app/shared/README.template.md +53 -0
  13. package/dist/templates/custom-app/shared/app.css +163 -0
  14. package/dist/templates/custom-app/shared/biome.json +36 -0
  15. package/dist/templates/custom-app/shared/css/app.module.scss +58 -0
  16. package/dist/templates/custom-app/shared/icon-active.svg +7 -0
  17. package/dist/templates/custom-app/shared/icon.svg +7 -0
  18. package/dist/templates/custom-app/shared/jsconfig.json +32 -0
  19. package/dist/templates/custom-app/shared/spice.config.js +9 -0
  20. package/dist/templates/custom-app/shared/spice.config.ts +9 -0
  21. package/dist/templates/custom-app/shared/tsconfig.json +32 -0
  22. package/dist/templates/custom-app/ts/react/eslint.config.ts +29 -0
  23. package/dist/templates/custom-app/ts/react/src/app.tsx +23 -0
  24. package/dist/templates/custom-app/ts/react/src/components/Onboarding.tsx +105 -0
  25. package/dist/templates/custom-app/ts/react/src/extension/index.tsx +27 -0
  26. package/dist/templates/extension/js/vanilla/src/components/Onboarding.js +71 -0
  27. package/dist/templates/extension/shared/DOT-gitignore +34 -0
  28. package/dist/templates/extension/shared/DOT-oxlintrc.json +36 -0
  29. package/dist/templates/extension/shared/spice.config.js +1 -1
  30. package/dist/templates/extension/shared/spice.config.ts +1 -1
  31. package/dist/templates/liveReload.js +0 -1
  32. package/dist/templates/theme/shared/DOT-gitignore +34 -0
  33. package/dist/templates/theme/shared/DOT-oxlintrc.json +36 -0
  34. package/dist/templates/theme/shared/spice.config.js +1 -1
  35. package/dist/templates/theme/shared/spice.config.ts +1 -1
  36. package/dist/templates/wrapper.js +6 -9
  37. package/package.json +7 -3
  38. package/templates/custom-app/js/react/eslint.config.ts +29 -0
  39. package/templates/custom-app/js/react/src/app.jsx +22 -0
  40. package/templates/custom-app/js/react/src/components/Onboarding.jsx +82 -0
  41. package/templates/custom-app/js/react/src/extension/index.jsx +23 -0
  42. package/templates/custom-app/meta.json +4 -0
  43. package/templates/custom-app/shared/DOT-gitignore +34 -0
  44. package/templates/custom-app/shared/DOT-oxlintrc.json +36 -0
  45. package/templates/custom-app/shared/README.template.md +53 -0
  46. package/templates/custom-app/shared/app.css +163 -0
  47. package/templates/custom-app/shared/biome.json +36 -0
  48. package/templates/custom-app/shared/css/app.module.scss +58 -0
  49. package/templates/custom-app/shared/icon-active.svg +7 -0
  50. package/templates/custom-app/shared/icon.svg +7 -0
  51. package/templates/custom-app/shared/jsconfig.json +32 -0
  52. package/templates/custom-app/shared/spice.config.js +9 -0
  53. package/templates/custom-app/shared/spice.config.ts +9 -0
  54. package/templates/custom-app/shared/tsconfig.json +32 -0
  55. package/templates/custom-app/ts/react/eslint.config.ts +29 -0
  56. package/templates/custom-app/ts/react/src/app.tsx +23 -0
  57. package/templates/custom-app/ts/react/src/components/Onboarding.tsx +105 -0
  58. package/templates/custom-app/ts/react/src/extension/index.tsx +27 -0
  59. package/templates/extension/js/vanilla/src/components/Onboarding.js +71 -0
  60. package/templates/extension/shared/DOT-gitignore +34 -0
  61. package/templates/extension/shared/DOT-oxlintrc.json +36 -0
  62. package/templates/extension/shared/spice.config.js +1 -1
  63. package/templates/extension/shared/spice.config.ts +1 -1
  64. package/templates/liveReload.js +0 -1
  65. package/templates/theme/shared/DOT-gitignore +34 -0
  66. package/templates/theme/shared/DOT-oxlintrc.json +36 -0
  67. package/templates/theme/shared/spice.config.js +1 -1
  68. package/templates/theme/shared/spice.config.ts +1 -1
  69. package/templates/wrapper.js +6 -9
  70. package/dist/client/index.d.mts +0 -2183
@@ -0,0 +1,23 @@
1
+ import styles from "@/css/app.module.scss";
2
+ import { useState } from "react";
3
+ import "@/app.css";
4
+
5
+ const App: React.FC = () => {
6
+ const [count, setCount] = useState(0);
7
+
8
+ const onButtonClick = () => {
9
+ setCount((prevCount) => prevCount + 1);
10
+ };
11
+
12
+ return (
13
+ <div className={styles.container}>
14
+ <div className={styles.title}>{"My Custom App!"}</div>
15
+ <button className={styles.button} onClick={onButtonClick}>
16
+ {"Count up"}
17
+ </button>
18
+ <div className={styles.counter}>{count}</div>
19
+ </div>
20
+ );
21
+ };
22
+
23
+ export default App;
@@ -0,0 +1,105 @@
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
26
+ className={`onboarding-overlay ${isFading ? "fade-out" : ""}`}
27
+ onClick={handleDismiss}
28
+ >
29
+ <div className="onboarding-card" onClick={(e) => e.stopPropagation()}>
30
+ <button
31
+ className="close-icon-btn"
32
+ onClick={handleDismiss}
33
+ aria-label="Close"
34
+ >
35
+ <svg width="12" height="12" viewBox="0 0 12 12" fill="none">
36
+ <path
37
+ d="M1 1L11 11M1 11L11 1"
38
+ stroke="currentColor"
39
+ strokeWidth="2"
40
+ strokeLinecap="round"
41
+ />
42
+ </svg>
43
+ </button>
44
+
45
+ <div className="status-badge">🚀 {config.projectName} Ready</div>
46
+
47
+ <div className="config-grid">
48
+ <span className="label">Framework</span>
49
+ <span className="value">{config.framework}</span>
50
+
51
+ <span className="label">Language</span>
52
+ <span className="value">{config.language}</span>
53
+
54
+ <span className="label">Manager</span>
55
+ <span className="value">{config.packageManager}</span>
56
+
57
+ <span className="label">Linter</span>
58
+ <span className="value">{config.linter}</span>
59
+ </div>
60
+
61
+ <div className="footer-tip">
62
+ Next Step: Edit <code>src/app.tsx</code>
63
+ </div>
64
+
65
+ <div className="onboarding-actions">
66
+ <button
67
+ className="get-started-btn"
68
+ onClick={() => openLink("{{get-started-link}}")}
69
+ >
70
+ Get Started
71
+ </button>
72
+ <button
73
+ className="get-started-btn"
74
+ onClick={() => {
75
+ Spicetify.Platform.History.push("{{project-url}}");
76
+ handleDismiss();
77
+ }}
78
+ >
79
+ Go to Custom App
80
+ </button>
81
+ <button
82
+ className="discord-btn"
83
+ onClick={() => openLink("{{discord-link}}")}
84
+ >
85
+ Join Discord
86
+ </button>
87
+ <button className="dismiss-btn" onClick={handleDismiss}>
88
+ Dismiss
89
+ </button>
90
+ </div>
91
+ </div>
92
+ </div>
93
+ );
94
+ };
95
+
96
+ export default Onboarding;
97
+
98
+ function openLink(url: string, newTab = true) {
99
+ if (!url) return;
100
+ if (newTab) {
101
+ window.open(url, "_blank", "noopener,noreferrer");
102
+ } else {
103
+ window.location.href = url;
104
+ }
105
+ }
@@ -0,0 +1,27 @@
1
+ import "@/extension/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,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,34 @@
1
+ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2
+
3
+ # dependencies
4
+ /node_modules
5
+ /.pnp
6
+ .pnp.*
7
+ .yarn/*
8
+ !.yarn/patches
9
+ !.yarn/plugins
10
+ !.yarn/releases
11
+ !.yarn/versions
12
+
13
+ # testing
14
+ /coverage
15
+
16
+ # build files
17
+ /dist
18
+
19
+ # production
20
+ /build
21
+ .spicetify/
22
+
23
+ # misc
24
+ .DS_Store
25
+ *.pem
26
+
27
+ # debug
28
+ npm-debug.log*
29
+ yarn-debug.log*
30
+ yarn-error.log*
31
+ .pnpm-debug.log*
32
+
33
+ # typescript
34
+ *.tsbuildinfo
@@ -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
+ }
@@ -1,4 +1,4 @@
1
- import { defineConfig } from "@spicetify/creator/client";
1
+ import { defineConfig } from "@spicetify/creator";
2
2
 
3
3
  // Learn more: {{config-reference-link}}
4
4
  export default defineConfig({
@@ -1,4 +1,4 @@
1
- import { defineConfig } from "@spicetify/creator/client";
1
+ import { defineConfig } from "@spicetify/creator";
2
2
 
3
3
  // Learn more: {{config-reference-link}}
4
4
  export default defineConfig({
@@ -63,7 +63,6 @@
63
63
  const script = document.createElement("script");
64
64
  script.id = JS_ID;
65
65
  script.src = SERVER + JS_PATH;
66
- script.type = "module";
67
66
  document.body.appendChild(script);
68
67
 
69
68
  connect();
@@ -0,0 +1,34 @@
1
+ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2
+
3
+ # dependencies
4
+ /node_modules
5
+ /.pnp
6
+ .pnp.*
7
+ .yarn/*
8
+ !.yarn/patches
9
+ !.yarn/plugins
10
+ !.yarn/releases
11
+ !.yarn/versions
12
+
13
+ # testing
14
+ /coverage
15
+
16
+ # build files
17
+ /dist
18
+
19
+ # production
20
+ /build
21
+ .spicetify/
22
+
23
+ # misc
24
+ .DS_Store
25
+ *.pem
26
+
27
+ # debug
28
+ npm-debug.log*
29
+ yarn-debug.log*
30
+ yarn-error.log*
31
+ .pnpm-debug.log*
32
+
33
+ # typescript
34
+ *.tsbuildinfo
@@ -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
+ }
@@ -1,4 +1,4 @@
1
- import { defineConfig } from "@spicetify/creator/client";
1
+ import { defineConfig } from "@spicetify/creator";
2
2
 
3
3
  // Learn more: {{config-reference-link}}
4
4
  export default defineConfig({
@@ -1,4 +1,4 @@
1
- import { defineConfig } from "@spicetify/creator/client";
1
+ import { defineConfig } from "@spicetify/creator";
2
2
 
3
3
  // Learn more: {{config-reference-link}}
4
4
  export default defineConfig({
@@ -1,14 +1,12 @@
1
1
  __ESBUILD__HAS_CSS &&
2
2
  (async () => {
3
3
  try {
4
- const css = `{{INJECTED_CSS_HERE}}`;
5
- if (css && css.trim().length !== 0) {
6
- const style = document.createElement("style");
7
- style.setAttribute("data-app", __ESBUILD__APP_ID);
8
- style.textContent = css;
9
- document.head.appendChild(style);
10
- }
11
- } catch {}
4
+ const css = __ESBUILD__INJECTED_CSS;
5
+ const style = document.createElement("style");
6
+ style.setAttribute("data-app", __ESBUILD__APP_ID);
7
+ style.textContent = css;
8
+ document.head.appendChild(style);
9
+ } catch { }
12
10
  })();
13
11
  (async () => {
14
12
  const _ID = `${__ESBUILD__APP_SLUG}-${__ESBUILD__APP_TYPE}`;
@@ -16,7 +14,6 @@ __ESBUILD__HAS_CSS &&
16
14
  window.SpiceGlobals[_ID] = {
17
15
  id: __ESBUILD__APP_ID,
18
16
  version: __ESBUILD__APP_VERSION,
19
- hash: __ESBUILD__APP_HASH,
20
17
  };
21
18
  const { id: appId, version: v } = window.SpiceGlobals[_ID];
22
19
  const _wait = (p, a = 0) =>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spicemod/creator",
3
- "version": "0.0.22",
3
+ "version": "0.0.23",
4
4
  "description": "Easily make Spicetify extensions and themes",
5
5
  "keywords": [
6
6
  "cli",
@@ -21,12 +21,16 @@
21
21
  },
22
22
  "files": [
23
23
  "dist",
24
- "templates"
24
+ "templates",
25
+ "client.d.ts"
25
26
  ],
26
27
  "type": "module",
27
28
  "exports": {
29
+ ".": "./dist/index.mjs",
30
+ "./client": {
31
+ "types": "./client.d.ts"
32
+ },
28
33
  "./bin": "./dist/bin.mjs",
29
- "./client": "./dist/client/index.mjs",
30
34
  "./package.json": "./package.json"
31
35
  },
32
36
  "scripts": {
@@ -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,22 @@
1
+ import styles from "@/css/app.module.scss";
2
+ import { useState } from "react";
3
+
4
+ const App = () => {
5
+ const [count, setCount] = useState(0);
6
+
7
+ const onButtonClick = () => {
8
+ setCount((prevCount) => prevCount + 1);
9
+ };
10
+
11
+ return (
12
+ <div className={styles.container}>
13
+ <div className={styles.title}>{"My Custom App!"}</div>
14
+ <button className={styles.button} onClick={onButtonClick}>
15
+ {"Count up"}
16
+ </button>
17
+ <div className={styles.counter}>{count}</div>
18
+ </div>
19
+ );
20
+ };
21
+
22
+ export default App;
@@ -0,0 +1,82 @@
1
+ import { useState } from "react";
2
+
3
+ const Onboarding = ({ config }) => {
4
+ const [isVisible, setIsVisible] = useState(true);
5
+ const [isFading, setIsFading] = useState(false);
6
+
7
+ const handleDismiss = () => {
8
+ setIsFading(true);
9
+ setTimeout(() => setIsVisible(false), 250);
10
+ };
11
+
12
+ if (!isVisible) return null;
13
+
14
+ return (
15
+ <div className={`onboarding-overlay ${isFading ? "fade-out" : ""}`} onClick={handleDismiss}>
16
+ <div className="onboarding-card" onClick={(e) => e.stopPropagation()}>
17
+ <button className="close-icon-btn" onClick={handleDismiss} aria-label="Close">
18
+ <svg width="12" height="12" viewBox="0 0 12 12" fill="none">
19
+ <path
20
+ d="M1 1L11 11M1 11L11 1"
21
+ stroke="currentColor"
22
+ strokeWidth="2"
23
+ strokeLinecap="round"
24
+ />
25
+ </svg>
26
+ </button>
27
+
28
+ <div className="status-badge">🚀 {config.projectName} Ready</div>
29
+
30
+ <div className="config-grid">
31
+ <span className="label">Framework</span>
32
+ <span className="value">{config.framework}</span>
33
+
34
+ <span className="label">Language</span>
35
+ <span className="value">{config.language}</span>
36
+
37
+ <span className="label">Manager</span>
38
+ <span className="value">{config.packageManager}</span>
39
+
40
+ <span className="label">Linter</span>
41
+ <span className="value">{config.linter}</span>
42
+ </div>
43
+
44
+ <div className="footer-tip">
45
+ Next Step: Edit <code>src/app.tsx</code>
46
+ </div>
47
+
48
+ <div className="onboarding-actions">
49
+ <button className="get-started-btn" onClick={() => openLink("{{get-started-link}}")}>
50
+ Get Started
51
+ </button>
52
+ <button
53
+ className="get-started-btn"
54
+ onClick={() => {
55
+ Spicetify.Platform.History.push("{{project-url}}");
56
+ handleDismiss();
57
+ }}
58
+ >
59
+ Go to Custom App
60
+ </button>
61
+ <button className="discord-btn" onClick={() => openLink("{{discord-link}}")}>
62
+ Join Discord
63
+ </button>
64
+ <button className="dismiss-btn" onClick={handleDismiss}>
65
+ Dismiss
66
+ </button>
67
+ </div>
68
+ </div>
69
+ </div>
70
+ );
71
+ };
72
+
73
+ export default Onboarding;
74
+
75
+ function openLink(url, newTab = true) {
76
+ if (!url) return;
77
+ if (newTab) {
78
+ window.open(url, "_blank", "noopener,noreferrer");
79
+ } else {
80
+ window.location.href = url;
81
+ }
82
+ }
@@ -0,0 +1,23 @@
1
+ import styles from "@/css/app.module.scss";
2
+ import { useState } from "react";
3
+ import "@/app.css";
4
+
5
+ const App = () => {
6
+ const [count, setCount] = useState(0);
7
+
8
+ const onButtonClick = () => {
9
+ setCount((prevCount) => prevCount + 1);
10
+ };
11
+
12
+ return (
13
+ <div className={styles.container}>
14
+ <div className={styles.title}>{"My Custom App!"}</div>
15
+ <button className={styles.button} onClick={onButtonClick}>
16
+ {"Count up"}
17
+ </button>
18
+ <div className={styles.counter}>{count}</div>
19
+ </div>
20
+ );
21
+ };
22
+
23
+ export default App;
@@ -0,0 +1,4 @@
1
+ {
2
+ "title": "Custom App",
3
+ "description": "scaffolding for your spicetify custom app"
4
+ }
@@ -0,0 +1,34 @@
1
+ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2
+
3
+ # dependencies
4
+ /node_modules
5
+ /.pnp
6
+ .pnp.*
7
+ .yarn/*
8
+ !.yarn/patches
9
+ !.yarn/plugins
10
+ !.yarn/releases
11
+ !.yarn/versions
12
+
13
+ # testing
14
+ /coverage
15
+
16
+ # build files
17
+ /dist
18
+
19
+ # production
20
+ /build
21
+ .spicetify/
22
+
23
+ # misc
24
+ .DS_Store
25
+ *.pem
26
+
27
+ # debug
28
+ npm-debug.log*
29
+ yarn-debug.log*
30
+ yarn-error.log*
31
+ .pnpm-debug.log*
32
+
33
+ # typescript
34
+ *.tsbuildinfo