Merge branch 'dev'

This commit is contained in:
Jeff Emmett 2026-03-03 14:32:19 -08:00
commit d04b38b1c1
1 changed files with 126 additions and 0 deletions

View File

@ -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 = `<span>${info.label}</span><span class="pin-icon">📌</span>`;
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") {