diff --git a/collections/notes/ttt/fdff/test_sub.md b/collections/notes/ttt/fdff/test_sub.md new file mode 100644 index 0000000..9fbcf0d --- /dev/null +++ b/collections/notes/ttt/fdff/test_sub.md @@ -0,0 +1,9 @@ + +# test + +- 1 +- 2 + + + + diff --git a/collections/notes/ttt/test.md b/collections/notes/ttt/test.md index c1f1849..9fbcf0d 100644 --- a/collections/notes/ttt/test.md +++ b/collections/notes/ttt/test.md @@ -1,4 +1,9 @@ -test +# test + +- 1 +- 2 + + diff --git a/static/css/file-tree.css b/static/css/file-tree.css index 7a5206d..13cbf87 100644 --- a/static/css/file-tree.css +++ b/static/css/file-tree.css @@ -1,7 +1,12 @@ -/* File tree styles */ +/* Bootstrap-styled File Tree */ .file-tree { - font-size: 14px; + font-size: 13px; user-select: none; + padding: 8px 0; +} + +.tree-node-wrapper { + margin: 0; } .tree-node { @@ -9,11 +14,14 @@ cursor: pointer; display: flex; align-items: center; - gap: 8px; + gap: 6px; border-radius: 4px; - margin: 2px 0; + margin: 1px 4px; color: var(--text-primary); - transition: background-color 0.15s ease; + transition: all 0.15s ease; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } .tree-node:hover { @@ -21,20 +29,49 @@ } .tree-node.active { - background-color: #0969da; + background-color: var(--link-color); + color: white; + font-weight: 500; +} + +.tree-node.active:hover { + background-color: var(--link-color); + filter: brightness(1.1); +} + +/* Toggle arrow */ +.tree-node-toggle { + display: inline-flex; + align-items: center; + justify-content: center; + width: 16px; + height: 16px; + font-size: 10px; + color: var(--text-secondary); + flex-shrink: 0; + transition: transform 0.2s ease; +} + +.tree-node-toggle.expanded { + transform: rotate(90deg); +} + +/* Icon styling */ +.tree-node-icon { + width: 16px; + height: 16px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + color: var(--text-secondary); +} + +.tree-node.active .tree-node-icon { color: white; } -body.dark-mode .tree-node.active { - background-color: #1f6feb; -} - -.tree-node-icon { - width: 16px; - text-align: center; - flex-shrink: 0; -} - +/* Content wrapper */ .tree-node-content { display: flex; align-items: center; @@ -48,41 +85,89 @@ body.dark-mode .tree-node.active { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + font-size: 13px; } -.tree-node-size { - font-size: 11px; +.file-size-badge { + font-size: 10px; color: var(--text-secondary); + margin-left: auto; flex-shrink: 0; + padding: 2px 6px; + background-color: var(--bg-tertiary); + border-radius: 3px; } +/* Children container */ .tree-children { - margin-left: 16px; + margin-left: 8px; + border-left: 1px solid var(--border-light); + padding-left: 4px; + max-height: 100%; + overflow: visible; } +.tree-children.collapsed { + display: none; +} + +/* Drag and drop */ .tree-node.dragging { opacity: 0.5; } .tree-node.drag-over { - background-color: var(--info-color); - color: white; + background-color: rgba(13, 110, 253, 0.2); + border: 1px dashed var(--link-color); } -/* Collection selector */ +/* Collection selector - Bootstrap styled */ .collection-selector { - margin-bottom: 10px; - padding: 8px; + margin: 12px 8px; + padding: 8px 12px; background-color: var(--bg-tertiary); - border-radius: 4px; + border-radius: 6px; + border: 1px solid var(--border-color); } -.collection-selector select { - width: 100%; - padding: 6px; +.collection-selector .form-label { + margin-bottom: 6px; + font-weight: 500; + font-size: 12px; + color: var(--text-secondary); +} + +.collection-selector .form-select-sm { + padding: 4px 8px; + font-size: 13px; background-color: var(--bg-primary); color: var(--text-primary); border: 1px solid var(--border-color); - border-radius: 4px; } +.collection-selector .form-select-sm:focus { + border-color: var(--link-color); + box-shadow: 0 0 0 0.2rem rgba(13, 110, 253, 0.25); +} + +/* Dark mode adjustments */ +body.dark-mode .tree-node:hover { + background-color: var(--bg-tertiary); +} + +body.dark-mode .tree-node.active { + background-color: var(--link-color); +} + +body.dark-mode .tree-children { + border-left-color: var(--border-color); +} + +/* Scrollbar in sidebar */ +.sidebar::-webkit-scrollbar-thumb { + background-color: var(--border-color); +} + +.sidebar::-webkit-scrollbar-thumb:hover { + background-color: var(--text-secondary); +} \ No newline at end of file diff --git a/static/css/layout.css b/static/css/layout.css index e0a0248..96bedc8 100644 --- a/static/css/layout.css +++ b/static/css/layout.css @@ -9,31 +9,89 @@ html, body { transition: background-color 0.3s ease, color 0.3s ease; } -.container-fluid { - height: calc(100% - 56px); +/* Column Resizer */ +.column-resizer { + width: 4px; + background-color: var(--border-color); + cursor: col-resize; + transition: background-color 0.2s ease; + user-select: none; + flex-shrink: 0; } +.column-resizer:hover { + background-color: var(--link-color); + width: 6px; + margin: 0 -1px; +} + +.column-resizer.dragging { + background-color: var(--link-color); +} + +/* Adjust container for flex layout */ +.container-fluid { + display: flex; + flex-direction: row; + height: calc(100% - 56px); + padding: 0; +} + +.row { + width: 100%; + display: flex; + flex-direction: row; + margin: 0; + height: 100%; +} + +#sidebarPane { + flex: 0 0 20%; + min-width: 150px; + max-width: 40%; + padding: 0; +} + +#editorPane { + flex: 1 1 40%; + min-width: 250px; + max-width: 70%; + padding: 0; +} + +#previewPane { + flex: 1 1 40%; + min-width: 250px; + max-width: 70%; + padding: 0; +} + +/* Sidebar - improved */ .sidebar { background-color: var(--bg-secondary); border-right: 1px solid var(--border-color); overflow-y: auto; + overflow-x: hidden; height: 100%; transition: background-color 0.3s ease; -} - -.editor-pane { - background-color: var(--bg-primary); - height: 100%; display: flex; flex-direction: column; - border-right: 1px solid var(--border-color); } -.preview-pane { - background-color: var(--bg-primary); - height: 100%; +.sidebar h6 { + margin: 12px 8px 6px; + font-size: 11px; + font-weight: 600; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +#fileTree { + flex: 1; overflow-y: auto; - padding: 20px; + overflow-x: hidden; + padding: 4px 0; } /* Navbar */ diff --git a/static/js/app.js b/static/js/app.js index 8859f6b..d090301 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -23,18 +23,18 @@ document.addEventListener('DOMContentLoaded', async () => { darkMode.toggle(); }); + // Initialize file tree + fileTree = new FileTree('fileTree', webdavClient); + fileTree.onFileSelect = async (item) => { + await loadFile(item.path); + }; + // Initialize collection selector collectionSelector = new CollectionSelector('collectionSelect', webdavClient); collectionSelector.onChange = async (collection) => { await fileTree.load(); }; await collectionSelector.load(); - - // Initialize file tree - fileTree = new FileTree('fileTree', webdavClient); - fileTree.onFileSelect = async (item) => { - await loadFile(item.path); - }; await fileTree.load(); // Initialize editor @@ -68,6 +68,13 @@ document.addEventListener('DOMContentLoaded', async () => { mermaid.initialize({ startOnLoad: true, theme: darkMode.isDark ? 'dark' : 'default' }); }); +// Listen for column resize events to refresh editor +window.addEventListener('column-resize', () => { + if (editor && editor.editor) { + editor.editor.refresh(); + } +}); + /** * File Operations */ diff --git a/static/js/column-resizer.js b/static/js/column-resizer.js new file mode 100644 index 0000000..c00ef06 --- /dev/null +++ b/static/js/column-resizer.js @@ -0,0 +1,102 @@ +/** + * Column Resizer Module + * Handles draggable column dividers + */ + +class ColumnResizer { + constructor() { + this.resizer1 = document.getElementById('resizer1'); + this.resizer2 = document.getElementById('resizer2'); + this.sidebarPane = document.getElementById('sidebarPane'); + this.editorPane = document.getElementById('editorPane'); + this.previewPane = document.getElementById('previewPane'); + + // Load saved dimensions + this.loadDimensions(); + + // Setup listeners + this.setupResizers(); + } + + setupResizers() { + this.resizer1.addEventListener('mousedown', (e) => this.startResize(e, 1)); + this.resizer2.addEventListener('mousedown', (e) => this.startResize(e, 2)); + } + + startResize(e, resizerId) { + e.preventDefault(); + + const startX = e.clientX; + const startWidth1 = this.sidebarPane.offsetWidth; + const startWidth2 = this.editorPane.offsetWidth; + const containerWidth = this.sidebarPane.parentElement.offsetWidth; + + const resizer = resizerId === 1 ? this.resizer1 : this.resizer2; + resizer.classList.add('dragging'); + + const handleMouseMove = (moveEvent) => { + const deltaX = moveEvent.clientX - startX; + + if (resizerId === 1) { + // Resize sidebar and editor + const newWidth1 = Math.max(150, Math.min(40 * containerWidth / 100, startWidth1 + deltaX)); + const newWidth2 = startWidth2 - (newWidth1 - startWidth1); + + this.sidebarPane.style.flex = `0 0 ${newWidth1}px`; + this.editorPane.style.flex = `1 1 ${newWidth2}px`; + } else if (resizerId === 2) { + // Resize editor and preview + const newWidth2 = Math.max(250, Math.min(70 * containerWidth / 100, startWidth2 + deltaX)); + const containerFlex = this.sidebarPane.offsetWidth; + + this.editorPane.style.flex = `0 0 ${newWidth2}px`; + this.previewPane.style.flex = `1 1 auto`; + } + }; + + const handleMouseUp = () => { + resizer.classList.remove('dragging'); + document.removeEventListener('mousemove', handleMouseMove); + document.removeEventListener('mouseup', handleMouseUp); + + // Save dimensions + this.saveDimensions(); + + // Trigger editor resize + if (window.editor && window.editor.editor) { + window.editor.editor.refresh(); + } + }; + + document.addEventListener('mousemove', handleMouseMove); + document.addEventListener('mouseup', handleMouseUp); + } + + saveDimensions() { + const dimensions = { + sidebar: this.sidebarPane.offsetWidth, + editor: this.editorPane.offsetWidth, + preview: this.previewPane.offsetWidth + }; + localStorage.setItem('columnDimensions', JSON.stringify(dimensions)); + } + + loadDimensions() { + const saved = localStorage.getItem('columnDimensions'); + if (!saved) return; + + try { + const { sidebar, editor, preview } = JSON.parse(saved); + this.sidebarPane.style.flex = `0 0 ${sidebar}px`; + this.editorPane.style.flex = `0 0 ${editor}px`; + this.previewPane.style.flex = `1 1 auto`; + } catch (error) { + console.error('Failed to load column dimensions:', error); + } + } +} + +// Initialize on DOM ready +document.addEventListener('DOMContentLoaded', () => { + window.columnResizer = new ColumnResizer(); +}); \ No newline at end of file diff --git a/static/js/file-tree.js b/static/js/file-tree.js index 10707a4..10e9ec8 100644 --- a/static/js/file-tree.js +++ b/static/js/file-tree.js @@ -25,7 +25,7 @@ class FileTree { const isDir = node.dataset.isdir === 'true'; // Toggle folder - if (e.target.closest('.tree-toggle')) { + if (e.target.closest('.tree-node-toggle')) { this.toggleFolder(node); return; } @@ -83,21 +83,23 @@ class FileTree { } createNodeElement(node, level) { + const nodeWrapper = document.createElement('div'); + nodeWrapper.className = 'tree-node-wrapper'; + nodeWrapper.style.marginLeft = `${level * 12}px`; + const div = document.createElement('div'); div.className = 'tree-node'; div.dataset.path = node.path; div.dataset.isdir = node.isDirectory; - div.style.paddingLeft = `${level * 20 + 10}px`; // Toggle arrow for folders if (node.isDirectory) { const toggle = document.createElement('span'); - toggle.className = 'tree-toggle'; - toggle.innerHTML = ''; + toggle.className = 'tree-node-toggle'; + toggle.innerHTML = '▶'; div.appendChild(toggle); } else { const spacer = document.createElement('span'); - spacer.className = 'tree-spacer'; spacer.style.width = '16px'; spacer.style.display = 'inline-block'; div.appendChild(spacer); @@ -105,45 +107,47 @@ class FileTree { // Icon const icon = document.createElement('i'); - if (node.isDirectory) { - icon.className = 'bi bi-folder-fill'; - icon.style.color = '#dcb67a'; - } else { - icon.className = 'bi bi-file-earmark-text'; - icon.style.color = '#6a9fb5'; - } + icon.className = `bi ${node.isDirectory ? 'bi-folder-fill' : 'bi-file-earmark-text'} tree-node-icon`; div.appendChild(icon); + // Content wrapper + const contentWrapper = document.createElement('div'); + contentWrapper.className = 'tree-node-content'; + // Name const name = document.createElement('span'); - name.className = 'tree-name'; + name.className = 'tree-node-name'; name.textContent = node.name; - div.appendChild(name); + name.title = node.name; // Tooltip on hover + contentWrapper.appendChild(name); // Size for files if (!node.isDirectory && node.size) { const size = document.createElement('span'); - size.className = 'tree-size'; + size.className = 'file-size-badge'; size.textContent = this.formatSize(node.size); - div.appendChild(size); + contentWrapper.appendChild(size); } - return div; + div.appendChild(contentWrapper); + nodeWrapper.appendChild(div); + + return nodeWrapper; } toggleFolder(nodeElement) { const childContainer = nodeElement.querySelector('.tree-children'); if (!childContainer) return; - const toggle = nodeElement.querySelector('.tree-toggle i'); + const toggle = nodeElement.querySelector('.tree-node-toggle'); const isExpanded = childContainer.style.display !== 'none'; if (isExpanded) { childContainer.style.display = 'none'; - toggle.className = 'bi bi-chevron-right'; + toggle.innerHTML = '▶'; } else { childContainer.style.display = 'block'; - toggle.className = 'bi bi-chevron-down'; + toggle.innerHTML = '▼'; } } diff --git a/templates/index.html b/templates/index.html index ba0d5f3..14e0242 100644 --- a/templates/index.html +++ b/templates/index.html @@ -51,19 +51,21 @@