fix: resolve text settings not applied

This commit is contained in:
2026-02-04 18:41:43 +05:00
parent 0dd397818a
commit cc6fae9a55
5 changed files with 87 additions and 34 deletions
+15 -20
View File
@@ -1,8 +1,10 @@
<script lang="ts"> <script lang="ts">
import Button from "$components/ui/Button.svelte"; import Button from "$components/ui/Button.svelte";
import type { TextAlign, TextItem } from "$lib/types/panel";
import { textSettingsStore, updateAllTextSettings } from "$stores/panelStore";
interface Props { interface Props {
onTextAdd: (text: string) => void; onTextAdd: (text: string, settings?: Partial<TextItem>) => void;
onTextUpdate: (id: string, text: string) => void; onTextUpdate: (id: string, text: string) => void;
onTextDelete: (id: string) => void; onTextDelete: (id: string) => void;
texts: Array<{ id: string; text: string }>; texts: Array<{ id: string; text: string }>;
@@ -13,15 +15,8 @@
let newText = $state(""); let newText = $state("");
let errorMessage = $state<string | undefined>(undefined); let errorMessage = $state<string | undefined>(undefined);
// Общие настройки текста для всех панелей // Use reactive store for text settings
let commonTextSettings = $state({ let commonTextSettings = $derived($textSettingsStore);
fontSize: 18,
fontFamily: "Arial",
color: "#ffffff",
textAlign: "left",
paddingX: 10,
verticalOffset: 0,
});
// Список доступных шрифтов // Список доступных шрифтов
const availableFonts = [ const availableFonts = [
@@ -48,7 +43,7 @@
try { try {
errorMessage = undefined; errorMessage = undefined;
onTextAdd(newText.trim()); onTextAdd(newText.trim(), commonTextSettings);
newText = ""; newText = "";
} catch (error) { } catch (error) {
errorMessage = error instanceof Error ? error.message : "Ошибка добавления текста"; errorMessage = error instanceof Error ? error.message : "Ошибка добавления текста";
@@ -133,7 +128,7 @@
step="1" step="1"
value={commonTextSettings.fontSize} value={commonTextSettings.fontSize}
oninput={(e: Event) => { oninput={(e: Event) => {
commonTextSettings.fontSize = parseInt((e.target as HTMLInputElement).value); updateAllTextSettings({ fontSize: parseInt((e.target as HTMLInputElement).value) });
}} }}
/> />
<span class="value-display">{commonTextSettings.fontSize}px</span> <span class="value-display">{commonTextSettings.fontSize}px</span>
@@ -146,7 +141,7 @@
<select <select
value={commonTextSettings.fontFamily} value={commonTextSettings.fontFamily}
onchange={(e) => { onchange={(e) => {
commonTextSettings.fontFamily = (e.target as HTMLSelectElement).value; updateAllTextSettings({ fontFamily: (e.target as HTMLSelectElement).value });
}} }}
> >
{#each availableFonts as font} {#each availableFonts as font}
@@ -163,7 +158,7 @@
type="color" type="color"
value={commonTextSettings.color} value={commonTextSettings.color}
oninput={(e) => { oninput={(e) => {
commonTextSettings.color = (e.target as HTMLInputElement).value; updateAllTextSettings({ color: (e.target as HTMLInputElement).value });
}} }}
/> />
</label> </label>
@@ -176,7 +171,7 @@
<button <button
class="align-btn {commonTextSettings.textAlign === 'left' ? 'active' : ''}" class="align-btn {commonTextSettings.textAlign === 'left' ? 'active' : ''}"
onclick={() => { onclick={() => {
commonTextSettings.textAlign = "left"; updateAllTextSettings({ textAlign: "left" as TextAlign });
}} }}
aria-label="Выровнять по левому краю" aria-label="Выровнять по левому краю"
> >
@@ -185,7 +180,7 @@
<button <button
class="align-btn {commonTextSettings.textAlign === 'center' ? 'active' : ''}" class="align-btn {commonTextSettings.textAlign === 'center' ? 'active' : ''}"
onclick={() => { onclick={() => {
commonTextSettings.textAlign = "center"; updateAllTextSettings({ textAlign: "center" as TextAlign });
}} }}
aria-label="Выровнять по центру" aria-label="Выровнять по центру"
> >
@@ -194,7 +189,7 @@
<button <button
class="align-btn {commonTextSettings.textAlign === 'right' ? 'active' : ''}" class="align-btn {commonTextSettings.textAlign === 'right' ? 'active' : ''}"
onclick={() => { onclick={() => {
commonTextSettings.textAlign = "right"; updateAllTextSettings({ textAlign: "right" as TextAlign });
}} }}
aria-label="Выровнять по правому краю" aria-label="Выровнять по правому краю"
> >
@@ -214,7 +209,7 @@
step="1" step="1"
value={commonTextSettings.paddingX} value={commonTextSettings.paddingX}
oninput={(e) => { oninput={(e) => {
commonTextSettings.paddingX = parseInt((e.target as HTMLInputElement).value); updateAllTextSettings({ paddingX: parseInt((e.target as HTMLInputElement).value) });
}} }}
/> />
<span class="value-display">{commonTextSettings.paddingX}px</span> <span class="value-display">{commonTextSettings.paddingX}px</span>
@@ -231,11 +226,11 @@
step="1" step="1"
value={commonTextSettings.verticalOffset} value={commonTextSettings.verticalOffset}
oninput={(e) => { oninput={(e) => {
commonTextSettings.verticalOffset = parseInt((e.target as HTMLInputElement).value); updateAllTextSettings({ verticalOffset: parseInt((e.target as HTMLInputElement).value) });
}} }}
/> />
<span class="value-display" <span class="value-display"
>{commonTextSettings.verticalOffset > 0 ? "+" : ""}{commonTextSettings.verticalOffset}px</span >{(commonTextSettings.verticalOffset ?? 0) > 0 ? "+" : ""}{commonTextSettings.verticalOffset ?? 0}px</span
> >
</label> </label>
</div> </div>
+2 -1
View File
@@ -1,9 +1,10 @@
<script lang="ts"> <script lang="ts">
import type { TextItem } from "$lib/types/panel";
import TextManager from "./TextManager.svelte"; import TextManager from "./TextManager.svelte";
interface Props { interface Props {
texts: Array<{ id: string; text: string }>; texts: Array<{ id: string; text: string }>;
onTextAdd: (text: string) => void; onTextAdd: (text: string, settings?: Partial<TextItem>) => void;
onTextUpdate: (id: string, text: string) => void; onTextUpdate: (id: string, text: string) => void;
onTextDelete: (id: string) => void; onTextDelete: (id: string) => void;
} }
+36 -3
View File
@@ -1,9 +1,10 @@
<script lang="ts"> <script lang="ts">
import type { Panel } from "$lib/types/panel"; import type { Panel, TextItem } from "$lib/types/panel";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { exportService } from "../services/exportService"; import { exportService } from "../services/exportService";
import { imageService } from "../services/imageService"; import { imageService } from "../services/imageService";
import { panelService } from "../services/panelService"; import { panelService } from "../services/panelService";
import { textSettingsStore } from "../stores/panelStore";
import AppContainer from "../components/layout/AppContainer.svelte"; import AppContainer from "../components/layout/AppContainer.svelte";
@@ -12,6 +13,38 @@
let texts = $state<Array<{ id: string; text: string }>>([]); let texts = $state<Array<{ id: string; text: string }>>([]);
let backgroundImage = $state<string | undefined>(undefined); let backgroundImage = $state<string | undefined>(undefined);
// Create a derived store for text settings to avoid cyclic dependencies
let textSettings = $derived($textSettingsStore);
// Listen for text settings changes and update all panels
let previousSettings = $state<string>("");
$effect(() => {
// Only depend on the derived settings value
const currentSettings = textSettings;
const settingsString = JSON.stringify(currentSettings);
// Only update if settings actually changed
if (settingsString !== previousSettings) {
console.log("[SETTINGS] Applying text settings to all panels:", currentSettings);
// Update panels with new settings
const updatedPanels = panels.map((panel) => ({
...panel,
text: { ...panel.text, ...currentSettings },
}));
// Use a microtask to avoid synchronous update issues
Promise.resolve().then(() => {
if (panels.length > 0) {
panels = updatedPanels;
}
});
previousSettings = settingsString;
}
});
onMount(async () => { onMount(async () => {
// Initialize with default texts for common Twitch panels // Initialize with default texts for common Twitch panels
console.log("[INIT] Creating default texts..."); console.log("[INIT] Creating default texts...");
@@ -62,9 +95,9 @@
imageService.handleCropCancel(); imageService.handleCropCancel();
} }
function handleAddText(text: string) { function handleAddText(text: string, settings?: Partial<TextItem>) {
texts = panelService.addText(texts, text); texts = panelService.addText(texts, text);
panels = panelService.updatePanelsFromTexts(texts, panels, backgroundImage!); panels = panelService.updatePanelsFromTexts(texts, panels, backgroundImage!, settings);
} }
function handleUpdateText(id: string, newText: string) { function handleUpdateText(id: string, newText: string) {
+8 -3
View File
@@ -1,4 +1,4 @@
import type { Panel } from "../lib/types/panel"; import type { Panel, TextItem } from "../lib/types/panel";
import { createPanelFromText, updatePanelText } from "../stores/panelStore"; import { createPanelFromText, updatePanelText } from "../stores/panelStore";
export class PanelService { export class PanelService {
@@ -43,13 +43,18 @@ export class PanelService {
return texts.filter((t) => t.id !== id); return texts.filter((t) => t.id !== id);
} }
updatePanelsFromTexts(texts: Array<{ id: string; text: string }>, panels: Panel[], backgroundImage: string): Panel[] { updatePanelsFromTexts(
texts: Array<{ id: string; text: string }>,
panels: Panel[],
backgroundImage: string,
textSettings?: Partial<TextItem>,
): Panel[] {
return texts.map((textItem) => { return texts.map((textItem) => {
const existingPanel = panels.find((p) => p.text.text === textItem.text); const existingPanel = panels.find((p) => p.text.text === textItem.text);
if (existingPanel) { if (existingPanel) {
return updatePanelText(existingPanel, textItem.text); return updatePanelText(existingPanel, textItem.text);
} }
return createPanelFromText(backgroundImage, textItem.text); return createPanelFromText(backgroundImage, textItem.text, 100, textSettings);
}); });
} }
+26 -7
View File
@@ -4,6 +4,20 @@ import { type Panel, type TextItem } from "../lib/types/panel";
export const panelStore: Writable<Panel | undefined> = writable(undefined); export const panelStore: Writable<Panel | undefined> = writable(undefined);
// Store for common text settings that should apply to all texts
export const textSettingsStore = writable<Partial<TextItem>>({
fontSize: 18,
fontFamily: "Arial",
color: "#ffffff",
textAlign: "left",
paddingX: 10,
verticalOffset: 0,
});
export const updateAllTextSettings = (settings: Partial<TextItem>) => {
textSettingsStore.update((current) => ({ ...current, ...settings }));
};
// Panel creation should go through panelService.updatePanelsFromTexts() // Panel creation should go through panelService.updatePanelsFromTexts()
// These functions are kept for backward compatibility but should be avoided // These functions are kept for backward compatibility but should be avoided
export const createEmptyPanel = (height: number = 100): Panel => { export const createEmptyPanel = (height: number = 100): Panel => {
@@ -48,16 +62,21 @@ export const updateTextProperties = (panel: Panel, updates: Partial<TextItem>):
}); });
}; };
export const createPanelFromText = (backgroundImage: string, text: string, height: number = 100): Panel => { export const createPanelFromText = (
backgroundImage: string,
text: string,
height: number = 100,
textSettings?: Partial<TextItem>,
): Panel => {
const newText: TextItem = { const newText: TextItem = {
id: uuidv4(), id: uuidv4(),
text, text,
fontSize: 18, fontSize: textSettings?.fontSize ?? 18,
fontFamily: "Arial", fontFamily: textSettings?.fontFamily ?? "Arial",
color: "#ffffff", color: textSettings?.color ?? "#ffffff",
textAlign: "center", textAlign: textSettings?.textAlign ?? "center",
paddingX: 10, paddingX: textSettings?.paddingX ?? 10,
verticalOffset: 0, verticalOffset: textSettings?.verticalOffset ?? 0,
}; };
return { return {