diff --git a/website/canvas.html b/website/canvas.html index a672c25..8d91c18 100644 --- a/website/canvas.html +++ b/website/canvas.html @@ -3960,6 +3960,132 @@ syncBottomToolbar(); }); + // ── Recent tools + Favorites (+) menu ── + const recentToolsEl = document.getElementById("recent-tools"); + const recentSep = document.getElementById("recent-sep"); + const toolPlusMenu = document.getElementById("tool-plus-menu"); + const toolPlusBtn = document.getElementById("tool-plus"); + + // Recent tools: track last 2 sidebar tools used (not basic canvas tools) + let recentTools = JSON.parse(localStorage.getItem("rspace_recent_tools") || "[]"); + // Pinned favorites + let pinnedTools = JSON.parse(localStorage.getItem("rspace_pinned_tools") || "[]"); + + // Tool registry: maps sidebar button IDs to display info + const sidebarToolRegistry = {}; + document.querySelectorAll("#toolbar .toolbar-dropdown button").forEach(btn => { + sidebarToolRegistry[btn.id] = { + id: btn.id, + label: btn.textContent.trim(), + title: btn.title || btn.textContent.trim(), + }; + }); + // Add direct sidebar buttons too + ["new-feed", "toggle-memory", "toggle-hide-forgotten", "feed-toggle"].forEach(id => { + const btn = document.getElementById(id); + if (btn) sidebarToolRegistry[id] = { id, label: btn.textContent.trim(), title: btn.title || btn.textContent.trim() }; + }); + + function trackRecentTool(toolId) { + if (!toolId || !sidebarToolRegistry[toolId]) return; + // Don't track if it's already pinned + if (pinnedTools.includes(toolId)) return; + recentTools = recentTools.filter(t => t !== toolId); + recentTools.unshift(toolId); + recentTools = recentTools.slice(0, 2); + localStorage.setItem("rspace_recent_tools", JSON.stringify(recentTools)); + renderRecentTools(); + } + + function renderRecentTools() { + recentToolsEl.innerHTML = ""; + // Show pinned first, then recent (excluding pinned) + const shown = [...pinnedTools, ...recentTools.filter(t => !pinnedTools.includes(t))].slice(0, 4); + if (shown.length > 0) recentSep.style.display = ""; + else recentSep.style.display = "none"; + + for (const toolId of shown) { + const info = sidebarToolRegistry[toolId]; + if (!info) continue; + const btn = document.createElement("button"); + btn.className = "recent-tool-btn"; + btn.title = info.title; + btn.textContent = info.label.split(" ")[0]; // emoji only + btn.addEventListener("click", (e) => { + e.stopPropagation(); + document.getElementById(toolId)?.click(); + }); + recentToolsEl.appendChild(btn); + } + } + + // Track clicks on sidebar tool buttons + document.querySelectorAll("#toolbar .toolbar-dropdown button, #toolbar-panel-body button").forEach(btn => { + btn.addEventListener("click", () => trackRecentTool(btn.id)); + }); + // Intercept panel button clicks for tracking (cloned dynamically) + document.getElementById("toolbar-panel-body")?.addEventListener("click", (e) => { + const btn = e.target.closest("button"); + if (btn && btn.id) trackRecentTool(btn.id); + }); + + // [+] menu: show all sidebar tools, allow pinning + toolPlusBtn.addEventListener("click", (e) => { + e.stopPropagation(); + toolPlusMenu.classList.toggle("open"); + if (toolPlusMenu.classList.contains("open")) renderPlusMenu(); + }); + + document.addEventListener("click", (e) => { + if (!e.target.closest(".tool-plus-wrap")) { + toolPlusMenu.classList.remove("open"); + } + }); + + function renderPlusMenu() { + toolPlusMenu.innerHTML = ""; + + // Group tools by their toolbar group + const groups = document.querySelectorAll("#toolbar .toolbar-group"); + for (const group of groups) { + const toggle = group.querySelector(".toolbar-group-toggle"); + const dropdown = group.querySelector(".toolbar-dropdown"); + if (!toggle || !dropdown) continue; + + const heading = document.createElement("div"); + heading.className = "menu-heading"; + heading.textContent = toggle.textContent.trim(); + toolPlusMenu.appendChild(heading); + + for (const origBtn of dropdown.querySelectorAll("button")) { + const toolId = origBtn.id; + if (!toolId) continue; + const info = sidebarToolRegistry[toolId]; + if (!info) continue; + + const btn = document.createElement("button"); + btn.className = pinnedTools.includes(toolId) ? "pinned" : ""; + btn.innerHTML = `${info.label}📌`; + btn.addEventListener("click", (e) => { + e.stopPropagation(); + if (pinnedTools.includes(toolId)) { + pinnedTools = pinnedTools.filter(t => t !== toolId); + } else { + pinnedTools.push(toolId); + if (pinnedTools.length > 4) pinnedTools.shift(); + } + localStorage.setItem("rspace_pinned_tools", JSON.stringify(pinnedTools)); + renderRecentTools(); + renderPlusMenu(); + }); + toolPlusMenu.appendChild(btn); + } + } + } + + // Initial render + renderRecentTools(); + // Whiteboard pointer handlers on the SVG overlay wbOverlay.addEventListener("pointerdown", (e) => { if (!wbTool || wbTool === "eraser") {