fix(library): visibility hover + workspace filter + tab Repository pour Gitea
FlowDeck CI / test (push) Failing after 4s
FlowDeck CI / docker (push) Has been skipped

- CSS hover-only: visibility:hidden/visible au lieu de opacity (infaillible)
- Workspace tab: filtré par workspace_id quand un workspace est actif
- Tab Private remplacé par Repository (visible seulement si Gitea workspace actif)
- Nouvel endpoint /api/library/repository pour le contenu Gitea
- dashboard.py passe is_gitea_workspace, owner, repo au template
This commit is contained in:
2026-07-19 13:32:08 -04:00
parent 04c059341d
commit 2ee0a05efd
3 changed files with 67 additions and 11 deletions
+4
View File
@@ -221,6 +221,10 @@ async def library_page(request: Request, owner: str = Query(default=""), repo: s
template = env.get_template("library.html")
sidebar["active_workspace_id"] = sidebar.get("nav_workspace_id", 0)
# Gitea workspace context for Repository tab
sidebar["is_gitea_workspace"] = bool(owner and repo)
sidebar["gitea_workspace_owner"] = owner
sidebar["gitea_workspace_repo"] = repo
return template.render(**sidebar)
+30
View File
@@ -271,11 +271,36 @@ async def library_children(page_id: int, request: Request):
return {"items": items}
@router.get("/repository")
async def library_repository(
request: Request,
gitea_owner: str = Query(default=""),
gitea_repo: str = Query(default=""),
):
"""Return Gitea repository pages/files for the Library Repository tab."""
if not gitea_owner or not gitea_repo:
return {"items": []}
uid = _get_user_id(request)
ws_key = f"{gitea_owner}/{gitea_repo}"
query = f"{BASE_SELECT} FROM pages p WHERE p.workspace = ? AND p.deleted_at IS NULL"
params = [ws_key]
query += " ORDER BY p.updated_at DESC LIMIT 100"
with get_conn() as conn:
rows = conn.execute(query, params).fetchall()
items = _rows_to_items(rows, uid)
_enrich_children(items)
return {"items": items}
@router.get("/workspace")
async def library_workspace(
request: Request,
source_type: str = Query(default="all"),
tree: int = Query(default=0),
workspace_id: int = Query(default=0),
):
if source_type not in SOURCE_TYPES:
source_type = "all"
@@ -283,6 +308,11 @@ async def library_workspace(
uid = _get_user_id(request)
query = f"{BASE_SELECT} FROM pages p WHERE p.workspace != '' AND p.parent_section != 'Trash' AND p.deleted_at IS NULL"
params: list = []
if workspace_id:
query += " AND p.workspace_id = ?"
params.append(workspace_id)
query, params = _apply_source_filter(query, params, source_type)
query += " ORDER BY p.updated_at DESC LIMIT 50"
+33 -11
View File
@@ -12,6 +12,9 @@
{% block content %}
<div x-data="libraryPage()" x-init="init()" @keydown.escape="closePeek()">
<div id="lib-workspace-id" style="display:none;">{{ active_workspace_id or 0 }}</div>
<div id="lib-gitea-active" style="display:none;">{{ '1' if is_gitea_workspace else '0' }}</div>
<div id="lib-gitea-owner" style="display:none;">{{ gitea_workspace_owner or '' }}</div>
<div id="lib-gitea-repo" style="display:none;">{{ gitea_workspace_repo or '' }}</div>
<style>
/* ── Library layout ── */
@@ -62,18 +65,18 @@
.row-title input{width:100%;font-size:13px;padding:2px 4px;background:var(--bg-primary);border:1px solid var(--accent);border-radius:4px;color:var(--text-primary);outline:none;}
/* ── Hover-only elements (appear on row hover) ── */
.hover-only{opacity:0;transition:opacity 0.12s;flex-shrink:0;}
.lib-row:hover .hover-only{opacity:1;}
.hover-only{visibility:hidden;opacity:0;transition:opacity 0.12s, visibility 0s 0.12s;flex-shrink:0;}
.lib-row:hover .hover-only{visibility:visible;opacity:1;transition:opacity 0.15s, visibility 0s 0s;}
/* Drag handle — 6 dots (hover-only, far left of name cell) */
.drag-handle{display:flex;flex-wrap:wrap;width:14px;height:14px;cursor:grab;align-content:space-between;justify-content:space-between;flex-shrink:0;}
.drag-handle{display:flex;flex-wrap:wrap;width:16px;height:16px;cursor:grab;align-content:space-between;justify-content:space-between;flex-shrink:0;}
.drag-handle .dot{width:3px;height:3px;background:var(--text-tertiary);border-radius:50%;}
.drag-handle:active{cursor:grabbing;}
/* Checkbox (hover-only) */
.lib-checkbox{width:15px;height:15px;border:1.5px solid var(--text-tertiary);border-radius:3px;cursor:pointer;flex-shrink:0;display:flex;align-items:center;justify-content:center;transition:all 0.1s;}
.lib-checkbox{width:15px;height:15px;border:1.5px solid var(--text-tertiary);border-radius:3px;cursor:pointer;flex-shrink:0;display:flex;align-items:center;justify-content:center;transition:border-color 0.1s;}
.lib-checkbox:hover{border-color:var(--text-primary);}
.lib-checkbox.checked{background:var(--accent);border-color:var(--accent);opacity:1;}
.lib-checkbox.checked{background:var(--accent);border-color:var(--accent);visibility:visible;opacity:1;}
.lib-checkbox.checked::after{content:'✓';color:#fff;font-size:10px;font-weight:700;}
/* OPEN button (hover-only, right side of name cell) */
@@ -176,9 +179,12 @@
<button class="lib-tab" :class="{ active: tab === 'published' }" @click="switchTab('published')">
<span class="tab-icon">🌐</span> Published
</button>
<button class="lib-tab" :class="{ active: tab === 'private' }" @click="switchTab('private')">
<button class="lib-tab" :class="{ active: tab === 'private' }" @click="switchTab('private')" x-show="!isGiteaActive">
<span class="tab-icon">🔒</span> Private
</button>
<button class="lib-tab" :class="{ active: tab === 'repository' }" @click="switchTab('repository')" x-show="isGiteaActive">
<span class="tab-icon">📦</span> Repository
</button>
<button class="lib-tab" :class="{ active: tab === 'workspace' }" @click="switchTab('workspace')">
<span class="tab-icon">📁</span> Workspace
</button>
@@ -330,6 +336,9 @@ function libraryPage() {
return {
tab: 'recents',
workspaceId: 0,
isGiteaActive: false,
giteaOwner: '',
giteaRepo: '',
items: [],
loading: false,
searchOpen: false,
@@ -388,12 +397,13 @@ function libraryPage() {
// ── Empty states ──
get emptyIcon() {
var icons = {recents:'🕒',favorites:'⭐',shared:'👥',published:'🌐',private:'🔒',workspace:'📁'};
var icons = {recents:'🕒',favorites:'⭐',shared:'👥',published:'🌐',private:'🔒',workspace:'📁',repository:'📦'};
return icons[this.tab] || '📄';
},
get emptyTitle() {
var titles = {recents:'No recent pages',favorites:'No favorites',shared:'No shared pages',
published:'No published pages',private:'No private pages',workspace:'No workspace pages'};
published:'No published pages',private:'No private pages',workspace:'No workspace pages',
repository:'Empty repository'};
return titles[this.tab] || 'No items';
},
get emptyText() {
@@ -402,7 +412,8 @@ function libraryPage() {
shared:'Shared pages from your team will appear here.',
published:'Pages published to the web will appear here.',
private:'Create a new page with the + button.',
workspace:'Pages inside workspaces will appear here.'};
workspace:'Pages inside workspaces will appear here.',
repository:'Repository files and pages will appear here.'};
return texts[this.tab] || '';
},
@@ -410,9 +421,13 @@ function libraryPage() {
init() {
var wsEl = document.getElementById('lib-workspace-id');
this.workspaceId = parseInt(wsEl ? wsEl.textContent : '0') || 0;
var giteaEl = document.getElementById('lib-gitea-active');
this.isGiteaActive = (giteaEl ? giteaEl.textContent : '0') === '1';
this.giteaOwner = (document.getElementById('lib-gitea-owner') || {}).textContent || '';
this.giteaRepo = (document.getElementById('lib-gitea-repo') || {}).textContent || '';
var params = new URLSearchParams(window.location.search);
var t = params.get('tab');
if (t && ['recents','favorites','shared','published','private','workspace'].indexOf(t) !== -1) {
if (t && ['recents','favorites','shared','published','private','workspace','repository'].indexOf(t) !== -1) {
this.tab = t;
}
this.loadTab();
@@ -420,6 +435,7 @@ function libraryPage() {
switchTab(t) {
if (this.tab === t) return;
if (t === 'repository' && !this.isGiteaActive) return;
this.tab = t;
this.items = [];
this.selected = {};
@@ -443,7 +459,13 @@ function libraryPage() {
try {
var url = '/api/library/' + this.tab;
var sep = '?';
if (this.workspaceId) { url += '?workspace_id=' + this.workspaceId; sep = '&'; }
if (this.workspaceId && (this.tab === 'recents' || this.tab === 'workspace')) {
url += sep + 'workspace_id=' + this.workspaceId; sep = '&';
}
if (this.tab === 'repository' && this.isGiteaActive) {
url += sep + 'gitea_owner=' + encodeURIComponent(this.giteaOwner) + '&gitea_repo=' + encodeURIComponent(this.giteaRepo);
sep = '&';
}
var r = await fetch(url);
var d = await r.json();
this.items = d.items || [];