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") {