From 6118193f19911250be11bf85b8774c351542b81b Mon Sep 17 00:00:00 2001 From: Hiren Kangad Date: Fri, 13 Mar 2026 09:47:00 +0530 Subject: [PATCH] feat(markdown): add MARKDOWN tool, toolbar button, and m/6 shortcuts --- frontend/src/canvas/tools.ts | 52 +++++++++++++++++++++++++++++ frontend/src/components/Toolbar.tsx | 13 ++++++++ 2 files changed, 65 insertions(+) diff --git a/frontend/src/canvas/tools.ts b/frontend/src/canvas/tools.ts index 3b4a013..06bf2ad 100644 --- a/frontend/src/canvas/tools.ts +++ b/frontend/src/canvas/tools.ts @@ -22,6 +22,7 @@ export enum ToolType { TEXT = 'TEXT', ERASER = 'ERASER', STICKY = 'STICKY', + MARKDOWN = 'MARKDOWN', } export interface ToolOptions { @@ -61,6 +62,8 @@ export interface ToolContext { switchToSelect?: () => void; /** When true, destructive/creative tool clicks (TEXT, ERASER) are suppressed. */ reviewMode?: boolean; + /** Called after a new markdown card is created, to immediately enter edit mode. */ + onMarkdownCreated?: (id: string) => void; } export function activateTool( @@ -390,6 +393,53 @@ export function activateTool( }; } + case ToolType.MARKDOWN: { + container.style.cursor = 'crosshair'; + + const onClick = (e: PointerEvent) => { + if (ctx.reviewMode) return; + const rect = container.getBoundingClientRect(); + const world = viewport.toWorld(e.clientX - rect.left, e.clientY - rect.top); + + const mdData = { + id: crypto.randomUUID(), + type: 'markdown' as const, + x: world.x - 225, + y: world.y - 20, + w: 450, + h: 60, + sx: 1, + sy: 1, + angle: 0, + z: scene.nextZ(), + opacity: 1, + locked: false, + name: '', + visible: true, + content: '', + bgColor: '#232336', + textColor: '#e0e0e0', + accentColor: '#7950f2', + padding: 16, + cornerRadius: 10, + }; + + scene._createItem(mdData, true); + scene._applyZOrder(); + ctx.broadcastElements?.([mdData.id]); + ctx.onChange(); + + ctx.switchToSelect?.(); + ctx.onMarkdownCreated?.(mdData.id); + container.removeEventListener('pointerdown', onClick); + }; + + container.addEventListener('pointerdown', onClick); + return () => { + container.removeEventListener('pointerdown', onClick); + }; + } + default: return null; } @@ -400,8 +450,10 @@ export const toolShortcuts: Record = { p: ToolType.PEN, t: ToolType.TEXT, s: ToolType.STICKY, + m: ToolType.MARKDOWN, '1': ToolType.SELECT, '3': ToolType.PEN, '4': ToolType.TEXT, '5': ToolType.STICKY, + '6': ToolType.MARKDOWN, }; diff --git a/frontend/src/components/Toolbar.tsx b/frontend/src/components/Toolbar.tsx index c066cf9..fe24d2e 100644 --- a/frontend/src/components/Toolbar.tsx +++ b/frontend/src/components/Toolbar.tsx @@ -72,6 +72,18 @@ function IconSticky() { ); } +function IconMarkdown() { + return ( + + + + + + + + ); +} + function IconReview() { return ( @@ -124,6 +136,7 @@ const toolButtons: { tool: ToolType; label: string; shortcut: string; numKey: st { tool: ToolType.PEN, label: 'Draw', shortcut: 'P', numKey: '3', Icon: IconPen, hint: 'Click and drag to draw freehand' }, { tool: ToolType.TEXT, label: 'Text', shortcut: 'T', numKey: '4', Icon: IconText, hint: 'Click on the canvas to place text' }, { tool: ToolType.STICKY, label: 'Sticky', shortcut: 'S', numKey: '5', Icon: IconSticky, hint: 'Click on the canvas to place a sticky note' }, + { tool: ToolType.MARKDOWN, label: 'Markdown', shortcut: 'M', numKey: '6', Icon: IconMarkdown, hint: 'Click on the canvas to place a markdown card' }, ]; const REVIEW_HINT = 'Click an object to place a comment pin. Press . to toggle.';