diff --git a/main.ts b/main.ts index a18fc61..2ff3c22 100644 --- a/main.ts +++ b/main.ts @@ -40,6 +40,7 @@ export default class SpaceforgePlugin extends Plugin { pomodoroService: PomodoroService; calendarEventService: CalendarEventService; + private readonly stylesheetPath: string = "styles.css"; private readonly stylesheetId: string = "spaceforge-styles"; private lastStylesModTime: number | null = null; @@ -94,6 +95,8 @@ export default class SpaceforgePlugin extends Plugin { this.calendarEventService.initialize(eventsArray); + + this.registerView( 'spaceforge-review-schedule', (leaf) => new ReviewSidebarView(leaf, this) @@ -165,7 +168,7 @@ export default class SpaceforgePlugin extends Plugin { this.getSidebarView()?.refresh(); }, 60 * 1000)); - this.app.workspace.onLayoutReady(() => this.activateSidebarView()); + // this.app.workspace.onLayoutReady(() => this.activateSidebarView()); // Removed automatic activation this.addStylesheet(); if (this.app.vault.adapter.stat && typeof this.app.vault.adapter.stat === 'function') { @@ -427,11 +430,31 @@ export default class SpaceforgePlugin extends Plugin { this.reviewScheduleService.lastLinkAnalysisTimestamp = typeof this.pluginState.lastLinkAnalysisTimestamp === 'number' ? this.pluginState.lastLinkAnalysisTimestamp : null; } catch (error) { - // Fallback to complete defaults if any error during loading sequence - this.settings = { ...DEFAULT_SETTINGS }; - this.pluginState = { ...DEFAULT_PLUGIN_STATE_DATA }; + console.error("Spaceforge: Error loading data:", error); - // Repopulate services with these fresh defaults + // SIMPLE RECOVERY: Try localStorage backup once, then use defaults + try { + const backupData = await this.app.loadLocalStorage('spaceforge-backup'); + if (backupData && typeof backupData === 'string') { + const parsedBackup = JSON.parse(backupData); + if (parsedBackup.reviewData) { + this.settings = { ...DEFAULT_SETTINGS, ...parsedBackup.settings }; + this.pluginState = { ...DEFAULT_PLUGIN_STATE_DATA, ...parsedBackup.reviewData }; + new Notice("Spaceforge: Recovered data from backup.", 5000); + } else { + throw new Error("Invalid backup format"); + } + } else { + throw new Error("No backup found"); + } + } catch (backupError) { + console.warn("Spaceforge: Backup recovery failed, using defaults:", backupError); + this.settings = { ...DEFAULT_SETTINGS }; + this.pluginState = { ...DEFAULT_PLUGIN_STATE_DATA }; + new Notice("Spaceforge: Error loading data. Using defaults to prevent crash.", 5000); + } + + // Repopulate services this.reviewScheduleService.schedules = this.pluginState.schedules || {}; this.reviewHistoryService.history = this.pluginState.history || []; this.reviewSessionService.reviewSessions = this.pluginState.reviewSessions || { sessions: {}, activeSessionId: null }; @@ -440,11 +463,9 @@ export default class SpaceforgePlugin extends Plugin { this.reviewScheduleService.customNoteOrder = this.pluginState.customNoteOrder || []; this.reviewScheduleService.lastLinkAnalysisTimestamp = this.pluginState.lastLinkAnalysisTimestamp ?? null; - // Ensure FSRS service is also updated with these default settings if (this.reviewScheduleService) { this.reviewScheduleService.updateAlgorithmServicesForSettingsChange(); } - new Notice("Spaceforge: Error loading data, initialized with defaults.", 5000); } } @@ -504,6 +525,7 @@ export default class SpaceforgePlugin extends Plugin { new Notice(`Spaceforge: Created directory for custom data: ${dirPathOnly}`, 3000); } + // SIMPLE SAVE: Use Obsidian's standard file operations const file = this.app.vault.getAbstractFileByPath(effectiveSavePath); if (file instanceof TFile) { await this.app.vault.modify(file, JSON.stringify(dataToSave, null, 2)); @@ -513,14 +535,13 @@ export default class SpaceforgePlugin extends Plugin { // new Notice(`Spaceforge: Data saved to custom path: ${effectiveSavePath}`, 3000); // Removed notice // Migration/Cleanup: If we just successfully saved to a custom path, - // and the old default data.json exists, remove it. + // and the old default data.json exists, KEEP IT as backup for safety. + // CRITICAL FIX: Never automatically delete user data to prevent data loss during reloads const oldFile = this.app.vault.getAbstractFileByPath(defaultPluginDataPath); if (oldFile instanceof TFile) { - // Check if this is a migration scenario (old data was loaded and new path was empty) - // This check is a bit implicit. A more robust way would be a flag. - // For now, if custom path is active and default exists, assume it's post-migration or user switched. - await this.app.vault.delete(oldFile); - new Notice(`Spaceforge: Removed old data file from default plugin folder as custom path is active.`, 5000); + // Instead of deleting, we'll keep the old file as a backup + // Users can manually clean it up later if needed + new Notice(`Spaceforge: Successfully saved to custom path. Original data file kept as backup for safety.`, 5000); } } catch (writeError) { new Notice(`Error saving data to custom path ${effectiveSavePath}: ${writeError.message}. Falling back to default path for this save.`, 10000); diff --git a/ui/settings-tab.ts b/ui/settings-tab.ts index 2bd4121..38093fc 100644 --- a/ui/settings-tab.ts +++ b/ui/settings-tab.ts @@ -1287,6 +1287,65 @@ export class SpaceforgeSettingTab extends PluginSettingTab { }); } + // ========= DATA MANAGEMENT SECTION ========= + const dataSection = createCollapsible('Data Management', 'database', false); + + const defaultPath = this.plugin.app.vault.configDir + `/plugins/${this.plugin.manifest.id}/data.json`; + let locationDesc = `Default: ${defaultPath}`; + if (this.plugin.settings.useCustomDataPath && this.plugin.settings.customDataPath) { + const customPath = this.plugin.settings.customDataPath.endsWith('/data.json') + ? this.plugin.settings.customDataPath + : `${this.plugin.settings.customDataPath}/data.json`; + locationDesc = `Custom: ${customPath}`; + } + + new Setting(dataSection) + .setName('Current data location') + .setDesc(locationDesc) + .addExtraButton(button => button + .setIcon('info') + .setTooltip('Current location of your Spaceforge data') + .onClick(() => { + const message = this.plugin.settings.useCustomDataPath && this.plugin.settings.customDataPath + ? `Your data is stored at: ${this.plugin.settings.customDataPath.endsWith('/data.json') ? this.plugin.settings.customDataPath : `${this.plugin.settings.customDataPath}/data.json`}` + : `Your data is stored at: ${defaultPath}`; + + new Notice(message, 8000); + })); + + // Check if old default data file exists when using custom path + if (this.plugin.settings.useCustomDataPath) { + const oldFile = this.plugin.app.vault.getAbstractFileByPath(defaultPath); + + if (oldFile) { + new Setting(dataSection) + .setName('Legacy data file found') + .setDesc(`A data file exists at the default location. You can safely delete it or keep it as a backup.`) + .addButton(button => button + .setButtonText('Keep as Backup') + .setIcon('save') + .onClick(() => { + new Notice('Legacy data file kept as backup. You can delete it manually when ready.', 5000); + })) + .addButton(button => button + .setButtonText('Delete Legacy File') + .setIcon('trash') + .setWarning() + .onClick(async () => { + const confirmed = confirm('Are you sure you want to delete the legacy data file? Make sure your custom data path is working correctly before proceeding.'); + if (confirmed) { + try { + await this.plugin.app.vault.delete(oldFile); + new Notice('Legacy data file deleted successfully.', 3000); + this.display(); // Refresh settings + } catch (error) { + new Notice('Failed to delete legacy file: ' + error.message, 5000); + } + } + })); + } + } + // Add global action buttons at the bottom createActionButtons(); } @@ -1378,5 +1437,7 @@ export class SpaceforgeSettingTab extends PluginSettingTab { row4.createEl('td', { text: 'Recalled easily' }); row4.createEl('td', { text: 'Largest increase in stability' }); } + + } }