mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-10-01 17:29:29 +05:30
feat(sidebar): daily-notes panel + Q&A deep-link to file offset
Daily notes panel:
- src/sidebar/daily-notes-panel.js — new sidebar panel that lists every
YYYY-MM-DD.md in the daily-notes dir (newest first), with a Today
button that creates/opens today's entry. Refresh button reloads.
Clicking a row calls onOpenFile(path). DOM is built with textContent
for dynamic fields — no XSS surface from a hostile filename.
- src/main.js — daily-notes:list IPC now returns absolute paths (joined
with the daily-notes dir) so the renderer can pass them straight to
open-file-path without re-synthesizing.
- src/renderer.js — registers the panel; icon for the daily-notes
button (calendar icon).
- src/index.html — calendar SVG icon for the daily-notes sidebar entry.
Q&A deep-link:
- src/main.js — open-file-path accepts either a string (legacy) or an
object {path, offset}. The offset is forwarded to the renderer via
file-opened.
- src/renderer.js — file-opened handler scrolls the editor to the
offset using EditorView.scrollIntoView(y: 'center') so the matching
passage lands in the middle of the visible area.
- search panel now passes {path, offset} to open-file-path so Q&A
results with chunk offsets jump straight to the passage.
Tests (12 new, tests/daily-notes-panel.test.js):
- mount, empty state, list rendering
- Today button → onOpenFile, 'Created today' / 'Today already exists' status
- error paths (openToday reject, listExisting reject, listExisting missing)
- click + Enter/Space on a row open the right path
- refresh() re-fetches, keeps status when keepStatus:true
- non-md entries filtered out
- XSS-safe textContent for dynamic data
Full suite: 73 suites, 873 tests, lint+format clean.
Amit Haridas
This commit is contained in:
+35
-2
@@ -22,6 +22,7 @@ const DOMPurify = createDOMPurify(window);
|
||||
const hljs = require('highlight.js');
|
||||
const { createEditor } = require('./editor/codemirror-setup');
|
||||
const { undo, redo } = require('@codemirror/commands');
|
||||
const { EditorView } = require('@codemirror/view');
|
||||
const { showMediaOperationsDialog } = require('./renderer/media-operations-dialog');
|
||||
const { showPdfBatchDialog } = require('./renderer/pdf-batch-dialog');
|
||||
const { showDocumentCompareDialog } = require('./renderer/document-compare-dialog');
|
||||
@@ -2241,11 +2242,27 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||
const el = document.getElementById('explorer-path');
|
||||
return el ? el.value.trim() || null : null;
|
||||
},
|
||||
onOpenFile: (filePath /* , offset */) => {
|
||||
ipcRenderer.send('open-file-path', filePath);
|
||||
onOpenFile: (filePath, offset) => {
|
||||
ipcRenderer.send('open-file-path', { path: filePath, offset });
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
// Daily notes journal browser — lists existing YYYY-MM-DD.md files and
|
||||
// surfaces a "Today" button that creates/opens today's entry. Renders
|
||||
// paths as a placeholder string; main rewrites them on click (via the
|
||||
// file-opened IPC path, which already handles any daily-notes path).
|
||||
sidebarManager.registerPanel('daily-notes', {
|
||||
title: 'Daily Notes',
|
||||
icon:
|
||||
'<svg viewBox="0 0 24 24" width="16" height="16"><path fill="currentColor" d="M19 4h-1V2h-2v2H8V2H6v2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2zm0 16H5V10h14v10zm0-12H5V6h14v2z"/></svg>',
|
||||
render: (container) =>
|
||||
require('./sidebar/daily-notes-panel').renderDailyNotesPanel(container, {
|
||||
openToday: ({ date } = {}) => ipcRenderer.invoke('daily-notes:open-today', { date }),
|
||||
listExisting: () => ipcRenderer.invoke('daily-notes:list'),
|
||||
onOpenFile: (filePath) => ipcRenderer.send('open-file-path', { path: filePath }),
|
||||
}),
|
||||
});
|
||||
sidebarManager.registerPanel('git', {
|
||||
title: 'Git',
|
||||
render: (container) =>
|
||||
@@ -2900,6 +2917,22 @@ ipcRenderer.on('file-opened', (event, data) => {
|
||||
|
||||
if (tabManager) {
|
||||
tabManager.openFile(data.path, data.content);
|
||||
// Deep-link from search/Q&A: scroll the editor to the matching offset.
|
||||
// openFile() loads the doc into the active tab; once it returns the
|
||||
// CodeMirror view exists and we can dispatch a selection.
|
||||
if (data && typeof data.offset === 'number' && data.offset > 0) {
|
||||
const tab = tabManager.tabs.get(tabManager.activeTabId);
|
||||
const view = tab && tab.editorView;
|
||||
if (view) {
|
||||
const docLen = view.state.doc.length;
|
||||
const pos = Math.min(data.offset, docLen);
|
||||
view.dispatch({
|
||||
selection: { anchor: pos, head: pos },
|
||||
effects: EditorView.scrollIntoView(pos, { y: 'center' }),
|
||||
});
|
||||
view.focus();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.error('[RENDERER] tabManager not initialized!');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user