fix: sidebar Repository et pages forge — erreurs console, cache assets (v7.70.1)
This commit is contained in:
@@ -1,5 +1,58 @@
|
|||||||
# Changelog - FlowDeck
|
# 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
|
## v7.69.9 (2026-10-09) — /local-workspace : chemin du header + navigation dans les dossiers réparée
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -140,7 +140,7 @@
|
|||||||
<div class="breadcrumb-row">
|
<div class="breadcrumb-row">
|
||||||
<a href="/workspaces" class="btn-icon nav-arrow" title="All workspaces">{{ fd_icon("home",14) }}</a>
|
<a href="/workspaces" class="btn-icon nav-arrow" title="All workspaces">{{ fd_icon("home",14) }}</a>
|
||||||
<div class="unified-breadcrumb">
|
<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">
|
<template x-for="(b, i) in folderStack" :key="i">
|
||||||
<span style="display:contents;">
|
<span style="display:contents;">
|
||||||
<span class="bc-sep">/</span>
|
<span class="bc-sep">/</span>
|
||||||
@@ -207,7 +207,16 @@
|
|||||||
|
|
||||||
<div class="gw-content" style="padding:12px 4px 24px;">
|
<div class="gw-content" style="padding:12px 4px 24px;">
|
||||||
<div x-show="!treeLoading">
|
<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">
|
<div class="empty-state">
|
||||||
<h2>{{ fd_icon("folder",16) }} Empty</h2>
|
<h2>{{ fd_icon("folder",16) }} Empty</h2>
|
||||||
<p>No files in this folder</p>
|
<p>No files in this folder</p>
|
||||||
|
|||||||
@@ -140,7 +140,7 @@
|
|||||||
<div class="breadcrumb-row">
|
<div class="breadcrumb-row">
|
||||||
<a href="/workspaces" class="btn-icon nav-arrow" title="All workspaces">{{ fd_icon("home",14) }}</a>
|
<a href="/workspaces" class="btn-icon nav-arrow" title="All workspaces">{{ fd_icon("home",14) }}</a>
|
||||||
<div class="unified-breadcrumb">
|
<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">
|
<template x-for="(b, i) in folderStack" :key="i">
|
||||||
<span style="display:contents;">
|
<span style="display:contents;">
|
||||||
<span class="bc-sep">/</span>
|
<span class="bc-sep">/</span>
|
||||||
@@ -207,7 +207,16 @@
|
|||||||
|
|
||||||
<div class="gw-content" style="padding:12px 4px 24px;">
|
<div class="gw-content" style="padding:12px 4px 24px;">
|
||||||
<div x-show="!treeLoading">
|
<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">
|
<div class="empty-state">
|
||||||
<h2>{{ fd_icon("folder",16) }} Empty</h2>
|
<h2>{{ fd_icon("folder",16) }} Empty</h2>
|
||||||
<p>No files in this folder</p>
|
<p>No files in this folder</p>
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ var _regGiteaWs = () => {
|
|||||||
sortedTreeItems: [],
|
sortedTreeItems: [],
|
||||||
filteredTreeItems: [],
|
filteredTreeItems: [],
|
||||||
treeLoading: false,
|
treeLoading: false,
|
||||||
|
treeError: false,
|
||||||
|
treeErrorText: '',
|
||||||
folderStack: [],
|
folderStack: [],
|
||||||
currentPath: '',
|
currentPath: '',
|
||||||
// Context menu
|
// Context menu
|
||||||
@@ -132,16 +134,30 @@ var _regGiteaWs = () => {
|
|||||||
|
|
||||||
async loadMainTree(path) {
|
async loadMainTree(path) {
|
||||||
this.treeLoading = true;
|
this.treeLoading = true;
|
||||||
|
this.treeError = false;
|
||||||
|
this.treeErrorText = '';
|
||||||
this.currentPath = path;
|
this.currentPath = path;
|
||||||
|
var forgeLabel = this.forge === 'github' ? 'GitHub' : 'Gitea';
|
||||||
try {
|
try {
|
||||||
var url = this.apiBase() + '/tree';
|
var url = this.apiBase() + '/tree';
|
||||||
if (path) url += '?path=' + encodeURIComponent(path);
|
if (path) url += '?path=' + encodeURIComponent(path);
|
||||||
var r = await fetch(url);
|
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();
|
var d = await r.json();
|
||||||
this.treeItems = d.tree || [];
|
this.treeItems = d.tree || [];
|
||||||
this._sortTree();
|
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; }
|
finally { this.treeLoading = false; }
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -199,13 +215,18 @@ var _regGiteaWs = () => {
|
|||||||
}).catch(function(){ window.showToast('Delete failed', 'error'); });
|
}).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() {
|
async loadSidebarTree() {
|
||||||
|
var forgeLabel = this.forge === 'github' ? 'GitHub' : 'Gitea';
|
||||||
try {
|
try {
|
||||||
var r = await fetch(this.apiBase() + '/tree');
|
var r = await fetch(this.apiBase() + '/tree');
|
||||||
if (!r.ok) {
|
if (!r.ok) {
|
||||||
// If API fails (e.g. no Gitea token), set a message
|
// API error (e.g. forge account not linked) — hint instead of
|
||||||
var container = document.getElementById('sidebar-gitea-items');
|
// leaving "Loading..." stuck in the Repository section.
|
||||||
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>';
|
this._sidebarHint(r.status === 401 ? 'Connect ' + forgeLabel + ' to browse files' : 'Could not load repository tree (HTTP ' + r.status + ')');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var d = await r.json();
|
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 += '<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>';
|
html += '<span class="page-icon">'+getSvgIcon('lock',14)+'</span><span class="page-name">Private Pages</span></li>';
|
||||||
container.innerHTML = html;
|
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();
|
this.setupSidebarClicks();
|
||||||
} catch(e) {
|
} 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() {
|
setupSidebarClicks() {
|
||||||
|
if (this._sidebarClicksBound) return;
|
||||||
|
this._sidebarClicksBound = true;
|
||||||
var self = this;
|
var self = this;
|
||||||
document.addEventListener('click', function(e) {
|
document.addEventListener('click', function(e) {
|
||||||
var el = e.target.closest('.sidebar-item[data-gitea-path]');
|
var el = e.target.closest('.sidebar-item[data-gitea-path]');
|
||||||
|
|||||||
Reference in New Issue
Block a user