managarten/packages/shared-splitscreen/src/utils/local-storage.ts
Till-JS f2ac3e245e feat(splitscreen): add split-screen feature for multi-app side-by-side view
Add new @manacore/shared-splitscreen package enabling iFrame-based
split-screen functionality across Calendar, Todo, and Contacts apps.

Features:
- SplitPaneContainer with CSS Grid layout
- AppPanel with iFrame sandbox permissions and loading/error states
- ResizeHandle with mouse, touch, and keyboard support (20-80% range)
- PanelControls for swap and close actions
- Svelte 5 runes-based store with Context API
- URL persistence (?panel=todo&split=60)
- localStorage persistence with versioning
- Mobile auto-disable (<1024px breakpoint)

Integration:
- PillNavigation: added onOpenInPanel prop and Ctrl/Cmd+click support
- PillDropdown: added split button per app item
- Calendar, Todo, Contacts layouts wrapped with SplitPaneContainer

Also fixes:
- WeekView.svelte: fixed {@const} placement error
- MultiDayView.svelte: fixed {@const} placement error

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-12 13:00:26 +01:00

97 lines
2.2 KiB
TypeScript

/**
* LocalStorage Utilities
* Handle persistent storage for split-screen preferences.
*/
import type { SplitScreenState, StorageConfig } from '../types.js';
import { DIVIDER_CONSTRAINTS } from '../types.js';
const STORAGE_VERSION = 1;
interface StoredState {
version: number;
state: Partial<SplitScreenState>;
}
/**
* Generate storage key for an app.
*/
function getStorageKey(config: StorageConfig): string {
return `${config.prefix}-splitscreen-${config.currentAppId}`;
}
/**
* Save split-screen state to localStorage.
*/
export function savePanelState(config: StorageConfig, state: Partial<SplitScreenState>): void {
if (typeof window === 'undefined') return;
try {
const stored: StoredState = {
version: STORAGE_VERSION,
state: {
dividerPosition: state.dividerPosition,
rightPanel: state.rightPanel,
},
};
localStorage.setItem(getStorageKey(config), JSON.stringify(stored));
} catch (_error) {
// localStorage not available or quota exceeded
}
}
/**
* Load split-screen state from localStorage.
*/
export function loadPanelState(config: StorageConfig): Partial<SplitScreenState> | null {
if (typeof window === 'undefined') return null;
try {
const raw = localStorage.getItem(getStorageKey(config));
if (!raw) return null;
const stored: StoredState = JSON.parse(raw);
// Version check for future migrations
if (stored.version !== STORAGE_VERSION) {
clearPanelState(config);
return null;
}
// Validate divider position
if (stored.state.dividerPosition !== undefined) {
stored.state.dividerPosition = Math.max(
DIVIDER_CONSTRAINTS.MIN,
Math.min(DIVIDER_CONSTRAINTS.MAX, stored.state.dividerPosition)
);
}
return stored.state;
} catch (_error) {
// localStorage not available or corrupted data
return null;
}
}
/**
* Clear split-screen state from localStorage.
*/
export function clearPanelState(config: StorageConfig): void {
if (typeof window === 'undefined') return;
try {
localStorage.removeItem(getStorageKey(config));
} catch (_error) {
// localStorage not available
}
}
/**
* Get default storage config with manacore prefix.
*/
export function createStorageConfig(currentAppId: string): StorageConfig {
return {
prefix: 'manacore',
currentAppId,
};
}