fix: affichage images PNG + preview hover opaque + preview images
FlowDeck CI / test (push) Failing after 4s
FlowDeck CI / docker (push) Has been skipped

1. File viewer: ws_id parsing cassé (parts[2] au lieu de parts[1]).
   Corrigé avec une boucle qui trouve le segment 'workspace_*'.
   Les images s'affichent maintenant correctement.

2. Preview hover: background rendu opaque (fallback #1a1a1a si var(--bg)
   absent), texte lisible. Ajout support preview image (thumbnail).

3. Preview body: changé de <pre x-text> à <div x-html> pour supporter
   le HTML (nécessaire pour les <img>).
This commit is contained in:
2026-07-11 09:43:22 -04:00
parent 37b1a7b078
commit 8bd737cef6
2 changed files with 24 additions and 13 deletions
+4 -2
View File
@@ -864,8 +864,10 @@ def _render_file_viewer(page: dict, request: Request) -> HTMLResponse:
# Build workspace_id from file_path
parts = file_path.split("/")
ws_id = ""
if len(parts) >= 3 and parts[2].startswith("workspace_"):
ws_id = parts[2].replace("workspace_", "")
for p in parts:
if p.startswith("workspace_"):
ws_id = p.replace("workspace_", "")
break
filename = parts[-1] if parts else title
file_url = f"/api/files/{ws_id}/{filename}" if ws_id else ""
+20 -11
View File
@@ -108,9 +108,9 @@
.search-clear:hover{color:var(--text);background:var(--bg-hover);}
/* File preview tooltip */
.file-preview{position:fixed;z-index:300;background:var(--bg);border:1px solid var(--border-strong);border-radius:10px;box-shadow:0 8px 30px rgba(0,0,0,.5);max-width:400px;min-width:200px;pointer-events:auto;}
.file-preview-header{padding:8px 12px;font-size:12px;font-weight:600;color:var(--text-secondary);border-bottom:1px solid var(--border);}
.file-preview-body{padding:10px 12px;margin:0;font-size:12px;line-height:1.5;color:var(--text);max-height:200px;overflow:auto;font-family:'Fira Code','Cascadia Code',monospace;white-space:pre-wrap;word-wrap:break-word;}
.file-preview{position:fixed;z-index:300;background:#1a1a1a;background:var(--bg,#1a1a1a);border:1px solid var(--border-strong,#333);border-radius:10px;box-shadow:0 8px 30px rgba(0,0,0,.5);max-width:400px;min-width:200px;pointer-events:auto;}
.file-preview-header{padding:8px 12px;font-size:12px;font-weight:600;color:var(--text-secondary,#888);border-bottom:1px solid var(--border,#333);}
.file-preview-body{padding:10px 12px;margin:0;font-size:12px;line-height:1.5;color:var(--text,#ddd);max-height:200px;overflow:auto;font-family:'Fira Code','Cascadia Code',monospace;white-space:pre-wrap;word-wrap:break-word;background:#141414;}
</style>
<div class="ws-layout" x-data="wsInit()"
@@ -278,7 +278,7 @@
:style="{ left: previewX + 'px', top: previewY + 'px' }"
@mouseenter="previewVisible = true" @mouseleave="previewVisible = false">
<div class="file-preview-header" x-text="previewName"></div>
<pre class="file-preview-body" x-text="previewContent"></pre>
<div class="file-preview-body" x-html="previewContent"></div>
</div>
</div>
@@ -423,16 +423,25 @@ document.addEventListener('alpine:init', function() {
this.previewName = node.name;
this.previewX = ev.clientX + 12;
this.previewY = ev.clientY + 12;
this.previewContent = 'Loading...';
this.previewVisible = true;
var self = this;
var wsId = {{ workspace_id }} || 1;
fetch('/api/files/' + wsId + '/' + encodeURIComponent(node.name))
.then(function(r) { return r.text(); })
.then(function(t) {
self.previewContent = t.substring(0, 500);
})
.catch(function() { self.previewContent = '(preview not available)'; });
var url = '/api/files/' + wsId + '/' + encodeURIComponent(node.name);
// Check if image
var imgExts = ['.png','.jpg','.jpeg','.gif','.webp','.svg','.bmp','.ico'];
var isImg = false;
for (var i=0; i<imgExts.length; i++) {
if ((node.name||'').toLowerCase().endsWith(imgExts[i])) { isImg = true; break; }
}
if (isImg) {
this.previewContent = '<img src="'+url+'" style="max-width:380px;max-height:180px;display:block;">';
} else {
this.previewContent = 'Loading...';
fetch(url)
.then(function(r) { return r.text(); })
.then(function(t) { self.previewContent = t.substring(0, 500); })
.catch(function() { self.previewContent = '(preview not available)'; });
}
},
hidePreview() {
this.previewVisible = false;