diff --git a/website/canvas.html b/website/canvas.html index 5d615fe..5e2d48a 100644 --- a/website/canvas.html +++ b/website/canvas.html @@ -2205,8 +2205,39 @@ } } - // Non-blocking: check trip availability after page settles - setTimeout(updateTravelToolbarState, 1500); + // ── Notes data cache for toolbar awareness ── + let _notesCache = null; // { notes: [], fetchedAt: 0 } + + async function fetchNotesData() { + if (_notesCache && Date.now() - _notesCache.fetchedAt < TRIP_CACHE_TTL) return _notesCache; + try { + const res = await fetch(`/${communitySlug}/rnotes/api/notes?limit=50`); + if (!res.ok) return { notes: [], fetchedAt: Date.now() }; + const data = await res.json(); + _notesCache = { notes: data.notes || [], fetchedAt: Date.now() }; + return _notesCache; + } catch { return { notes: [], fetchedAt: Date.now() }; } + } + + const NOTE_BTN_IDS = ["new-markdown", "new-obs-note"]; + + async function updateNoteToolbarState() { + const data = await fetchNotesData(); + const hasNotes = data.notes.length > 0; + for (const id of NOTE_BTN_IDS) { + const btn = document.getElementById(id); + if (btn) { + btn.classList.toggle("toolbar-disabled", !hasNotes); + if (!hasNotes) btn.title = "No notes yet — create notes in rNotes first"; + } + } + } + + // Non-blocking: check data availability after page settles + setTimeout(() => { + updateTravelToolbarState(); + updateNoteToolbarState(); + }, 1500); // Initialize Presence for real-time cursors const peerId = generatePeerId(); @@ -3170,8 +3201,15 @@ installSelectionTransforms(); // Toolbar button handlers — set pending tool for click-to-place - document.getElementById("new-markdown").addEventListener("click", () => { - setPendingTool("folk-markdown", { content: "# New Note\n\nStart typing..." }); + document.getElementById("new-markdown").addEventListener("click", async () => { + const data = await fetchNotesData(); + if (data.notes.length === 0) { + setPendingTool("folk-markdown", { content: "# New Note\n\nStart typing..." }); + return; + } + const note = pickFromList(data.notes, n => n.title || "Untitled", "Select a note"); + if (!note) return; + setPendingTool("folk-markdown", { content: note.content || `# ${note.title}\n\n${note.content_plain || ""}` }); }); document.getElementById("new-wrapper").addEventListener("click", () => { @@ -3210,7 +3248,19 @@ document.getElementById("new-prompt").addEventListener("click", () => setPendingTool("folk-prompt")); document.getElementById("new-transcription").addEventListener("click", () => setPendingTool("folk-transcription")); document.getElementById("new-video-chat").addEventListener("click", () => setPendingTool("folk-video-chat")); - document.getElementById("new-obs-note").addEventListener("click", () => setPendingTool("folk-obs-note")); + document.getElementById("new-obs-note").addEventListener("click", async () => { + const data = await fetchNotesData(); + if (data.notes.length === 0) { + setPendingTool("folk-obs-note"); + return; + } + const note = pickFromList(data.notes, n => n.title || "Untitled", "Select a note"); + if (!note) return; + setPendingTool("folk-obs-note", { + title: note.title || "Untitled", + content: note.content || note.content_plain || "", + }); + }); document.getElementById("new-workflow").addEventListener("click", () => { setPendingTool("folk-workflow-block", { blockType: "trigger", @@ -3868,7 +3918,7 @@ } canvasContent.addEventListener("contextmenu", (e) => { - const shapeEl = e.target.closest("folk-shape, folk-markdown, folk-wrapper, folk-slide, folk-chat, folk-embed, folk-calendar, folk-map, folk-image-gen, folk-video-gen, folk-prompt, folk-zine-gen, folk-transcription, folk-video-chat, folk-obs-note, folk-workflow-block, folk-itinerary, folk-destination, folk-budget, folk-packing-list, folk-booking, folk-token-mint, folk-token-ledger, folk-choice-vote, folk-choice-rank, folk-choice-spider, folk-social-post, folk-rapp, folk-feed, folk-piano, folk-splat, folk-blender, folk-drawfast, folk-freecad, folk-kicad"); + const shapeEl = e.target.closest("folk-shape, folk-markdown, folk-wrapper, folk-slide, folk-chat, folk-embed, folk-calendar, folk-map, folk-image-gen, folk-video-gen, folk-prompt, folk-zine-gen, folk-transcription, folk-video-chat, folk-obs-note, folk-workflow-block, folk-itinerary, folk-destination, folk-budget, folk-packing-list, folk-booking, folk-token-mint, folk-token-ledger, folk-choice-vote, folk-choice-rank, folk-choice-spider, folk-choice-conviction, folk-social-post, folk-rapp, folk-feed, folk-piano, folk-splat, folk-blender, folk-drawfast, folk-freecad, folk-kicad"); if (!shapeEl || !shapeEl.id) return; e.preventDefault(); @@ -4004,7 +4054,7 @@ "folk-budget": "💰", "folk-packing-list": "🎒", "folk-booking": "✈️", "folk-token-mint": "🪙", "folk-token-ledger": "📒", "folk-choice-vote": "☑", "folk-choice-rank": "📊", - "folk-choice-spider": "🕸", "folk-social-post": "📱", + "folk-choice-spider": "🕸", "folk-choice-conviction": "⏳", "folk-social-post": "📱", "folk-splat": "🔮", "folk-blender": "🧊", "folk-drawfast": "✏️", "folk-freecad": "📐", "folk-kicad": "🔌", "folk-rapp": "📱", "folk-feed": "🔄", "folk-arrow": "↗️", @@ -4717,7 +4767,7 @@ function sortFeedShapes(key) { const shapes = [...canvasContent.querySelectorAll( - 'folk-shape, folk-markdown, folk-wrapper, folk-slide, folk-chat, folk-obs-note, folk-rapp, folk-embed, folk-drawfast, folk-prompt, folk-zine-gen, folk-workflow-block, folk-choice-vote, folk-choice-rank, folk-choice-spider, folk-token, folk-google-item, folk-social-post, folk-calendar, folk-map, folk-piano, folk-splat, folk-video-chat, folk-transcription, folk-image-gen, folk-video-gen, folk-zine-gen, folk-blender, folk-freecad, folk-kicad, folk-itinerary, folk-destination, folk-budget, folk-packing-list, folk-booking' + 'folk-shape, folk-markdown, folk-wrapper, folk-slide, folk-chat, folk-obs-note, folk-rapp, folk-embed, folk-drawfast, folk-prompt, folk-zine-gen, folk-workflow-block, folk-choice-vote, folk-choice-rank, folk-choice-spider, folk-choice-conviction, folk-token, folk-google-item, folk-social-post, folk-calendar, folk-map, folk-piano, folk-splat, folk-video-chat, folk-transcription, folk-image-gen, folk-video-gen, folk-zine-gen, folk-blender, folk-freecad, folk-kicad, folk-itinerary, folk-destination, folk-budget, folk-packing-list, folk-booking' )]; shapes.sort((a, b) => {