fix(local-workspace): boite de renommage a la taille du nom + bouton Open a cote
FlowDeck CI / test (push) Failing after 15s
FlowDeck CI / docker (push) Skipped

- La boite d'edition inline est desormais large comme le nom du fichier
  (largeur mesuree a l'ouverture via _fitNameWidth, min 60px, bornee a
  l'espace disponible pour les tres longs noms — le texte defile dedans).
- Le bouton Open est place juste a droite de la boite (marge 6px) au lieu du
  bord du panneau : deplace dans la cellule .name.file, passee en inline-flex
  (overflow hidden + max-width) pour ne jamais passer a la ligne.
- Applique aux deux rendus de l'arbre (vue principale x-for + renderChildren).
- Verifie en Chrome headless : input = largeur du nom (38%/12% de la ligne au
  lieu de 100%), bouton Open a 6px de la boite, pas de passage a la ligne.
- Tests : 278 passent.
This commit is contained in:
2026-09-07 18:11:05 -04:00
parent bd582866d1
commit 52d7a0d006
4 changed files with 50 additions and 16 deletions
+17
View File
@@ -1,5 +1,22 @@
# Changelog — FlowDeck
## v5.1.10 (2026-09-07) — Espace local : boîte de renommage à la taille du nom
> Renommer un fichier en cliquant sur son nom ouvrait une boîte d'édition qui
> s'étendait sur toute la largeur de la ligne, et le bouton « Open » restait
> collé à droite du panneau.
- La boîte de renommage inline (`inline-rename-input`) est maintenant **large
comme le nom du fichier** : la largeur est mesurée à l'ouverture
(`_fitNameWidth`) avec un minimum de 60 px et est bornée à l'espace
disponible pour les très longs noms (le texte défile alors dans la boîte).
- Le bouton **Open** est placé **juste à droite de la boîte d'édition** (marge
6 px) au lieu du bord droit : il est déplacé à l'intérieur de la cellule
`.name.file`, devenue `inline-flex` (avec `overflow:hidden` + `max-width`),
pour que le bouton ne passe jamais à la ligne même en colonne étroite.
- S'applique aux deux rendus de l'arbre : la vue principale (x-for) et
`renderChildren` (dossiers imbriqués / arbre statique).
## v5.1.9 (2026-09-07) — Validation robuste des modèles Nvidia + logo Agent noir & blanc
> La liste Nvidia « diminuée » de la v5.1.8 contenait encore des modèles en
+1 -1
View File
@@ -1 +1 @@
5.1.9
5.1.10
+1 -1
View File
@@ -60,7 +60,7 @@ async def lifespan(_app: FastAPI):
app = FastAPI(
title="FlowDeck",
version="5.1.9",
version="5.1.10",
docs_url="/docs" if settings.log_level == "DEBUG" else None,
redoc_url=None,
lifespan=lifespan,
+31 -14
View File
@@ -104,7 +104,7 @@
.ws-checkbox.checked svg{width:10px;height:10px;color:#fff;}
/* ── OPEN button ── */
.btn-open{display:inline-flex;align-items:center;gap:4px;padding:3px 8px;border:1px solid var(--border);border-radius:4px;font-size:11px;font-weight:500;color:var(--text-primary);background:var(--bg-tertiary);cursor:pointer;white-space:nowrap;transition:all 0.1s;flex-shrink:0;text-decoration:none;margin-left:auto;}
.btn-open{display:inline-flex;align-items:center;gap:4px;padding:3px 8px;border:1px solid var(--border);border-radius:4px;font-size:11px;font-weight:500;color:var(--text-primary);background:var(--bg-tertiary);cursor:pointer;white-space:nowrap;transition:all 0.1s;flex-shrink:0;text-decoration:none;margin-left:6px;}
.btn-open:hover{background:var(--bg-active);}
.btn-open svg{width:12px;height:12px;}
@@ -162,6 +162,9 @@
.ws-tree-item .name.folder:hover{color:var(--accent);}
.ws-tree-item .name.file{cursor:pointer;}
.ws-tree-item .name.file:hover{color:var(--accent);}
.ws-tree-item .name.file{display:inline-flex;align-items:center;max-width:100%;overflow:hidden;}
.ws-tree-item .name.file .inline-rename-input{flex:0 1 auto;}
.ws-tree-item .name.file .btn-open{flex:0 0 auto;}
.ws-tree-item .actions{display:none;gap:2px;}
.ws-tree-item:hover .actions{display:flex;}
.ws-chevron{width:16px;height:16px;flex-shrink:0;display:inline-flex;align-items:center;justify-content:center;background:none;border:none;color:var(--text-tertiary);cursor:pointer;font-size:8px;padding:0;border-radius:3px;transition:transform 150ms,color 100ms;}
@@ -277,7 +280,7 @@
.child-count{font-size:11px;color:var(--text-tertiary);margin-left:4px;}
/* Inline rename */
.inline-rename-input{background:var(--bg);border:1px solid var(--accent);border-radius:4px;color:var(--text);font-size:13px;padding:2px 6px;outline:none;min-width:100px;}
.inline-rename-input{background:var(--bg);border:1px solid var(--accent);border-radius:4px;color:var(--text);font-size:13px;padding:2px 6px;outline:none;min-width:60px;}
/* Drag count badge */
.drag-badge{position:fixed;pointer-events:none;z-index:999;background:var(--accent);color:#fff;font-size:12px;font-weight:700;padding:2px 8px;border-radius:10px;transform:translate(-50%,-50%);}
@@ -1208,18 +1211,16 @@ window._wsInitData = (function() {
+ ' onmouseenter="var d=window._wsData;if(d)d.showPreview(event,{id:'+escId+',name:\''+escName+'\',is_folder:false,content_format:\''+(c.content_format||'')+'\'})"'
+ ' onmouseleave="var d=window._wsData;if(d)d.hidePreview()">'
+ '<span id="rc-name-'+escId+'">'+c.name+'</span>'
+ '<input id="rc-input-'+escId+'" class="inline-rename-input" style="display:none;width:100%"'
+ '<input id="rc-input-'+escId+'" class="inline-rename-input" style="display:none"'
+ ' onkeydown="if(event.key===\'Enter\')window._wsData.commitRenderChildRename('+escId+')"'
+ ' onblur="window._wsData.commitRenderChildRename('+escId+')"'
+ ' onclick="event.stopPropagation()">'
+ '<a href="#" class="btn-open hover-only"'
+ ' onclick="event.stopPropagation();event.preventDefault();var d=window._wsData;if(d)d.openSidePeek('+escId+')"'
+ ' title="Open in side preview">↢ Open</a>'
+ '</span>';
}
// Open button for files — to the right of title (like Library)
if (!c.is_folder) {
html += '<a href="#" class="btn-open hover-only" style="margin-left:auto;"'
+ ' onclick="event.stopPropagation();event.preventDefault();var d=window._wsData;if(d)d.openSidePeek('+escId+')"'
+ ' title="Open in side preview">↢ Open</a>';
}
html += '<div class="actions">';
if (c.is_folder) {
html += '<span style="display:flex;gap:2px">';
@@ -1258,6 +1259,9 @@ window._wsInitData = (function() {
nameSpan.style.display = 'none';
input.style.display = '';
input.value = currentName;
var w = this._fitNameWidth(currentName);
var avail = input.parentElement ? input.parentElement.clientWidth : 0;
if (avail > 60) input.style.width = Math.min(w, avail) + 'px'; else input.style.width = w + 'px';
input.focus();
input.select();
},
@@ -1290,6 +1294,16 @@ window._wsInitData = (function() {
return s.replace(/\\/g,'\\\\').replace(/'/g,"\\'").replace(/"/g,'&quot;');
},
_fitNameWidth(text) {
var off = document.createElement('span');
off.style.cssText = 'position:absolute;visibility:hidden;white-space:pre;font-size:13px;';
off.textContent = (text || '').toString();
document.body.appendChild(off);
var w = off.offsetWidth;
document.body.removeChild(off);
return Math.max(w + 16, 60);
},
// ── Event delegation for renderChildren action buttons ──
handleTreeAction(ev) {
var btn = (ev.target.closest && ev.target.closest('[data-ws-action]'))
@@ -1425,7 +1439,12 @@ window._wsInitData = (function() {
var self = this;
this.$nextTick(function() {
var inp = self.$el.querySelector('.inline-rename-input:not([style*="display: none"])');
if (inp) { inp.focus(); inp.select(); }
if (inp) {
var w = self._fitNameWidth(self.renamingName);
var avail = inp.parentElement ? inp.parentElement.clientWidth : 0;
if (avail > 60) inp.style.width = Math.min(w, avail) + 'px'; else inp.style.width = w + 'px';
inp.focus(); inp.select();
}
});
},
async commitRename(node) {
@@ -2521,7 +2540,7 @@ console.log('FLOWDECK v2.4.6 — IIFE done, _wsInitData keys:', Object.keys(wind
x-text="'(' + node.child_count + ')'"></span>
<input x-show="renamingId === node.id" class="inline-rename-input" x-model="renamingName"
@keydown.enter="commitRename(node)" @keydown.escape="cancelRename()"
@blur="commitRename(node)" @click.stop style="width:100%">
@blur="commitRename(node)" @click.stop>
</span>
</template>
<template x-if="!node.is_folder">
@@ -2532,12 +2551,10 @@ console.log('FLOWDECK v2.4.6 — IIFE done, _wsInitData keys:', Object.keys(wind
<span x-show="renamingId !== node.id" x-text="node.name"></span>
<input x-show="renamingId === node.id" class="inline-rename-input" x-model="renamingName"
@keydown.enter="commitRename(node)" @keydown.escape="cancelRename()"
@blur="commitRename(node)" @click.stop style="width:100%">
@blur="commitRename(node)" @click.stop>
<a href="#" class="btn-open hover-only" @click.prevent.stop="selectForPreview(node)" title="Open in side preview">↢ Open</a>
</span>
</template>
<template x-if="!node.is_folder">
<a href="#" class="btn-open hover-only" @click.prevent.stop="selectForPreview(node)" title="Open in side preview" style="margin-left:auto;">↢ Open</a>
</template>
<div class="actions">
<template x-if="node.is_folder">