mirror of
https://github.com/olehomelchenko/astrolabe-nvc.git
synced 2025-12-21 21:22:23 +00:00
346 lines
10 KiB
Markdown
346 lines
10 KiB
Markdown
# Alpine.js Migration Plan
|
|
|
|
## Overview
|
|
|
|
Incremental migration of Astrolabe from vanilla JavaScript to Alpine.js for reactive UI management. Each phase is independently deployable and leaves the project fully functional.
|
|
|
|
## Guiding Principles
|
|
|
|
1. **Each step is independently deployable** - Project always works
|
|
2. **No big-bang rewrites** - Small, focused changes
|
|
3. **Test after each step** - Catch issues early
|
|
4. **Alpine + Vanilla coexist** - No forced conversions
|
|
5. **SnippetStorage/DatasetStorage remain authoritative** - Alpine is view layer only
|
|
6. **Migration only, no new features** - Convert existing functionality without adding new UI features
|
|
|
|
## Architecture Philosophy
|
|
|
|
```
|
|
┌─────────────────────┐
|
|
│ Alpine.js (7KB) │ ← Reactivity + UI bindings
|
|
└──────────┬──────────┘
|
|
│ calls
|
|
▼
|
|
┌─────────────────────┐
|
|
│ Storage Layer │ ← All business logic
|
|
│ - SnippetStorage │ (filtering, sorting, CRUD)
|
|
│ - DatasetStorage │
|
|
└─────────────────────┘
|
|
```
|
|
|
|
**Clean separation:**
|
|
- **Alpine**: Handles reactivity, DOM updates, user interactions
|
|
- **Storage**: Single source of truth for data logic
|
|
|
|
---
|
|
|
|
## Phase 1: Snippet Panel ✅ COMPLETE
|
|
|
|
**Status**: Done
|
|
**Files**: `index.html`, `src/js/snippet-manager.js`, `src/js/app.js`
|
|
|
|
### What Was Converted
|
|
|
|
- Snippet list rendering with `x-for`
|
|
- Search with `x-model` (reactive filtering)
|
|
- Sort controls with `@click` and `:class`
|
|
- Selection highlighting with `:class`
|
|
- Ghost card (+ Create New Snippet)
|
|
|
|
### What Stayed Vanilla
|
|
|
|
- SnippetStorage (localStorage operations)
|
|
- Editor integration
|
|
- Meta fields (name, comment)
|
|
- All CRUD business logic
|
|
|
|
### Key Learnings
|
|
|
|
- Alpine store keeps minimal UI state (`currentSnippetId`)
|
|
- Storage layer does all filtering/sorting
|
|
- Alpine component is thin wrapper
|
|
- Automatic reactivity eliminates manual DOM updates
|
|
|
|
---
|
|
|
|
## Phase 2: Dataset Manager Modal ✅ COMPLETE
|
|
|
|
**Status**: Done
|
|
**Files**: `src/js/dataset-manager.js`, `index.html`
|
|
|
|
### What Was Converted
|
|
|
|
- Dataset list rendering with `x-for` template
|
|
- Selection highlighting with `:class` binding to Alpine store
|
|
- Empty state with `x-show`
|
|
- Click handlers with `@click`
|
|
|
|
**Note**: Dataset modal did NOT have sort/search controls before migration, so none were added.
|
|
|
|
### Implementation Approach
|
|
|
|
1. Add Alpine store with `currentDatasetId` for selection state
|
|
2. Create `datasetList()` component as thin wrapper around existing logic
|
|
3. Move meta formatting logic from inline HTML strings to component methods
|
|
4. Convert HTML to use Alpine directives
|
|
5. Update `renderDatasetList()` to trigger Alpine component refresh
|
|
6. Update `selectDataset()` to update Alpine store instead of manual DOM manipulation
|
|
|
|
### What Stays Vanilla
|
|
|
|
- DatasetStorage (IndexedDB operations)
|
|
- Dataset detail panel
|
|
- Preview table rendering
|
|
- Import/export logic
|
|
- All dataset form/edit functionality
|
|
|
|
### Key Learnings
|
|
|
|
- Alpine component is very thin
|
|
- Most logic moved from inline HTML strings to component methods
|
|
- Net code increase: only +20 lines total
|
|
- Same pattern as Phase 1: minimal, focused conversion
|
|
- Don't add features during migration - only convert what exists
|
|
|
|
---
|
|
|
|
## Phase 3: View Mode Toggle (Draft/Published) ✅ COMPLETE
|
|
|
|
**Status**: Done
|
|
**Files**: `index.html`, `src/js/snippet-manager.js`, `src/js/app.js`, `src/js/config.js`
|
|
|
|
### What Was Converted
|
|
|
|
- View mode toggle buttons (Draft/Published) with `:class` binding and `@click` handlers
|
|
- All references to `currentViewMode` global variable now use Alpine store
|
|
- Removed vanilla event listeners from app.js
|
|
- Removed `currentViewMode` global variable from config.js
|
|
|
|
### Implementation Approach
|
|
|
|
1. Added `viewMode` property to Alpine snippets store (default: 'draft')
|
|
2. Converted button HTML to use `:class` binding and `@click` handlers
|
|
3. Updated all references to `currentViewMode` to use `Alpine.store('snippets').viewMode`
|
|
4. Simplified `updateViewModeUI()` function (now only handles publish/revert button visibility)
|
|
5. Removed vanilla event listeners from app.js
|
|
|
|
### What Stays Vanilla
|
|
|
|
- Editor integration logic
|
|
- Publish/discard actions
|
|
- Publish/revert button visibility logic (handled by `updateViewModeUI`)
|
|
|
|
### Key Learnings
|
|
|
|
- Alpine store provides clean reactive state for view mode
|
|
- Toggle button active states now automatically update via Alpine `:class` binding
|
|
- All business logic references updated to use Alpine store instead of global variable
|
|
- `updateViewModeUI` simplified but still needed for publish/revert button management
|
|
|
|
---
|
|
|
|
## Phase 4: Settings Modal ✅ COMPLETE
|
|
|
|
**Status**: Done
|
|
**Files**: `src/js/user-settings.js`, `index.html`, `src/js/app.js`
|
|
|
|
### What Was Converted
|
|
|
|
- Settings modal form with all input controls using `x-model`
|
|
- Apply/Reset/Cancel buttons with `@click` handlers
|
|
- Computed `isDirty` property to enable/disable Apply button
|
|
- Conditional custom date format field with `x-show`
|
|
- Slider value displays with `x-text`
|
|
- All form state management moved to Alpine component
|
|
|
|
### Implementation Approach
|
|
|
|
1. Created `settingsPanel()` component in `user-settings.js` with form state tracking
|
|
2. Used `x-model` (with `.number` modifier where needed) for all form inputs:
|
|
- Theme selects
|
|
- Font size and render debounce sliders
|
|
- Tab size select
|
|
- Checkboxes (minimap, word wrap, line numbers)
|
|
- Date format and custom format input
|
|
3. Added computed `isDirty` getter to compare current vs. original state
|
|
4. Added computed `showCustomDateFormat` getter for conditional field visibility
|
|
5. Moved all apply/reset/cancel logic into Alpine component methods
|
|
6. Removed vanilla event listeners and old `loadSettingsIntoUI()` / `applySettings()` functions
|
|
|
|
### What Stays Vanilla
|
|
|
|
- Settings storage layer (getSettings, updateSettings, etc.)
|
|
- Settings validation logic (validateSetting)
|
|
- Editor option updates (applied from within Alpine component)
|
|
- Theme application logic (applied from within Alpine component)
|
|
- Modal open/close functions (simple ModalManager calls)
|
|
|
|
### Key Learnings
|
|
|
|
- Alpine component handles all form state reactivity
|
|
- `isDirty` computed property automatically enables/disables Apply button
|
|
- `x-model.number` modifier ensures numeric values for sliders and selects
|
|
- `x-show` provides clean conditional rendering for custom date format field
|
|
- All business logic (validation, saving, applying) stays in existing functions
|
|
- Net code reduction: ~150 lines of manual DOM manipulation removed
|
|
|
|
---
|
|
|
|
## Phase 5: Chart Builder Modal
|
|
|
|
**Status**: Planned
|
|
**Files**: `src/js/chart-builder.js`, `index.html`
|
|
|
|
### What to Convert
|
|
|
|
Chart builder form with dataset selection, chart type, and field mappings.
|
|
|
|
### Implementation Approach
|
|
|
|
1. Create `chartBuilder()` component with form state
|
|
2. Load datasets on init, populate dropdowns with `x-for`
|
|
3. Track selected dataset and available fields
|
|
4. Generate spec preview with computed property
|
|
5. Enable/disable insert button based on validation
|
|
|
|
### What Stays Vanilla
|
|
|
|
- Dataset field detection
|
|
- Type inference logic
|
|
- Spec generation utilities
|
|
|
|
---
|
|
|
|
## Phase 6: Meta Fields (Name, Comment)
|
|
|
|
**Status**: Planned
|
|
**Files**: `index.html`, `src/js/snippet-manager.js`
|
|
|
|
### What to Convert
|
|
|
|
Name and comment fields with auto-save functionality.
|
|
|
|
### Implementation Approach
|
|
|
|
1. Add `snippetName` and `snippetComment` to `snippetList()` component
|
|
2. Use `x-model` with debounced input handlers
|
|
3. Call `loadMetadata()` when snippet selected
|
|
4. Auto-save on change with 500ms debounce
|
|
|
|
### What Stays Vanilla
|
|
|
|
- SnippetStorage save operations
|
|
|
|
---
|
|
|
|
## Phase 7: Panel Visibility Toggles
|
|
|
|
**Status**: Planned
|
|
**Files**: `index.html`, `src/js/panel-manager.js`
|
|
|
|
### What to Convert
|
|
|
|
Toggle buttons for showing/hiding panels.
|
|
|
|
### Implementation Approach
|
|
|
|
1. Create Alpine store for UI state (panel visibility flags)
|
|
2. Convert toggle buttons to use `:class` and `@click`
|
|
3. Add `x-show` to panels with transitions
|
|
4. Persist visibility state to localStorage
|
|
|
|
### What Stays Vanilla
|
|
|
|
- Panel resizing logic
|
|
- Keyboard shortcuts
|
|
|
|
---
|
|
|
|
## Phase 8: Toast Notifications (Optional)
|
|
|
|
**Status**: Planned
|
|
**Files**: `src/js/config.js`, `index.html`
|
|
|
|
### What to Convert
|
|
|
|
Toast notification system with auto-dismiss.
|
|
|
|
### Implementation Approach
|
|
|
|
1. Create Alpine store for toast queue
|
|
2. Render toasts with `x-for` and transitions
|
|
3. Update `Toast` utility to add items to Alpine store
|
|
4. Auto-dismiss with setTimeout
|
|
|
|
### What Stays Vanilla
|
|
|
|
- Toast message generation
|
|
|
|
---
|
|
|
|
## Recommended Order
|
|
|
|
1. ✅ **Phase 1: Snippet Panel** - DONE
|
|
2. ✅ **Phase 2: Dataset Manager** - DONE
|
|
3. ✅ **Phase 3: View Mode Toggle** - DONE
|
|
4. ✅ **Phase 4: Settings Modal** - DONE
|
|
5. **Phase 6: Meta Fields** - Before Chart Builder (simpler)
|
|
6. **Phase 7: Panel Toggles** - Quick win
|
|
7. **Phase 5: Chart Builder** - More complex, save for when confident
|
|
8. **Phase 8: Toast Notifications** - Optional polish
|
|
|
|
---
|
|
|
|
## Emergency Rollback Plan
|
|
|
|
If a phase causes issues:
|
|
|
|
1. **Quick Fix**: Comment out Alpine directives, uncomment vanilla JS
|
|
2. **Git Revert**: Each phase is a separate commit
|
|
3. **Hybrid Mode**: Alpine and vanilla can coexist - revert problematic sections only
|
|
|
|
---
|
|
|
|
## Post-Migration
|
|
|
|
After all phases complete:
|
|
|
|
### Code Cleanup
|
|
- Remove no-op functions
|
|
- Remove unused vanilla event listeners
|
|
- Clean up global state variables
|
|
- Update JSDoc comments
|
|
|
|
### Documentation
|
|
- Update architecture.md
|
|
- Document Alpine components
|
|
- Add Alpine.js to dependencies list
|
|
- Update CLAUDE.md with Alpine patterns
|
|
|
|
---
|
|
|
|
## Questions & Decisions Log
|
|
|
|
| Date | Phase | Question | Decision | Rationale |
|
|
|------|-------|----------|----------|-----------|
|
|
| 2025-01-24 | 1 | Store snippets in Alpine or Storage? | Storage | Single source of truth, Alpine just views |
|
|
| 2025-01-24 | 1 | Keep old functions as stubs? | Yes | Backwards compatibility, easier rollback |
|
|
| 2025-01-24 | 2 | Add sort/search to dataset modal? | No | Migration only - don't add features that didn't exist |
|
|
| 2025-01-24 | 2 | How much net code increase is acceptable? | ~20 lines | Alpine boilerplate worth it for reactivity gains |
|
|
|
|
---
|
|
|
|
## Success Metrics
|
|
|
|
### Quantitative
|
|
- ~300+ lines of code removed overall
|
|
- No performance regression
|
|
- Zero increase in bug reports
|
|
- All tests passing
|
|
|
|
### Qualitative
|
|
- Code is more readable
|
|
- New features easier to add
|
|
- Less manual DOM manipulation
|
|
- Clearer separation of concerns
|