feat(markdown): add MARKDOWN tool, toolbar button, and m/6 shortcuts
This commit is contained in:
@@ -22,6 +22,7 @@ export enum ToolType {
|
|||||||
TEXT = 'TEXT',
|
TEXT = 'TEXT',
|
||||||
ERASER = 'ERASER',
|
ERASER = 'ERASER',
|
||||||
STICKY = 'STICKY',
|
STICKY = 'STICKY',
|
||||||
|
MARKDOWN = 'MARKDOWN',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ToolOptions {
|
export interface ToolOptions {
|
||||||
@@ -61,6 +62,8 @@ export interface ToolContext {
|
|||||||
switchToSelect?: () => void;
|
switchToSelect?: () => void;
|
||||||
/** When true, destructive/creative tool clicks (TEXT, ERASER) are suppressed. */
|
/** When true, destructive/creative tool clicks (TEXT, ERASER) are suppressed. */
|
||||||
reviewMode?: boolean;
|
reviewMode?: boolean;
|
||||||
|
/** Called after a new markdown card is created, to immediately enter edit mode. */
|
||||||
|
onMarkdownCreated?: (id: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function activateTool(
|
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:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -400,8 +450,10 @@ export const toolShortcuts: Record<string, ToolType> = {
|
|||||||
p: ToolType.PEN,
|
p: ToolType.PEN,
|
||||||
t: ToolType.TEXT,
|
t: ToolType.TEXT,
|
||||||
s: ToolType.STICKY,
|
s: ToolType.STICKY,
|
||||||
|
m: ToolType.MARKDOWN,
|
||||||
'1': ToolType.SELECT,
|
'1': ToolType.SELECT,
|
||||||
'3': ToolType.PEN,
|
'3': ToolType.PEN,
|
||||||
'4': ToolType.TEXT,
|
'4': ToolType.TEXT,
|
||||||
'5': ToolType.STICKY,
|
'5': ToolType.STICKY,
|
||||||
|
'6': ToolType.MARKDOWN,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -72,6 +72,18 @@ function IconSticky() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function IconMarkdown() {
|
||||||
|
return (
|
||||||
|
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<rect x="2" y="1" width="14" height="16" rx="2" />
|
||||||
|
<path d="M5 5h8" />
|
||||||
|
<path d="M5 8h6" />
|
||||||
|
<path d="M5 11h4" />
|
||||||
|
<circle cx="13" cy="13" r="2" fill="currentColor" stroke="none" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function IconReview() {
|
function IconReview() {
|
||||||
return (
|
return (
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5">
|
||||||
@@ -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.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.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.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.';
|
const REVIEW_HINT = 'Click an object to place a comment pin. Press . to toggle.';
|
||||||
|
|||||||
Reference in New Issue
Block a user