diff --git a/package-lock.json b/package-lock.json index f6ba400..2667aed 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "electron-log": "^5.3.0", "electron-updater": "^6.3.0", "express": "^4.21.0", + "marked": "^17.0.4", "morphdom": "^2.7.8", "node-pty": "^1.0.0", "ws": "^8.18.0" @@ -5999,6 +6000,18 @@ "dev": true, "license": "MIT" }, + "node_modules/marked": { + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/marked/-/marked-17.0.4.tgz", + "integrity": "sha512-NOmVMM+KAokHMvjWmC5N/ZOvgmSWuqJB8FoYI019j4ogb/PeRMKoKIjReZ2w3376kkA8dSJIP8uD993Kxc0iRQ==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, "node_modules/matcher": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", diff --git a/package.json b/package.json index ba9b9b8..a33fd3a 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "electron-log": "^5.3.0", "electron-updater": "^6.3.0", "express": "^4.21.0", + "marked": "^17.0.4", "morphdom": "^2.7.8", "node-pty": "^1.0.0", "ws": "^8.18.0" diff --git a/public/app.js b/public/app.js index f5a0ccc..60aa856 100644 --- a/public/app.js +++ b/public/app.js @@ -22,11 +22,14 @@ const planViewerEditorEl = document.getElementById('plan-viewer-editor'); const planCopyPathBtn = document.getElementById('plan-copy-path-btn'); const planCopyContentBtn = document.getElementById('plan-copy-content-btn'); const planSaveBtn = document.getElementById('plan-save-btn'); +const planPreviewBtn = document.getElementById('plan-preview-btn'); +const planViewerPreviewEl = document.getElementById('plan-viewer-preview'); let currentPlanContent = ''; let currentPlanFilePath = ''; let currentPlanFilename = ''; let planEditorView = null; +let planPreviewMode = false; const loadingStatus = document.getElementById('loading-status'); const sessionFilters = document.getElementById('session-filters'); const searchBar = document.getElementById('search-bar'); @@ -41,6 +44,8 @@ const memoryViewerEditorEl = document.getElementById('memory-viewer-editor'); const memoryCopyPathBtn = document.getElementById('memory-copy-path-btn'); const memoryCopyContentBtn = document.getElementById('memory-copy-content-btn'); const memorySaveBtn = document.getElementById('memory-save-btn'); +const memoryPreviewBtn = document.getElementById('memory-preview-btn'); +const memoryViewerPreviewEl = document.getElementById('memory-viewer-preview'); const terminalArea = document.getElementById('terminal-area'); const settingsViewer = document.getElementById('settings-viewer'); const settingsViewerTitle = document.getElementById('settings-viewer-title'); @@ -1861,6 +1866,14 @@ async function openPlan(plan) { planViewerTitle.textContent = plan.title; planViewerFilepath.textContent = currentPlanFilePath; + // Reset preview mode when switching files + if (planPreviewMode) { + planPreviewMode = toggleMarkdownPreview({ + editorEl: planViewerEditorEl, previewEl: planViewerPreviewEl, + toggleBtn: planPreviewBtn, editorView: planEditorView, isPreview: true, + }); + } + // Create or update CodeMirror editor if (!planEditorView) { planEditorView = window.createPlanEditor(planViewerEditorEl); @@ -1896,6 +1909,34 @@ planSaveBtn.addEventListener('click', async () => { flashButtonText(planSaveBtn, 'Saved!'); }); +function toggleMarkdownPreview({ editorEl, previewEl, toggleBtn, editorView, isPreview }) { + if (!isPreview) { + const content = editorView ? editorView.state.doc.toString() : ''; + previewEl.innerHTML = window.marked.parse(content); + editorEl.style.display = 'none'; + previewEl.style.display = 'block'; + toggleBtn.textContent = 'Edit'; + toggleBtn.classList.add('active'); + return true; + } else { + previewEl.style.display = 'none'; + editorEl.style.display = ''; + toggleBtn.textContent = 'Preview'; + toggleBtn.classList.remove('active'); + return false; + } +} + +planPreviewBtn.addEventListener('click', () => { + planPreviewMode = toggleMarkdownPreview({ + editorEl: planViewerEditorEl, + previewEl: planViewerPreviewEl, + toggleBtn: planPreviewBtn, + editorView: planEditorView, + isPreview: planPreviewMode, + }); +}); + function hideAllViewers() { planViewer.style.display = 'none'; statsViewer.style.display = 'none'; @@ -2503,6 +2544,7 @@ let cachedMemoryData = { global: { files: [] }, projects: [] }; let memoryEditorView = null; let currentMemoryFilePath = null; let currentMemoryContent = ''; +let memoryPreviewMode = false; const memoryCollapsedState = new Map(); // key → boolean (true = collapsed) async function loadMemories() { @@ -2642,6 +2684,14 @@ async function openMemory(file) { memoryViewerTitle.textContent = file.filename; memoryViewerFilename.textContent = file.filePath; + // Reset preview mode when switching files + if (memoryPreviewMode) { + memoryPreviewMode = toggleMarkdownPreview({ + editorEl: memoryViewerEditorEl, previewEl: memoryViewerPreviewEl, + toggleBtn: memoryPreviewBtn, editorView: memoryEditorView, isPreview: true, + }); + } + // Create or update CodeMirror editor if (!memoryEditorView) { memoryEditorView = window.createPlanEditor(memoryViewerEditorEl); @@ -2671,6 +2721,16 @@ memorySaveBtn.addEventListener('click', async () => { } }); +memoryPreviewBtn.addEventListener('click', () => { + memoryPreviewMode = toggleMarkdownPreview({ + editorEl: memoryViewerEditorEl, + previewEl: memoryViewerPreviewEl, + toggleBtn: memoryPreviewBtn, + editorView: memoryEditorView, + isPreview: memoryPreviewMode, + }); +}); + // --- New session dialog --- async function resolveDefaultSessionOptions(project) { const effective = await window.api.getEffectiveSettings(project.projectPath); diff --git a/public/codemirror-setup.js b/public/codemirror-setup.js index 3a38a7d..fc0d89c 100644 --- a/public/codemirror-setup.js +++ b/public/codemirror-setup.js @@ -13,6 +13,7 @@ import { python } from '@codemirror/lang-python'; import { json } from '@codemirror/lang-json'; import { html } from '@codemirror/lang-html'; import { css } from '@codemirror/lang-css'; +import { marked } from 'marked'; import { rust } from '@codemirror/lang-rust'; import { go } from '@codemirror/lang-go'; import { java } from '@codemirror/lang-java'; @@ -202,3 +203,6 @@ window.createUnifiedMergeViewer = createUnifiedMergeViewer; window.CMEditorView = EditorView; window.CMEditorState = EditorState; window.CMMergeView = MergeView; + +marked.setOptions({ breaks: true, gfm: true }); +window.marked = marked; diff --git a/public/index.html b/public/index.html index 18afd3e..16ca8cf 100644 --- a/public/index.html +++ b/public/index.html @@ -58,11 +58,13 @@