fix: Gitea sidebar tree + header actions inline with title
FlowDeck CI / test (push) Failing after 4s
FlowDeck CI / docker (push) Has been skipped

Gitea workspace sidebar:
- loadSidebarTree() now uses innerHTML replacement instead of DOM
  manipulation (avoids Alpine reactivity overwrites)
- Ensures workspace section is visible when tree loads
- Shows error message when Gitea API fails (no token/gateway)
- Better error handling with console.error catch

Header actions inline:
- page_editor: moved Edited/Share/Copy/Favorite/More buttons into
  the unified header (right_actions), accessible via window.E
  (editorState global reference set in init)
- Removed duplicate page-topbar div from content area
- Share dialog, More menu, and Activity popover stay in content
  area (triggered by header buttons via window.E references)
- Gitea workspace: header now uses page-action-btn for consistency
This commit is contained in:
2026-07-16 13:08:14 -04:00
parent 3210ac65a6
commit f61f8b61ae
2 changed files with 31 additions and 79 deletions
+29 -51
View File
@@ -6,7 +6,7 @@
{% block topbar %}
{% set breadcrumb_items = [{"label": "Home", "url": "/workspaces"}, {"label": owner ~ "/" ~ repo, "url": None}] %}
{% set page_icon = "🔗" %}
{% set right_actions = '<button class="topbar-btn" @click="showNewFile=!showNewFile" title="New file">📄+</button><button class="topbar-btn" @click="triggerUpload()" title="Upload file">📤</button><input type="file" id="gitea-upload-input" style="display:none" @change="doUpload($event)" multiple><button class="topbar-btn" @click="refreshTree()" title="Refresh">🔄</button><a href="/accounts/settings" class="topbar-btn" title="Settings">⚙</a>' %}
{% set right_actions = '<button class="page-action-btn" @click="showNewFile=!showNewFile" title="New file">📄+</button><button class="page-action-btn" @click="triggerUpload()" title="Upload file">📤</button><input type="file" id="gitea-upload-input" style="display:none" @change="doUpload($event)" multiple><button class="page-action-btn" @click="refreshTree()" title="Refresh">🔄</button><a href="/accounts/settings" class="topbar-btn" title="Settings">⚙</a>' %}
{% include '_header.html' %}
{% endblock %}
@@ -310,63 +310,41 @@ function giteaWorkspace() {
async loadSidebarTree() {
try {
var r = await fetch('/api/gitea/projects/' + this.owner + '/' + this.repo + '/tree');
if (!r.ok) return;
if (!r.ok) {
// If API fails (e.g. no Gitea token), set a message
var container = document.getElementById('sidebar-workspace-items');
if (container) container.innerHTML = '<li class="sidebar-item empty-hint"><span class="page-icon">🔗</span><span class="page-name text-dim">Connect Gitea to browse files</span></li>';
return;
}
var d = await r.json();
var items = d.tree || [];
var container = document.getElementById('sidebar-workspace-items');
if (!container) return;
// Ensure workspace section is visible
if (window.appState && window.appState.sectionsOpen && !window.appState.sectionsOpen.workspace) {
window.appState.sectionsOpen.workspace = true;
}
container.innerHTML = '';
var self = this;
items.forEach(function(item) {
// Build tree HTML as string and set once (more performant)
var html = '';
for (var i = 0; i < items.length; i++) {
var item = items[i];
var icon = item.type === 'folder' ? '📁' : '📄';
var li = document.createElement('li');
li.className = 'sidebar-item';
li.setAttribute('data-gitea-path', item.path);
li.setAttribute('data-gitea-type', item.type === 'folder' ? 'folder' : 'file');
li.setAttribute('data-gitea-sha', item.sha || '');
li.style.cssText = 'padding-left:12px;cursor:pointer;display:flex;align-items:center;gap:4px;';
// Checkbox (hidden by default, visible in multi-select mode)
var cb = document.createElement('input');
cb.type = 'checkbox';
cb.className = 'gitea-checkbox';
cb.style.cssText = 'flex-shrink:0;margin:0;display:none;';
cb.addEventListener('click', function(ev) {
ev.stopPropagation();
self.selectItem(item.path, li, ev);
});
li.appendChild(cb);
// Icon
var iconSpan = document.createElement('span');
iconSpan.className = 'page-icon';
iconSpan.textContent = icon;
li.appendChild(iconSpan);
// Name (with double-click rename)
var nameSpan = document.createElement('span');
nameSpan.className = 'page-name';
nameSpan.textContent = item.name;
nameSpan.addEventListener('dblclick', function(ev) {
ev.preventDefault();
ev.stopPropagation();
self.startInlineRename(nameSpan, item.path, li);
});
li.appendChild(nameSpan);
container.appendChild(li);
});
html += '<li class="sidebar-item" data-gitea-path="' + item.path + '" data-gitea-type="' + (item.type === 'folder' ? 'folder' : 'file') + '" data-gitea-sha="' + (item.sha || '') + '" style="padding-left:12px;cursor:pointer;display:flex;align-items:center;gap:4px;">';
html += '<span class="page-icon">' + icon + '</span>';
html += '<span class="page-name">' + item.name + '</span>';
html += '</li>';
}
// Add Private Pages link
var sep = document.createElement('li');
sep.style.cssText = 'border-top:1px solid var(--border);margin:8px 0;';
container.appendChild(sep);
var pli = document.createElement('li');
pli.className = 'sidebar-item';
pli.style.cssText = 'padding-left:12px;cursor:pointer;';
pli.innerHTML = '<span class="page-icon">🔒</span><span class="page-name">Private Pages</span>';
pli.addEventListener('click', function(e) {
e.preventDefault(); e.stopPropagation();
self.showPrivate = true; self.showFile = false; self.showEditor = false;
self.loadPrivatePages();
});
container.appendChild(pli);
} catch(e) { console.error('Sidebar tree load failed:', e); }
html += '<li style="border-top:1px solid var(--border);margin:8px 0;"></li>';
html += '<li class="sidebar-item" id="gitea-private-link" style="padding-left:12px;cursor:pointer;display:flex;align-items:center;gap:4px;">';
html += '<span class="page-icon">🔒</span><span class="page-name">Private Pages</span></li>';
container.innerHTML = html;
// Re-attach event listeners
this.setupSidebarClicks();
} catch(e) {
console.error('Gitea sidebar tree load failed:', e);
}
},
setupSidebarClicks() {
+2 -28
View File
@@ -2,35 +2,11 @@
page_title %}{{ page.title }}{% endblock %} {% block topbar %}
{% set page_icon = "📄" %}
{% set page_title = page.title %}
{% set right_actions = '<span class="topbar-edited" style="cursor:pointer;" @click="window.E && (window.E.activityOpen = !window.E.activityOpen)">Edited <span x-text="window.E && window.E.timeAgo || \'\'"></span> ▾</span><button class="topbar-btn share-btn" @click="window.E && (window.E.shareOpen = !window.E.shareOpen)">🔒 Share ▾</button><button class="topbar-btn" @click="window.E && window.E.copyPageLink()" title="Copy link">🔗</button><button class="topbar-btn star-btn" @click="window.E && window.E.toggleFavorite()">⭐</button><button class="topbar-btn" @click="window.E && (window.E.moreOpen = !window.E.moreOpen)" style="position:relative">⋯</button>' %}
{% include '_header.html' %}
{% endblock %} {% block content %}
<div class="page-editor-wrapper" x-data="editorState()">
<!-- ═══════════ Action Bar (Share, Copy, Favorite, More) ═══════════ -->
<div class="page-topbar">
<div class="page-topbar-right" style="position: relative">
<span class="topbar-edited" style="cursor:pointer;" @click="activityOpen = !activityOpen">Edited <span x-text="timeAgo"></span> ▾</span>
<button
class="page-action-btn share-btn"
@click="shareOpen = !shareOpen"
>
🔒 Share <span class="chevron-down">▾</span>
</button>
<button
class="page-action-btn"
@click="copyPageLink"
title="Copy link"
>
🔗
</button>
<button class="page-action-btn star-btn" @click="toggleFavorite">
<span
x-text="favorited ? '⭐' : '☆'"
:class="{ starred: favorited }"
></span>
</button>
<button class="page-action-btn" @click="moreOpen = !moreOpen">
⋮
</button>
<!-- Share/More dialogs (in content area, triggered by header buttons) -->
<!-- ═══════════ Share / Publish Dialog ═══════════ -->
<div
@@ -410,8 +386,6 @@ page_title %}{{ page.title }}{% endblock %} {% block topbar %}
</div>
</div>
</div>
</div>
</div>
<!-- ═══════════ Page Content ═══════════ -->
<div class="page-cover-area">