Documentation Index Fetch the complete documentation index at: https://docs.debian.com.mx/llms.txt
Use this file to discover all available pages before exploring further.
Storage Management
Time Capsule uses a layered storage approach with IndexedDB as the primary storage mechanism, localStorage as a fallback, and an in-memory cache for performance.
Storage Architecture
SettingsManager Implementation
The SettingsManager class provides a unified interface for all configuration and state management.
Singleton Pattern
class SettingsManager {
private static instance : SettingsManager ;
private settings : SystemSettings ;
private readonly CURRENT_VERSION = '1.1.1' ;
private constructor () {
this . settings = this . getDefaultSettings ();
this . load ();
this . checkVersion ();
}
public static getInstance () : SettingsManager {
if ( ! SettingsManager . instance ) {
SettingsManager . instance = new SettingsManager ();
}
return SettingsManager . instance ;
}
}
export const settingsManager = SettingsManager . getInstance ();
Settings Structure
Settings are organized into logical sections:
SystemSettings Interface
Default Settings
export interface SystemSettings {
theme : {
colors : Record < string , string >;
fonts : Record < string , string >;
};
mouse : Record < string , any >;
keyboard : Record < string , any >;
beep : Record < string , any >;
session : {
windows : Record < string , {
top : string ;
left : string ;
display : string ;
maximized : boolean ;
}>;
};
desktop : Record < string , any >;
}
Version Management
Settings are versioned to handle schema changes and cache invalidation:
private checkVersion (): void {
const lastVersion = storageAdapter . getItemSync (
` ${ STORAGE_KEY } -version`
);
if ( lastVersion !== this . CURRENT_VERSION ) {
logger . log (
`[SettingsManager] Version mismatch ` +
`( ${ lastVersion } vs ${ this . CURRENT_VERSION } ). ` +
`Resetting cache...`
);
this . resetToDefaults ();
storageAdapter . setItemSync (
` ${ STORAGE_KEY } -version` ,
this . CURRENT_VERSION
);
}
}
When the version changes, all cached settings are cleared and reset to defaults. This prevents issues from schema changes.
Storage Adapter
The StorageAdapter provides a unified API abstracting the underlying storage mechanism.
Synchronous Operations
private load (): void {
try {
const saved = storageAdapter . getItemSync ( STORAGE_KEY );
if ( saved ) {
this . settings = JSON . parse ( saved );
logger . log ( '[SettingsManager] Unified settings loaded.' );
} else {
this . migrateLegacySettings ();
}
} catch ( e ) {
console . error ( '[SettingsManager] Failed to load:' , e );
}
}
public save (): void {
try {
storageAdapter . setItemSync (
STORAGE_KEY ,
JSON . stringify ( this . settings )
);
} catch ( e ) {
console . error ( '[SettingsManager] Failed to save:' , e );
}
}
Legacy Migration
Old settings stored in fragmented keys are automatically migrated:
private migrateLegacySettings (): void {
logger . log (
'[SettingsManager] Migrating from legacy settings...'
);
// Migrate Style/Theme (cde-styles)
const oldStyles = storageAdapter . getItemSync ( 'cde-styles' );
if ( oldStyles ) {
const parsed = JSON . parse ( oldStyles );
this . settings . theme . colors = parsed . colors || {};
this . settings . theme . fonts = parsed . fonts || {};
}
// Migrate Mouse
const oldMouse = storageAdapter . getItemSync (
'cde-mouse-settings'
);
if ( oldMouse ) {
this . settings . mouse = JSON . parse ( oldMouse );
}
// Migrate Keyboard
const oldKeyboard = storageAdapter . getItemSync (
'cde-keyboard-settings'
);
if ( oldKeyboard ) {
this . settings . keyboard = JSON . parse ( oldKeyboard );
}
// Migrate Beep
const oldBeep = storageAdapter . getItemSync (
'cde-beep-settings'
);
if ( oldBeep ) {
this . settings . beep = JSON . parse ( oldBeep );
}
this . save ();
logger . log ( '[SettingsManager] Migration completed.' );
}
Section Management
Settings are accessed and updated by section:
Section Operations
Usage Example
public setSection (
section : keyof SystemSettings ,
data : any
): void {
( this . settings as any )[ section ] = data ;
this . save ();
}
public getSection ( section : keyof SystemSettings ): any {
return this . settings [ section ];
}
public getAll (): SystemSettings {
return this . settings ;
}
Window Session Persistence
Window positions and states are automatically saved:
Window Session
Window Manager Integration
public updateWindowSession ( id : string , data : any ): void {
this . settings . session . windows [ id ] = {
... this . settings . session . windows [ id ],
... data
};
this . save ();
}
IndexedDB Usage
While SettingsManager currently uses synchronous localStorage operations via the adapter, the architecture supports IndexedDB for future enhancements.
Planned IndexedDB Schema
Database Structure
Store Schema
const DB_NAME = 'cde-time-capsule' ;
const DB_VERSION = 1 ;
const STORES = {
SETTINGS: 'settings' , // User preferences
SESSION: 'session' , // Window state
FILESYSTEM: 'filesystem' , // VFS data (future)
CACHE: 'cache' , // Temporary data
};
Cache Management
Memory Cache
SettingsManager maintains an in-memory cache for fast access:
class SettingsManager {
private settings : SystemSettings ; // Memory cache
// Direct memory access (fast)
public getSection ( section : keyof SystemSettings ) : any {
return this . settings [ section ];
}
// Write to memory + storage
public setSection ( section : keyof SystemSettings , data : any ) : void {
this . settings [ section ] = data ; // Update cache
this . save (); // Persist to storage
}
}
XPM Backdrop Cache
Rendered backdrop images are cached to avoid re-parsing expensive XPM files:
// Clear cache when theme colors change
public saveColor (): void {
const theme = settingsManager . getSection ( 'theme' );
theme . colors = this . theme . styles ;
settingsManager . setSection ( 'theme' , theme );
// Invalidate XPM cache
this . backdrop . clearCache ();
this . backdrop . apply ();
}
Synchronous Operations
SettingsManager uses synchronous localStorage operations for simplicity and reliability:
Synchronous storage operations are acceptable here because:
Settings data is small (typically < 50KB)
Operations are infrequent (user actions only)
Blocking UI briefly is better than race conditions
localStorage is much faster than IndexedDB for small data
Batched Updates
Avoid calling save() in tight loops:
Bad Practice
Good Practice
// DON'T: Save on every value change
for ( const [ key , value ] of Object . entries ( colors )) {
settings . theme . colors [ key ] = value ;
settingsManager . save (); // ❌ Multiple disk writes
}
Selective Section Updates
Only update the section that changed:
// Get section reference
const theme = settingsManager . getSection ( 'theme' );
// Modify section
theme . colors [ '--window-color' ] = '#4d648d' ;
// Save entire settings (includes updated theme)
settingsManager . setSection ( 'theme' , theme );
Error Handling
Graceful degradation ensures the application continues working even if storage fails:
private load (): void {
try {
const saved = storageAdapter . getItemSync ( STORAGE_KEY );
if ( saved ) {
this . settings = JSON . parse ( saved );
}
} catch ( e ) {
console . error ( '[SettingsManager] Load failed:' , e );
// Continue with default settings
}
}
public save (): void {
try {
storageAdapter . setItemSync (
STORAGE_KEY ,
JSON . stringify ( this . settings )
);
} catch ( e ) {
console . error ( '[SettingsManager] Save failed:' , e );
// User changes will be lost on refresh, but app continues
}
}
Storage Quota
Check Available Space
async function getStorageEstimate () {
if ( 'storage' in navigator &&
'estimate' in navigator . storage ) {
const estimate = await navigator . storage . estimate ();
return {
usage: estimate . usage || 0 ,
quota: estimate . quota || 0 ,
percentage : (
( estimate . usage || 0 ) / ( estimate . quota || 1 )
) * 100
};
}
return null ;
}
Handle Quota Exceeded
try {
storageAdapter . setItemSync ( key , value );
} catch ( error ) {
if ( error . name === 'QuotaExceededError' ) {
// Clear old cache entries
console . warn ( 'Storage quota exceeded, clearing cache' );
// Implement cache cleanup strategy
}
}
Best Practices
Always access settings by section rather than modifying the entire settings object: // Good
const theme = settingsManager . getSection ( 'theme' );
theme . colors [ '--window-color' ] = '#4d648d' ;
settingsManager . setSection ( 'theme' , theme );
// Bad
const all = settingsManager . getAll ();
all . theme . colors [ '--window-color' ] = '#4d648d' ;
// No automatic save!
Handle Missing Sections Gracefully
Always provide defaults for potentially missing data: const theme = settingsManager . getSection ( 'theme' );
const colors = theme ?. colors || {};
const windowColor = colors [ '--window-color' ] || '#4d648d' ;
When making breaking changes to settings structure, increment the version: private readonly CURRENT_VERSION = '1.2.0' ; // Changed from 1.1.1
This triggers automatic cache reset on next load.
Testing Storage
Manual Testing in Console
// Check current settings
console . log ( settingsManager . getAll ());
// Modify theme colors
const theme = settingsManager . getSection ( 'theme' );
theme . colors [ '--window-color' ] = '#ff0000' ;
settingsManager . setSection ( 'theme' , theme );
// Verify persistence (refresh page and check)
location . reload ();
console . log ( settingsManager . getSection ( 'theme' ). colors );
// Check localStorage directly
console . log ( localStorage . getItem ( 'cde-system-settings' ));
Clear All Settings
// In browser console
localStorage . clear ();
location . reload ();
// Or programmatically
settingsManager . resetToDefaults ();
Architecture Overall system architecture and design patterns
Window Manager Window session persistence and restoration
Virtual Filesystem Future VFS storage integration
Development Build new features using SettingsManager