diff --git a/src/lib/components/theme/ThemeEditorWindow.svelte b/src/lib/components/theme/ThemeEditorWindow.svelte index cafacab..edde8ed 100644 --- a/src/lib/components/theme/ThemeEditorWindow.svelte +++ b/src/lib/components/theme/ThemeEditorWindow.svelte @@ -3343,7 +3343,8 @@ async function openPublishDialog() { if (exporting || saving || publishing) return; - if (!$currentUser) { + const user = $currentUser; + if (!user) { sileo.error({ title: 'Authentication Required', description: @@ -3361,8 +3362,12 @@ return; } - if (dirty) { - await saveTheme({ notify: false }); + const authorIdChanged = definition.authorId !== user.id; + if (authorIdChanged) definition.authorId = user.id; + + if (dirty || authorIdChanged) { + const saved = await saveTheme({ notify: false }); + if (!saved) return; } publishThemeId = definition.workshopId ?? ''; diff --git a/src/lib/themeV2.ts b/src/lib/themeV2.ts index 504c1eb..497764b 100644 --- a/src/lib/themeV2.ts +++ b/src/lib/themeV2.ts @@ -646,6 +646,8 @@ export type ThemeV2Definition = { version: string; apiVersion: '2.0'; author: string; + /** EZPPFarm user ID of the publisher, written when the theme is published. */ + authorId?: number; description?: string; /** Workshop discovery tags for this theme. */ tags?: string[]; @@ -755,6 +757,11 @@ const normalizeWorkshopId = (raw: unknown): string | undefined => { return WORKSHOP_ID_PATTERN.test(id) ? id : undefined; }; +const normalizeAuthorId = (raw: unknown): number | undefined => { + if (typeof raw !== 'number' || !Number.isSafeInteger(raw) || raw <= 0) return undefined; + return raw; +}; + const clampInteger = (value: unknown, fallback: number, min: number, max: number) => Math.round(clamp(value, fallback, min, max)); @@ -1913,6 +1920,7 @@ export const normalizeThemeV2 = (raw: Partial): ThemeV2Defini version: text(raw.version, '1.0.0'), apiVersion: '2.0', author: text(raw.author, 'Unknown'), + authorId: normalizeAuthorId(raw.authorId), description: text(raw.description), tags: normalizeThemeTags(raw.tags), workshopId: normalizeWorkshopId(raw.workshopId), diff --git a/src/lib/themes.ts b/src/lib/themes.ts index 4fb7d0e..0570227 100644 --- a/src/lib/themes.ts +++ b/src/lib/themes.ts @@ -16,6 +16,8 @@ export type Theme = { version: string; apiVersion: string; author: string; + /** EZPPFarm user ID stored in the manifest when the theme is published. */ + authorId?: number; description: string; nsfw: boolean; scriptUrl: string; @@ -44,6 +46,7 @@ export type ThemeInfo = { apiVersion?: string; workshopId?: string; author?: string; + authorId?: number; description?: string; nsfw?: boolean; entry?: string; @@ -173,6 +176,12 @@ const themeFromFolder = async ( version: parsed.version ?? '1.0.0', apiVersion, author: parsed.author ?? 'Unknown', + authorId: + typeof parsed.authorId === 'number' && + Number.isSafeInteger(parsed.authorId) && + parsed.authorId > 0 + ? parsed.authorId + : undefined, description: parsed.description ?? '', nsfw: parsed.nsfw === true, scriptUrl, @@ -523,6 +532,7 @@ export const reloadThemes = async () => { preview: theme.preview, version: theme.version, author: theme.author, + authorId: theme.authorId, apiVersion: theme.apiVersion, description: theme.description, nsfw: theme.nsfw, diff --git a/src/screens/Launch.svelte b/src/screens/Launch.svelte index e866717..4ae5d33 100644 --- a/src/screens/Launch.svelte +++ b/src/screens/Launch.svelte @@ -422,6 +422,14 @@ return ownedWorkshopThemeNames.includes(theme.name.trim().toLowerCase()); }; + const themeAuthorAvatarUrl = (theme: Theme) => { + if (theme.authorId && theme.authorId > 0) { + return `https://a.ez-pp.farm/${theme.authorId}`; + } + const author = theme.author.trim(); + return author ? `https://a.ez-pp.farm/${encodeURIComponent(author)}` : undefined; + }; + onMount(() => { let loadedUserId: number | undefined; return currentUser.subscribe((user) => { @@ -4073,9 +4081,7 @@ class="size-5 shrink-0 border border-white/20 bg-theme-800" >