Spaces:
Paused
Paused
Savestate: Revert cinematic animation to top/left/width for smoother trajectory, and add drag support to popover, bump version to 1.8.25
Browse files- index.html +1 -1
- src/views/documents/documents-view.js +82 -22
index.html
CHANGED
|
@@ -51,7 +51,7 @@
|
|
| 51 |
</head>
|
| 52 |
<body class="dark-theme in-chat-view">
|
| 53 |
|
| 54 |
-
<div id="live-version-badge" style="position: fixed; top: 50px; left: 50%; transform: translateX(-50%); font-size: 10px; color: var(--text-secondary); opacity: 0.6; z-index: 99999; pointer-events: none; font-weight: 500; font-family: monospace; letter-spacing: 1px; background: rgba(0,0,0,0.2); padding: 2px 8px; border-radius: 10px; border: 1px solid rgba(255,255,255,0.05);">BUILD 1.8.
|
| 55 |
|
| 56 |
<!-- SVG Filters for Liquid Glass Refraction -->
|
| 57 |
<svg width="0" height="0" style="position: absolute; pointer-events: none;">
|
|
|
|
| 51 |
</head>
|
| 52 |
<body class="dark-theme in-chat-view">
|
| 53 |
|
| 54 |
+
<div id="live-version-badge" style="position: fixed; top: 50px; left: 50%; transform: translateX(-50%); font-size: 10px; color: var(--text-secondary); opacity: 0.6; z-index: 99999; pointer-events: none; font-weight: 500; font-family: monospace; letter-spacing: 1px; background: rgba(0,0,0,0.2); padding: 2px 8px; border-radius: 10px; border: 1px solid rgba(255,255,255,0.05);">BUILD 1.8.25</div>
|
| 55 |
|
| 56 |
<!-- SVG Filters for Liquid Glass Refraction -->
|
| 57 |
<svg width="0" height="0" style="position: absolute; pointer-events: none;">
|
src/views/documents/documents-view.js
CHANGED
|
@@ -488,19 +488,23 @@ export class DocumentsView {
|
|
| 488 |
folderEl.style.position = 'fixed';
|
| 489 |
folderEl.style.top = rect.top + 'px';
|
| 490 |
folderEl.style.left = rect.left + 'px';
|
| 491 |
-
|
| 492 |
-
folderEl.style.
|
| 493 |
-
|
| 494 |
-
|
| 495 |
-
const randomRot = (Math.random() > 0.5 ? 1 : -1) * (8 + Math.random() * 8);
|
| 496 |
-
folderEl.dataset.rot = randomRot;
|
| 497 |
-
|
| 498 |
-
// Combine everything into a single transform
|
| 499 |
-
folderEl.style.setProperty('--target-transform', `translate(${translateX}px, ${translateY}px) scale(${scale}) rotate(${randomRot}deg)`);
|
| 500 |
|
| 501 |
folderEl.classList.remove('vault-folder-pressing');
|
| 502 |
folderEl.classList.add('vault-folder-detail');
|
| 503 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 504 |
// 4. Update ambient title
|
| 505 |
const titleEl = document.querySelector('.vault-ambient-title');
|
| 506 |
if (titleEl) titleEl.textContent = folderName;
|
|
@@ -619,6 +623,7 @@ export class DocumentsView {
|
|
| 619 |
|
| 620 |
// 3. Get fresh layout position in case of scroll
|
| 621 |
folderEl.classList.remove('vault-folder-detail');
|
|
|
|
| 622 |
folderEl.style.position = '';
|
| 623 |
folderEl.style.top = '';
|
| 624 |
folderEl.style.left = '';
|
|
@@ -627,30 +632,34 @@ export class DocumentsView {
|
|
| 627 |
|
| 628 |
const freshRect = folderEl.getBoundingClientRect();
|
| 629 |
|
| 630 |
-
//
|
|
|
|
|
|
|
|
|
|
| 631 |
const targetWidth = window.innerWidth * 0.5;
|
| 632 |
const targetTop = window.innerHeight - (targetWidth * 1.3) - 120;
|
| 633 |
const targetLeft = window.innerWidth * 0.05;
|
| 634 |
-
const scale = targetWidth / freshRect.width;
|
| 635 |
-
const translateX = targetLeft - freshRect.left;
|
| 636 |
-
const translateY = targetTop - freshRect.top;
|
| 637 |
-
const randomRot = folderEl.dataset.rot || -12;
|
| 638 |
|
| 639 |
-
folderEl.style.
|
|
|
|
|
|
|
| 640 |
|
| 641 |
-
// Put back to fixed detail state instantly
|
| 642 |
-
folderEl.style.position = 'fixed';
|
| 643 |
-
folderEl.style.top = freshRect.top + 'px';
|
| 644 |
-
folderEl.style.left = freshRect.left + 'px';
|
| 645 |
-
folderEl.style.width = freshRect.width + 'px';
|
| 646 |
folderEl.classList.add('vault-folder-detail');
|
|
|
|
|
|
|
|
|
|
| 647 |
|
| 648 |
// Force reflow
|
| 649 |
folderEl.offsetHeight;
|
| 650 |
|
| 651 |
-
// Now animate smoothly
|
| 652 |
-
folderEl.style.transition = 'transform 0.45s cubic-bezier(.22,.68,0,1.1)';
|
|
|
|
|
|
|
|
|
|
|
|
|
| 653 |
folderEl.classList.remove('vault-folder-detail');
|
|
|
|
| 654 |
|
| 655 |
await this._wait(450);
|
| 656 |
|
|
@@ -1097,6 +1106,51 @@ export class DocumentsView {
|
|
| 1097 |
|
| 1098 |
popover.style.top = `${top}px`;
|
| 1099 |
popover.style.left = `${left}px`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1100 |
|
| 1101 |
popover.querySelector('#popover-save').addEventListener('click', () => {
|
| 1102 |
const newColor = popover.querySelector('#popover-color').value;
|
|
@@ -1106,9 +1160,15 @@ export class DocumentsView {
|
|
| 1106 |
});
|
| 1107 |
|
| 1108 |
const closePopover = (e) => {
|
|
|
|
|
|
|
| 1109 |
if (!popover.contains(e.target) && !btnEl.contains(e.target)) {
|
| 1110 |
popover.remove();
|
| 1111 |
document.removeEventListener('click', closePopover);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1112 |
}
|
| 1113 |
};
|
| 1114 |
setTimeout(() => {
|
|
|
|
| 488 |
folderEl.style.position = 'fixed';
|
| 489 |
folderEl.style.top = rect.top + 'px';
|
| 490 |
folderEl.style.left = rect.left + 'px';
|
| 491 |
+
// Combine everything into a single transform for rotation ONLY
|
| 492 |
+
folderEl.style.setProperty('--folder-rot', `${randomRot}deg`);
|
| 493 |
+
if (randomRot < 0) folderEl.classList.add('tilt-left');
|
| 494 |
+
else folderEl.classList.add('tilt-right');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 495 |
|
| 496 |
folderEl.classList.remove('vault-folder-pressing');
|
| 497 |
folderEl.classList.add('vault-folder-detail');
|
| 498 |
|
| 499 |
+
// Force reflow
|
| 500 |
+
folderEl.offsetHeight;
|
| 501 |
+
|
| 502 |
+
// Animate to detail position using top, left, width
|
| 503 |
+
folderEl.style.transition = 'top 0.45s cubic-bezier(.22,.68,0,1.1), left 0.45s cubic-bezier(.22,.68,0,1.1), width 0.45s cubic-bezier(.22,.68,0,1.1), transform 0.45s cubic-bezier(.22,.68,0,1.1)';
|
| 504 |
+
folderEl.style.top = targetTop + 'px';
|
| 505 |
+
folderEl.style.left = targetLeft + 'px';
|
| 506 |
+
folderEl.style.width = targetWidth + 'px';
|
| 507 |
+
|
| 508 |
// 4. Update ambient title
|
| 509 |
const titleEl = document.querySelector('.vault-ambient-title');
|
| 510 |
if (titleEl) titleEl.textContent = folderName;
|
|
|
|
| 623 |
|
| 624 |
// 3. Get fresh layout position in case of scroll
|
| 625 |
folderEl.classList.remove('vault-folder-detail');
|
| 626 |
+
folderEl.classList.remove('tilt-left', 'tilt-right');
|
| 627 |
folderEl.style.position = '';
|
| 628 |
folderEl.style.top = '';
|
| 629 |
folderEl.style.left = '';
|
|
|
|
| 632 |
|
| 633 |
const freshRect = folderEl.getBoundingClientRect();
|
| 634 |
|
| 635 |
+
// Put back to fixed detail state instantly
|
| 636 |
+
folderEl.style.position = 'fixed';
|
| 637 |
+
|
| 638 |
+
// Restore current detail position before animating
|
| 639 |
const targetWidth = window.innerWidth * 0.5;
|
| 640 |
const targetTop = window.innerHeight - (targetWidth * 1.3) - 120;
|
| 641 |
const targetLeft = window.innerWidth * 0.05;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 642 |
|
| 643 |
+
folderEl.style.top = targetTop + 'px';
|
| 644 |
+
folderEl.style.left = targetLeft + 'px';
|
| 645 |
+
folderEl.style.width = targetWidth + 'px';
|
| 646 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 647 |
folderEl.classList.add('vault-folder-detail');
|
| 648 |
+
const rot = folderEl.dataset.rot || -12;
|
| 649 |
+
if (rot < 0) folderEl.classList.add('tilt-left');
|
| 650 |
+
else folderEl.classList.add('tilt-right');
|
| 651 |
|
| 652 |
// Force reflow
|
| 653 |
folderEl.offsetHeight;
|
| 654 |
|
| 655 |
+
// Now animate smoothly back to grid rect
|
| 656 |
+
folderEl.style.transition = 'top 0.45s cubic-bezier(.22,.68,0,1.1), left 0.45s cubic-bezier(.22,.68,0,1.1), width 0.45s cubic-bezier(.22,.68,0,1.1), transform 0.45s cubic-bezier(.22,.68,0,1.1)';
|
| 657 |
+
folderEl.style.top = freshRect.top + 'px';
|
| 658 |
+
folderEl.style.left = freshRect.left + 'px';
|
| 659 |
+
folderEl.style.width = freshRect.width + 'px';
|
| 660 |
+
|
| 661 |
folderEl.classList.remove('vault-folder-detail');
|
| 662 |
+
folderEl.classList.remove('tilt-left', 'tilt-right');
|
| 663 |
|
| 664 |
await this._wait(450);
|
| 665 |
|
|
|
|
| 1106 |
|
| 1107 |
popover.style.top = `${top}px`;
|
| 1108 |
popover.style.left = `${left}px`;
|
| 1109 |
+
popover.style.touchAction = 'none'; // Prevent scrolling while dragging
|
| 1110 |
+
|
| 1111 |
+
// --- Drag Support ---
|
| 1112 |
+
let isDragging = false;
|
| 1113 |
+
let dragStartX = 0;
|
| 1114 |
+
let dragStartY = 0;
|
| 1115 |
+
let popoverStartLeft = 0;
|
| 1116 |
+
let popoverStartTop = 0;
|
| 1117 |
+
|
| 1118 |
+
const dragStart = (e) => {
|
| 1119 |
+
// Don't drag if clicking inside input/button/grid
|
| 1120 |
+
if (e.target.closest('input, button, .icon-grid-item')) return;
|
| 1121 |
+
|
| 1122 |
+
isDragging = true;
|
| 1123 |
+
const clientX = e.touches ? e.touches[0].clientX : e.clientX;
|
| 1124 |
+
const clientY = e.touches ? e.touches[0].clientY : e.clientY;
|
| 1125 |
+
dragStartX = clientX;
|
| 1126 |
+
dragStartY = clientY;
|
| 1127 |
+
popoverStartLeft = parseFloat(popover.style.left);
|
| 1128 |
+
popoverStartTop = parseFloat(popover.style.top);
|
| 1129 |
+
};
|
| 1130 |
+
|
| 1131 |
+
const dragMove = (e) => {
|
| 1132 |
+
if (!isDragging) return;
|
| 1133 |
+
e.preventDefault(); // Stop scrolling
|
| 1134 |
+
const clientX = e.touches ? e.touches[0].clientX : e.clientX;
|
| 1135 |
+
const clientY = e.touches ? e.touches[0].clientY : e.clientY;
|
| 1136 |
+
const dx = clientX - dragStartX;
|
| 1137 |
+
const dy = clientY - dragStartY;
|
| 1138 |
+
popover.style.left = `${popoverStartLeft + dx}px`;
|
| 1139 |
+
popover.style.top = `${popoverStartTop + dy}px`;
|
| 1140 |
+
};
|
| 1141 |
+
|
| 1142 |
+
const dragEnd = () => {
|
| 1143 |
+
isDragging = false;
|
| 1144 |
+
};
|
| 1145 |
+
|
| 1146 |
+
popover.addEventListener('mousedown', dragStart);
|
| 1147 |
+
document.addEventListener('mousemove', dragMove);
|
| 1148 |
+
document.addEventListener('mouseup', dragEnd);
|
| 1149 |
+
|
| 1150 |
+
popover.addEventListener('touchstart', dragStart, { passive: false });
|
| 1151 |
+
document.addEventListener('touchmove', dragMove, { passive: false });
|
| 1152 |
+
document.addEventListener('touchend', dragEnd);
|
| 1153 |
+
// --------------------
|
| 1154 |
|
| 1155 |
popover.querySelector('#popover-save').addEventListener('click', () => {
|
| 1156 |
const newColor = popover.querySelector('#popover-color').value;
|
|
|
|
| 1160 |
});
|
| 1161 |
|
| 1162 |
const closePopover = (e) => {
|
| 1163 |
+
// Don't close if we were just dragging
|
| 1164 |
+
if (isDragging) return;
|
| 1165 |
if (!popover.contains(e.target) && !btnEl.contains(e.target)) {
|
| 1166 |
popover.remove();
|
| 1167 |
document.removeEventListener('click', closePopover);
|
| 1168 |
+
document.removeEventListener('mousemove', dragMove);
|
| 1169 |
+
document.removeEventListener('mouseup', dragEnd);
|
| 1170 |
+
document.removeEventListener('touchmove', dragMove);
|
| 1171 |
+
document.removeEventListener('touchend', dragEnd);
|
| 1172 |
}
|
| 1173 |
};
|
| 1174 |
setTimeout(() => {
|