From 9ee2960b774255c35b9224d44d4df6ea397e5d85 Mon Sep 17 00:00:00 2001 From: Etienne Delvarre Date: Fri, 29 May 2026 14:30:07 +0200 Subject: [PATCH] feat: reorganize Vision layout + moodboard image drag repositioning - Phrase + Tags side by side (2 columns) - Moodboard full width - Synopsis full width - On veut / On ne veut pas as separate cards (2 columns) - Moodboard images: drag to reposition in any direction (2D) - Double-click to replace image, single-click to upload on empty Co-Authored-By: Claude Opus 4.6 --- public/index.html | 89 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 61 insertions(+), 28 deletions(-) diff --git a/public/index.html b/public/index.html index 6aa7de4..0b2060d 100644 --- a/public/index.html +++ b/public/index.html @@ -269,10 +269,10 @@ window.dispatchEvent(new Event('tiptap-loaded')); .two-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; } .list-block h4 { font-family: 'Playfair Display', serif; font-size: 15px; font-weight: 600; margin-bottom: 12px; } - .list-block h4.yes { color: #5a7a3a; } - .list-block h4.no { color: #a0523d; } - .dark .list-block h4.yes { color: #8aaa6a; } - .dark .list-block h4.no { color: #c07050; } + .list-block h4.yes, .v-card h3.yes { color: #5a7a3a; } + .list-block h4.no, .v-card h3.no { color: #a0523d; } + .dark .list-block h4.yes, .dark .v-card h3.yes { color: #8aaa6a; } + .dark .list-block h4.no, .dark .v-card h3.no { color: #c07050; } .list-item { padding: 10px 0; font-size: 15px; color: var(--text); display: flex; align-items: flex-start; gap: 8px; } .list-item .bullet { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; margin-top: 8px; } .list-item.yes .bullet { background: #5a7a3a; } @@ -291,7 +291,9 @@ window.dispatchEvent(new Event('tiptap-loaded')); .mood-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 16px; } .mood-card { position: relative; border-radius: 10px; overflow: hidden; background: var(--bg-alt); border: 1px solid var(--border); } - .mood-card-img { width: 100%; aspect-ratio: 1; background-size: cover; background-position: center; cursor: pointer; background-color: var(--bg-alt); display: flex; align-items: center; justify-content: center; } + .mood-card-img { width: 100%; aspect-ratio: 1; background-size: cover; background-position: center; cursor: pointer; background-color: var(--bg-alt); display: flex; align-items: center; justify-content: center; user-select: none; } + .mood-card-img.has-image { cursor: grab; } + .mood-card-img.has-image.dragging { cursor: grabbing; } .mood-card-img.empty { border: 1px dashed var(--border); border-radius: 10px 10px 0 0; } .mood-card-img.empty::after { content: '+'; font-size: 28px; color: var(--text-hint); } .mood-card-caption { padding: 8px 10px; font-size: 13px; color: var(--text); min-height: 32px; } @@ -473,40 +475,36 @@ window.dispatchEvent(new Event('tiptap-loaded')); -
+

L'histoire en une phrase

+
+

Ambiance, époque, rythme

+
+
+ +
+

Moodboard

+
+ +
+

Synopsis

-

Ambiance, époque, rythme

-
+

Ce qu'on veut

+
-

Moodboard

-
- -
- -
-

Ce qu'on veut / ne veut pas

-
-
-

On veut

-
-
-
-

On ne veut pas

-
-
-
+

Ce qu'on ne veut pas

+
@@ -1222,9 +1220,42 @@ function renderMoodboard() { card.className = 'mood-card'; const imgDiv = document.createElement('div'); - imgDiv.className = 'mood-card-img' + (item.image ? '' : ' empty'); - if (item.image) imgDiv.style.backgroundImage = `url(${item.image})`; - imgDiv.onclick = () => { moodUploadTarget = i; document.getElementById('mood-file-input').click(); }; + const hasImg = !!item.image; + imgDiv.className = 'mood-card-img' + (hasImg ? ' has-image' : ' empty'); + if (hasImg) { + imgDiv.style.backgroundImage = `url(${item.image})`; + imgDiv.style.backgroundPosition = `${item.posX ?? 50}% ${item.posY ?? 50}%`; + } + // Click to upload (only if no image or double-click) + imgDiv.ondblclick = () => { moodUploadTarget = i; document.getElementById('mood-file-input').click(); }; + if (!hasImg) imgDiv.onclick = () => { moodUploadTarget = i; document.getElementById('mood-file-input').click(); }; + // Drag to reposition + if (hasImg) { + let startX, startY, startPosX, startPosY; + const onMove = (e) => { + const rect = imgDiv.getBoundingClientRect(); + const dx = ((e.clientX - startX) / rect.width) * -100; + const dy = ((e.clientY - startY) / rect.height) * -100; + item.posX = Math.max(0, Math.min(100, startPosX + dx)); + item.posY = Math.max(0, Math.min(100, startPosY + dy)); + imgDiv.style.backgroundPosition = `${item.posX}% ${item.posY}%`; + }; + const onUp = () => { + imgDiv.classList.remove('dragging'); + window.removeEventListener('mousemove', onMove); + window.removeEventListener('mouseup', onUp); + saveVisionSection('moodboard', JSON.stringify(items)); + }; + imgDiv.addEventListener('mousedown', (e) => { + if (e.button !== 0) return; + e.preventDefault(); + imgDiv.classList.add('dragging'); + startX = e.clientX; startY = e.clientY; + startPosX = item.posX ?? 50; startPosY = item.posY ?? 50; + window.addEventListener('mousemove', onMove); + window.addEventListener('mouseup', onUp); + }); + } const caption = document.createElement('div'); caption.className = 'mood-card-caption'; @@ -1292,6 +1323,8 @@ document.addEventListener('DOMContentLoaded', () => { try { items = JSON.parse(row?.Contenu || '[]'); } catch { items = []; } if (items[moodUploadTarget]) { items[moodUploadTarget].image = data.path; + items[moodUploadTarget].posX = 50; + items[moodUploadTarget].posY = 50; saveVisionSection('moodboard', JSON.stringify(items)); renderMoodboard(); }