strapi-plugin-radiocult-uploader 1.0.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/LICENSE +21 -0
- package/README.md +129 -0
- package/dist/admin/App-Cd-TZrqC.mjs +1179 -0
- package/dist/admin/App-DTOuR7aY.js +1199 -0
- package/dist/admin/Settings-B3cZR7VF.mjs +516 -0
- package/dist/admin/Settings-DHv9SmRU.js +534 -0
- package/dist/admin/en-CTCtUg23.mjs +8 -0
- package/dist/admin/en-D_61ojL7.js +8 -0
- package/dist/admin/index-BLAtUo_j.js +334 -0
- package/dist/admin/index-Dj3e3A79.mjs +318 -0
- package/dist/admin/index.js +4 -0
- package/dist/admin/index.mjs +4 -0
- package/dist/admin/src/api.d.ts +107 -0
- package/dist/admin/src/components/Initializer.d.ts +5 -0
- package/dist/admin/src/components/Panel.d.ts +18 -0
- package/dist/admin/src/components/PluginIcon.d.ts +2 -0
- package/dist/admin/src/index.d.ts +3 -0
- package/dist/admin/src/pages/App.d.ts +2 -0
- package/dist/admin/src/pages/HomePage.d.ts +6 -0
- package/dist/admin/src/pages/Settings.d.ts +8 -0
- package/dist/admin/src/pages/Uploader.d.ts +10 -0
- package/dist/admin/src/pluginId.d.ts +1 -0
- package/dist/admin/src/theme/tokens.d.ts +96 -0
- package/dist/admin/src/utils/getTranslation.d.ts +2 -0
- package/dist/admin/tokens-ACCekCH6.mjs +161 -0
- package/dist/admin/tokens-CDeNcvZq.js +178 -0
- package/dist/server/index.js +1193 -0
- package/dist/server/index.mjs +1165 -0
- package/dist/server/src/bootstrap.d.ts +5 -0
- package/dist/server/src/config/index.d.ts +17 -0
- package/dist/server/src/content-types/index.d.ts +51 -0
- package/dist/server/src/content-types/track-record/schema.json.d.ts +29 -0
- package/dist/server/src/controllers/index.d.ts +21 -0
- package/dist/server/src/controllers/settings.d.ts +16 -0
- package/dist/server/src/controllers/uploader.d.ts +10 -0
- package/dist/server/src/controllers/utils.d.ts +16 -0
- package/dist/server/src/destroy.d.ts +5 -0
- package/dist/server/src/index.d.ts +267 -0
- package/dist/server/src/middlewares/index.d.ts +2 -0
- package/dist/server/src/policies/index.d.ts +2 -0
- package/dist/server/src/register.d.ts +5 -0
- package/dist/server/src/routes/admin/index.d.ts +22 -0
- package/dist/server/src/routes/index.d.ts +19 -0
- package/dist/server/src/services/id3.d.ts +18 -0
- package/dist/server/src/services/index.d.ts +156 -0
- package/dist/server/src/services/mapping.d.ts +47 -0
- package/dist/server/src/services/radiocult.d.ts +115 -0
- package/dist/server/src/services/records.d.ts +25 -0
- package/dist/server/src/services/settings.d.ts +103 -0
- package/dist/server/src/utils/errors.d.ts +5 -0
- package/dist/server/src/utils/plugin.d.ts +4 -0
- package/logo.png +0 -0
- package/package.json +98 -0
- package/scripts/migrate-from-fields.mjs +78 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { ThemeSettings } from './theme/tokens';
|
|
2
|
+
export type PlatformKey = 'mixcloud' | 'soundcloud';
|
|
3
|
+
export declare const PLATFORMS: PlatformKey[];
|
|
4
|
+
export declare const PLATFORM_LABEL: Record<PlatformKey, string>;
|
|
5
|
+
export interface PlatformStatus {
|
|
6
|
+
state: 'not_published' | 'publishing' | 'linked' | 'stalled';
|
|
7
|
+
requestedAt: string | null;
|
|
8
|
+
link: string | null;
|
|
9
|
+
}
|
|
10
|
+
export interface RcStatus {
|
|
11
|
+
enabled: boolean;
|
|
12
|
+
trackId: string | null;
|
|
13
|
+
uploadedAt: string | null;
|
|
14
|
+
platforms: Record<PlatformKey, PlatformStatus>;
|
|
15
|
+
rateLimitMessage?: string | null;
|
|
16
|
+
details: {
|
|
17
|
+
title: string;
|
|
18
|
+
showName: string;
|
|
19
|
+
artist: string;
|
|
20
|
+
album: string;
|
|
21
|
+
description: string;
|
|
22
|
+
genres: string[];
|
|
23
|
+
image: {
|
|
24
|
+
name: string;
|
|
25
|
+
previewUrl: string;
|
|
26
|
+
usable: boolean;
|
|
27
|
+
} | null;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export interface ContentTypeInfo {
|
|
31
|
+
uid: string;
|
|
32
|
+
displayName: string;
|
|
33
|
+
attributes: {
|
|
34
|
+
name: string;
|
|
35
|
+
type: string;
|
|
36
|
+
target: string | null;
|
|
37
|
+
targetAttributes: string[] | null;
|
|
38
|
+
}[];
|
|
39
|
+
}
|
|
40
|
+
export interface SettingsPayload {
|
|
41
|
+
credentials: {
|
|
42
|
+
stationId: string;
|
|
43
|
+
enabled: boolean;
|
|
44
|
+
apiKeySet: boolean;
|
|
45
|
+
};
|
|
46
|
+
mapping: Record<string, string | null>;
|
|
47
|
+
defaults: {
|
|
48
|
+
artist: string;
|
|
49
|
+
description: string;
|
|
50
|
+
};
|
|
51
|
+
theme: ThemeSettings;
|
|
52
|
+
envLocks: {
|
|
53
|
+
apiKey: boolean;
|
|
54
|
+
stationId: boolean;
|
|
55
|
+
enabled: boolean;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export declare const apiError: (error: any) => string;
|
|
59
|
+
export declare const fetchStatus: (documentId: string, refresh?: boolean) => Promise<RcStatus>;
|
|
60
|
+
export declare const publishTrack: (documentId: string, platform: PlatformKey, body: {
|
|
61
|
+
title: string;
|
|
62
|
+
tags: string[];
|
|
63
|
+
description: string;
|
|
64
|
+
}, force?: boolean) => Promise<unknown>;
|
|
65
|
+
export declare const saveTrackDetails: (documentId: string, meta: {
|
|
66
|
+
title: string;
|
|
67
|
+
artist: string;
|
|
68
|
+
album: string;
|
|
69
|
+
}) => Promise<unknown>;
|
|
70
|
+
export declare const fetchUiConfig: () => Promise<{
|
|
71
|
+
theme: ThemeSettings;
|
|
72
|
+
contentType: string | null;
|
|
73
|
+
}>;
|
|
74
|
+
export declare const fetchSettings: () => Promise<SettingsPayload>;
|
|
75
|
+
export declare const saveSettings: (patch: Record<string, unknown>) => Promise<SettingsPayload>;
|
|
76
|
+
export declare const testConnection: () => Promise<{
|
|
77
|
+
ok: boolean;
|
|
78
|
+
message: string;
|
|
79
|
+
}>;
|
|
80
|
+
export declare const fetchContentTypes: () => Promise<ContentTypeInfo[]>;
|
|
81
|
+
export declare const fetchDiagnostics: () => Promise<{
|
|
82
|
+
nodeVersion: string;
|
|
83
|
+
openAsBlob: boolean;
|
|
84
|
+
bodyMaxFileSize: number | null;
|
|
85
|
+
bodyLimitOk: boolean | null;
|
|
86
|
+
cronEnabled: boolean;
|
|
87
|
+
}>;
|
|
88
|
+
export interface UploadCallbacks {
|
|
89
|
+
onProgress: (pct: number | null) => void;
|
|
90
|
+
onDone: (body: {
|
|
91
|
+
trackId?: string;
|
|
92
|
+
warnings?: string[];
|
|
93
|
+
}) => void;
|
|
94
|
+
onConflict: (message: string) => void;
|
|
95
|
+
onError: (message: string, status?: number) => void;
|
|
96
|
+
onAbort: () => void;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Uploads the audio file with progress reporting. Returns the XHR so the
|
|
100
|
+
* caller can abort. `token` comes from useAuth — fetch() has no upload
|
|
101
|
+
* progress events, so this one call is a raw XHR.
|
|
102
|
+
*/
|
|
103
|
+
export declare const uploadTrack: (documentId: string, file: File, meta: {
|
|
104
|
+
title: string;
|
|
105
|
+
artist: string;
|
|
106
|
+
album: string;
|
|
107
|
+
}, force: boolean, token: string, callbacks: UploadCallbacks) => XMLHttpRequest;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edit-view side panel: shows where the entry has got to (uploaded /
|
|
3
|
+
* publishing / linked) and opens the uploader page. Only rendered on the
|
|
4
|
+
* content type configured in the plugin settings.
|
|
5
|
+
*/
|
|
6
|
+
import * as React from 'react';
|
|
7
|
+
/**
|
|
8
|
+
* PanelComponent for content-manager's addEditViewSidePanel. Called as a
|
|
9
|
+
* plain function during render — it must not use hooks itself; everything
|
|
10
|
+
* stateful lives in the components above.
|
|
11
|
+
*/
|
|
12
|
+
export declare const RadioCultPanel: (props: {
|
|
13
|
+
model: string;
|
|
14
|
+
documentId?: string;
|
|
15
|
+
}) => {
|
|
16
|
+
title: string;
|
|
17
|
+
content: React.JSX.Element;
|
|
18
|
+
} | null;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin settings page: Radio Cult credentials, content-type field mapping,
|
|
3
|
+
* publish defaults, uploader theme (preset + font + colour overrides + logo) and
|
|
4
|
+
* environment diagnostics.
|
|
5
|
+
*/
|
|
6
|
+
import * as React from 'react';
|
|
7
|
+
declare const Settings: () => React.JSX.Element;
|
|
8
|
+
export default Settings;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Episode Uploader page: step 1 edit track details, step 2 upload the
|
|
3
|
+
* audio to Radio Cult (with real progress), step 3 publish to Mixcloud /
|
|
4
|
+
* SoundCloud, plus a terminal-style activity log. Rendered inside the admin
|
|
5
|
+
* at /admin/plugins/radiocult-uploader/upload/:documentId, themed via the
|
|
6
|
+
* plugin settings (CSS custom properties on the wrapper).
|
|
7
|
+
*/
|
|
8
|
+
import * as React from 'react';
|
|
9
|
+
declare const Uploader: () => React.JSX.Element;
|
|
10
|
+
export default Uploader;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PLUGIN_ID = "radiocult-uploader";
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Uploader page theme system. A theme is a flat map of colour tokens; presets
|
|
3
|
+
* provide complete maps and the settings page lets users override a small,
|
|
4
|
+
* curated subset of them (see EDITABLE_TOKENS) plus the font, logo and station
|
|
5
|
+
* name. Tokens are applied as CSS custom properties (--rcu-*) on the uploader
|
|
6
|
+
* page wrapper.
|
|
7
|
+
*/
|
|
8
|
+
export interface ThemeTokens {
|
|
9
|
+
/** Page background gradient, light end. */
|
|
10
|
+
pageBg: string;
|
|
11
|
+
/** Page background gradient, dark end. */
|
|
12
|
+
pageBgDeep: string;
|
|
13
|
+
/** Main text / primary UI colour. */
|
|
14
|
+
text: string;
|
|
15
|
+
/** Secondary text. */
|
|
16
|
+
textDim: string;
|
|
17
|
+
/** Gradient partner for text on headings and buttons. */
|
|
18
|
+
contrast: string;
|
|
19
|
+
/** Card background. */
|
|
20
|
+
card: string;
|
|
21
|
+
/** Card / input border. */
|
|
22
|
+
cardBorder: string;
|
|
23
|
+
/** Input background. */
|
|
24
|
+
inputBg: string;
|
|
25
|
+
/** Sunken areas (log background, progress track). */
|
|
26
|
+
sunk: string;
|
|
27
|
+
/** Success. */
|
|
28
|
+
green: string;
|
|
29
|
+
/** Warning / in-progress. */
|
|
30
|
+
amber: string;
|
|
31
|
+
/** Error / destructive. */
|
|
32
|
+
red: string;
|
|
33
|
+
/** Mixcloud accent. */
|
|
34
|
+
mixcloud: string;
|
|
35
|
+
/** SoundCloud accent. */
|
|
36
|
+
soundcloud: string;
|
|
37
|
+
/** Text on filled buttons and step badges. */
|
|
38
|
+
buttonText: string;
|
|
39
|
+
/** Strong shadow colour (with alpha). */
|
|
40
|
+
shadow: string;
|
|
41
|
+
/** Soft shadow / focus ring colour (with alpha). */
|
|
42
|
+
shadowSoft: string;
|
|
43
|
+
/** Vinyl record groove colours. */
|
|
44
|
+
vinylDark: string;
|
|
45
|
+
vinylMid: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The only tokens the settings page exposes. Deliberately short — everything
|
|
49
|
+
* else follows the preset — and deliberately all plain hex, so every one of
|
|
50
|
+
* them gets a real colour picker rather than a read-only swatch.
|
|
51
|
+
*/
|
|
52
|
+
export declare const EDITABLE_TOKENS: {
|
|
53
|
+
key: keyof ThemeTokens;
|
|
54
|
+
label: string;
|
|
55
|
+
hint: string;
|
|
56
|
+
}[];
|
|
57
|
+
export declare const EDITABLE_TOKEN_KEYS: (keyof ThemeTokens)[];
|
|
58
|
+
/** Drops any stored override that is no longer user-editable. */
|
|
59
|
+
export declare const pickEditableOverrides: (overrides: Partial<ThemeTokens> | null | undefined) => Partial<ThemeTokens>;
|
|
60
|
+
export interface FontOption {
|
|
61
|
+
label: string;
|
|
62
|
+
/** CSS font-family stack. */
|
|
63
|
+
stack: string;
|
|
64
|
+
/** Google Fonts css2 `family=` value, or null for the system stack. */
|
|
65
|
+
google: string | null;
|
|
66
|
+
}
|
|
67
|
+
/** System default plus a handful of Google Fonts, loaded on demand. */
|
|
68
|
+
export declare const FONTS: Record<string, FontOption>;
|
|
69
|
+
export declare const DEFAULT_FONT = "system";
|
|
70
|
+
export declare const resolveFont: (theme: Partial<ThemeSettings> | null | undefined) => FontOption;
|
|
71
|
+
/** Google Fonts stylesheet URL for a font, or null when none is needed. */
|
|
72
|
+
export declare const googleFontHref: (font: FontOption) => string | null;
|
|
73
|
+
/**
|
|
74
|
+
* Loads a Google Fonts stylesheet on demand. Links are keyed by href and left
|
|
75
|
+
* in <head> once added, so switching back and forth in settings is instant and
|
|
76
|
+
* nothing is fetched twice.
|
|
77
|
+
*/
|
|
78
|
+
export declare const useGoogleFont: (href: string | null) => void;
|
|
79
|
+
export declare const PRESETS: Record<string, {
|
|
80
|
+
label: string;
|
|
81
|
+
tokens: ThemeTokens;
|
|
82
|
+
}>;
|
|
83
|
+
export interface ThemeSettings {
|
|
84
|
+
preset: string;
|
|
85
|
+
overrides: Partial<ThemeTokens>;
|
|
86
|
+
/** Key into FONTS. */
|
|
87
|
+
fontFamily: string;
|
|
88
|
+
logoUrl: string | null;
|
|
89
|
+
stationName: string;
|
|
90
|
+
}
|
|
91
|
+
/** Effective tokens: preset (falling back to paper) + user overrides. */
|
|
92
|
+
export declare const resolveTokens: (theme: Partial<ThemeSettings> | null | undefined) => ThemeTokens;
|
|
93
|
+
/** CSS custom properties for the uploader wrapper. */
|
|
94
|
+
export declare const tokensToCssVars: (tokens: ThemeTokens) => Record<string, string>;
|
|
95
|
+
/** Colour vars plus the font var, ready to spread onto a wrapper's style. */
|
|
96
|
+
export declare const themeToCssVars: (theme: Partial<ThemeSettings> | null | undefined) => Record<string, string>;
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
const EDITABLE_TOKENS = [
|
|
3
|
+
{ key: "pageBg", label: "Background", hint: "Top of the page gradient" },
|
|
4
|
+
{ key: "pageBgDeep", label: "Background (gradient end)", hint: "Bottom of the page gradient" },
|
|
5
|
+
{ key: "text", label: "Text", hint: "Headings, body text and filled buttons" },
|
|
6
|
+
{ key: "textDim", label: "Secondary text", hint: "Hints, labels and captions" },
|
|
7
|
+
{ key: "contrast", label: "Accent", hint: "Second colour in heading and button gradients" },
|
|
8
|
+
{ key: "buttonText", label: "Button text", hint: "Text on filled buttons and step badges" }
|
|
9
|
+
];
|
|
10
|
+
const EDITABLE_TOKEN_KEYS = EDITABLE_TOKENS.map((t) => t.key);
|
|
11
|
+
const pickEditableOverrides = (overrides) => Object.fromEntries(
|
|
12
|
+
Object.entries(overrides ?? {}).filter(
|
|
13
|
+
([key]) => EDITABLE_TOKEN_KEYS.includes(key)
|
|
14
|
+
)
|
|
15
|
+
);
|
|
16
|
+
const SYSTEM_STACK = `-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif`;
|
|
17
|
+
const FONTS = {
|
|
18
|
+
system: { label: "System default", stack: SYSTEM_STACK, google: null },
|
|
19
|
+
inter: {
|
|
20
|
+
label: "Inter",
|
|
21
|
+
stack: `'Inter', ${SYSTEM_STACK}`,
|
|
22
|
+
google: "Inter:wght@400;600;700;800"
|
|
23
|
+
},
|
|
24
|
+
"dm-sans": {
|
|
25
|
+
label: "DM Sans",
|
|
26
|
+
stack: `'DM Sans', ${SYSTEM_STACK}`,
|
|
27
|
+
google: "DM+Sans:wght@400;500;700"
|
|
28
|
+
},
|
|
29
|
+
poppins: {
|
|
30
|
+
label: "Poppins",
|
|
31
|
+
stack: `'Poppins', ${SYSTEM_STACK}`,
|
|
32
|
+
google: "Poppins:wght@400;600;700;800"
|
|
33
|
+
},
|
|
34
|
+
"space-grotesk": {
|
|
35
|
+
label: "Space Grotesk",
|
|
36
|
+
stack: `'Space Grotesk', ${SYSTEM_STACK}`,
|
|
37
|
+
google: "Space+Grotesk:wght@400;500;700"
|
|
38
|
+
},
|
|
39
|
+
"work-sans": {
|
|
40
|
+
label: "Work Sans",
|
|
41
|
+
stack: `'Work Sans', ${SYSTEM_STACK}`,
|
|
42
|
+
google: "Work+Sans:wght@400;600;700;800"
|
|
43
|
+
},
|
|
44
|
+
"libre-franklin": {
|
|
45
|
+
label: "Libre Franklin",
|
|
46
|
+
stack: `'Libre Franklin', ${SYSTEM_STACK}`,
|
|
47
|
+
google: "Libre+Franklin:wght@400;600;700;800"
|
|
48
|
+
},
|
|
49
|
+
"playfair-display": {
|
|
50
|
+
label: "Playfair Display (serif)",
|
|
51
|
+
stack: `'Playfair Display', Georgia, serif`,
|
|
52
|
+
google: "Playfair+Display:wght@400;600;700;800"
|
|
53
|
+
},
|
|
54
|
+
"ibm-plex-mono": {
|
|
55
|
+
label: "IBM Plex Mono",
|
|
56
|
+
stack: `'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace`,
|
|
57
|
+
google: "IBM+Plex+Mono:wght@400;500;700"
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const DEFAULT_FONT = "system";
|
|
61
|
+
const resolveFont = (theme) => FONTS[theme?.fontFamily ?? DEFAULT_FONT] ?? FONTS[DEFAULT_FONT];
|
|
62
|
+
const googleFontHref = (font) => font.google ? `https://fonts.googleapis.com/css2?family=${font.google}&display=swap` : null;
|
|
63
|
+
const useGoogleFont = (href) => {
|
|
64
|
+
React.useEffect(() => {
|
|
65
|
+
if (!href || document.querySelector(`link[data-rcu-font][href="${href}"]`)) return;
|
|
66
|
+
const link = document.createElement("link");
|
|
67
|
+
link.rel = "stylesheet";
|
|
68
|
+
link.href = href;
|
|
69
|
+
link.setAttribute("data-rcu-font", "");
|
|
70
|
+
document.head.appendChild(link);
|
|
71
|
+
}, [href]);
|
|
72
|
+
};
|
|
73
|
+
const paper = {
|
|
74
|
+
pageBg: "#C0B7AF",
|
|
75
|
+
pageBgDeep: "#ADA39B",
|
|
76
|
+
text: "#322824",
|
|
77
|
+
textDim: "#5c524d",
|
|
78
|
+
contrast: "#000000",
|
|
79
|
+
card: "rgba(255, 255, 255, 0.26)",
|
|
80
|
+
cardBorder: "rgba(50, 40, 36, 0.22)",
|
|
81
|
+
inputBg: "rgba(255, 255, 255, 0.42)",
|
|
82
|
+
sunk: "rgba(50, 40, 36, 0.09)",
|
|
83
|
+
green: "#3f6b52",
|
|
84
|
+
amber: "#8a6a24",
|
|
85
|
+
red: "#8f3a2f",
|
|
86
|
+
mixcloud: "#3f6266",
|
|
87
|
+
soundcloud: "#8f5a2f",
|
|
88
|
+
buttonText: "#C0B7AF",
|
|
89
|
+
shadow: "rgba(50, 40, 36, 0.3)",
|
|
90
|
+
shadowSoft: "rgba(50, 40, 36, 0.16)",
|
|
91
|
+
vinylDark: "#1b1512",
|
|
92
|
+
vinylMid: "#2f2521"
|
|
93
|
+
};
|
|
94
|
+
const midnight = {
|
|
95
|
+
pageBg: "#150b2a",
|
|
96
|
+
pageBgDeep: "#0d0620",
|
|
97
|
+
text: "#f0e9fb",
|
|
98
|
+
textDim: "#9d92b8",
|
|
99
|
+
contrast: "#ff2e88",
|
|
100
|
+
card: "rgba(255, 255, 255, 0.05)",
|
|
101
|
+
cardBorder: "rgba(240, 233, 251, 0.16)",
|
|
102
|
+
inputBg: "rgba(255, 255, 255, 0.07)",
|
|
103
|
+
sunk: "rgba(0, 0, 0, 0.3)",
|
|
104
|
+
green: "#4fd08a",
|
|
105
|
+
amber: "#e8bb54",
|
|
106
|
+
red: "#ff5c4d",
|
|
107
|
+
mixcloud: "#52aab4",
|
|
108
|
+
soundcloud: "#ff7733",
|
|
109
|
+
buttonText: "#150b2a",
|
|
110
|
+
shadow: "rgba(0, 0, 0, 0.5)",
|
|
111
|
+
shadowSoft: "rgba(255, 46, 136, 0.25)",
|
|
112
|
+
vinylDark: "#07030f",
|
|
113
|
+
vinylMid: "#1e1433"
|
|
114
|
+
};
|
|
115
|
+
const slate = {
|
|
116
|
+
pageBg: "#dde3e8",
|
|
117
|
+
pageBgDeep: "#c6cfd6",
|
|
118
|
+
text: "#26313a",
|
|
119
|
+
textDim: "#5b6873",
|
|
120
|
+
contrast: "#0a5c8a",
|
|
121
|
+
card: "rgba(255, 255, 255, 0.45)",
|
|
122
|
+
cardBorder: "rgba(38, 49, 58, 0.2)",
|
|
123
|
+
inputBg: "rgba(255, 255, 255, 0.6)",
|
|
124
|
+
sunk: "rgba(38, 49, 58, 0.08)",
|
|
125
|
+
green: "#2e7d4f",
|
|
126
|
+
amber: "#9a6d1c",
|
|
127
|
+
red: "#a63a2e",
|
|
128
|
+
mixcloud: "#28707a",
|
|
129
|
+
soundcloud: "#b35f2a",
|
|
130
|
+
buttonText: "#eef2f5",
|
|
131
|
+
shadow: "rgba(38, 49, 58, 0.28)",
|
|
132
|
+
shadowSoft: "rgba(38, 49, 58, 0.14)",
|
|
133
|
+
vinylDark: "#161b1f",
|
|
134
|
+
vinylMid: "#2b343b"
|
|
135
|
+
};
|
|
136
|
+
const PRESETS = {
|
|
137
|
+
paper: { label: "Paper (light)", tokens: paper },
|
|
138
|
+
midnight: { label: "Midnight (dark)", tokens: midnight },
|
|
139
|
+
slate: { label: "Slate (light)", tokens: slate }
|
|
140
|
+
};
|
|
141
|
+
const resolveTokens = (theme) => ({
|
|
142
|
+
...(PRESETS[theme?.preset ?? "paper"] ?? PRESETS.paper).tokens,
|
|
143
|
+
...pickEditableOverrides(theme?.overrides)
|
|
144
|
+
});
|
|
145
|
+
const tokensToCssVars = (tokens) => Object.fromEntries(Object.entries(tokens).map(([key, value]) => [`--rcu-${key}`, value]));
|
|
146
|
+
const themeToCssVars = (theme) => ({
|
|
147
|
+
...tokensToCssVars(resolveTokens(theme)),
|
|
148
|
+
"--rcu-fontFamily": resolveFont(theme).stack
|
|
149
|
+
});
|
|
150
|
+
export {
|
|
151
|
+
EDITABLE_TOKENS as E,
|
|
152
|
+
FONTS as F,
|
|
153
|
+
PRESETS as P,
|
|
154
|
+
resolveTokens as a,
|
|
155
|
+
tokensToCssVars as b,
|
|
156
|
+
googleFontHref as g,
|
|
157
|
+
pickEditableOverrides as p,
|
|
158
|
+
resolveFont as r,
|
|
159
|
+
themeToCssVars as t,
|
|
160
|
+
useGoogleFont as u
|
|
161
|
+
};
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const React = require("react");
|
|
3
|
+
function _interopNamespace(e) {
|
|
4
|
+
if (e && e.__esModule) return e;
|
|
5
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
6
|
+
if (e) {
|
|
7
|
+
for (const k in e) {
|
|
8
|
+
if (k !== "default") {
|
|
9
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
10
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
get: () => e[k]
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
n.default = e;
|
|
18
|
+
return Object.freeze(n);
|
|
19
|
+
}
|
|
20
|
+
const React__namespace = /* @__PURE__ */ _interopNamespace(React);
|
|
21
|
+
const EDITABLE_TOKENS = [
|
|
22
|
+
{ key: "pageBg", label: "Background", hint: "Top of the page gradient" },
|
|
23
|
+
{ key: "pageBgDeep", label: "Background (gradient end)", hint: "Bottom of the page gradient" },
|
|
24
|
+
{ key: "text", label: "Text", hint: "Headings, body text and filled buttons" },
|
|
25
|
+
{ key: "textDim", label: "Secondary text", hint: "Hints, labels and captions" },
|
|
26
|
+
{ key: "contrast", label: "Accent", hint: "Second colour in heading and button gradients" },
|
|
27
|
+
{ key: "buttonText", label: "Button text", hint: "Text on filled buttons and step badges" }
|
|
28
|
+
];
|
|
29
|
+
const EDITABLE_TOKEN_KEYS = EDITABLE_TOKENS.map((t) => t.key);
|
|
30
|
+
const pickEditableOverrides = (overrides) => Object.fromEntries(
|
|
31
|
+
Object.entries(overrides ?? {}).filter(
|
|
32
|
+
([key]) => EDITABLE_TOKEN_KEYS.includes(key)
|
|
33
|
+
)
|
|
34
|
+
);
|
|
35
|
+
const SYSTEM_STACK = `-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif`;
|
|
36
|
+
const FONTS = {
|
|
37
|
+
system: { label: "System default", stack: SYSTEM_STACK, google: null },
|
|
38
|
+
inter: {
|
|
39
|
+
label: "Inter",
|
|
40
|
+
stack: `'Inter', ${SYSTEM_STACK}`,
|
|
41
|
+
google: "Inter:wght@400;600;700;800"
|
|
42
|
+
},
|
|
43
|
+
"dm-sans": {
|
|
44
|
+
label: "DM Sans",
|
|
45
|
+
stack: `'DM Sans', ${SYSTEM_STACK}`,
|
|
46
|
+
google: "DM+Sans:wght@400;500;700"
|
|
47
|
+
},
|
|
48
|
+
poppins: {
|
|
49
|
+
label: "Poppins",
|
|
50
|
+
stack: `'Poppins', ${SYSTEM_STACK}`,
|
|
51
|
+
google: "Poppins:wght@400;600;700;800"
|
|
52
|
+
},
|
|
53
|
+
"space-grotesk": {
|
|
54
|
+
label: "Space Grotesk",
|
|
55
|
+
stack: `'Space Grotesk', ${SYSTEM_STACK}`,
|
|
56
|
+
google: "Space+Grotesk:wght@400;500;700"
|
|
57
|
+
},
|
|
58
|
+
"work-sans": {
|
|
59
|
+
label: "Work Sans",
|
|
60
|
+
stack: `'Work Sans', ${SYSTEM_STACK}`,
|
|
61
|
+
google: "Work+Sans:wght@400;600;700;800"
|
|
62
|
+
},
|
|
63
|
+
"libre-franklin": {
|
|
64
|
+
label: "Libre Franklin",
|
|
65
|
+
stack: `'Libre Franklin', ${SYSTEM_STACK}`,
|
|
66
|
+
google: "Libre+Franklin:wght@400;600;700;800"
|
|
67
|
+
},
|
|
68
|
+
"playfair-display": {
|
|
69
|
+
label: "Playfair Display (serif)",
|
|
70
|
+
stack: `'Playfair Display', Georgia, serif`,
|
|
71
|
+
google: "Playfair+Display:wght@400;600;700;800"
|
|
72
|
+
},
|
|
73
|
+
"ibm-plex-mono": {
|
|
74
|
+
label: "IBM Plex Mono",
|
|
75
|
+
stack: `'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace`,
|
|
76
|
+
google: "IBM+Plex+Mono:wght@400;500;700"
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
const DEFAULT_FONT = "system";
|
|
80
|
+
const resolveFont = (theme) => FONTS[theme?.fontFamily ?? DEFAULT_FONT] ?? FONTS[DEFAULT_FONT];
|
|
81
|
+
const googleFontHref = (font) => font.google ? `https://fonts.googleapis.com/css2?family=${font.google}&display=swap` : null;
|
|
82
|
+
const useGoogleFont = (href) => {
|
|
83
|
+
React__namespace.useEffect(() => {
|
|
84
|
+
if (!href || document.querySelector(`link[data-rcu-font][href="${href}"]`)) return;
|
|
85
|
+
const link = document.createElement("link");
|
|
86
|
+
link.rel = "stylesheet";
|
|
87
|
+
link.href = href;
|
|
88
|
+
link.setAttribute("data-rcu-font", "");
|
|
89
|
+
document.head.appendChild(link);
|
|
90
|
+
}, [href]);
|
|
91
|
+
};
|
|
92
|
+
const paper = {
|
|
93
|
+
pageBg: "#C0B7AF",
|
|
94
|
+
pageBgDeep: "#ADA39B",
|
|
95
|
+
text: "#322824",
|
|
96
|
+
textDim: "#5c524d",
|
|
97
|
+
contrast: "#000000",
|
|
98
|
+
card: "rgba(255, 255, 255, 0.26)",
|
|
99
|
+
cardBorder: "rgba(50, 40, 36, 0.22)",
|
|
100
|
+
inputBg: "rgba(255, 255, 255, 0.42)",
|
|
101
|
+
sunk: "rgba(50, 40, 36, 0.09)",
|
|
102
|
+
green: "#3f6b52",
|
|
103
|
+
amber: "#8a6a24",
|
|
104
|
+
red: "#8f3a2f",
|
|
105
|
+
mixcloud: "#3f6266",
|
|
106
|
+
soundcloud: "#8f5a2f",
|
|
107
|
+
buttonText: "#C0B7AF",
|
|
108
|
+
shadow: "rgba(50, 40, 36, 0.3)",
|
|
109
|
+
shadowSoft: "rgba(50, 40, 36, 0.16)",
|
|
110
|
+
vinylDark: "#1b1512",
|
|
111
|
+
vinylMid: "#2f2521"
|
|
112
|
+
};
|
|
113
|
+
const midnight = {
|
|
114
|
+
pageBg: "#150b2a",
|
|
115
|
+
pageBgDeep: "#0d0620",
|
|
116
|
+
text: "#f0e9fb",
|
|
117
|
+
textDim: "#9d92b8",
|
|
118
|
+
contrast: "#ff2e88",
|
|
119
|
+
card: "rgba(255, 255, 255, 0.05)",
|
|
120
|
+
cardBorder: "rgba(240, 233, 251, 0.16)",
|
|
121
|
+
inputBg: "rgba(255, 255, 255, 0.07)",
|
|
122
|
+
sunk: "rgba(0, 0, 0, 0.3)",
|
|
123
|
+
green: "#4fd08a",
|
|
124
|
+
amber: "#e8bb54",
|
|
125
|
+
red: "#ff5c4d",
|
|
126
|
+
mixcloud: "#52aab4",
|
|
127
|
+
soundcloud: "#ff7733",
|
|
128
|
+
buttonText: "#150b2a",
|
|
129
|
+
shadow: "rgba(0, 0, 0, 0.5)",
|
|
130
|
+
shadowSoft: "rgba(255, 46, 136, 0.25)",
|
|
131
|
+
vinylDark: "#07030f",
|
|
132
|
+
vinylMid: "#1e1433"
|
|
133
|
+
};
|
|
134
|
+
const slate = {
|
|
135
|
+
pageBg: "#dde3e8",
|
|
136
|
+
pageBgDeep: "#c6cfd6",
|
|
137
|
+
text: "#26313a",
|
|
138
|
+
textDim: "#5b6873",
|
|
139
|
+
contrast: "#0a5c8a",
|
|
140
|
+
card: "rgba(255, 255, 255, 0.45)",
|
|
141
|
+
cardBorder: "rgba(38, 49, 58, 0.2)",
|
|
142
|
+
inputBg: "rgba(255, 255, 255, 0.6)",
|
|
143
|
+
sunk: "rgba(38, 49, 58, 0.08)",
|
|
144
|
+
green: "#2e7d4f",
|
|
145
|
+
amber: "#9a6d1c",
|
|
146
|
+
red: "#a63a2e",
|
|
147
|
+
mixcloud: "#28707a",
|
|
148
|
+
soundcloud: "#b35f2a",
|
|
149
|
+
buttonText: "#eef2f5",
|
|
150
|
+
shadow: "rgba(38, 49, 58, 0.28)",
|
|
151
|
+
shadowSoft: "rgba(38, 49, 58, 0.14)",
|
|
152
|
+
vinylDark: "#161b1f",
|
|
153
|
+
vinylMid: "#2b343b"
|
|
154
|
+
};
|
|
155
|
+
const PRESETS = {
|
|
156
|
+
paper: { label: "Paper (light)", tokens: paper },
|
|
157
|
+
midnight: { label: "Midnight (dark)", tokens: midnight },
|
|
158
|
+
slate: { label: "Slate (light)", tokens: slate }
|
|
159
|
+
};
|
|
160
|
+
const resolveTokens = (theme) => ({
|
|
161
|
+
...(PRESETS[theme?.preset ?? "paper"] ?? PRESETS.paper).tokens,
|
|
162
|
+
...pickEditableOverrides(theme?.overrides)
|
|
163
|
+
});
|
|
164
|
+
const tokensToCssVars = (tokens) => Object.fromEntries(Object.entries(tokens).map(([key, value]) => [`--rcu-${key}`, value]));
|
|
165
|
+
const themeToCssVars = (theme) => ({
|
|
166
|
+
...tokensToCssVars(resolveTokens(theme)),
|
|
167
|
+
"--rcu-fontFamily": resolveFont(theme).stack
|
|
168
|
+
});
|
|
169
|
+
exports.EDITABLE_TOKENS = EDITABLE_TOKENS;
|
|
170
|
+
exports.FONTS = FONTS;
|
|
171
|
+
exports.PRESETS = PRESETS;
|
|
172
|
+
exports.googleFontHref = googleFontHref;
|
|
173
|
+
exports.pickEditableOverrides = pickEditableOverrides;
|
|
174
|
+
exports.resolveFont = resolveFont;
|
|
175
|
+
exports.resolveTokens = resolveTokens;
|
|
176
|
+
exports.themeToCssVars = themeToCssVars;
|
|
177
|
+
exports.tokensToCssVars = tokensToCssVars;
|
|
178
|
+
exports.useGoogleFont = useGoogleFont;
|