7.8 KiB
Astrolabe Refactoring Plan
Date: 2025-10-19 Goal: Reduce code duplication and improve maintainability while preserving simplicity Expected Impact: Reduce codebase by ~400-500 lines (~7-10%)
High Impact, Low Effort Refactorings
1. Consolidate Event Handlers in app.js
Current: 795 lines with ~200 lines of repetitive addEventListener calls Target: ~650-700 lines Savings: ~100-150 lines
Issues:
- Lines 118-434 are primarily boilerplate event registration
- Repetitive patterns for modal open/close
- Each modal has dedicated open/close functions that just toggle display
- Event listeners attached individually to each button
Implementation:
// Generic modal manager
const ModalManager = {
open(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.style.display = 'flex';
this.trackOpen(modalId);
}
},
close(modalId) {
const modal = document.getElementById(modalId);
if (modal) modal.style.display = 'none';
},
trackOpen(modalId) {
const trackingMap = {
'help-modal': 'modal-help',
'donate-modal': 'modal-donate',
'settings-modal': 'modal-settings',
'dataset-modal': 'modal-dataset'
};
if (trackingMap[modalId]) {
Analytics.track(trackingMap[modalId], `Open ${modalId}`);
}
}
};
// Event delegation for modal closes
document.addEventListener('click', (e) => {
// Close buttons
if (e.target.matches('[id$="-modal-close"]')) {
const modalId = e.target.id.replace('-close', '');
ModalManager.close(modalId);
}
// Overlay clicks
if (e.target.classList.contains('modal')) {
ModalManager.close(e.target.id);
}
});
Files to modify:
src/js/app.js- Consolidate modal handlers
2. Better Utilize generic-storage-ui.js
Current: generic-storage-ui.js exists but is underused Target: Apply consistently across snippet-manager.js and dataset-manager.js Savings: ~100-150 lines
Issues:
- Both managers have duplicate patterns for:
- List rendering with item selection
- Linked item display
- Modal management
- Form validation
- Delete confirmations
- generic-storage-ui.js has the abstractions but they're not fully applied
Duplicated Functions to Consolidate:
Snippet Manager:
renderSnippetList()→ UserenderGenericList()attachSnippetEventListeners()→ Integrated into generic rendererselectSnippet()→ UseselectGenericItem()updateLinkedDatasets()→ Already usesupdateGenericLinkedItems()✓deleteSnippet()→ Already usesconfirmGenericDeletion()✓
Dataset Manager:
renderDatasetList()→ UserenderGenericList()attachDatasetEventListeners()→ Integrated into generic rendererselectDataset()→ UseselectGenericItem()updateLinkedSnippets()→ UseupdateGenericLinkedItems()deleteCurrentDataset()→ UseconfirmGenericDeletion()
Implementation Strategy:
- Enhance
renderGenericList()to handle ghost cards (e.g., "Create New Snippet") - Add optional transformer for list items (format functions)
- Ensure
selectGenericItem()handles both numeric and string IDs - Update both managers to use generic functions consistently
Files to modify:
src/js/generic-storage-ui.js- Enhance existing functionssrc/js/snippet-manager.js- Apply generic list/select functionssrc/js/dataset-manager.js- Apply generic list/select functions
3. Simplify user-settings.js
Current: 300 lines with complex dot-path API Target: ~220-250 lines Savings: ~50-80 lines
Issues:
- Dot-path API (
getSetting('editor.fontSize')) is over-engineered for ~10 settings validateSetting()has 40 lines for simple validation- Import/export functions (30 lines) appear unused
- Path parsing logic (20 lines) in getter/setter
Implementation:
// Simpler API - direct property access
const settings = getSettings();
settings.editor.fontSize = 14;
saveSettings();
// Or keep helper but simplify:
function getSetting(path) {
const [section, key] = path.split('.');
return userSettings?.[section]?.[key];
}
function updateSetting(path, value) {
const [section, key] = path.split('.');
if (userSettings[section]) {
userSettings[section][key] = value;
return saveSettings();
}
return false;
}
Changes:
- Simplify getter/setter - assume 2-level structure (section.key)
- Inline validation - check ranges directly in
updateSettings() - Remove import/export if unused (check usage first)
- Keep
formatDate()logic - it's used throughout UI
Files to modify:
src/js/user-settings.js- Simplify API and validation
Medium Impact, Medium Effort (Future)
4. Refactor config.js into Focused Modules
Current: 257 lines of mixed concerns Target: Split into 3-4 focused files Savings: 0 LOC, but improves organization
Proposed Structure:
src/js/
state.js - URLState, global variables (editor, currentSnippetId, etc.)
ui-utils.js - Toast, formatBytes, ModalManager
analytics.js - Analytics wrapper
defaults.js - sampleSpec, constants (STORAGE_LIMIT_BYTES, etc.)
Benefits:
- Easier to find utilities
- Clearer dependencies
- Better mental model of what's "global"
Files to create/modify:
- Create:
src/js/state.js,src/js/ui-utils.js,src/js/analytics.js,src/js/defaults.js - Modify:
src/js/config.js(delete after migration) - Update:
index.htmlscript tags
5. Extract snippet-drafts.js from snippet-manager.js
Current: snippet-manager.js at 1393 lines handles many concerns Target: Split draft workflow into separate module Savings: 0 LOC, but improves maintainability
Functions to Extract:
loadSnippetIntoEditor()updateViewModeUI()switchViewMode()publishDraft()revertDraft()autoSaveDraft()debouncedAutoSave()initializeAutoSave()
Lines: ~200-250 lines → snippet-drafts.js
Benefits:
- Easier to work on CRUD without draft complexity
- Draft workflow becomes a clear, separate concern
- Reduces cognitive load when reading snippet-manager.js
Files to create/modify:
- Create:
src/js/snippet-drafts.js - Modify:
src/js/snippet-manager.js(remove draft functions) - Update:
index.htmlscript tag order
Implementation Order
Phase 1: High Impact, Low Effort (This Session)
- ✅ Create this refactoring plan
- ✅ Consolidate event handlers in app.js (795 → 672 lines, -123)
- ✅ Simplify user-settings.js (300 → 224 lines, -76)
- ✅ Added ModalManager to config.js (257 → 303 lines, +46)
- ⏸️ Enhance and apply generic-storage-ui.js (IN PROGRESS)
Net Savings So Far: 153 lines (2.5% reduction)
Phase 2: Medium Effort (Future Session)
- Refactor config.js into focused modules
- Extract snippet-drafts.js
Testing Strategy
After each refactoring step:
- Open
index.htmlin browser - Test core workflows:
- Create/edit/delete snippet
- Create/edit/delete dataset
- Draft/publish workflow
- Search and sort snippets
- Import/export snippets
- Settings modal
- All keyboard shortcuts
- Check browser console for errors
- Verify localStorage and IndexedDB persistence
Rollback Strategy
- Each refactoring committed separately
- Git tags for each major step
- Can revert individual changes via
git revert
Success Metrics
- LOC Reduction: 400-500 lines (~7-10%)
- Duplication: Near-zero between snippet/dataset managers
- Complexity: event handlers reduced from ~30 to ~10
- Maintainability: Adding dataset search becomes trivial (reuse snippet search pattern)
- Philosophy: Code size better matches feature importance
Notes
- Keep changes incremental and testable
- Preserve all existing functionality
- Don't introduce new dependencies
- Maintain vanilla JS approach
- Update comments for clarity