refactor(calendar): simplify settings from 41 to 18 persisted preferences

Remove unused header/dateStrip settings (hardcode defaults), move runtime
UI state (sidebar, tags, immersive mode) to non-persisted $state() variables,
and add localStorage migration for existing users.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-03-20 16:40:47 +01:00
parent 98d1d1cc90
commit 93e1c7de4a
9 changed files with 278 additions and 453 deletions

View file

@ -1,67 +1,26 @@
<script lang="ts">
import { viewStore } from '$lib/stores/view.svelte';
import { settingsStore } from '$lib/stores/settings.svelte';
import { format } from 'date-fns';
import { de } from 'date-fns/locale';
// Get weekday format string based on setting
function getWeekdayFormat(): string {
switch (settingsStore.headerWeekdayFormat) {
case 'full':
return 'EEEE';
case 'short':
return 'EEE';
case 'hidden':
return '';
}
}
// Get date format string based on settings
function getDateFormat(includeYear: boolean = true): string {
const parts: string[] = [];
// Weekday
const weekdayFormat = getWeekdayFormat();
if (weekdayFormat) {
parts.push(weekdayFormat);
}
// Date with optional month
if (settingsStore.headerShowDate) {
if (settingsStore.headerAlwaysShowMonth) {
parts.push(includeYear ? 'd.M. MMMM yyyy' : 'd.M.');
} else {
parts.push(includeYear ? 'd. MMMM yyyy' : 'd.');
}
} else if (includeYear) {
// Only month and year if date is hidden
parts.push('MMMM yyyy');
}
return parts.join(', ');
}
// Format title based on view type and settings
// Format title based on view type
let title = $derived.by(() => {
const date = viewStore.currentDate;
const rangeStart = viewStore.viewRange.start;
const rangeEnd = viewStore.viewRange.end;
// Helper to format date range
const formatRange = () => {
const showMonth = settingsStore.headerAlwaysShowMonth;
const startFormat = showMonth ? 'd.M.' : 'd.';
if (rangeStart.getMonth() === rangeEnd.getMonth()) {
return (
format(rangeStart, startFormat, { locale: de }) +
format(rangeStart, 'd.', { locale: de }) +
' - ' +
format(rangeEnd, showMonth ? 'd.M. MMMM yyyy' : 'd. MMMM yyyy', { locale: de })
format(rangeEnd, 'd. MMMM yyyy', { locale: de })
);
}
return (
format(rangeStart, showMonth ? 'd.M. MMM' : 'd. MMM', { locale: de }) +
format(rangeStart, 'd. MMM', { locale: de }) +
' - ' +
format(rangeEnd, showMonth ? 'd.M. MMM yyyy' : 'd. MMM yyyy', { locale: de })
format(rangeEnd, 'd. MMM yyyy', { locale: de })
);
};
@ -78,7 +37,7 @@
});
</script>
<header class="calendar-header" class:compact={settingsStore.headerCompact} role="banner">
<header class="calendar-header" role="banner">
<h1 class="header-title" aria-live="polite">{title}</h1>
</header>
@ -101,19 +60,4 @@
font-size: 1rem;
}
}
/* Compact variant */
.calendar-header.compact {
padding: 0.5rem 1rem;
}
.calendar-header.compact .header-title {
font-size: 1rem;
}
@media (max-width: 640px) {
.calendar-header.compact .header-title {
font-size: 0.875rem;
}
}
</style>

View file

@ -10,8 +10,6 @@
subDays,
startOfDay,
isWithinInterval,
getWeek,
startOfWeek,
} from 'date-fns';
import { de } from 'date-fns/locale';
import { onMount, tick } from 'svelte';
@ -64,17 +62,6 @@
return { significant: false, emoji: '' };
}
// Check if a date is the first day of the week (respects weekStartsOn setting)
function isFirstDayOfWeek(date: Date): boolean {
const weekStart = startOfWeek(date, { weekStartsOn: settingsStore.weekStartsOn });
return isSameDay(date, weekStart);
}
// Get week number for a date
function getWeekNumber(date: Date): number {
return getWeek(date, { weekStartsOn: settingsStore.weekStartsOn });
}
// Reactive view range - needed to trigger re-renders
let viewRange = $derived(viewStore.viewRange);
let currentDate = $derived(viewStore.currentDate);
@ -264,12 +251,8 @@
{@const isFirstOfMonth = day.getDate() === 1}
{@const moonPhase = isSignificantMoonPhase(day)}
{@const eventCount = getEventCount(day)}
{@const showWeekNumber = settingsStore.dateStripShowWeekNumbers && isFirstDayOfWeek(day)}
{#if isFirstOfMonth}
<div
class="month-divider"
class:show-line={settingsStore.dateStripShowMonthDividers}
></div>
<div class="month-divider show-line"></div>
{/if}
<button
class="day-item"
@ -283,15 +266,10 @@
onclick={() => handleDayClick(day)}
class:is-today={dayIsToday}
>
{#if showWeekNumber}
<span class="week-number-label">KW {getWeekNumber(day)}</span>
{/if}
{#if moonPhase.significant && settingsStore.dateStripShowMoonPhases}
<span class="moon-indicator">{moonPhase.emoji}</span>
{/if}
{#if settingsStore.dateStripShowWeekday}
<span class="day-weekday">{format(day, 'EE', { locale: de })}</span>
{/if}
<span class="day-weekday">{format(day, 'EE', { locale: de })}</span>
<span class="day-number">{format(day, 'd')}</span>
{#if eventCount > 0 && settingsStore.dateStripShowEventIndicators}
<div class="event-dots">
@ -463,20 +441,6 @@
line-height: 1;
}
.week-number-label {
position: absolute;
top: -14px;
left: 50%;
transform: translateX(-50%);
font-size: 0.5625rem;
font-weight: 600;
color: hsl(var(--color-muted-foreground));
white-space: nowrap;
pointer-events: none;
text-transform: uppercase;
letter-spacing: 0.02em;
}
.event-dots {
display: flex;
gap: 2px;
@ -611,11 +575,6 @@
top: -14px;
}
.week-number-label {
top: -12px;
font-size: 0.5rem;
}
.day-number {
font-size: 1rem;
}
@ -662,11 +621,6 @@
top: -12px;
}
.date-strip-wrapper.compact .week-number-label {
top: -10px;
font-size: 0.5rem;
}
.date-strip-wrapper.compact .month-divider {
height: 28px;
}

View file

@ -10,7 +10,7 @@
let { isToolbarExpanded = false, isMobile = false }: Props = $props();
function handleClick() {
settingsStore.set('dateStripCollapsed', false);
settingsStore.setDateStripCollapsed(false);
}
// Format current date for FAB display: "Dez 14"

View file

@ -224,7 +224,7 @@
{/if}
<!-- Layer 1: DateStrip -->
{#if unifiedBarStore.showDateStrip && !unifiedBarStore.legacyDateStripCollapsed}
{#if unifiedBarStore.showDateStrip}
<div
class="unified-bar-layer date-strip-layer"
style="z-index: {layerZIndices.date}; bottom: 70px;"

View file

@ -1,6 +1,5 @@
<script lang="ts">
import { viewStore } from '$lib/stores/view.svelte';
import { settingsStore } from '$lib/stores/settings.svelte';
import type { CalendarViewType } from '@calendar/shared';
interface Props {
isToolbarExpanded?: boolean;
@ -13,13 +12,6 @@
viewStore.setViewType(view);
}
// View labels (short versions for pill)
const viewLabels: Record<CalendarViewType, string> = {
week: '7',
month: 'M',
agenda: 'A',
};
// View titles for tooltip
const viewTitles: Record<CalendarViewType, string> = {
week: 'Wochenansicht',
@ -27,8 +19,7 @@
agenda: 'Agenda',
};
// Get enabled views from settings
let enabledViews = $derived(settingsStore.quickViewPillViews);
const enabledViews: CalendarViewType[] = ['week', 'month', 'agenda'];
</script>
<!-- svelte-ignore a11y_no_static_element_interactions -->

View file

@ -1,6 +1,5 @@
<script lang="ts">
import { viewStore } from '$lib/stores/view.svelte';
import { settingsStore } from '$lib/stores/settings.svelte';
import type { CalendarViewType } from '@calendar/shared';
interface Props {
/** Bottom offset from viewport bottom (default: '70px') */
@ -27,15 +26,12 @@
agenda: 'Agenda',
};
// Get enabled views from settings
let enabledViews = $derived(settingsStore.quickViewPillViews);
const enabledViews: CalendarViewType[] = ['week', 'month', 'agenda'];
// Get label for a view
function getViewLabel(view: CalendarViewType): string {
return viewLabels[view] || '';
}
// Get title for a view
function getViewTitle(view: CalendarViewType): string {
return viewTitles[view] || '';
}

View file

@ -12,7 +12,6 @@ import { userSettings } from './user-settings.svelte';
export type WeekStartDay = 0 | 1;
export type TimeFormat = '24h' | '12h';
export type AllDayDisplayMode = 'header' | 'block';
export type WeekdayFormat = 'full' | 'short' | 'hidden';
export type SttLanguage = 'de' | 'auto';
export interface CalendarAppSettings extends Record<string, unknown> {
@ -27,42 +26,13 @@ export interface CalendarAppSettings extends Record<string, unknown> {
dayEndHour: number;
allDayDisplayMode: AllDayDisplayMode;
// Header settings
headerCompact: boolean;
headerWeekdayFormat: WeekdayFormat;
headerShowDate: boolean;
headerAlwaysShowMonth: boolean;
// DateStrip settings
dateStripShowMoonPhases: boolean;
dateStripShowEventIndicators: boolean;
dateStripShowWeekday: boolean;
dateStripHighlightWeekends: boolean;
dateStripShowMonthDividers: boolean;
dateStripCompact: boolean;
dateStripShowWeekNumbers: boolean;
dateStripCollapsed: boolean;
// TagStrip settings
tagStripCollapsed: boolean;
selectedTagIds: string[];
// Immersive Mode settings
immersiveModeEnabled: boolean;
// Birthday settings
// Display settings
showBirthdays: boolean;
showBirthdayAge: boolean;
// Task settings
showTasksInCalendar: boolean;
// UI settings
sidebarCollapsed: boolean;
// Quick View Pill settings
quickViewPillViews: CalendarViewType[];
customDayCount: number;
dateStripShowMoonPhases: boolean;
dateStripShowEventIndicators: boolean;
dateStripHighlightWeekends: boolean;
dateStripCompact: boolean;
// Event defaults
defaultEventDuration: number;
@ -72,6 +42,14 @@ export interface CalendarAppSettings extends Record<string, unknown> {
sttLanguage: SttLanguage;
}
// --- UI state (not persisted, resets on page load) ---
let _dateStripCollapsed = $state(false);
let _tagStripCollapsed = $state(true);
let _selectedTagIds = $state<string[]>([]);
let _immersiveModeEnabled = $state(false);
let _showTasksInCalendar = $state(false);
let _sidebarCollapsed = $state(true);
const DEFAULT_SETTINGS: CalendarAppSettings = {
defaultView: 'week',
weekStartsOn: 1,
@ -82,27 +60,12 @@ const DEFAULT_SETTINGS: CalendarAppSettings = {
dayStartHour: 6,
dayEndHour: 20,
allDayDisplayMode: 'header',
headerCompact: false,
headerWeekdayFormat: 'full',
headerShowDate: true,
headerAlwaysShowMonth: false,
dateStripShowMoonPhases: true,
dateStripShowEventIndicators: true,
dateStripShowWeekday: true,
dateStripHighlightWeekends: true,
dateStripShowMonthDividers: true,
dateStripCompact: false,
dateStripShowWeekNumbers: false,
dateStripCollapsed: false,
tagStripCollapsed: true,
selectedTagIds: [],
immersiveModeEnabled: false,
showBirthdays: true,
showBirthdayAge: true,
showTasksInCalendar: false,
sidebarCollapsed: true,
quickViewPillViews: ['week', 'month', 'agenda'],
customDayCount: 30,
defaultEventDuration: 60,
defaultReminder: 15,
sttLanguage: 'de',
@ -131,11 +94,43 @@ const baseStore = createAppSettingsStore<CalendarAppSettings>(
}
);
// Always start with tasks/sidebar hidden when the app loads
// (don't persist this from previous sessions)
// Migrate: strip removed keys from localStorage to keep it clean
if (browser) {
baseStore.set('showTasksInCalendar', false);
baseStore.set('sidebarCollapsed', true);
try {
const raw = localStorage.getItem('calendar-settings');
if (raw) {
const stored = JSON.parse(raw);
const removedKeys = [
'headerCompact',
'headerWeekdayFormat',
'headerShowDate',
'headerAlwaysShowMonth',
'dateStripShowWeekday',
'dateStripShowMonthDividers',
'dateStripShowWeekNumbers',
'dateStripCollapsed',
'tagStripCollapsed',
'selectedTagIds',
'immersiveModeEnabled',
'showTasksInCalendar',
'sidebarCollapsed',
'quickViewPillViews',
'customDayCount',
];
let changed = false;
for (const key of removedKeys) {
if (key in stored) {
delete stored[key];
changed = true;
}
}
if (changed) {
localStorage.setItem('calendar-settings', JSON.stringify(stored));
}
}
} catch {
// ignore migration errors
}
}
// Load settings from cloud
@ -158,9 +153,8 @@ export const settingsStore = {
update: baseStore.update,
reset: baseStore.reset,
getDefaults: baseStore.getDefaults,
toggleImmersiveMode: baseStore.toggleImmersiveMode,
// Convenience getters
// Persisted preference getters
get defaultView() {
return baseStore.settings.defaultView;
},
@ -188,78 +182,30 @@ export const settingsStore = {
get allDayDisplayMode() {
return baseStore.settings.allDayDisplayMode;
},
get headerCompact() {
return baseStore.settings.headerCompact;
},
get headerWeekdayFormat() {
return baseStore.settings.headerWeekdayFormat;
},
get headerShowDate() {
return baseStore.settings.headerShowDate;
},
get headerAlwaysShowMonth() {
return baseStore.settings.headerAlwaysShowMonth;
},
get dateStripShowMoonPhases() {
return baseStore.settings.dateStripShowMoonPhases;
},
get dateStripShowEventIndicators() {
return baseStore.settings.dateStripShowEventIndicators;
},
get dateStripShowWeekday() {
return baseStore.settings.dateStripShowWeekday;
},
get dateStripHighlightWeekends() {
return baseStore.settings.dateStripHighlightWeekends;
},
get dateStripShowMonthDividers() {
return baseStore.settings.dateStripShowMonthDividers;
},
get dateStripCompact() {
return baseStore.settings.dateStripCompact;
},
get dateStripShowWeekNumbers() {
return baseStore.settings.dateStripShowWeekNumbers;
},
get dateStripCollapsed() {
return baseStore.settings.dateStripCollapsed;
},
get tagStripCollapsed() {
return baseStore.settings.tagStripCollapsed;
},
get selectedTagIds() {
return baseStore.settings.selectedTagIds;
},
get hasSelectedTags() {
return baseStore.settings.selectedTagIds.length > 0;
},
get immersiveModeEnabled() {
return baseStore.settings.immersiveModeEnabled;
},
get showBirthdays() {
return baseStore.settings.showBirthdays;
},
get showBirthdayAge() {
return baseStore.settings.showBirthdayAge;
},
get showTasksInCalendar() {
return baseStore.settings.showTasksInCalendar;
},
get defaultEventDuration() {
return baseStore.settings.defaultEventDuration;
},
get defaultReminder() {
return baseStore.settings.defaultReminder;
},
get sidebarCollapsed() {
return baseStore.settings.sidebarCollapsed;
},
get quickViewPillViews() {
return baseStore.settings.quickViewPillViews;
},
get customDayCount() {
return baseStore.settings.customDayCount;
},
get sttLanguage() {
return baseStore.settings.sttLanguage;
},
@ -267,20 +213,36 @@ export const settingsStore = {
return cloudSyncEnabled;
},
// --- UI state getters (non-persisted) ---
get dateStripCollapsed() {
return _dateStripCollapsed;
},
get tagStripCollapsed() {
return _tagStripCollapsed;
},
get selectedTagIds() {
return _selectedTagIds;
},
get hasSelectedTags() {
return _selectedTagIds.length > 0;
},
get immersiveModeEnabled() {
return _immersiveModeEnabled;
},
get showTasksInCalendar() {
return _showTasksInCalendar;
},
get sidebarCollapsed() {
return _sidebarCollapsed;
},
// Cloud sync methods
enableCloudSync() {
cloudSyncEnabled = true;
if (!initialSyncDone) {
const cloudSettings = loadFromCloud();
if (cloudSettings && Object.keys(cloudSettings).length > 0) {
// Exclude showTasksInCalendar and sidebarCollapsed from cloud sync
// - always start with tasks hidden and sidebar collapsed
const {
showTasksInCalendar: _,
sidebarCollapsed: __,
...settingsWithoutTasks
} = cloudSettings;
baseStore.update(settingsWithoutTasks);
baseStore.update(cloudSettings);
} else {
syncToCloud(baseStore.settings);
}
@ -292,32 +254,40 @@ export const settingsStore = {
cloudSyncEnabled = false;
},
// Calendar-specific toggle methods
// UI state toggle methods (non-persisted)
toggleSidebar() {
baseStore.set('sidebarCollapsed', !baseStore.settings.sidebarCollapsed);
_sidebarCollapsed = !_sidebarCollapsed;
},
toggleTagStrip() {
baseStore.set('tagStripCollapsed', !baseStore.settings.tagStripCollapsed);
_tagStripCollapsed = !_tagStripCollapsed;
},
toggleTagSelection(tagId: string) {
const currentIds = baseStore.settings.selectedTagIds;
const isSelected = currentIds.includes(tagId);
const newIds = isSelected ? currentIds.filter((id) => id !== tagId) : [...currentIds, tagId];
baseStore.set('selectedTagIds', newIds);
const isSelected = _selectedTagIds.includes(tagId);
_selectedTagIds = isSelected
? _selectedTagIds.filter((id) => id !== tagId)
: [..._selectedTagIds, tagId];
},
isTagSelected(tagId: string): boolean {
return baseStore.settings.selectedTagIds.includes(tagId);
return _selectedTagIds.includes(tagId);
},
clearTagSelection() {
baseStore.set('selectedTagIds', []);
_selectedTagIds = [];
},
toggleTasksInCalendar() {
baseStore.set('showTasksInCalendar', !baseStore.settings.showTasksInCalendar);
_showTasksInCalendar = !_showTasksInCalendar;
},
toggleImmersiveMode() {
_immersiveModeEnabled = !_immersiveModeEnabled;
},
setDateStripCollapsed(value: boolean) {
_dateStripCollapsed = value;
},
// Time formatting helpers

View file

@ -13,55 +13,55 @@ export type UnifiedBarLayer = 'input' | 'date' | 'tag' | 'toolbar' | 'settings';
// Store interface
export interface UnifiedBarSettings extends Record<string, unknown> {
// UnifiedBar mode control
unifiedBarMode: UnifiedBarMode;
unifiedBarActiveLayer: UnifiedBarLayer;
// UnifiedBar mode control
unifiedBarMode: UnifiedBarMode;
unifiedBarActiveLayer: UnifiedBarLayer;
// Legacy compatibility (keep existing settings working)
dateStripCollapsed: boolean;
tagStripCollapsed: boolean;
calendarToolbarCollapsed: boolean;
// Legacy compatibility (keep existing settings working)
dateStripCollapsed: boolean;
tagStripCollapsed: boolean;
calendarToolbarCollapsed: boolean;
// New UnifiedBar-specific settings
showQuickInput: boolean;
showDateStrip: boolean;
showTagStrip: boolean;
showCalendarToolbar: boolean;
overlayMenuOpen: boolean;
// New UnifiedBar-specific settings
showQuickInput: boolean;
showDateStrip: boolean;
showTagStrip: boolean;
showCalendarToolbar: boolean;
overlayMenuOpen: boolean;
// Animation and interaction preferences
barAnimationDuration: number;
enableHapticFeedback: boolean;
autoCollapseBars: boolean;
// Animation and interaction preferences
barAnimationDuration: number;
enableHapticFeedback: boolean;
autoCollapseBars: boolean;
// Quick access toggles
quickAccessActions: string[];
// Quick access toggles
quickAccessActions: string[];
}
const DEFAULT_SETTINGS: UnifiedBarSettings = {
// Default to collapsed mode with only input bar visible
unifiedBarMode: 'collapsed',
unifiedBarActiveLayer: 'input',
// Default to collapsed mode with only input bar visible
unifiedBarMode: 'collapsed',
unifiedBarActiveLayer: 'input',
// Legacy compatibility
dateStripCollapsed: false,
tagStripCollapsed: true,
calendarToolbarCollapsed: true,
// Legacy compatibility
dateStripCollapsed: false,
tagStripCollapsed: true,
calendarToolbarCollapsed: true,
// New settings
showQuickInput: true,
showDateStrip: true,
showTagStrip: false,
showCalendarToolbar: false,
overlayMenuOpen: false,
// New settings
showQuickInput: true,
showDateStrip: true,
showTagStrip: false,
showCalendarToolbar: false,
overlayMenuOpen: false,
// Interaction preferences
barAnimationDuration: 300,
enableHapticFeedback: true,
autoCollapseBars: false,
// Interaction preferences
barAnimationDuration: 300,
enableHapticFeedback: true,
autoCollapseBars: false,
// Quick actions
quickAccessActions: ['new-event', 'search', 'today', 'calendar-toggle'],
// Quick actions
quickAccessActions: ['new-event', 'search', 'today', 'calendar-toggle'],
};
// Cloud sync state
@ -70,197 +70,169 @@ let initialSyncDone = $state(false);
// Sync to cloud callback
async function syncToCloud(settings: UnifiedBarSettings) {
if (!cloudSyncEnabled || typeof window === 'undefined') return;
try {
await userSettings.updateDeviceAppSettings(settings as unknown as Record<string, unknown>);
} catch (e) {
console.error('Failed to sync unified bar settings to cloud:', e);
}
if (!cloudSyncEnabled || typeof window === 'undefined') return;
try {
await userSettings.updateDeviceAppSettings(settings as unknown as Record<string, unknown>);
} catch (e) {
console.error('Failed to sync unified bar settings to cloud:', e);
}
}
// Create base store
const baseStore = createAppSettingsStore<UnifiedBarSettings>(
'unified-bar-settings',
DEFAULT_SETTINGS,
{
onSettingsChange: syncToCloud,
}
'unified-bar-settings',
DEFAULT_SETTINGS,
{
onSettingsChange: syncToCloud,
}
);
// Load settings from cloud
function loadFromCloud(): Partial<UnifiedBarSettings> | null {
if (!userSettings.loaded) return null;
const cloudSettings = userSettings.currentDeviceAppSettings;
if (cloudSettings && Object.keys(cloudSettings).length > 0) {
return cloudSettings as unknown as Partial<UnifiedBarSettings>;
}
return null;
if (!userSettings.loaded) return null;
const cloudSettings = userSettings.currentDeviceAppSettings;
if (cloudSettings && Object.keys(cloudSettings).length > 0) {
return cloudSettings as unknown as Partial<UnifiedBarSettings>;
}
return null;
}
export const unifiedBarStore = {
// Base store methods
get settings() {
return baseStore.settings;
},
initialize: baseStore.initialize,
set: baseStore.set,
update: baseStore.update,
reset: baseStore.reset,
getDefaults: baseStore.getDefaults,
// Base store methods
get settings() {
return baseStore.settings;
},
initialize: baseStore.initialize,
set: baseStore.set,
update: baseStore.update,
reset: baseStore.reset,
getDefaults: baseStore.getDefaults,
// Mode management
get mode() {
return baseStore.settings.unifiedBarMode;
},
// Mode management
get mode() {
return baseStore.settings.unifiedBarMode;
},
get activeLayer() {
return baseStore.settings.unifiedBarActiveLayer;
},
get activeLayer() {
return baseStore.settings.unifiedBarActiveLayer;
},
get isOverlayOpen() {
return baseStore.settings.overlayMenuOpen;
},
get isOverlayOpen() {
return baseStore.settings.overlayMenuOpen;
},
// Layer visibility helpers
get showQuickInput() {
return baseStore.settings.showQuickInput;
},
// Layer visibility helpers
get showQuickInput() {
return baseStore.settings.showQuickInput;
},
get showDateStrip() {
return baseStore.settings.showDateStrip && !baseStore.settings.dateStripCollapsed;
},
get showDateStrip() {
return baseStore.settings.showDateStrip && !baseStore.settings.dateStripCollapsed;
},
get showTagStrip() {
return baseStore.settings.showTagStrip && !baseStore.settings.tagStripCollapsed;
},
get showTagStrip() {
return baseStore.settings.showTagStrip && !baseStore.settings.tagStripCollapsed;
},
get showCalendarToolbar() {
return baseStore.settings.showCalendarToolbar && !baseStore.settings.calendarToolbarCollapsed;
},
get showCalendarToolbar() {
return baseStore.settings.showCalendarToolbar && !baseStore.settings.calendarToolbarCollapsed;
},
// Mode switching
setMode(mode: UnifiedBarMode) {
baseStore.set('unifiedBarMode', mode);
},
// Mode switching
setMode(mode: UnifiedBarMode) {
baseStore.set('unifiedBarMode', mode);
},
toggleOverlay() {
const isOpen = baseStore.settings.overlayMenuOpen;
baseStore.set('overlayMenuOpen', !isOpen);
if (!isOpen) {
baseStore.set('unifiedBarMode', 'overlay');
} else {
baseStore.set('unifiedBarMode', 'collapsed');
}
},
toggleOverlay() {
const isOpen = baseStore.settings.overlayMenuOpen;
baseStore.set('overlayMenuOpen', !isOpen);
if (!isOpen) {
baseStore.set('unifiedBarMode', 'overlay');
} else {
baseStore.set('unifiedBarMode', 'collapsed');
}
},
setActiveLayer(layer: UnifiedBarLayer) {
baseStore.set('unifiedBarActiveLayer', layer);
},
setActiveLayer(layer: UnifiedBarLayer) {
baseStore.set('unifiedBarActiveLayer', layer);
},
// Layer toggles
toggleQuickInput() {
baseStore.set('showQuickInput', !baseStore.settings.showQuickInput);
},
// Layer toggles
toggleQuickInput() {
baseStore.set('showQuickInput', !baseStore.settings.showQuickInput);
},
toggleDateStrip() {
const newValue = !baseStore.settings.showDateStrip;
baseStore.set('showDateStrip', newValue);
baseStore.set('dateStripCollapsed', !newValue);
},
toggleDateStrip() {
const newValue = !baseStore.settings.showDateStrip;
baseStore.set('showDateStrip', newValue);
baseStore.set('dateStripCollapsed', !newValue);
},
toggleTagStrip() {
const newValue = !baseStore.settings.showTagStrip;
baseStore.set('showTagStrip', newValue);
baseStore.set('tagStripCollapsed', !newValue);
},
toggleTagStrip() {
const newValue = !baseStore.settings.showTagStrip;
baseStore.set('showTagStrip', newValue);
baseStore.set('tagStripCollapsed', !newValue);
},
toggleCalendarToolbar() {
const newValue = !baseStore.settings.showCalendarToolbar;
baseStore.set('showCalendarToolbar', newValue);
baseStore.set('calendarToolbarCollapsed', !newValue);
},
toggleCalendarToolbar() {
const newValue = !baseStore.settings.showCalendarToolbar;
baseStore.set('showCalendarToolbar', newValue);
baseStore.set('calendarToolbarCollapsed', !newValue);
},
// Quick actions
expandToLayer(layer: UnifiedBarLayer) {
baseStore.set('unifiedBarMode', 'expanded');
baseStore.set('unifiedBarActiveLayer', layer);
// Quick actions
expandToLayer(layer: UnifiedBarLayer) {
baseStore.set('unifiedBarMode', 'expanded');
baseStore.set('unifiedBarActiveLayer', layer);
// Auto-show the layer if hidden
switch (layer) {
case 'date':
if (!baseStore.settings.showDateStrip) {
baseStore.set('showDateStrip', true);
baseStore.set('dateStripCollapsed', false);
}
break;
case 'tag':
if (!baseStore.settings.showTagStrip) {
baseStore.set('showTagStrip', true);
baseStore.set('tagStripCollapsed', false);
}
break;
case 'toolbar':
if (!baseStore.settings.showCalendarToolbar) {
baseStore.set('showCalendarToolbar', true);
baseStore.set('calendarToolbarCollapsed', false);
}
break;
}
},
// Auto-show the layer if hidden
switch (layer) {
case 'date':
if (!baseStore.settings.showDateStrip) {
baseStore.set('showDateStrip', true);
baseStore.set('dateStripCollapsed', false);
}
break;
case 'tag':
if (!baseStore.settings.showTagStrip) {
baseStore.set('showTagStrip', true);
baseStore.set('tagStripCollapsed', false);
}
break;
case 'toolbar':
if (!baseStore.settings.showCalendarToolbar) {
baseStore.set('showCalendarToolbar', true);
baseStore.set('calendarToolbarCollapsed', false);
}
break;
}
},
collapseAll() {
baseStore.set('unifiedBarMode', 'collapsed');
baseStore.set('unifiedBarActiveLayer', 'input');
if (baseStore.settings.autoCollapseBars) {
baseStore.set('showDateStrip', false);
baseStore.set('showTagStrip', false);
baseStore.set('showCalendarToolbar', false);
}
},
collapseAll() {
baseStore.set('unifiedBarMode', 'collapsed');
baseStore.set('unifiedBarActiveLayer', 'input');
if (baseStore.settings.autoCollapseBars) {
baseStore.set('showDateStrip', false);
baseStore.set('showTagStrip', false);
baseStore.set('showCalendarToolbar', false);
}
},
// Cloud sync methods
enableCloudSync() {
cloudSyncEnabled = true;
if (!initialSyncDone) {
const cloudSettings = loadFromCloud();
if (cloudSettings && Object.keys(cloudSettings).length > 0) {
baseStore.update(cloudSettings);
} else {
syncToCloud(baseStore.settings);
}
initialSyncDone = true;
}
},
// Cloud sync methods
enableCloudSync() {
cloudSyncEnabled = true;
if (!initialSyncDone) {
const cloudSettings = loadFromCloud();
if (cloudSettings && Object.keys(cloudSettings).length > 0) {
baseStore.update(cloudSettings);
} else {
syncToCloud(baseStore.settings);
}
initialSyncDone = true;
}
},
disableCloudSync() {
cloudSyncEnabled = false;
},
// Legacy compatibility helpers (for gradual migration)
get legacyDateStripCollapsed() {
return baseStore.settings.dateStripCollapsed;
},
get legacyTagStripCollapsed() {
return baseStore.settings.tagStripCollapsed;
},
// Sync from legacy settings (migration helper)
syncFromLegacySettings(legacy: { dateStripCollapsed?: boolean; tagStripCollapsed?: boolean }) {
const updates: Partial<UnifiedBarSettings> = {};
if (legacy.dateStripCollapsed !== undefined) {
updates.dateStripCollapsed = legacy.dateStripCollapsed;
updates.showDateStrip = !legacy.dateStripCollapsed;
}
if (legacy.tagStripCollapsed !== undefined) {
updates.tagStripCollapsed = legacy.tagStripCollapsed;
updates.showTagStrip = !legacy.tagStripCollapsed;
}
if (Object.keys(updates).length > 0) {
baseStore.update(updates);
}
},
disableCloudSync() {
cloudSyncEnabled = false;
},
};

View file

@ -10,7 +10,6 @@ vi.mock('$app/environment', () => ({
vi.mock('./settings.svelte', () => ({
settingsStore: {
weekStartsOn: 1 as 0 | 1,
customDayCount: 30,
defaultView: 'week',
initialize: vi.fn(),
},
@ -19,13 +18,12 @@ vi.mock('./settings.svelte', () => ({
// Mock @manacore/shared-stores
vi.mock('@manacore/shared-stores', () => ({
createAppSettingsStore: vi.fn(() => ({
settings: { weekStartsOn: 1, customDayCount: 30, defaultView: 'week' },
settings: { weekStartsOn: 1, defaultView: 'week' },
initialize: vi.fn(),
set: vi.fn(),
update: vi.fn(),
reset: vi.fn(),
getDefaults: vi.fn(),
toggleImmersiveMode: vi.fn(),
})),
}));