|
|
|
|
@ -702,8 +702,6 @@
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div id="toolbar">
|
|
|
|
|
<button id="quick-add" title="Add rApp" style="background:#14b8a6;color:white;font-size:18px;font-weight:700;padding:6px 10px;border:none;border-radius:8px;cursor:pointer;text-align:center;line-height:1;">+</button>
|
|
|
|
|
<span class="toolbar-sep"></span>
|
|
|
|
|
<div class="toolbar-group">
|
|
|
|
|
<button class="toolbar-group-toggle">✏️ Draw</button>
|
|
|
|
|
<div class="toolbar-dropdown">
|
|
|
|
|
@ -717,13 +715,17 @@
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="toolbar-group">
|
|
|
|
|
<button class="toolbar-group-toggle">📝 Create</button>
|
|
|
|
|
<button class="toolbar-group-toggle">📝 Note</button>
|
|
|
|
|
<div class="toolbar-dropdown">
|
|
|
|
|
<button id="new-markdown" title="New Note">📝 Note</button>
|
|
|
|
|
<button id="new-wrapper" title="New Card">🗂️ Card</button>
|
|
|
|
|
<button id="new-slide" title="New Slide">🎞️ Slide</button>
|
|
|
|
|
<button id="new-obs-note" title="New Rich Note">📓 Rich Note</button>
|
|
|
|
|
<button id="new-chat" title="New Chat">💬 Chat</button>
|
|
|
|
|
<button id="embed-notes" title="Embed rNotes">📝 rNotes</button>
|
|
|
|
|
<button id="embed-books" title="Embed rBooks">📚 rBooks</button>
|
|
|
|
|
<button id="embed-pubs" title="Embed rPubs">📖 rPubs</button>
|
|
|
|
|
<button id="embed-forum" title="Embed rForum">💬 rForum</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
@ -737,6 +739,7 @@
|
|
|
|
|
<button id="new-drawfast" title="New Drawing">✏️ Drawfast</button>
|
|
|
|
|
<button id="new-freecad" title="New FreeCAD">📐 FreeCAD</button>
|
|
|
|
|
<button id="new-kicad" title="New KiCAD PCB">🔌 KiCAD PCB</button>
|
|
|
|
|
<button id="embed-swag" title="Embed rSwag">🎨 rSwag</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
@ -746,6 +749,8 @@
|
|
|
|
|
<button id="new-transcription" title="New Transcription">🎤 Transcribe</button>
|
|
|
|
|
<button id="new-video-chat" title="New Video Call">📹 Video Call</button>
|
|
|
|
|
<button id="new-piano" title="New Piano">🎹 Piano</button>
|
|
|
|
|
<button id="embed-photos" title="Embed rPhotos">📸 rPhotos</button>
|
|
|
|
|
<button id="embed-tube" title="Embed rTube">🎬 rTube</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
@ -757,6 +762,12 @@
|
|
|
|
|
<button id="new-calendar" title="New Calendar">📅 Calendar</button>
|
|
|
|
|
<button id="new-map" title="New Map">🗺️ Map</button>
|
|
|
|
|
<button id="new-social-post" title="New Post">📱 Social Post</button>
|
|
|
|
|
<button id="embed-files" title="Embed rFiles">📁 rFiles</button>
|
|
|
|
|
<button id="embed-work" title="Embed rWork">📋 rWork</button>
|
|
|
|
|
<button id="embed-inbox" title="Embed rInbox">📧 rInbox</button>
|
|
|
|
|
<button id="embed-cart" title="Embed rCart">🛒 rCart</button>
|
|
|
|
|
<button id="embed-data" title="Embed rData">📊 rData</button>
|
|
|
|
|
<button id="embed-network" title="Embed rNetwork">🌍 rNetwork</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
@ -786,28 +797,9 @@
|
|
|
|
|
<button id="new-choice-rank" title="New Ranking">📊 Ranking</button>
|
|
|
|
|
<button id="new-choice-spider" title="New Scoring">🕸 Scoring</button>
|
|
|
|
|
<button id="new-token" title="New Token">🪙 Token</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="toolbar-group">
|
|
|
|
|
<button class="toolbar-group-toggle">📱 rApps</button>
|
|
|
|
|
<div class="toolbar-dropdown">
|
|
|
|
|
<button id="embed-notes" title="Embed rNotes">📝 rNotes</button>
|
|
|
|
|
<button id="embed-photos" title="Embed rPhotos">📸 rPhotos</button>
|
|
|
|
|
<button id="embed-books" title="Embed rBooks">📚 rBooks</button>
|
|
|
|
|
<button id="embed-pubs" title="Embed rPubs">📖 rPubs</button>
|
|
|
|
|
<button id="embed-files" title="Embed rFiles">📁 rFiles</button>
|
|
|
|
|
<button id="embed-work" title="Embed rWork">📋 rWork</button>
|
|
|
|
|
<button id="embed-forum" title="Embed rForum">💬 rForum</button>
|
|
|
|
|
<button id="embed-inbox" title="Embed rInbox">📧 rInbox</button>
|
|
|
|
|
<button id="embed-tube" title="Embed rTube">🎬 rTube</button>
|
|
|
|
|
<button id="embed-funds" title="Embed rFunds">🌊 rFunds</button>
|
|
|
|
|
<button id="embed-wallet" title="Embed rWallet">💰 rWallet</button>
|
|
|
|
|
<button id="embed-vote" title="Embed rVote">🗳️ rVote</button>
|
|
|
|
|
<button id="embed-cart" title="Embed rCart">🛒 rCart</button>
|
|
|
|
|
<button id="embed-data" title="Embed rData">📊 rData</button>
|
|
|
|
|
<button id="embed-network" title="Embed rNetwork">🌍 rNetwork</button>
|
|
|
|
|
<button id="embed-swag" title="Embed rSwag">🎨 rSwag</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
@ -2377,12 +2369,12 @@
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
document.getElementById("zoom-in").addEventListener("click", () => {
|
|
|
|
|
scale = Math.min(scale * 1.25, maxScale);
|
|
|
|
|
scale = Math.min(scale * 1.1, maxScale);
|
|
|
|
|
updateCanvasTransform();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
document.getElementById("zoom-out").addEventListener("click", () => {
|
|
|
|
|
scale = Math.max(scale / 1.25, minScale);
|
|
|
|
|
scale = Math.max(scale / 1.1, minScale);
|
|
|
|
|
updateCanvasTransform();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@ -2398,22 +2390,9 @@
|
|
|
|
|
const toolbarEl = document.getElementById("toolbar");
|
|
|
|
|
|
|
|
|
|
mobileMenuBtn.addEventListener("click", () => {
|
|
|
|
|
// On mobile, first tap opens rApps group in the popout panel
|
|
|
|
|
const rAppsGroup = toolbarEl.querySelector(".toolbar-group:has(#embed-notes)")
|
|
|
|
|
|| [...toolbarEl.querySelectorAll(".toolbar-group")].find(g =>
|
|
|
|
|
g.querySelector(".toolbar-group-toggle")?.textContent.includes("rApps"));
|
|
|
|
|
if (rAppsGroup && !toolbarEl.classList.contains("mobile-open")) {
|
|
|
|
|
toolbarEl.classList.add("mobile-open");
|
|
|
|
|
mobileMenuBtn.textContent = "✕";
|
|
|
|
|
// Auto-open the rApps panel
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
if (typeof openToolbarPanel === "function") openToolbarPanel(rAppsGroup);
|
|
|
|
|
}, 50);
|
|
|
|
|
} else {
|
|
|
|
|
const isOpen = toolbarEl.classList.toggle("mobile-open");
|
|
|
|
|
mobileMenuBtn.textContent = isOpen ? "✕" : "✚";
|
|
|
|
|
if (!isOpen && typeof closeToolbarPanel === "function") closeToolbarPanel();
|
|
|
|
|
}
|
|
|
|
|
const isOpen = toolbarEl.classList.toggle("mobile-open");
|
|
|
|
|
mobileMenuBtn.textContent = isOpen ? "✕" : "✚";
|
|
|
|
|
if (!isOpen && typeof closeToolbarPanel === "function") closeToolbarPanel();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Auto-close toolbar after tapping a shape-creation button on mobile
|
|
|
|
|
@ -2495,21 +2474,6 @@
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Desktop quick-add button → opens the rApps popout panel
|
|
|
|
|
document.getElementById("quick-add")?.addEventListener("click", (e) => {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
const rAppsGroup = toolbarEl.querySelector(".toolbar-group:has(#embed-notes)")
|
|
|
|
|
|| [...toolbarEl.querySelectorAll(".toolbar-group")].find(g =>
|
|
|
|
|
g.querySelector(".toolbar-group-toggle")?.textContent.includes("rApps"));
|
|
|
|
|
if (rAppsGroup) {
|
|
|
|
|
if (activeToolbarGroup === rAppsGroup) {
|
|
|
|
|
closeToolbarPanel();
|
|
|
|
|
} else {
|
|
|
|
|
openToolbarPanel(rAppsGroup);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Collapse/expand toolbar
|
|
|
|
|
const collapseBtn = document.getElementById("toolbar-collapse");
|
|
|
|
|
collapseBtn.addEventListener("click", () => {
|
|
|
|
|
@ -2520,11 +2484,11 @@
|
|
|
|
|
|
|
|
|
|
// Mobile zoom controls (separate from toolbar)
|
|
|
|
|
document.getElementById("mz-in").addEventListener("click", () => {
|
|
|
|
|
scale = Math.min(scale * 1.25, maxScale);
|
|
|
|
|
scale = Math.min(scale * 1.1, maxScale);
|
|
|
|
|
updateCanvasTransform();
|
|
|
|
|
});
|
|
|
|
|
document.getElementById("mz-out").addEventListener("click", () => {
|
|
|
|
|
scale = Math.max(scale / 1.25, minScale);
|
|
|
|
|
scale = Math.max(scale / 1.1, minScale);
|
|
|
|
|
updateCanvasTransform();
|
|
|
|
|
});
|
|
|
|
|
document.getElementById("mz-reset").addEventListener("click", () => {
|
|
|
|
|
@ -2534,8 +2498,9 @@
|
|
|
|
|
updateCanvasTransform();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Touch gesture handling for two-finger pan
|
|
|
|
|
// Touch gesture handling for two-finger pan + pinch-to-zoom
|
|
|
|
|
let lastTouchCenter = null;
|
|
|
|
|
let lastTouchDist = null;
|
|
|
|
|
|
|
|
|
|
function getTouchCenter(touches) {
|
|
|
|
|
return {
|
|
|
|
|
@ -2544,6 +2509,12 @@
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getTouchDist(touches) {
|
|
|
|
|
const dx = touches[0].clientX - touches[1].clientX;
|
|
|
|
|
const dy = touches[0].clientY - touches[1].clientY;
|
|
|
|
|
return Math.hypot(dx, dy);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
canvas.addEventListener("touchstart", (e) => {
|
|
|
|
|
if (e.touches.length === 2) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
@ -2552,6 +2523,7 @@
|
|
|
|
|
panPointerId = null;
|
|
|
|
|
canvas.style.cursor = "";
|
|
|
|
|
lastTouchCenter = getTouchCenter(e.touches);
|
|
|
|
|
lastTouchDist = getTouchDist(e.touches);
|
|
|
|
|
}
|
|
|
|
|
}, { passive: false });
|
|
|
|
|
|
|
|
|
|
@ -2559,13 +2531,30 @@
|
|
|
|
|
if (e.touches.length === 2) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
|
|
|
|
|
// Two-finger pan (no zoom)
|
|
|
|
|
const currentCenter = getTouchCenter(e.touches);
|
|
|
|
|
const currentDist = getTouchDist(e.touches);
|
|
|
|
|
|
|
|
|
|
if (lastTouchCenter) {
|
|
|
|
|
// Two-finger pan
|
|
|
|
|
panX += currentCenter.x - lastTouchCenter.x;
|
|
|
|
|
panY += currentCenter.y - lastTouchCenter.y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (lastTouchDist && lastTouchDist > 0) {
|
|
|
|
|
// Pinch-to-zoom around gesture center
|
|
|
|
|
const zoomDelta = currentDist / lastTouchDist;
|
|
|
|
|
const newScale = Math.min(Math.max(scale * zoomDelta, minScale), maxScale);
|
|
|
|
|
// Adjust pan so zoom centers on pinch midpoint
|
|
|
|
|
const rect = canvas.getBoundingClientRect();
|
|
|
|
|
const cx = currentCenter.x - rect.left;
|
|
|
|
|
const cy = currentCenter.y - rect.top;
|
|
|
|
|
panX = cx - (cx - panX) * (newScale / scale);
|
|
|
|
|
panY = cy - (cy - panY) * (newScale / scale);
|
|
|
|
|
scale = newScale;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
lastTouchCenter = currentCenter;
|
|
|
|
|
lastTouchDist = currentDist;
|
|
|
|
|
|
|
|
|
|
updateCanvasTransform();
|
|
|
|
|
}
|
|
|
|
|
@ -2574,6 +2563,7 @@
|
|
|
|
|
canvas.addEventListener("touchend", (e) => {
|
|
|
|
|
if (e.touches.length < 2) {
|
|
|
|
|
lastTouchCenter = null;
|
|
|
|
|
lastTouchDist = null;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@ -2581,9 +2571,15 @@
|
|
|
|
|
canvas.addEventListener("wheel", (e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
if (e.ctrlKey) {
|
|
|
|
|
// Ctrl+wheel (or trackpad pinch) = zoom
|
|
|
|
|
const zoomFactor = e.deltaY > 0 ? 0.9 : 1.1;
|
|
|
|
|
scale = Math.min(Math.max(scale * zoomFactor, minScale), maxScale);
|
|
|
|
|
// Ctrl+wheel (or trackpad pinch) = zoom centered on cursor
|
|
|
|
|
const zoomFactor = e.deltaY > 0 ? 0.95 : 1.05;
|
|
|
|
|
const newScale = Math.min(Math.max(scale * zoomFactor, minScale), maxScale);
|
|
|
|
|
const rect = canvas.getBoundingClientRect();
|
|
|
|
|
const cx = e.clientX - rect.left;
|
|
|
|
|
const cy = e.clientY - rect.top;
|
|
|
|
|
panX = cx - (cx - panX) * (newScale / scale);
|
|
|
|
|
panY = cy - (cy - panY) * (newScale / scale);
|
|
|
|
|
scale = newScale;
|
|
|
|
|
} else {
|
|
|
|
|
// Regular wheel/two-finger scroll = pan
|
|
|
|
|
panX -= e.deltaX;
|
|
|
|
|
|