Egendefinerte tema-slots: lagre opptil 6 temaer for gjenbruk
Rad 1: 8 forhåndsdefinerte presets (faste) Rad 2: opptil 6 bruker-slots — klikk + for å lagre gjeldende tema, klikk for å bruke, høyreklikk for å slette. Lagres i workspace-metadata.savedThemes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ebcacf4847
commit
a2fc8609d4
2 changed files with 66 additions and 0 deletions
|
|
@ -42,6 +42,10 @@
|
||||||
theme?: ThemeConfig;
|
theme?: ThemeConfig;
|
||||||
/** Callback when theme changes */
|
/** Callback when theme changes */
|
||||||
onThemeChange?: (theme: ThemeConfig) => void;
|
onThemeChange?: (theme: ThemeConfig) => void;
|
||||||
|
/** User's saved theme slots (up to 6) */
|
||||||
|
savedThemes?: ThemeConfig[];
|
||||||
|
/** Callback when saved themes change */
|
||||||
|
onSavedThemesChange?: (themes: ThemeConfig[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
|
|
@ -57,6 +61,8 @@
|
||||||
activeTraits,
|
activeTraits,
|
||||||
theme = DEFAULT_THEME,
|
theme = DEFAULT_THEME,
|
||||||
onThemeChange,
|
onThemeChange,
|
||||||
|
savedThemes = [],
|
||||||
|
onSavedThemesChange,
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
const isPersonalWorkspace = $derived(!collectionId);
|
const isPersonalWorkspace = $derived(!collectionId);
|
||||||
|
|
@ -266,6 +272,19 @@
|
||||||
onThemeChange?.(preset);
|
onThemeChange?.(preset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MAX_SAVED = 6;
|
||||||
|
|
||||||
|
function saveCurrentTheme() {
|
||||||
|
if (savedThemes.length >= MAX_SAVED) return;
|
||||||
|
const newSaved = [...savedThemes, { ...theme }];
|
||||||
|
onSavedThemesChange?.(newSaved);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteSavedTheme(index: number) {
|
||||||
|
const newSaved = savedThemes.filter((_, i) => i !== index);
|
||||||
|
onSavedThemesChange?.(newSaved);
|
||||||
|
}
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
// Click outside
|
// Click outside
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
|
|
@ -498,6 +517,29 @@
|
||||||
</button>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="settings-presets settings-user-themes">
|
||||||
|
{#each savedThemes as saved, i (i)}
|
||||||
|
<button
|
||||||
|
class="settings-preset settings-saved-preset"
|
||||||
|
title="Lagret tema {i + 1} (høyreklikk for å slette)"
|
||||||
|
onclick={() => applyPreset(saved)}
|
||||||
|
oncontextmenu={(e) => { e.preventDefault(); deleteSavedTheme(i); }}
|
||||||
|
>
|
||||||
|
<span class="settings-preset-swatch"
|
||||||
|
style:background={presetAccentCSS(saved)}
|
||||||
|
>{i + 1}</span>
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
{#if savedThemes.length < MAX_SAVED}
|
||||||
|
<button
|
||||||
|
class="settings-preset settings-save-btn"
|
||||||
|
title="Lagre gjeldende tema"
|
||||||
|
onclick={saveCurrentTheme}
|
||||||
|
>
|
||||||
|
<span class="settings-preset-swatch settings-save-swatch">+</span>
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Surface selector + sliders -->
|
<!-- Surface selector + sliders -->
|
||||||
<div class="settings-color-group">
|
<div class="settings-color-group">
|
||||||
|
|
@ -811,6 +853,18 @@
|
||||||
}
|
}
|
||||||
.settings-preset:hover { border-color: var(--color-accent, #6366f1); }
|
.settings-preset:hover { border-color: var(--color-accent, #6366f1); }
|
||||||
.settings-preset-swatch { width: 28px; height: 28px; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 14px; }
|
.settings-preset-swatch { width: 28px; height: 28px; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 14px; }
|
||||||
|
.settings-user-themes { margin-top: 4px; }
|
||||||
|
.settings-saved-preset .settings-preset-swatch { font-size: 11px; font-weight: 600; color: white; text-shadow: 0 1px 2px rgba(0,0,0,0.5); }
|
||||||
|
.settings-save-swatch {
|
||||||
|
background: transparent !important;
|
||||||
|
border: 2px dashed var(--color-border, #2a2a2e);
|
||||||
|
color: var(--color-text-dim, #5a5a66);
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.settings-save-btn:hover .settings-save-swatch {
|
||||||
|
border-color: var(--color-accent, #6366f1);
|
||||||
|
color: var(--color-text-muted, #8a8a96);
|
||||||
|
}
|
||||||
|
|
||||||
/* Per-color controls */
|
/* Per-color controls */
|
||||||
.settings-color-group { margin-bottom: 8px; }
|
.settings-color-group { margin-bottom: 8px; }
|
||||||
|
|
|
||||||
|
|
@ -87,6 +87,9 @@
|
||||||
if (typeof meta.gridEnabled === 'boolean') {
|
if (typeof meta.gridEnabled === 'boolean') {
|
||||||
gridEnabled = meta.gridEnabled;
|
gridEnabled = meta.gridEnabled;
|
||||||
}
|
}
|
||||||
|
if (Array.isArray(meta.savedThemes)) {
|
||||||
|
savedThemes = meta.savedThemes as ThemeConfig[];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
|
@ -110,6 +113,7 @@
|
||||||
let layoutInitialized = $state(false);
|
let layoutInitialized = $state(false);
|
||||||
let savedCamera = $state<Camera>({ x: 0, y: 0, zoom: 1.0 });
|
let savedCamera = $state<Camera>({ x: 0, y: 0, zoom: 1.0 });
|
||||||
let gridEnabled = $state(false);
|
let gridEnabled = $state(false);
|
||||||
|
let savedThemes = $state<ThemeConfig[]>([]);
|
||||||
|
|
||||||
// When workspace node appears in store (after creation), load its layout
|
// When workspace node appears in store (after creation), load its layout
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
|
|
@ -156,6 +160,7 @@
|
||||||
workspace_layout: layout,
|
workspace_layout: layout,
|
||||||
camera: savedCamera,
|
camera: savedCamera,
|
||||||
gridEnabled,
|
gridEnabled,
|
||||||
|
savedThemes,
|
||||||
preferences: {
|
preferences: {
|
||||||
...(currentMeta.preferences ?? {}),
|
...(currentMeta.preferences ?? {}),
|
||||||
theme: themeToMetadata(currentTheme),
|
theme: themeToMetadata(currentTheme),
|
||||||
|
|
@ -246,6 +251,11 @@
|
||||||
persistMetadata();
|
persistMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleSavedThemesChange(themes: ThemeConfig[]) {
|
||||||
|
savedThemes = themes;
|
||||||
|
persistMetadata();
|
||||||
|
}
|
||||||
|
|
||||||
function loadThemeFromMeta(meta: Record<string, unknown>) {
|
function loadThemeFromMeta(meta: Record<string, unknown>) {
|
||||||
const loaded = loadThemeFromMetadata(meta);
|
const loaded = loadThemeFromMetadata(meta);
|
||||||
if (loaded) {
|
if (loaded) {
|
||||||
|
|
@ -347,6 +357,8 @@
|
||||||
activeTraits={activeLayoutTraits}
|
activeTraits={activeLayoutTraits}
|
||||||
theme={currentTheme}
|
theme={currentTheme}
|
||||||
onThemeChange={handleThemeChange}
|
onThemeChange={handleThemeChange}
|
||||||
|
{savedThemes}
|
||||||
|
onSavedThemesChange={handleSavedThemesChange}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Main content -->
|
<!-- Main content -->
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue