diff --git a/src/workspaces/components/MarkdownEditor.tsx b/src/workspaces/components/MarkdownEditor.tsx
deleted file mode 100644
index be3c14dd..00000000
--- a/src/workspaces/components/MarkdownEditor.tsx
+++ /dev/null
@@ -1,115 +0,0 @@
-import { useState } from 'react';
-import md from 'markdown-it';
-import ReactMarkdown from 'react-markdown';
-import remarkGfm from 'remark-gfm';
-import rehypeRaw from 'rehype-raw';
-import { Button } from '@/components/ui/button';
-import { Code, Eye } from 'lucide-react';
-
-type MarkdownEditorProps = {
- content: string;
- onChange: (content: string) => void;
- placeholder?: string;
- greeting?: string;
- firstName?: string;
- lastName?: string;
-};
-
-export function MarkdownEditor({
- content,
- onChange,
- placeholder = 'Enter markdown...',
- greeting,
- firstName,
- lastName,
-}: MarkdownEditorProps) {
- const [showPreview, setShowPreview] = useState(false);
-
- const markdown = md({
- html: true,
- breaks: true,
- linkify: true,
- });
-
- const processGreeting = (text: string) => {
- return text
- .replace(/\{\{firstName\}\}/g, firstName || '')
- .replace(/\{\{fullName\}\}/g, `${firstName || ''} ${lastName || ''}`.trim());
- };
-
- const processedGreeting = greeting ? processGreeting(greeting) : '';
- const greetingHtml = processedGreeting ? `
${processedGreeting}
` : '';
- const preview = markdown.render(content);
-
- return (
-
- {/* Toolbar */}
-
- setShowPreview(false)}>
-
- Markdown
-
- setShowPreview(true)}>
-
- Preview
-
-
-
- {/* Content Area */}
-
-
- );
-}
diff --git a/src/workspaces/components/ui/start-confetti-button.tsx b/src/workspaces/components/ui/start-confetti-button.tsx
deleted file mode 100644
index 0097e8f6..00000000
--- a/src/workspaces/components/ui/start-confetti-button.tsx
+++ /dev/null
@@ -1,212 +0,0 @@
-import React, { useEffect, useRef, useState, useCallback } from 'react';
-import { Button } from '@/components/ui/button';
-import { Play } from 'lucide-react';
-
-export default function StartConfettiButton({
- isDraftLike,
- onStart,
- className,
- label = 'Start',
-}: {
- isDraftLike: boolean;
- onStart?: () => void;
- className?: string;
- label?: string;
-}) {
- const [showConfetti, setShowConfetti] = useState(false);
- const canvasRef = useRef(null);
- const rafRef = useRef(null);
-
- // ---- Confetti Engine (inline, light) ----
- const runConfetti = useCallback(() => {
- const canvas = canvasRef.current!;
- const ctx = canvas.getContext('2d')!;
- const DPR = Math.max(1, Math.min(2, window.devicePixelRatio || 1));
-
- // size canvas
- const resize = () => {
- const { innerWidth: w, innerHeight: h } = window;
- canvas.width = Math.floor(w * DPR);
- canvas.height = Math.floor(h * DPR);
- canvas.style.width = `${w}px`;
- canvas.style.height = `${h}px`;
- ctx.setTransform(DPR, 0, 0, DPR, 0, 0);
- };
- resize();
-
- const onResize = () => resize();
- window.addEventListener('resize', onResize);
-
- const colors = [
- '#ef4444', // red-500
- '#f59e0b', // amber-500
- '#10b981', // emerald-500
- '#3b82f6', // blue-500
- '#8b5cf6', // violet-500
- '#ec4899', // pink-500
- '#22c55e', // green-500
- '#eab308', // yellow-500
- ];
-
- type Particle = {
- x: number;
- y: number;
- w: number;
- h: number;
- vx: number;
- vy: number;
- rot: number;
- rotSpeed: number;
- tilt: number;
- tiltSpeed: number;
- color: string;
- alpha: number;
- decay: number;
- shape: 'rect' | 'triangle';
- };
-
- const rand = (min: number, max: number) => Math.random() * (max - min) + min;
-
- const particles: Particle[] = [];
- const bursts = 5;
- const perBurst = 80;
- const spread = Math.PI / 3;
- const gravity = 0.25;
- const air = 0.002;
- const floorFriction = 0.96;
-
- for (let b = 0; b < bursts; b++) {
- const originX = (window.innerWidth / (bursts + 1)) * (b + 1);
- const originY = window.innerHeight * 0.15;
- const angleBase = -Math.PI / 2;
- for (let i = 0; i < perBurst; i++) {
- const angle = angleBase + rand(-spread, spread);
- const speed = rand(6, 12);
- particles.push({
- x: originX + rand(-8, 8),
- y: originY + rand(-8, 8),
- w: rand(4, 8),
- h: rand(6, 14),
- vx: Math.cos(angle) * speed,
- vy: Math.sin(angle) * speed,
- rot: rand(0, Math.PI * 2),
- rotSpeed: rand(-0.2, 0.2),
- tilt: rand(-0.8, 0.8),
- tiltSpeed: rand(-0.02, 0.02),
- color: colors[(Math.random() * colors.length) | 0] ?? '#ef4444',
- alpha: 1,
- decay: rand(0.004, 0.01),
- shape: Math.random() < 0.3 ? 'triangle' : 'rect',
- });
- }
- }
-
- const tick = () => {
- ctx.clearRect(0, 0, canvas.width, canvas.height);
-
- for (const p of particles) {
- // physics
- p.vy += gravity;
- p.vx *= 1 - air;
- p.vy *= 1 - air * 0.5;
- p.x += p.vx;
- p.y += p.vy;
- p.rot += p.rotSpeed;
- p.tilt += p.tiltSpeed;
- p.alpha -= p.decay;
-
- // floor bounce & friction
- const floor = window.innerHeight - 6;
- if (p.y > floor) {
- p.y = floor;
- p.vy *= -0.45;
- p.vx *= floorFriction;
- p.rotSpeed *= floorFriction;
- p.tiltSpeed *= floorFriction;
- }
-
- // draw
- if (p.alpha > 0) {
- ctx.globalAlpha = Math.max(0, p.alpha);
- ctx.save();
- ctx.translate(p.x, p.y);
- ctx.rotate(p.rot + p.tilt);
- ctx.fillStyle = p.color;
-
- if (p.shape === 'rect') {
- ctx.fillRect(-p.w / 2, -p.h / 2, p.w, p.h);
- } else {
- ctx.beginPath();
- ctx.moveTo(0, -p.h / 2);
- ctx.lineTo(p.w / 2, p.h / 2);
- ctx.lineTo(-p.w / 2, p.h / 2);
- ctx.closePath();
- ctx.fill();
- }
- ctx.restore();
- }
- }
-
- const alive = particles.some((p) => p.alpha > 0.02);
- if (alive) {
- rafRef.current = requestAnimationFrame(tick);
- } else {
- // cleanup
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- window.removeEventListener('resize', onResize);
- }
- };
-
- tick();
- }, []);
-
- const handleStart = () => {
- onStart?.();
-
- if (isDraftLike) {
- setShowConfetti(true);
- }
- };
-
- useEffect(() => {
- if (!showConfetti) return;
-
- runConfetti();
-
- const t = setTimeout(() => {
- setShowConfetti(false);
- }, 2200);
-
- return () => {
- clearTimeout(t);
- if (rafRef.current) cancelAnimationFrame(rafRef.current);
- rafRef.current = null;
- };
- }, [showConfetti, runConfetti]);
-
- return (
- <>
-
-
- {label}
-
-
- {/* Confetti Overlay */}
- {showConfetti && (
-
- )}
- >
- );
-}
diff --git a/todo/inputs-outside-forms/_index.md b/todo/inputs-outside-forms/_index.md
index 6702648b..0201da5e 100644
--- a/todo/inputs-outside-forms/_index.md
+++ b/todo/inputs-outside-forms/_index.md
@@ -49,9 +49,10 @@ handling.
- `Dashboards/DashboardPreview`'s `NamePanel` (name + description) has its submit button in a
_different_, independently-mounted panel (`CreatePanel`) elsewhere in the workspace layout tree — a
`