fix: sidebar Repository et pages forge — erreurs console, cache assets (v7.70.1)
FlowDeck CI / lint (push) Successful in 1m37s
FlowDeck CI / docker (push) Canceled after 0s
FlowDeck CI / test (push) Canceled after 1h9m58s

This commit is contained in:
2026-10-09 23:33:48 -04:00
parent d95178156f
commit 1fedafa808
5 changed files with 109 additions and 12 deletions
+53
View File
@@ -1,5 +1,58 @@
# Changelog - FlowDeck
## v7.70.1 (2026-10-10) — Refonte navigation Repository : fix cache + erreurs console
### Fixed
- **Sidebar Repository et pages forge : erreurs console
« Undefined variable: filteredTreeItems / viewMode » corrigées** —
la refonte avait fait évoluer `gitea_workspace.html` sans faire évoluer
`?v=` (VERSION inchangée) : les navigateurs rejouaient l'ancien
`gitea_workspace.js` en cache contre le nouveau HTML, d'où des variables
Alpine inconnues et un arbre Repository inopérant. Le bump de version
invalide le cache et resynchronise HTML/JS.
- **Échecs réseau/Gitea désormais visibles au lieu d'un silence console** —
`loadSidebarTree`/`loadMainTree` affichent un message dans la section
Repository (« Connect Gitea/GitHub… », « Could not reach… » avec bouton
Retry) au lieu de laisser « Loading... » bloqué et de n'écrire que dans
la console.
- **Clics sidebar Repository attachés une seule fois** — `setupSidebarClicks`
posait un listener global `document` à chaque refresh, accumulant les
doublons (double toggle/fetch). Garde `_sidebarClicksBound`.
## v7.70.0 (2026-10-09) — Projet Gitea : présentation + workspace local en parallèle
### Fixed
- **Ouverture d'un projet Gitea : le workspace local miroir est désormais
ouvert en parallèle dès le premier affichage** — `GET /gitea-workspace`
calculait la sidebar **avant** de créer le workspace miroir, donc le
premier rendu montrait « No workspace open » / un `localWsId: 0` et la
section « Workspace » restait vide (il fallait recharger). Le miroir est
maintenant créé/résolu **avant** `_sidebar_data`, qui reçoit en plus le
contexte projet explicite `owner`/`repo` : la navigation directe ou un
favori `?owner=…&repo=…` (sans cookie `flowdeck_workspace`) résout
correctement le miroir au lieu de retomber sur « aucune espace ouverte ».
- **La carte « Workspace » de la sidebar n'est plus écrasée par l'arbre du
dépôt** — `loadGiteaTree()` (base.html) ciblait `#sidebar-workspace-items`
(l'arbre local) au lieu de `#sidebar-gitea-items` (la section
« Repository (Gitea) »), et son garde testait le mauvais conteneur : à
chaque page en contexte Gitea, il remplaçait l'arbre du miroir local par
une copie non interactive de la racine du dépôt. Il cible désormais la
section Repository — l'équivalent local et le dépôt distant coexistent.
- **`GET /api/sidebar/workspace-tree` rend le miroir local, plus
« Remote workspace »** — après une création/renommage/déplacement (appel
`refreshSidebarTree()`), le cookie `gitea:owner:repo` renvoyait un
placeholder qui effaçait l'arbre du miroir. La route résout le workspace
miroir et rend son arbre (repli « No pages yet » s'il n'existe pas).
### Tests
- `tests/test_v7700_gitea_project_mirror.py` : création + affichage du miroir
au premier `GET /gitea-workspace` (cookie ou URL seule), rendu du miroir par
`/api/sidebar/workspace-tree`, et scan statique que `loadGiteaTree` cible
bien `#sidebar-gitea-items`.
## v7.69.9 (2026-10-09) — /local-workspace : chemin du header + navigation dans les dossiers réparée
### Fixed
+1 -1
View File
@@ -1 +1 @@
7.69.9
7.70.1
+11 -2
View File
@@ -140,7 +140,7 @@
<div class="breadcrumb-row">
<a href="/workspaces" class="btn-icon nav-arrow" title="All workspaces">{{ fd_icon("home",14) }}</a>
<div class="unified-breadcrumb">
<span class="bc-root" x-text="owner + '/' + repo"></span>
<a href="#" @click.prevent="navigateToRoot()" class="bc-root" style="cursor:pointer;" x-text="owner + '/' + repo"></a>
<template x-for="(b, i) in folderStack" :key="i">
<span style="display:contents;">
<span class="bc-sep">/</span>
@@ -207,7 +207,16 @@
<div class="gw-content" style="padding:12px 4px 24px;">
<div x-show="!treeLoading">
<template x-if="(filteredTreeItems || []).length === 0">
<template x-if="treeError">
<div class="empty-state">
<h2>{{ fd_icon("link",16) }} Repository unavailable</h2>
<p x-text="treeErrorText || 'Could not load the repository tree.'"></p>
<div class="empty-actions">
<button class="btn btn-primary" @click="refreshTree()">Retry</button>
</div>
</div>
</template>
<template x-if="!treeError && (filteredTreeItems || []).length === 0">
<div class="empty-state">
<h2>{{ fd_icon("folder",16) }} Empty</h2>
<p>No files in this folder</p>
+11 -2
View File
@@ -140,7 +140,7 @@
<div class="breadcrumb-row">
<a href="/workspaces" class="btn-icon nav-arrow" title="All workspaces">{{ fd_icon("home",14) }}</a>
<div class="unified-breadcrumb">
<span class="bc-root" x-text="owner + '/' + repo"></span>
<a href="#" @click.prevent="navigateToRoot()" class="bc-root" style="cursor:pointer;" x-text="owner + '/' + repo"></a>
<template x-for="(b, i) in folderStack" :key="i">
<span style="display:contents;">
<span class="bc-sep">/</span>
@@ -207,7 +207,16 @@
<div class="gw-content" style="padding:12px 4px 24px;">
<div x-show="!treeLoading">
<template x-if="(filteredTreeItems || []).length === 0">
<template x-if="treeError">
<div class="empty-state">
<h2>{{ fd_icon("link",16) }} Repository unavailable</h2>
<p x-text="treeErrorText || 'Could not load the repository tree.'"></p>
<div class="empty-actions">
<button class="btn btn-primary" @click="refreshTree()">Retry</button>
</div>
</div>
</template>
<template x-if="!treeError && (filteredTreeItems || []).length === 0">
<div class="empty-state">
<h2>{{ fd_icon("folder",16) }} Empty</h2>
<p>No files in this folder</p>
+33 -7
View File
@@ -45,6 +45,8 @@ var _regGiteaWs = () => {
sortedTreeItems: [],
filteredTreeItems: [],
treeLoading: false,
treeError: false,
treeErrorText: '',
folderStack: [],
currentPath: '',
// Context menu
@@ -132,16 +134,30 @@ var _regGiteaWs = () => {
async loadMainTree(path) {
this.treeLoading = true;
this.treeError = false;
this.treeErrorText = '';
this.currentPath = path;
var forgeLabel = this.forge === 'github' ? 'GitHub' : 'Gitea';
try {
var url = this.apiBase() + '/tree';
if (path) url += '?path=' + encodeURIComponent(path);
var r = await fetch(url);
if (!r.ok) { this.treeItems = []; this.sortedTreeItems = []; this.filteredTreeItems = []; return; }
if (!r.ok) {
this.treeItems = []; this.sortedTreeItems = []; this.filteredTreeItems = [];
this.treeError = true;
this.treeErrorText = r.status === 401
? 'Connect your ' + forgeLabel + ' account (Settings → Integrations) to browse this repository.'
: 'Repository API answered HTTP ' + r.status + '.';
return;
}
var d = await r.json();
this.treeItems = d.tree || [];
this._sortTree();
} catch { this.treeItems = []; this.sortedTreeItems = []; this.filteredTreeItems = []; }
} catch {
this.treeItems = []; this.sortedTreeItems = []; this.filteredTreeItems = [];
this.treeError = true;
this.treeErrorText = 'Could not reach the repository — the server may be restarting. Retry in a moment.';
}
finally { this.treeLoading = false; }
},
@@ -199,13 +215,18 @@ var _regGiteaWs = () => {
}).catch(function(){ window.showToast('Delete failed', 'error'); });
},
_sidebarHint(msg) {
var container = document.getElementById('sidebar-gitea-items');
if (container) container.innerHTML = '<li class="sidebar-item empty-hint"><span class="page-icon">'+getSvgIcon('link',14)+'</span><span class="page-name text-dim">' + msg + '</span></li>';
},
async loadSidebarTree() {
var forgeLabel = this.forge === 'github' ? 'GitHub' : 'Gitea';
try {
var r = await fetch(this.apiBase() + '/tree');
if (!r.ok) {
// If API fails (e.g. no Gitea token), set a message
var container = document.getElementById('sidebar-gitea-items');
if (container) container.innerHTML = '<li class="sidebar-item empty-hint"><span class="page-icon">'+getSvgIcon('link',14)+'</span><span class="page-name text-dim">Connect Gitea to browse files</span></li>';
// API error (e.g. forge account not linked) — hint instead of
// leaving "Loading..." stuck in the Repository section.
this._sidebarHint(r.status === 401 ? 'Connect ' + forgeLabel + ' to browse files' : 'Could not load repository tree (HTTP ' + r.status + ')');
return;
}
var d = await r.json();
@@ -248,14 +269,19 @@ var _regGiteaWs = () => {
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">'+getSvgIcon('lock',14)+'</span><span class="page-name">Private Pages</span></li>';
container.innerHTML = html;
// Re-attach event listeners
// Attach sidebar clicks once (loadSidebarTree runs on every refresh —
// re-attaching would stack duplicate global listeners).
this.setupSidebarClicks();
} catch(e) {
console.error('Gitea sidebar tree load failed:', e);
// Network failure (server restarting, offline…) — hint in place of
// "Loading..." so the Repository section never looks broken.
this._sidebarHint('Could not reach the repository — retry in a moment');
}
},
setupSidebarClicks() {
if (this._sidebarClicksBound) return;
this._sidebarClicksBound = true;
var self = this;
document.addEventListener('click', function(e) {
var el = e.target.closest('.sidebar-item[data-gitea-path]');