diff --git a/app/templates/library.html b/app/templates/library.html index ad7fcfd..c629e4b 100644 --- a/app/templates/library.html +++ b/app/templates/library.html @@ -220,61 +220,7 @@
Last edited
Last visited
-
- +
@@ -369,6 +315,61 @@ function libraryPage() { // โ”€โ”€ Computed flat list (NOT a getter โ€” Alpine 3.14.9 bug) โ”€โ”€ flatItems: [], + tableHtml: '', + + _buildTableHtml() { + var self = this; + var html = ''; + for (var i = 0; i < this.flatItems.length; i++) { + var item = this.flatItems[i]; + var selClass = this.selected[item.id] ? ' selected' : ''; + var indent = 'indent-' + (item.depth || 0); + var checkClass = this.selected[item.id] ? ' checked' : ''; + var chevOpen = this.expanded[item.id] ? ' open' : ''; + var isRenaming = this.renamingId === item.id; + html += '
'; + html += '
'; + // Drag handle + html += '
'; + html += ''; + html += ''; + html += '
'; + // Checkbox + html += '
'; + // Chevron or spacer + if (item.has_children) { + html += ''; + } else { + html += ''; + } + // Icon + html += '' + (item.icon || '\ud83d\udcc4') + ''; + // Title or rename input + if (isRenaming) { + html += ''; + } else { + html += '' + self._escHtml(item.title) + ''; + } + // OPEN button + html += ''; + html += '
'; + // Author + html += '
'; + html += '
' + (item.author_initial || '?') + '
'; + html += '' + (item.author || '\u2014') + '
'; + // Source + html += '
' + (item.source_label || 'Private') + '
'; + // Edited + html += '
' + self.formatTime(item.updated_at) + '
'; + // Visited + html += '
' + self.formatTime(item.visited_at) + '
'; + html += '
'; + } + this.tableHtml = html; + }, + + _escHtml(s) { var d=document.createElement('div'); d.textContent = s||''; return d.innerHTML; }, + _escAttr(s) { return String(s||'').replace(/&/g,'&').replace(/"/g,'"').replace(//g,'>'); }, _recomputeFlatItems() { var result = []; @@ -383,6 +384,45 @@ function libraryPage() { this.flatItems.splice(0, this.flatItems.length, ...result); this.selectedCount = Object.keys(this.selected).length; this.allSelected = this.flatItems.length > 0 && this.selectedCount === this.flatItems.length; + this._buildTableHtml(); + }, + + // โ”€โ”€ Global dispatch (bypasses Alpine x-for reactivity issues) โ”€โ”€ + toggleItemById(id) { + var item = this._findItem(id); + if (!item) return; + this.toggleItem(item); + }, + toggleExpandById(id) { + var item = this._findItem(id); + if (!item) return; + this.toggleExpand(item); + }, + startRenameById(id) { + var item = this._findItem(id); + if (!item) return; + this.startRename(item); + }, + commitRenameById(id) { + var item = this._findItem(id); + if (!item) { this.renamingId = null; this._buildTableHtml(); return; } + this.commitRename(item); + }, + openItemById(id) { + var item = this._findItem(id); + if (!item) return; + this.openItem(item); + }, + openPeekById(id) { + var item = this._findItem(id); + if (!item) return; + this.openPeek(item); + }, + _findItem(id) { + for (var i = 0; i < this.flatItems.length; i++) { + if (this.flatItems[i].id === id) return this.flatItems[i]; + } + return null; }, _flatten(nodes, out, depth) { @@ -421,6 +461,7 @@ function libraryPage() { // โ”€โ”€ Init โ”€โ”€ init() { + window._libData = this; // expose for onclick handlers console.log('[Library] init start'); var wsEl = document.getElementById('lib-workspace-id'); this.workspaceId = parseInt(wsEl ? wsEl.textContent : '0') || 0; @@ -483,6 +524,7 @@ function libraryPage() { console.log('[Library] loaded ' + this.items.length + ' items'); this._markTree(this.items, 0); this._recomputeFlatItems(); + this._buildTableHtml(); console.log('[Library] flatItems after recompute: ' + this.flatItems.length); // Force Alpine to re-render (known issue with x-for in 3.14.9) var self = this; @@ -526,6 +568,7 @@ function libraryPage() { delete this.expanded[item.id]; this.expanded = Object.assign({}, this.expanded); this._recomputeFlatItems(); + this._buildTableHtml(); return; } // Expand โ€” load children if not loaded yet @@ -543,6 +586,7 @@ function libraryPage() { } } this._recomputeFlatItems(); + this._buildTableHtml(); }, _markChildren(nodes, depth) { @@ -564,6 +608,7 @@ function libraryPage() { } this.selected = Object.assign({}, this.selected); this._recomputeFlatItems(); + this._buildTableHtml(); }, toggleSelectAll() { @@ -577,12 +622,14 @@ function libraryPage() { this.selected = s; } this._recomputeFlatItems(); + this._buildTableHtml(); }, clearSelection() { this.selected = {}; this.moreOpen = false; this._recomputeFlatItems(); + this._buildTableHtml(); }, // โ”€โ”€ Multi-select actions โ”€โ”€ @@ -631,17 +678,22 @@ function libraryPage() { startRename(item) { this.renamingId = item.id; this.renameValue = item.title; + this._buildTableHtml(); var self = this; this.$nextTick(function() { - var inp = document.querySelector('.row-title input'); - if (inp) { inp.focus(); inp.select(); } + var inp = document.querySelector('.row-title input, [onkeydown*=\"commitRenameById\"]'); + // Try to find the rename input that was just created + var inputs = document.querySelectorAll('#lib-table-body input'); + for (var i = 0; i < inputs.length; i++) { + if (inputs[i].value === item.title) { inputs[i].focus(); inputs[i].select(); break; } + } }); }, async commitRename(item) { var newName = (this.renameValue || '').trim(); this.renamingId = null; - if (!newName || newName === item.title) return; + if (!newName || newName === item.title) { this._buildTableHtml(); return; } try { var r = await fetch('/api/pages/' + item.id + '/rename', { method:'PUT', headers:{'Content-Type':'application/json'}, @@ -651,6 +703,7 @@ function libraryPage() { item.title = newName; } } catch(e) {} + this._buildTableHtml(); }, // โ”€โ”€ Add new โ”€โ”€