diff --git a/index.html b/index.html
index 9fd88cc..770a67d 100644
--- a/index.html
+++ b/index.html
@@ -196,7 +196,7 @@
+
diff --git a/project-docs/alpine-migration-plan.md b/project-docs/alpine-migration-plan.md
index 7ddfe9f..df2652c 100644
--- a/project-docs/alpine-migration-plan.md
+++ b/project-docs/alpine-migration-plan.md
@@ -104,26 +104,38 @@ Incremental migration of Astrolabe from vanilla JavaScript to Alpine.js for reac
---
-## Phase 3: View Mode Toggle (Draft/Published)
+## Phase 3: View Mode Toggle (Draft/Published) ✅ COMPLETE
-**Status**: Planned
-**Files**: `index.html`, `src/js/snippet-manager.js`
+**Status**: Done
+**Files**: `index.html`, `src/js/snippet-manager.js`, `src/js/app.js`, `src/js/config.js`
-### What to Convert
+### What Was Converted
-View mode toggle buttons (Draft/Published) currently use manual class manipulation.
+- 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. Add `viewMode` property to Alpine snippets store (default: 'draft')
-2. Convert button HTML to use `:class` binding and `@click` handlers
-3. Update `loadSnippetIntoEditor()` to read view mode from Alpine store
-4. Remove `updateViewModeUI()` function (no longer needed)
+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
---
@@ -248,7 +260,7 @@ Toast notification system with auto-dismiss.
1. ✅ **Phase 1: Snippet Panel** - DONE
2. ✅ **Phase 2: Dataset Manager** - DONE
-3. **Phase 3: View Mode Toggle** - Quick win
+3. ✅ **Phase 3: View Mode Toggle** - DONE
4. **Phase 4: Settings Modal** - Another modal, builds confidence
5. **Phase 6: Meta Fields** - Before Chart Builder (simpler)
6. **Phase 7: Panel Toggles** - Quick win
diff --git a/src/js/app.js b/src/js/app.js
index 929b683..afb2251 100644
--- a/src/js/app.js
+++ b/src/js/app.js
@@ -367,14 +367,7 @@ document.addEventListener('DOMContentLoaded', function () {
}
});
- // View mode toggle buttons
- document.getElementById('view-draft').addEventListener('click', () => {
- switchViewMode('draft');
- });
-
- document.getElementById('view-published').addEventListener('click', () => {
- switchViewMode('published');
- });
+ // View mode toggle buttons (now handled by Alpine.js in index.html)
// Preview fit mode buttons
document.getElementById('preview-fit-default').addEventListener('click', () => {
@@ -481,7 +474,7 @@ const KeyboardActions = {
},
publishDraft: function() {
- if (currentViewMode === 'draft' && window.currentSnippetId) {
+ if (Alpine.store('snippets').viewMode === 'draft' && window.currentSnippetId) {
publishDraft();
}
},
diff --git a/src/js/config.js b/src/js/config.js
index a6fc3a1..4c174f4 100644
--- a/src/js/config.js
+++ b/src/js/config.js
@@ -4,7 +4,6 @@ const APP_VERSION = '0.3.0';
// Global variables and configuration
let editor; // Global editor instance
let renderTimeout; // For debouncing
-let currentViewMode = 'draft'; // Track current view mode: 'draft' or 'published'
// Panel resizing variables
let isResizing = false;
diff --git a/src/js/snippet-manager.js b/src/js/snippet-manager.js
index e17252e..e147a5c 100644
--- a/src/js/snippet-manager.js
+++ b/src/js/snippet-manager.js
@@ -4,7 +4,8 @@
// Business logic stays in SnippetStorage
document.addEventListener('alpine:init', () => {
Alpine.store('snippets', {
- currentSnippetId: null
+ currentSnippetId: null,
+ viewMode: 'draft' // 'draft' or 'published'
});
});
@@ -559,7 +560,7 @@ function autoSaveDraft() {
if (!window.currentSnippetId || !editor) return;
// Only save to draft if we're in draft mode
- if (currentViewMode !== 'draft') return;
+ if (Alpine.store('snippets').viewMode !== 'draft') return;
try {
const currentSpec = JSON.parse(editor.getValue());
@@ -592,13 +593,13 @@ function debouncedAutoSave() {
if (window.isUpdatingEditor) return;
// If viewing published and no draft exists, create draft automatically
- if (currentViewMode === 'published') {
+ if (Alpine.store('snippets').viewMode === 'published') {
const snippet = getCurrentSnippet();
if (snippet) {
const hasDraft = JSON.stringify(snippet.spec) !== JSON.stringify(snippet.draftSpec);
if (!hasDraft) {
// No draft exists, automatically switch to draft mode
- currentViewMode = 'draft';
+ Alpine.store('snippets').viewMode = 'draft';
updateViewModeUI(snippet);
editor.updateOptions({ readOnly: false });
}
@@ -859,7 +860,7 @@ async function extractToDataset() {
SnippetStorage.saveSnippet(snippet);
// Update editor with new spec
- if (editor && currentViewMode === 'draft') {
+ if (editor && Alpine.store('snippets').viewMode === 'draft') {
window.isUpdatingEditor = true;
editor.setValue(JSON.stringify(snippet.draftSpec, null, 2));
window.isUpdatingEditor = false;
@@ -935,7 +936,7 @@ function loadSnippetIntoEditor(snippet) {
const hasDraft = JSON.stringify(snippet.spec) !== JSON.stringify(snippet.draftSpec);
- if (currentViewMode === 'draft') {
+ if (Alpine.store('snippets').viewMode === 'draft') {
editor.setValue(JSON.stringify(snippet.draftSpec, null, 2));
editor.updateOptions({ readOnly: false });
} else {
@@ -954,19 +955,11 @@ function updateViewModeUI(snippet) {
const publishBtn = document.getElementById('publish-btn');
const revertBtn = document.getElementById('revert-btn');
- // Update toggle button states
- if (currentViewMode === 'draft') {
- draftBtn.classList.add('active');
- publishedBtn.classList.remove('active');
- } else {
- draftBtn.classList.remove('active');
- publishedBtn.classList.add('active');
- }
-
- // Show/hide and enable/disable action buttons based on mode
+ // Update toggle button states (now handled by Alpine :class binding)
+ // But we still need to update the action buttons (publish/revert)
const hasDraft = JSON.stringify(snippet.spec) !== JSON.stringify(snippet.draftSpec);
- if (currentViewMode === 'draft') {
+ if (Alpine.store('snippets').viewMode === 'draft') {
// In draft mode: show both buttons, enable based on draft existence
publishBtn.classList.add('visible');
revertBtn.classList.add('visible');
@@ -981,7 +974,7 @@ function updateViewModeUI(snippet) {
// Switch view mode
function switchViewMode(mode) {
- currentViewMode = mode;
+ Alpine.store('snippets').viewMode = mode;
const snippet = getCurrentSnippet();
if (snippet) {
loadSnippetIntoEditor(snippet);
@@ -1026,7 +1019,7 @@ function revertDraft() {
SnippetStorage.saveSnippet(snippet);
// Reload editor if in draft view
- if (currentViewMode === 'draft') {
+ if (Alpine.store('snippets').viewMode === 'draft') {
loadSnippetIntoEditor(snippet);
}