feat(graph): add canvas-native document type icons for all supported types

- Add 10 new icon drawing functions: Notion, Google Docs, Slides, Cloud,
  X/Twitter, Play/Video, Image, Text/Note, and more
- Extend drawDocIcon switch to handle all 19+ document types from web app:
  word, excel, google_sheet, google_slide, powerpoint, onedrive, google_drive,
  tweet, youtube, video, image, text, note, onenote, mcp
- Update mock data with additional document types for better demo coverage
- All icons are pure canvas vector drawings (no image assets needed)
This commit is contained in:
Vorflux AI 2026-03-28 06:52:31 +00:00
parent 2a18fb4df0
commit c6ac3e0651
2 changed files with 231 additions and 4 deletions

View file

@ -735,9 +735,14 @@ function drawDocIcon(
break
case "doc":
case "docx":
drawTextLabel(ctx, x, y, size, "DOC", 0.28)
case "word":
case "microsoft_word":
drawTextLabel(ctx, x, y, size, "W", 0.4)
break
case "csv":
case "excel":
case "microsoft_excel":
case "google_sheet":
drawGridIcon(ctx, x, y, size)
break
case "json":
@ -745,12 +750,40 @@ function drawDocIcon(
break
case "notion":
case "notion_doc":
drawTextLabel(ctx, x, y, size, "N", 0.4)
drawNotionIcon(ctx, x, y, size)
break
case "google_doc":
case "google_sheet":
drawGoogleDocIcon(ctx, x, y, size)
break
case "google_slide":
drawTextLabel(ctx, x, y, size, "G", 0.4)
case "powerpoint":
case "microsoft_powerpoint":
drawSlidesIcon(ctx, x, y, size)
break
case "google_drive":
case "onedrive":
drawCloudIcon(ctx, x, y, size)
break
case "tweet":
drawXIcon(ctx, x, y, size)
break
case "youtube":
case "video":
drawPlayIcon(ctx, x, y, size)
break
case "image":
drawImageIcon(ctx, x, y, size)
break
case "text":
case "note":
drawTextNoteIcon(ctx, x, y, size)
break
case "onenote":
case "microsoft_onenote":
drawTextLabel(ctx, x, y, size, "N", 0.4)
break
case "mcp":
drawTextLabel(ctx, x, y, size, "MCP", 0.25)
break
default:
drawDocOutline(ctx, x, y, size)
@ -859,6 +892,193 @@ function drawDocOutline(
ctx.stroke()
}
/** Draw a simplified Notion "N" logo mark */
function drawNotionIcon(
ctx: CanvasRenderingContext2D,
x: number,
y: number,
size: number,
): void {
const w = size * 0.5
const h = size * 0.6
// Outer rounded rect outline
const r = size * 0.08
ctx.lineWidth = Math.max(1, size / 14)
roundRect(ctx, x - w / 2, y - h / 2, w, h, r)
ctx.stroke()
// Inner "N" shape
const inset = size * 0.12
const left = x - w / 2 + inset
const right = x + w / 2 - inset
const top = y - h / 2 + inset
const bottom = y + h / 2 - inset
ctx.beginPath()
ctx.moveTo(left, top)
ctx.lineTo(left, bottom)
ctx.moveTo(left, top)
ctx.lineTo(right, bottom)
ctx.moveTo(right, top)
ctx.lineTo(right, bottom)
ctx.stroke()
}
/** Draw a Google Docs icon (document with lines) */
function drawGoogleDocIcon(
ctx: CanvasRenderingContext2D,
x: number,
y: number,
size: number,
): void {
const w = size * 0.55
const h = size * 0.7
const fold = size * 0.15
// Document body with corner fold
ctx.beginPath()
ctx.moveTo(x - w / 2, y - h / 2)
ctx.lineTo(x + w / 2 - fold, y - h / 2)
ctx.lineTo(x + w / 2, y - h / 2 + fold)
ctx.lineTo(x + w / 2, y + h / 2)
ctx.lineTo(x - w / 2, y + h / 2)
ctx.closePath()
ctx.stroke()
// Text lines
const lineW = w * 0.6
const sp = size * 0.1
ctx.beginPath()
ctx.moveTo(x - lineW / 2, y - sp)
ctx.lineTo(x + lineW / 2, y - sp)
ctx.moveTo(x - lineW / 2, y + sp * 0.3)
ctx.lineTo(x + lineW / 2, y + sp * 0.3)
ctx.moveTo(x - lineW / 2, y + sp * 1.6)
ctx.lineTo(x + lineW / 3, y + sp * 1.6)
ctx.stroke()
}
/** Draw a slides/presentation icon (rectangle with play triangle) */
function drawSlidesIcon(
ctx: CanvasRenderingContext2D,
x: number,
y: number,
size: number,
): void {
const w = size * 0.7
const h = size * 0.5
ctx.strokeRect(x - w / 2, y - h / 2, w, h)
// Small play triangle in center
const triSize = size * 0.15
ctx.beginPath()
ctx.moveTo(x - triSize * 0.5, y - triSize * 0.7)
ctx.lineTo(x - triSize * 0.5, y + triSize * 0.7)
ctx.lineTo(x + triSize * 0.7, y)
ctx.closePath()
ctx.fill()
}
/** Draw a cloud icon for drive/storage types */
function drawCloudIcon(
ctx: CanvasRenderingContext2D,
x: number,
y: number,
size: number,
): void {
const s = size * 0.35
ctx.beginPath()
// Cloud shape using arcs
ctx.arc(x - s * 0.3, y + s * 0.1, s * 0.5, Math.PI * 0.7, Math.PI * 1.9)
ctx.arc(x + s * 0.1, y - s * 0.3, s * 0.55, Math.PI * 1.1, Math.PI * 0.3)
ctx.arc(x + s * 0.5, y + s * 0.1, s * 0.4, Math.PI * 1.4, Math.PI * 0.6)
ctx.lineTo(x - s * 0.7, y + s * 0.45)
ctx.closePath()
ctx.stroke()
}
/** Draw an X (formerly Twitter) icon */
function drawXIcon(
ctx: CanvasRenderingContext2D,
x: number,
y: number,
size: number,
): void {
const s = size * 0.3
ctx.lineWidth = Math.max(1.5, size / 10)
ctx.beginPath()
// X shape (two crossing strokes)
ctx.moveTo(x - s, y - s)
ctx.lineTo(x + s, y + s)
ctx.moveTo(x + s, y - s)
ctx.lineTo(x - s, y + s)
ctx.stroke()
}
/** Draw a play button icon for video/youtube */
function drawPlayIcon(
ctx: CanvasRenderingContext2D,
x: number,
y: number,
size: number,
): void {
const r = size * 0.38
// Rounded rectangle background
const w = r * 2.2
const h = r * 1.5
const cr = size * 0.08
roundRect(ctx, x - w / 2, y - h / 2, w, h, cr)
ctx.stroke()
// Play triangle
const triH = size * 0.22
ctx.beginPath()
ctx.moveTo(x - triH * 0.45, y - triH)
ctx.lineTo(x - triH * 0.45, y + triH)
ctx.lineTo(x + triH * 0.7, y)
ctx.closePath()
ctx.fill()
}
/** Draw an image/photo icon (landscape with mountain) */
function drawImageIcon(
ctx: CanvasRenderingContext2D,
x: number,
y: number,
size: number,
): void {
const w = size * 0.7
const h = size * 0.55
ctx.strokeRect(x - w / 2, y - h / 2, w, h)
// Mountain shape
ctx.beginPath()
ctx.moveTo(x - w / 2 + w * 0.1, y + h / 2 - h * 0.1)
ctx.lineTo(x - w * 0.05, y - h * 0.05)
ctx.lineTo(x + w * 0.15, y + h * 0.15)
ctx.lineTo(x + w * 0.25, y - h * 0.02)
ctx.lineTo(x + w / 2 - w * 0.1, y + h / 2 - h * 0.1)
ctx.stroke()
// Sun circle
const sunR = size * 0.06
ctx.beginPath()
ctx.arc(x - w * 0.15, y - h * 0.15, sunR, 0, Math.PI * 2)
ctx.fill()
}
/** Draw a text/note icon (lines of text) */
function drawTextNoteIcon(
ctx: CanvasRenderingContext2D,
x: number,
y: number,
size: number,
): void {
const w = size * 0.55
const h = size * 0.6
const sp = h / 5
ctx.beginPath()
for (let i = 0; i < 4; i++) {
const lineY = y - h / 2 + sp * (i + 0.5)
const lineW = i === 3 ? w * 0.6 : w
ctx.moveTo(x - w / 2, lineY)
ctx.lineTo(x - w / 2 + lineW, lineY)
}
ctx.stroke()
}
/** Lighten a 6-digit hex color by a fraction (0-1). Cached to avoid per-frame parsing. */
export function lightenColor(hex: string, amount: number): string {
if (

View file

@ -29,6 +29,13 @@ const DOCUMENT_TYPES = [
"json",
"notion",
"text",
"google_doc",
"google_sheet",
"google_slide",
"tweet",
"youtube",
"image",
"note",
] as const
const TITLE_PREFIXES = [