fix: theme editor layout positioning

This commit is contained in:
Moyasee
2025-11-07 20:12:50 +02:00
parent 154b6271a1
commit b6bbf05da6
7 changed files with 116 additions and 54 deletions

View File

@@ -95,6 +95,7 @@ import "./themes/toggle-custom-theme";
import "./themes/copy-theme-achievement-sound";
import "./themes/remove-theme-achievement-sound";
import "./themes/get-theme-sound-path";
import "./themes/get-theme-sound-data-url";
import "./themes/import-theme-sound-from-store";
import "./download-sources/remove-download-source";
import "./download-sources/get-download-sources";

View File

@@ -0,0 +1,38 @@
import { registerEvent } from "../register-event";
import { getThemeSoundPath } from "@main/helpers";
import fs from "node:fs";
import path from "node:path";
import { logger } from "@main/services";
const getThemeSoundDataUrl = async (
_event: Electron.IpcMainInvokeEvent,
themeId: string
): Promise<string | null> => {
try {
const soundPath = getThemeSoundPath(themeId);
if (!soundPath || !fs.existsSync(soundPath)) {
return null;
}
const buffer = await fs.promises.readFile(soundPath);
const ext = path.extname(soundPath).toLowerCase().slice(1);
const mimeTypes: Record<string, string> = {
mp3: "audio/mpeg",
wav: "audio/wav",
ogg: "audio/ogg",
m4a: "audio/mp4",
};
const mimeType = mimeTypes[ext] || "audio/mpeg";
const base64 = buffer.toString("base64");
return `data:${mimeType};base64,${base64}`;
} catch (error) {
logger.error("Failed to get theme sound data URL", error);
return null;
}
};
registerEvent("getThemeSoundDataUrl", getThemeSoundDataUrl);

View File

@@ -577,6 +577,8 @@ contextBridge.exposeInMainWorld("electron", {
ipcRenderer.invoke("removeThemeAchievementSound", themeId),
getThemeSoundPath: (themeId: string) =>
ipcRenderer.invoke("getThemeSoundPath", themeId),
getThemeSoundDataUrl: (themeId: string) =>
ipcRenderer.invoke("getThemeSoundDataUrl", themeId),
importThemeSoundFromStore: (
themeId: string,
themeName: string,

View File

@@ -416,6 +416,7 @@ declare global {
) => Promise<void>;
removeThemeAchievementSound: (themeId: string) => Promise<void>;
getThemeSoundPath: (themeId: string) => Promise<string | null>;
getThemeSoundDataUrl: (themeId: string) => Promise<string | null>;
importThemeSoundFromStore: (
themeId: string,
themeName: string,

View File

@@ -130,9 +130,11 @@ export const getAchievementSoundUrl = async (): Promise<string> => {
const activeTheme = await window.electron.getActiveCustomTheme();
if (activeTheme?.hasCustomSound) {
const soundPath = await window.electron.getThemeSoundPath(activeTheme.id);
if (soundPath) {
return `file://${soundPath}`;
const soundDataUrl = await window.electron.getThemeSoundDataUrl(
activeTheme.id
);
if (soundDataUrl) {
return soundDataUrl;
}
}
} catch (error) {

View File

@@ -47,6 +47,8 @@
position: relative;
border: 1px solid globals.$muted-color;
border-radius: 2px;
flex: 1;
min-width: 0;
}
&__footer {
@@ -80,7 +82,7 @@
}
&__info {
padding: 16px;
padding: 8px;
p {
font-size: 16px;
@@ -93,25 +95,39 @@
&__notification-preview {
padding-top: 12px;
display: flex;
flex-direction: column;
flex-direction: row;
gap: 16px;
align-items: flex-start;
&__select-variation {
flex: inherit;
}
}
&__notification-preview-controls {
display: flex;
flex-direction: column;
gap: 16px;
flex-shrink: 0;
}
&__notification-controls {
display: flex;
flex-direction: row;
align-items: center;
gap: 16px;
gap: 8px;
}
&__sound-controls {
display: flex;
flex-direction: row;
flex-direction: column;
gap: 8px;
flex-wrap: wrap;
width: fit-content;
button,
.button {
width: auto;
align-self: flex-start;
}
}
}

View File

@@ -213,57 +213,59 @@ export default function ThemeEditor() {
<div className="theme-editor__footer">
<CollapsedMenu title={t("notification_preview")}>
<div className="theme-editor__notification-preview">
<div className="theme-editor__notification-controls">
<SelectField
className="theme-editor__notification-preview__select-variation"
label={t("variation")}
options={Object.values(notificationVariations).map(
(variation) => {
return {
key: variation,
value: variation,
label: t(variation),
};
<div className="theme-editor__notification-preview-controls">
<div className="theme-editor__notification-controls">
<SelectField
className="theme-editor__notification-preview__select-variation"
label={t("variation")}
options={Object.values(notificationVariations).map(
(variation) => {
return {
key: variation,
value: variation,
label: t(variation),
};
}
)}
onChange={(value) =>
setNotificationVariation(
value.target.value as keyof typeof notificationVariations
)
}
)}
onChange={(value) =>
setNotificationVariation(
value.target.value as keyof typeof notificationVariations
)
}
/>
/>
<SelectField
label={t("alignment")}
value={notificationAlignment}
onChange={(e) =>
setNotificationAlignment(
e.target.value as AchievementCustomNotificationPosition
)
}
options={achievementCustomNotificationPositionOptions}
/>
</div>
<SelectField
label={t("alignment")}
value={notificationAlignment}
onChange={(e) =>
setNotificationAlignment(
e.target.value as AchievementCustomNotificationPosition
)
}
options={achievementCustomNotificationPositionOptions}
/>
</div>
<div className="theme-editor__sound-controls">
<Button theme="outline" onClick={handleSelectSound}>
<UploadIcon />
{theme?.hasCustomSound
? t("change_achievement_sound")
: t("select_achievement_sound")}
</Button>
{theme?.hasCustomSound && (
<Button theme="outline" onClick={handleRemoveSound}>
<TrashIcon />
{t("remove_achievement_sound")}
<div className="theme-editor__sound-controls">
<Button theme="outline" onClick={handleSelectSound}>
<UploadIcon />
{theme?.hasCustomSound
? t("change_achievement_sound")
: t("select_achievement_sound")}
</Button>
)}
<Button theme="outline" onClick={handlePreviewSound}>
<PlayIcon />
{t("preview_sound")}
</Button>
{theme?.hasCustomSound && (
<Button theme="outline" onClick={handleRemoveSound}>
<TrashIcon />
{t("remove_achievement_sound")}
</Button>
)}
<Button theme="outline" onClick={handlePreviewSound}>
<PlayIcon />
{t("preview_sound")}
</Button>
</div>
</div>
<div className="theme-editor__notification-preview-wrapper">