diff --git a/app/templates/base.html b/app/templates/base.html index 6cf449a..1cc24ea 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -116,14 +116,18 @@ @dragover.prevent="dragOver($event, {{ page.db_id }}, {{ 'true' if page.is_folder else 'false' }})" @dragleave="dragLeave($event)" @drop.prevent="drop($event, {{ page.db_id }})" - style="padding-left:{{ 8 + depth * 16 }}px;"> + style="padding-left:{{ 4 + depth * 12 }}px;" + @contextmenu.prevent="showWsContext($event, {{ page.db_id }}, '{{ page.name | e }}', {{ 'true' if page.is_folder else 'false' }})" + @touchstart="wsTouchStart($event)" + @touchend="wsTouchEnd($event, {{ page.db_id }}, '{{ page.name | e }}', {{ 'true' if page.is_folder else 'false' }})" + @touchmove="wsTouchMove($event)"> {% if page.is_folder %} {% else %} - + {% endif %} {{ page.icon }} {% if page.is_folder %} @@ -134,13 +138,6 @@ {% else %} {{ page.name }} {% endif %} - {% if page.children %}
  • @@ -446,6 +443,36 @@ + +
    + +
    +
    + ✏️ Rename +
    +
    + 🗑 Delete +
    +
    +
    +
    + +
    +
    +
    +
    @@ -492,6 +519,64 @@ contextActions: {}, showNewPageMenu: false, + // ── Workspace tree context menu ── + wsCtx: { visible: false, x: 0, y: 0, id: null, name: '', isFolder: false }, + wsTouchTimer: null, + showWsContext(ev, id, name, isFolder) { + this.wsCtx = { visible: true, x: ev.clientX, y: ev.clientY, id: id, name: name, isFolder: isFolder }; + }, + hideWsContext() { this.wsCtx.visible = false; }, + wsTouchStart(ev) { + var self = this; + this.wsTouchTimer = setTimeout(function() { + self.wsTouchTimer = null; + var el = ev.target.closest('.ws-tree-item'); + if (el) { + var id = parseInt(el.getAttribute('data-id')); + var name = el.getAttribute('data-name') || ''; + var isFolder = el.getAttribute('data-is-folder') === 'true'; + var touch = ev.touches[0]; + self.wsCtx = { visible: true, x: touch.clientX, y: touch.clientY, id: id, name: name, isFolder: isFolder }; + } + }, 500); + }, + wsTouchEnd(ev, id, name, isFolder) { + if (this.wsTouchTimer) { clearTimeout(this.wsTouchTimer); this.wsTouchTimer = null; return; } + // Already opened by touchstart; prevent Alpine @touchend from also firing the context menu + ev.preventDefault(); + }, + wsTouchMove(ev) { + if (this.wsTouchTimer) { clearTimeout(this.wsTouchTimer); this.wsTouchTimer = null; } + }, + wsCtxAction(action) { + var id = this.wsCtx.id; + var name = this.wsCtx.name; + this.wsCtx.visible = false; + if (!id) return; + switch(action) { + case 'new-file': + this.newPageInFolder(id); + break; + case 'new-folder': + this.newFolderInFolder(id); + break; + case 'rename': + var newName = prompt('Rename:', name); + if (newName && newName.trim()) { + fetch('/api/local-workspace/items/' + id, { + method: 'PUT', + headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': this.getCsrfToken() }, + body: JSON.stringify({ name: newName.trim() }) + }).then(function(r) { if (r.ok) window.location.reload(); }); + } + break; + case 'delete': + if (!confirm('Delete "' + name + '"?')) break; + this.deleteWorkspacePage(id); + break; + } + }, + // ── Favorites tracking ── favoriteIds: new Set([{% for p in favorite_pages %}'{{ p.id }}'{% if not loop.last %},{% endif %}{% endfor %}]), diff --git a/app/templates/local_workspace.html b/app/templates/local_workspace.html index 7f1d481..504474f 100644 --- a/app/templates/local_workspace.html +++ b/app/templates/local_workspace.html @@ -158,6 +158,8 @@ 📁
    + +
    @@ -448,6 +450,41 @@ document.addEventListener('alpine:init', function() { window.location = '/local-workspace?folder=' + id; }, + // ── Expand / Collapse all ── + expandAll() { + var self = this; + this._walkTree(this.tree, function(n) { + if (n.is_folder && n.children && n.children.length > 0) { + self.expanded[n.id] = true; + } + }); + this.expanded = Object.assign({}, this.expanded); + // Toggle static HTML for renderChildren + this._toggleAllUl(this.$el, true); + }, + collapseAll() { + this.expanded = {}; + this._toggleAllUl(this.$el, false); + }, + _walkTree(nodes, fn) { + for (var i=0; i