@spicetify/kit 0.1.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.
- package/bin/spicetify-kit.js +4 -0
- package/dist/build.js +335 -0
- package/dist/check.js +284 -0
- package/dist/classmap.js +187 -0
- package/dist/cli.js +53 -0
- package/dist/create.js +429 -0
- package/dist/dev.js +100 -0
- package/dist/from-theme.js +96 -0
- package/dist/install.js +60 -0
- package/dist/launch.js +122 -0
- package/dist/pack.js +31 -0
- package/dist/push.js +211 -0
- package/dist/vault.js +108 -0
- package/package.json +49 -0
- package/vendor/classmaps/1020096/classmap-19f856aefd5.json +109 -0
- package/vendor/shims/chunks.d.ts +10 -0
- package/vendor/shims/remote-modules.d.ts +6 -0
- package/vendor/shims/spicetify.d.ts +2437 -0
- package/vendor/stdlib/deps.ts +24 -0
- package/vendor/stdlib/index.ts +24 -0
- package/vendor/stdlib/lib/modal.tsx +128 -0
- package/vendor/stdlib/lib/popover.ts +84 -0
- package/vendor/stdlib/lib/primitives-classes.ts +66 -0
- package/vendor/stdlib/lib/primitives-vanilla.ts +277 -0
- package/vendor/stdlib/lib/primitives.tsx +314 -0
- package/vendor/stdlib/load.ts +17 -0
- package/vendor/stdlib/metadata.json +18 -0
- package/vendor/stdlib/mixin.ts +32 -0
- package/vendor/stdlib/mod.ts +26 -0
- package/vendor/stdlib/src/chunks.d.ts +10 -0
- package/vendor/stdlib/src/client.ts +114 -0
- package/vendor/stdlib/src/createIconComponent.tsx +29 -0
- package/vendor/stdlib/src/events.mix.ts +55 -0
- package/vendor/stdlib/src/events.ts +58 -0
- package/vendor/stdlib/src/expose/GraphQL.ts +40 -0
- package/vendor/stdlib/src/expose/Platform.ts +48 -0
- package/vendor/stdlib/src/expose/React.ts +95 -0
- package/vendor/stdlib/src/expose/ReactFlipToolkitSpring.ts +27 -0
- package/vendor/stdlib/src/expose/ReduxStore.ts +39 -0
- package/vendor/stdlib/src/expose/SettingsSection.ts +60 -0
- package/vendor/stdlib/src/expose/Snackbar.ts +34 -0
- package/vendor/stdlib/src/expose/Tippy.ts +26 -0
- package/vendor/stdlib/src/expose/enqueueImageSnackbar.ts +25 -0
- package/vendor/stdlib/src/expose/index.ts +14 -0
- package/vendor/stdlib/src/expose/jsx-runtime.ts +46 -0
- package/vendor/stdlib/src/expose/react-dom-shim.ts +50 -0
- package/vendor/stdlib/src/expose/react-shim.ts +116 -0
- package/vendor/stdlib/src/logger.ts +35 -0
- package/vendor/stdlib/src/playbar-compat.tsx +136 -0
- package/vendor/stdlib/src/registers/index.ts +176 -0
- package/vendor/stdlib/src/registers/menu.ts +160 -0
- package/vendor/stdlib/src/registers/mount.ts +307 -0
- package/vendor/stdlib/src/registers/nativeAnchors.ts +49 -0
- package/vendor/stdlib/src/registers/navlink.tsx +165 -0
- package/vendor/stdlib/src/registers/order.ts +22 -0
- package/vendor/stdlib/src/registers/panel.ts +168 -0
- package/vendor/stdlib/src/registers/playbarButton.tsx +93 -0
- package/vendor/stdlib/src/registers/playbarWidget.tsx +71 -0
- package/vendor/stdlib/src/registers/registry.ts +12 -0
- package/vendor/stdlib/src/registers/root.ts +101 -0
- package/vendor/stdlib/src/registers/route.ts +123 -0
- package/vendor/stdlib/src/registers/settingsRow.tsx +56 -0
- package/vendor/stdlib/src/registers/settingsSection.ts +59 -0
- package/vendor/stdlib/src/registers/topbarLeftButton.tsx +86 -0
- package/vendor/stdlib/src/registers/topbarRightButton.tsx +87 -0
- package/vendor/stdlib/src/storage.ts +163 -0
- package/vendor/stdlib/src/util.ts +62 -0
- package/vendor/stdlib/src/utils/index.ts +16 -0
- package/vendor/stdlib/src/webpack/ClassNames.gen.ts +10 -0
- package/vendor/stdlib/src/webpack/ClassNames.ts +6 -0
- package/vendor/stdlib/src/webpack/ClassNames.xpui.ts +16 -0
- package/vendor/stdlib/src/webpack/ComponentLibrary.gen.ts +10 -0
- package/vendor/stdlib/src/webpack/ComponentLibrary.ts +6 -0
- package/vendor/stdlib/src/webpack/ComponentLibrary.xpui.ts +18 -0
- package/vendor/stdlib/src/webpack/FilterContext.gen.ts +10 -0
- package/vendor/stdlib/src/webpack/FilterContext.ts +6 -0
- package/vendor/stdlib/src/webpack/FilterContext.xpui.ts +10 -0
- package/vendor/stdlib/src/webpack/Mousetrap.gen.ts +10 -0
- package/vendor/stdlib/src/webpack/Mousetrap.ts +6 -0
- package/vendor/stdlib/src/webpack/Mousetrap.xpui.ts +12 -0
- package/vendor/stdlib/src/webpack/React.gen.ts +14 -0
- package/vendor/stdlib/src/webpack/React.ts +6 -0
- package/vendor/stdlib/src/webpack/React.xpui.ts +15 -0
- package/vendor/stdlib/src/webpack/ReactComponents.desktop.ts +17 -0
- package/vendor/stdlib/src/webpack/ReactComponents.gen.ts +74 -0
- package/vendor/stdlib/src/webpack/ReactComponents.panel.ts +19 -0
- package/vendor/stdlib/src/webpack/ReactComponents.ts +50 -0
- package/vendor/stdlib/src/webpack/ReactComponents.xpui.ts +164 -0
- package/vendor/stdlib/src/webpack/ReactFlipToolkit.gen.ts +12 -0
- package/vendor/stdlib/src/webpack/ReactFlipToolkit.ts +6 -0
- package/vendor/stdlib/src/webpack/ReactFlipToolkit.xpui.ts +14 -0
- package/vendor/stdlib/src/webpack/ReactHooks.fullscreen.ts +15 -0
- package/vendor/stdlib/src/webpack/ReactHooks.gen.ts +32 -0
- package/vendor/stdlib/src/webpack/ReactHooks.ts +6 -0
- package/vendor/stdlib/src/webpack/ReactHooks.xpui.ts +38 -0
- package/vendor/stdlib/src/webpack/ReactQuery.gen.ts +26 -0
- package/vendor/stdlib/src/webpack/ReactQuery.ts +6 -0
- package/vendor/stdlib/src/webpack/ReactQuery.xpui.ts +46 -0
- package/vendor/stdlib/src/webpack/ReactRouter.gen.ts +12 -0
- package/vendor/stdlib/src/webpack/ReactRouter.ts +6 -0
- package/vendor/stdlib/src/webpack/ReactRouter.xpui.ts +22 -0
- package/vendor/stdlib/src/webpack/Snackbar.gen.ts +12 -0
- package/vendor/stdlib/src/webpack/Snackbar.ts +6 -0
- package/vendor/stdlib/src/webpack/Snackbar.xpui.ts +27 -0
- package/vendor/stdlib/src/webpack/URI.gen.ts +26 -0
- package/vendor/stdlib/src/webpack/URI.ts +82 -0
- package/vendor/stdlib/src/webpack/URI.xpui.ts +111 -0
- package/vendor/stdlib/src/webpack/capture-readiness.ts +49 -0
- package/vendor/stdlib/src/webpack/index.ts +186 -0
- package/vendor/stdlib/src/webpack/misc.gen.ts +16 -0
- package/vendor/stdlib/src/webpack/misc.ts +6 -0
- package/vendor/stdlib/src/webpack/misc.xpui.ts +27 -0
- package/vendor/stdlib/src/wpunpk.mix.ts +104 -0
- package/vendor/stdlib/src/wpunpk.ts +36 -0
- package/vendor/stdlib/vendor/rxjs.d.ts +5 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2024 Delusoire
|
|
3
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { transformer } from "../../mixin.ts";
|
|
7
|
+
|
|
8
|
+
export type GraphQLOp = "query" | "mutation";
|
|
9
|
+
export type GraphQLDef<N extends string, O extends GraphQLOp> = {
|
|
10
|
+
name: N;
|
|
11
|
+
operation: O;
|
|
12
|
+
sha256Hash: string;
|
|
13
|
+
value: null;
|
|
14
|
+
};
|
|
15
|
+
export type GraphQLDefs = { [O in GraphQLOp]: { [N in string]: GraphQLDef<N, O> } };
|
|
16
|
+
|
|
17
|
+
export const GraphQLDefs = {
|
|
18
|
+
query: {},
|
|
19
|
+
mutation: {},
|
|
20
|
+
} as GraphQLDefs;
|
|
21
|
+
|
|
22
|
+
transformer(
|
|
23
|
+
(emit) => (str) => {
|
|
24
|
+
emit();
|
|
25
|
+
|
|
26
|
+
const matches = str.matchAll(
|
|
27
|
+
/(=new [a-zA-Z_\$][\w\$]*\.[a-zA-Z_\$][\w\$]*\("(?<name>\w+)","(?<operation>query|mutation)","(?<sha256Hash>[\w\d]{64})",null\))/g,
|
|
28
|
+
);
|
|
29
|
+
for (const match of matches) {
|
|
30
|
+
const { name, operation, sha256Hash } = match.groups!;
|
|
31
|
+
// @ts-ignore
|
|
32
|
+
GraphQLDefs[operation][name] = { name, operation, sha256Hash, value: null };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return str;
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
glob: /.+\.js$/,
|
|
39
|
+
},
|
|
40
|
+
);
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2024 Delusoire
|
|
3
|
+
* Copyright (C) 2026 Afonso Jorge Ramos
|
|
4
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// The original exposed Platform via a runtime source transform of the client
|
|
8
|
+
// core bundle, which never executes on snapshot builds. Resolve lazily: the
|
|
9
|
+
// mixin phase imports this module before the client (and Spicetify._platform)
|
|
10
|
+
// exists, so capture must happen on first use, not at import time.
|
|
11
|
+
let cached: unknown;
|
|
12
|
+
|
|
13
|
+
function resolvePlatform(): any {
|
|
14
|
+
if (cached === undefined) {
|
|
15
|
+
cached = globalThis.Spicetify?._platform ?? null;
|
|
16
|
+
}
|
|
17
|
+
return cached ?? undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const Platform: any = new Proxy(
|
|
21
|
+
{},
|
|
22
|
+
{
|
|
23
|
+
get: (_, key) => {
|
|
24
|
+
const p = resolvePlatform();
|
|
25
|
+
if (!p) return undefined;
|
|
26
|
+
if (key in p) return p[key];
|
|
27
|
+
if (typeof key === "string" && key.startsWith("get") && typeof p.getRegistry === "function") {
|
|
28
|
+
const description = key.slice(3);
|
|
29
|
+
for (const s of p.getRegistry()._map.keys()) {
|
|
30
|
+
if (s.description === description) return () => p.getRegistry().resolve(s);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return undefined;
|
|
34
|
+
},
|
|
35
|
+
has: (_, key) => {
|
|
36
|
+
const p = resolvePlatform();
|
|
37
|
+
if (!p) return false;
|
|
38
|
+
if (key in p) return true;
|
|
39
|
+
if (typeof key === "string" && key.startsWith("get") && typeof p.getRegistry === "function") {
|
|
40
|
+
const description = key.slice(3);
|
|
41
|
+
for (const s of p.getRegistry()._map.keys()) {
|
|
42
|
+
if (s.description === description) return true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2024 Delusoire
|
|
3
|
+
* Copyright (C) 2026 Afonso Jorge Ramos
|
|
4
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { exports as webpackExports } from "../webpack/index.ts";
|
|
8
|
+
|
|
9
|
+
// One React rule: module trees mix stdlib chrome, module components, and
|
|
10
|
+
// client components, so hooks and context identity must resolve to the
|
|
11
|
+
// client's own React instance. The 2024 runtime unified the other way
|
|
12
|
+
// (pre-boot factory swap to the esm.sh copy); post-boot that is impossible,
|
|
13
|
+
// so the exposed React lazily forwards to the client instance once webpack
|
|
14
|
+
// is captured.
|
|
15
|
+
//
|
|
16
|
+
// The esm.sh copy only backs pre-capture access — element creation is
|
|
17
|
+
// instance-independent, and module hooks never run pre-capture (the loader
|
|
18
|
+
// gates module loads on webpackLoaded). It therefore loads lazily, and only
|
|
19
|
+
// on an actual pre-capture miss: a static import here would put esm.sh in
|
|
20
|
+
// every client's boot path and make an offline boot fail the whole module
|
|
21
|
+
// graph.
|
|
22
|
+
// Fires when any lazy fallback module lands, so the named live bindings in
|
|
23
|
+
// the react shims can run a second populate pass. Without it, a capture miss
|
|
24
|
+
// (needle drift after a client update) leaves the named bindings frozen
|
|
25
|
+
// undefined even though the default-export proxy path recovers.
|
|
26
|
+
const recoverySubscribers = new Set<() => void>();
|
|
27
|
+
let anyFallbackLanded = false;
|
|
28
|
+
export function onFallbackRecovery(cb: () => void): void {
|
|
29
|
+
recoverySubscribers.add(cb);
|
|
30
|
+
if (anyFallbackLanded) cb();
|
|
31
|
+
}
|
|
32
|
+
const notifyRecovery = () => {
|
|
33
|
+
anyFallbackLanded = true;
|
|
34
|
+
for (const cb of recoverySubscribers) {
|
|
35
|
+
try {
|
|
36
|
+
cb();
|
|
37
|
+
} catch {
|
|
38
|
+
/* a subscriber must not break the others */
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const lazyFallback = (url: string, pick: (m: Record<string, unknown>) => object | undefined) => {
|
|
44
|
+
let mod: object | undefined;
|
|
45
|
+
let requested = false;
|
|
46
|
+
return (): object | undefined => {
|
|
47
|
+
if (!mod && !requested) {
|
|
48
|
+
requested = true;
|
|
49
|
+
void import(url)
|
|
50
|
+
.then((m) => {
|
|
51
|
+
mod = pick(m);
|
|
52
|
+
notifyRecovery();
|
|
53
|
+
})
|
|
54
|
+
.catch(() => {
|
|
55
|
+
/* offline: the client instance is the real path anyway */
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return mod;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const lazyInstance = <T extends object>(find: () => object | undefined, fallback: () => object | undefined): T => {
|
|
63
|
+
let cached: object | undefined;
|
|
64
|
+
return new Proxy({} as T, {
|
|
65
|
+
get: (_target, key) => {
|
|
66
|
+
cached ??= find();
|
|
67
|
+
return ((cached ?? fallback()) as Record<PropertyKey, unknown> | undefined)?.[key];
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
type ReactModule = typeof import("react");
|
|
73
|
+
type ReactDOMModule = typeof import("react-dom") & typeof import("react-dom/client");
|
|
74
|
+
|
|
75
|
+
export const React: ReactModule = lazyInstance(
|
|
76
|
+
() =>
|
|
77
|
+
webpackExports?.find(
|
|
78
|
+
(m) =>
|
|
79
|
+
m &&
|
|
80
|
+
typeof m.createElement === "function" &&
|
|
81
|
+
typeof m.useReducer === "function" &&
|
|
82
|
+
typeof m.useEffect === "function",
|
|
83
|
+
),
|
|
84
|
+
lazyFallback("https://esm.sh/react@18.3.1", (m) => (m.default ?? m) as object),
|
|
85
|
+
);
|
|
86
|
+
export const ReactDOM: ReactDOMModule = lazyInstance(
|
|
87
|
+
() => webpackExports?.find((m) => m && typeof m.createRoot === "function" && typeof m.createPortal === "function"),
|
|
88
|
+
lazyFallback("https://esm.sh/react-dom@18.3.1", (m) => (m.default ?? m) as object),
|
|
89
|
+
);
|
|
90
|
+
// The client bundles its own react-dom/server (renderToString consumers);
|
|
91
|
+
// forward to it, esm.sh only as the same lazy last resort.
|
|
92
|
+
export const ReactDOMServer: typeof import("react-dom/server") = lazyInstance(
|
|
93
|
+
() => webpackExports?.find((m) => m && typeof m.renderToString === "function"),
|
|
94
|
+
lazyFallback("https://esm.sh/react-dom@18.3.1/server", (m) => (m.default ?? m) as object),
|
|
95
|
+
);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2024 Delusoire
|
|
3
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { transformer } from "../../mixin.ts";
|
|
7
|
+
|
|
8
|
+
import type { spring } from "npm:react-flip-toolkit";
|
|
9
|
+
|
|
10
|
+
export type ReactFlipToolkitSpring = typeof spring;
|
|
11
|
+
export let ReactFlipToolkitSpring: ReactFlipToolkitSpring;
|
|
12
|
+
|
|
13
|
+
transformer<ReactFlipToolkitSpring>(
|
|
14
|
+
(emit) => (str) => {
|
|
15
|
+
str = str.replace(
|
|
16
|
+
/([a-zA-Z_\$][\w\$]*)=((?:function|\()([\w$.,{}()= ]+(?:springConfig|overshootClamping)){2})/,
|
|
17
|
+
"$1=__ReactFlipToolkitSpring=$2",
|
|
18
|
+
);
|
|
19
|
+
Object.defineProperty(globalThis, "__ReactFlipToolkitSpring", { set: emit });
|
|
20
|
+
return str;
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
glob: /^\/vendor~xpui\.js/,
|
|
24
|
+
},
|
|
25
|
+
).then(($) => {
|
|
26
|
+
ReactFlipToolkitSpring = $;
|
|
27
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2024 Delusoire
|
|
3
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { transformer } from "../../mixin.ts";
|
|
7
|
+
|
|
8
|
+
type Store = any;
|
|
9
|
+
|
|
10
|
+
export type ReduxStore = Store;
|
|
11
|
+
export let ReduxStore: ReduxStore;
|
|
12
|
+
export let Platform: any;
|
|
13
|
+
|
|
14
|
+
transformer(
|
|
15
|
+
(emit) => (str) => {
|
|
16
|
+
str = str.replace(
|
|
17
|
+
/\.jsx\)\(([a-zA-Z_\$][\w\$]*),\{store:([a-zA-Z_\$][\w\$]*),platform:([a-zA-Z_\$][\w\$]*)\}\)/,
|
|
18
|
+
".jsx)($1,{store:__ReduxStore=$2,platform:__Platform=$3})",
|
|
19
|
+
);
|
|
20
|
+
Object.defineProperty(globalThis, "__ReduxStore", {
|
|
21
|
+
set: (value) => {
|
|
22
|
+
emit();
|
|
23
|
+
ReduxStore = value;
|
|
24
|
+
},
|
|
25
|
+
get: () => ReduxStore,
|
|
26
|
+
});
|
|
27
|
+
// Object.defineProperty(globalThis, "__Platform", {
|
|
28
|
+
// set: (value) => {
|
|
29
|
+
// emit();
|
|
30
|
+
// Platform = value;
|
|
31
|
+
// },
|
|
32
|
+
// get: () => Platform,
|
|
33
|
+
// });
|
|
34
|
+
return str;
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
glob: /^\/xpui\.js/,
|
|
38
|
+
},
|
|
39
|
+
);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2024 Delusoire
|
|
3
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { transformer } from "../../mixin.ts";
|
|
7
|
+
|
|
8
|
+
export type SettingsSectionProps = { filterMatchQuery: string; children?: React.ReactNode };
|
|
9
|
+
export type SettingsSection = React.FC<SettingsSectionProps>;
|
|
10
|
+
export let SettingsSection: SettingsSection;
|
|
11
|
+
|
|
12
|
+
export const future = {
|
|
13
|
+
push: () => {},
|
|
14
|
+
pull(fn: () => void) {
|
|
15
|
+
const push = this.push;
|
|
16
|
+
this.push = () => {
|
|
17
|
+
push();
|
|
18
|
+
fn();
|
|
19
|
+
};
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type SettingsSectionTitleProps = { children?: React.ReactNode };
|
|
24
|
+
export type SettingsSectionTitle = React.FC<SettingsSectionTitleProps>;
|
|
25
|
+
export let SettingsSectionTitle: SettingsSectionTitle;
|
|
26
|
+
|
|
27
|
+
transformer<SettingsSection>(
|
|
28
|
+
(emit) => (str) => {
|
|
29
|
+
str = str.replace(
|
|
30
|
+
/(\.jsxs\)\()([a-zA-Z_\$][\w\$]*)([^=]*"desktop.settings.compatibility")/,
|
|
31
|
+
"$1(__SettingsSection=$2)$3",
|
|
32
|
+
);
|
|
33
|
+
Object.defineProperty(globalThis, "__SettingsSection", { set: emit });
|
|
34
|
+
return str;
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
glob: /^\/xpui-routes-desktop-settings\.js/,
|
|
38
|
+
wait: false,
|
|
39
|
+
},
|
|
40
|
+
).then(($) => {
|
|
41
|
+
SettingsSection = $;
|
|
42
|
+
future.push();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
transformer<SettingsSectionTitle>(
|
|
46
|
+
(emit) => (str) => {
|
|
47
|
+
str = str.replace(
|
|
48
|
+
/("desktop.settings.compatibility"[^=]*?\.jsx\)\()([a-zA-Z_\$][\w\$]*)/,
|
|
49
|
+
"$1(__SettingsSectionTitle=$2)",
|
|
50
|
+
);
|
|
51
|
+
Object.defineProperty(globalThis, "__SettingsSectionTitle", { set: emit });
|
|
52
|
+
return str;
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
glob: /^\/xpui-routes-desktop-settings\.js/,
|
|
56
|
+
wait: false,
|
|
57
|
+
},
|
|
58
|
+
).then(($) => {
|
|
59
|
+
SettingsSectionTitle = $;
|
|
60
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2024 Delusoire
|
|
3
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { transformer } from "../../mixin.ts";
|
|
7
|
+
|
|
8
|
+
import type SnackbarT from "npm:notistack";
|
|
9
|
+
|
|
10
|
+
export type Snackbar = typeof SnackbarT;
|
|
11
|
+
export let Snackbar: Snackbar;
|
|
12
|
+
|
|
13
|
+
transformer<Snackbar>(
|
|
14
|
+
(emit) => (str) => {
|
|
15
|
+
str = str.replace(
|
|
16
|
+
/var ([a-zA-Z_\$][\w\$]*);return\(\1=([^;]*?)\)\.enqueueSnackbar=/,
|
|
17
|
+
"var $1;return($1=__Snackbar=$2).enqueueSnackbar=",
|
|
18
|
+
);
|
|
19
|
+
let __Snackbar: Snackbar | undefined = undefined;
|
|
20
|
+
Object.defineProperty(globalThis, "__Snackbar", {
|
|
21
|
+
set: (value) => {
|
|
22
|
+
emit(value);
|
|
23
|
+
__Snackbar = value;
|
|
24
|
+
},
|
|
25
|
+
get: () => __Snackbar,
|
|
26
|
+
});
|
|
27
|
+
return str;
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
glob: /^\/vendor~xpui\.js/,
|
|
31
|
+
},
|
|
32
|
+
).then(($) => {
|
|
33
|
+
Snackbar = $;
|
|
34
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2024 Delusoire
|
|
3
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { transformer } from "../../mixin.ts";
|
|
7
|
+
|
|
8
|
+
type TippyT = any;
|
|
9
|
+
|
|
10
|
+
export type Tippy = TippyT;
|
|
11
|
+
export let Tippy: Tippy;
|
|
12
|
+
|
|
13
|
+
transformer<Tippy>(
|
|
14
|
+
(emit) => (str) => {
|
|
15
|
+
str = str.replace(/(([a-zA-Z_\$][\w\$]*)\.setDefaultProps=)/, "__Tippy=$2;$1");
|
|
16
|
+
Object.defineProperty(globalThis, "__Tippy", {
|
|
17
|
+
set: emit,
|
|
18
|
+
});
|
|
19
|
+
return str;
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
glob: /^\/vendor~xpui\.js/,
|
|
23
|
+
},
|
|
24
|
+
).then(($) => {
|
|
25
|
+
Tippy = $;
|
|
26
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2024 Delusoire
|
|
3
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { transformer } from "../../mixin.ts";
|
|
7
|
+
|
|
8
|
+
export let enqueueImageSnackbar: any;
|
|
9
|
+
|
|
10
|
+
// TODO: replace with a custom enqueueCustomSnackbar wrapper
|
|
11
|
+
transformer(
|
|
12
|
+
(emit) => (str) => {
|
|
13
|
+
str = str.replace(/(\(\({[^}]*,\s*imageSrc)/, "__enqueueImageSnackbar=$1");
|
|
14
|
+
Object.defineProperty(globalThis, "__enqueueImageSnackbar", {
|
|
15
|
+
set: emit,
|
|
16
|
+
});
|
|
17
|
+
return str;
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
glob: /^\/xpui\.js/,
|
|
21
|
+
wait: false,
|
|
22
|
+
},
|
|
23
|
+
).then(($: any) => {
|
|
24
|
+
enqueueImageSnackbar = $;
|
|
25
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2024 Delusoire
|
|
3
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import "./enqueueImageSnackbar.ts";
|
|
7
|
+
import "./GraphQL.ts";
|
|
8
|
+
import "./Platform.ts";
|
|
9
|
+
import "./React.ts";
|
|
10
|
+
import "./ReactFlipToolkitSpring.ts";
|
|
11
|
+
import "./ReduxStore.ts";
|
|
12
|
+
import "./SettingsSection.ts";
|
|
13
|
+
import "./Snackbar.ts";
|
|
14
|
+
import "./Tippy.ts";
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2026 Afonso Jorge Ramos
|
|
3
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Automatic-runtime JSX backed by the captured client React. The kit build
|
|
7
|
+
// aliases "react/jsx-runtime" here so a module's boot never depends on the
|
|
8
|
+
// network (the esm.sh runtime it replaces was a static import in every
|
|
9
|
+
// built .tsx module). Element creation must go through the client instance
|
|
10
|
+
// anyway — one React rule.
|
|
11
|
+
|
|
12
|
+
import { React } from "./React.ts";
|
|
13
|
+
|
|
14
|
+
// A stable local sentinel rather than a snapshot of React.Fragment: this
|
|
15
|
+
// module can be evaluated before the webpack capture completes (a mixin or
|
|
16
|
+
// any early importer is enough), and a `const Fragment = React.Fragment`
|
|
17
|
+
// taken at that moment freezes undefined forever — after which every
|
|
18
|
+
// fragment in every module renders as React error #130 and the route
|
|
19
|
+
// overlay shows a blank page. All fragment usage flows through jsx/jsxs
|
|
20
|
+
// below, so the sentinel is swapped for the real React.Fragment at call
|
|
21
|
+
// time, when the capture is live.
|
|
22
|
+
export const Fragment: unknown = Symbol.for("spicetify.jsx.Fragment");
|
|
23
|
+
|
|
24
|
+
// The automatic runtime passes children inside props and the key as a third
|
|
25
|
+
// argument; createElement wants the key in props. Children stay in props
|
|
26
|
+
// untouched — createElement reads config.children when no vararg children
|
|
27
|
+
// are given — because re-spreading them as varargs changes arity: a
|
|
28
|
+
// one-element array arrives as a bare child and an empty array as
|
|
29
|
+
// undefined, so any component that does children.map() breaks only when the
|
|
30
|
+
// list happens to have one entry. jsx/jsxs differ only in a static-children
|
|
31
|
+
// guarantee createElement doesn't care about.
|
|
32
|
+
//
|
|
33
|
+
// The sentinel translation only covers these create paths. An element built
|
|
34
|
+
// by handing this module's Fragment straight to React.createElement fails
|
|
35
|
+
// visibly, and element.type === Fragment is always false for JSX-created
|
|
36
|
+
// elements — both need unusual hand-written imports.
|
|
37
|
+
function create(type: unknown, props: Record<string, unknown> | null, key?: unknown) {
|
|
38
|
+
if (type === Fragment) type = React.Fragment;
|
|
39
|
+
const config = key !== undefined ? { ...props, key } : (props ?? {});
|
|
40
|
+
const ce = React.createElement as (...args: unknown[]) => unknown;
|
|
41
|
+
return ce(type, config);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const jsx = create;
|
|
45
|
+
export const jsxs = create;
|
|
46
|
+
export const jsxDEV = create;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2026 Afonso Jorge Ramos
|
|
3
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// npm-style `import ... from "react-dom"` resolves here, forwarding to the
|
|
7
|
+
// client's own ReactDOM. Live bindings populated at capture time rather than
|
|
8
|
+
// init-time snapshots — see react-shim.ts for why a snapshot here freezes
|
|
9
|
+
// undefined for the whole session.
|
|
10
|
+
|
|
11
|
+
import { warn } from "../logger.ts";
|
|
12
|
+
import { onWebpackCaptured } from "../webpack/index.ts";
|
|
13
|
+
import { onFallbackRecovery, ReactDOM } from "./React.ts";
|
|
14
|
+
|
|
15
|
+
const RD = ReactDOM as any;
|
|
16
|
+
|
|
17
|
+
export default ReactDOM;
|
|
18
|
+
|
|
19
|
+
export let createPortal: any;
|
|
20
|
+
export let createRoot: any;
|
|
21
|
+
export let findDOMNode: any;
|
|
22
|
+
export let flushSync: any;
|
|
23
|
+
export let hydrate: any;
|
|
24
|
+
export let hydrateRoot: any;
|
|
25
|
+
export let render: any;
|
|
26
|
+
export let unmountComponentAtNode: any;
|
|
27
|
+
export let version: any;
|
|
28
|
+
|
|
29
|
+
function populate() {
|
|
30
|
+
createPortal = RD.createPortal;
|
|
31
|
+
createRoot = RD.createRoot;
|
|
32
|
+
findDOMNode = RD.findDOMNode;
|
|
33
|
+
flushSync = RD.flushSync;
|
|
34
|
+
hydrate = RD.hydrate;
|
|
35
|
+
hydrateRoot = RD.hydrateRoot;
|
|
36
|
+
render = RD.render;
|
|
37
|
+
unmountComponentAtNode = RD.unmountComponentAtNode;
|
|
38
|
+
version = RD.version;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
onWebpackCaptured(() => {
|
|
42
|
+
populate();
|
|
43
|
+
if (typeof createRoot !== "function") {
|
|
44
|
+
warn(
|
|
45
|
+
"[stdlib] capture health: the client ReactDOM was not found in the webpack capture — " +
|
|
46
|
+
"named `react-dom` imports are degraded until the fallback loads",
|
|
47
|
+
);
|
|
48
|
+
onFallbackRecovery(populate);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2026 Afonso Jorge Ramos
|
|
3
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// npm-style `import ... from "react"` in module code resolves here (the kit
|
|
7
|
+
// build externalizes it to this runtime URL), so every module shares the
|
|
8
|
+
// client's React instance — hooks, context, and Component identity all match
|
|
9
|
+
// the renderer. The jsx runtime is stdlib-local too (./jsx-runtime.ts), so a
|
|
10
|
+
// built module's boot never depends on the network.
|
|
11
|
+
//
|
|
12
|
+
// Live bindings, not init-time snapshots: a `const X = R.X` taken while this
|
|
13
|
+
// file evaluates freezes whatever the lazy proxy returned at that moment —
|
|
14
|
+
// undefined, whenever anything evaluates the shim before the webpack capture
|
|
15
|
+
// (the Fragment bug, at 33x the blast radius). `export let` populated by the
|
|
16
|
+
// capture callback keeps the bindings live: consumers evaluate post-capture
|
|
17
|
+
// (the loader gates module loads on webpackLoaded) and read real values.
|
|
18
|
+
|
|
19
|
+
import { warn } from "../logger.ts";
|
|
20
|
+
import { onWebpackCaptured } from "../webpack/index.ts";
|
|
21
|
+
import { onFallbackRecovery, React } from "./React.ts";
|
|
22
|
+
|
|
23
|
+
const R = React as any;
|
|
24
|
+
|
|
25
|
+
export default React;
|
|
26
|
+
|
|
27
|
+
export let Children: any;
|
|
28
|
+
export let Component: any;
|
|
29
|
+
export let Fragment: any;
|
|
30
|
+
export let Profiler: any;
|
|
31
|
+
export let PureComponent: any;
|
|
32
|
+
export let StrictMode: any;
|
|
33
|
+
export let Suspense: any;
|
|
34
|
+
export let cloneElement: any;
|
|
35
|
+
export let createContext: any;
|
|
36
|
+
export let createElement: any;
|
|
37
|
+
export let createFactory: any;
|
|
38
|
+
export let createRef: any;
|
|
39
|
+
export let forwardRef: any;
|
|
40
|
+
export let isValidElement: any;
|
|
41
|
+
export let lazy: any;
|
|
42
|
+
export let memo: any;
|
|
43
|
+
export let startTransition: any;
|
|
44
|
+
export let useCallback: any;
|
|
45
|
+
export let useContext: any;
|
|
46
|
+
export let useDebugValue: any;
|
|
47
|
+
export let useDeferredValue: any;
|
|
48
|
+
export let useEffect: any;
|
|
49
|
+
export let useId: any;
|
|
50
|
+
export let useImperativeHandle: any;
|
|
51
|
+
export let useInsertionEffect: any;
|
|
52
|
+
export let useLayoutEffect: any;
|
|
53
|
+
export let useMemo: any;
|
|
54
|
+
export let useReducer: any;
|
|
55
|
+
export let useRef: any;
|
|
56
|
+
export let useState: any;
|
|
57
|
+
export let useSyncExternalStore: any;
|
|
58
|
+
export let useTransition: any;
|
|
59
|
+
export let version: any;
|
|
60
|
+
|
|
61
|
+
// Capture health (D3): a populate that comes up empty means the React
|
|
62
|
+
// needle missed the capture — the silent needle-drift failure after a client
|
|
63
|
+
// update. Say so where the manager can show it, and re-populate when the
|
|
64
|
+
// esm.sh fallback lands (L2) so named imports degrade to the fallback copy
|
|
65
|
+
// instead of staying frozen undefined.
|
|
66
|
+
function populate() {
|
|
67
|
+
Children = R.Children;
|
|
68
|
+
Component = R.Component;
|
|
69
|
+
Fragment = R.Fragment;
|
|
70
|
+
Profiler = R.Profiler;
|
|
71
|
+
PureComponent = R.PureComponent;
|
|
72
|
+
StrictMode = R.StrictMode;
|
|
73
|
+
Suspense = R.Suspense;
|
|
74
|
+
cloneElement = R.cloneElement;
|
|
75
|
+
createContext = R.createContext;
|
|
76
|
+
createElement = R.createElement;
|
|
77
|
+
createFactory = R.createFactory;
|
|
78
|
+
createRef = R.createRef;
|
|
79
|
+
forwardRef = R.forwardRef;
|
|
80
|
+
isValidElement = R.isValidElement;
|
|
81
|
+
lazy = R.lazy;
|
|
82
|
+
memo = R.memo;
|
|
83
|
+
startTransition = R.startTransition;
|
|
84
|
+
useCallback = R.useCallback;
|
|
85
|
+
useContext = R.useContext;
|
|
86
|
+
useDebugValue = R.useDebugValue;
|
|
87
|
+
useDeferredValue = R.useDeferredValue;
|
|
88
|
+
useEffect = R.useEffect;
|
|
89
|
+
useId = R.useId;
|
|
90
|
+
useImperativeHandle = R.useImperativeHandle;
|
|
91
|
+
useInsertionEffect = R.useInsertionEffect;
|
|
92
|
+
useLayoutEffect = R.useLayoutEffect;
|
|
93
|
+
useMemo = R.useMemo;
|
|
94
|
+
useReducer = R.useReducer;
|
|
95
|
+
useRef = R.useRef;
|
|
96
|
+
useState = R.useState;
|
|
97
|
+
useSyncExternalStore = R.useSyncExternalStore;
|
|
98
|
+
useTransition = R.useTransition;
|
|
99
|
+
version = R.version;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
onWebpackCaptured(() => {
|
|
103
|
+
populate();
|
|
104
|
+
if (typeof createElement !== "function") {
|
|
105
|
+
warn(
|
|
106
|
+
"[stdlib] capture health: the client React was not found in the webpack capture — " +
|
|
107
|
+
"named `react` imports are degraded until the fallback loads (needle drift after a Spotify update?)",
|
|
108
|
+
);
|
|
109
|
+
onFallbackRecovery(() => {
|
|
110
|
+
populate();
|
|
111
|
+
if (typeof createElement === "function") {
|
|
112
|
+
warn("[stdlib] capture health: named `react` imports recovered via the fallback copy");
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2024 Delusoire
|
|
3
|
+
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// warn logs to the console and, when the loader's diagnostics buffer exists,
|
|
7
|
+
// records the entry so management UIs can surface drift without the
|
|
8
|
+
// devtools console. stdlib never creates the buffer — the loader owns it.
|
|
9
|
+
const DIAGNOSTICS_CAP = 200;
|
|
10
|
+
|
|
11
|
+
const safeString = (value: unknown): string => {
|
|
12
|
+
try {
|
|
13
|
+
return String(value);
|
|
14
|
+
} catch {
|
|
15
|
+
return "[unprintable]";
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const warn = (...args: unknown[]): void => {
|
|
20
|
+
console.warn(...args);
|
|
21
|
+
// Same cap the loader enforces; this is a public barrel export, so it
|
|
22
|
+
// must neither grow the buffer unboundedly nor throw on hostile args.
|
|
23
|
+
try {
|
|
24
|
+
const buffer = (
|
|
25
|
+
globalThis as never as {
|
|
26
|
+
__SPICETIFY_DIAGNOSTICS__?: Array<{ ts: number; level: string; message: string }>;
|
|
27
|
+
}
|
|
28
|
+
).__SPICETIFY_DIAGNOSTICS__;
|
|
29
|
+
if (!buffer) return;
|
|
30
|
+
buffer.push({ ts: Date.now(), level: "warn", message: args.map(safeString).join(" ") });
|
|
31
|
+
if (buffer.length > DIAGNOSTICS_CAP) buffer.splice(0, buffer.length - DIAGNOSTICS_CAP);
|
|
32
|
+
} catch {
|
|
33
|
+
// Never break the caller for a diagnostics write.
|
|
34
|
+
}
|
|
35
|
+
};
|