fix(library): visibility hover + workspace filter + tab Repository pour Gitea
- 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:
@@ -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)
|
||||
|
||||
|
||||
|
||||
@@ -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
@@ -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 || [];
|
||||
|
||||
Reference in New Issue
Block a user