Files
agent-ui/frontend/src/components/transcript-debug/aquaticBackground/svgPatterns.ts
josedario87 eb69c0b2cf feat: Modular aquatic background system and shared CodeBlock component
Add aquaticBackground/ module with OceanScene (unified gradient, light rays,
sea floor, corals, seaweed, decorations), plus independent overlay layers
(BubbleStream, FishSchool, JellyfishDrift, EventOverlay, EdgeFade). Includes
event scheduling engine with 4 frequency tiers (minutes/hours/days/months)
and 20 random events with localStorage persistence.

Add shared CodeBlock component with copy-to-clipboard button, terminal-matched
monospace font (Consolas), and proper line-height/letter-spacing. Refactor
EditCard, WriteCard, TaskCard, and ToolResultBlock to use CodeBlock. Fix
markdown code block alignment to match terminal rendering.
2026-02-19 17:15:36 -06:00

140 lines
7.4 KiB
TypeScript

/** Encode an SVG string to a CSS-usable data URI */
export function svgDataUri(svg: string): string {
return `url("data:image/svg+xml,${encodeURIComponent(svg)}")`
}
/** Generate a pixel art fish SVG */
export function fishSvg(opts: {
w: number
h: number
body: string
accent: string
eye?: string
facing?: 'left' | 'right'
}): string {
const { w, h, body, accent, facing = 'right' } = opts
const eye = opts.eye ?? '#000'
// Build a generic pixel fish: body rect, tail, eye
const hw = Math.floor(w / 2)
const hh = Math.floor(h / 2)
const tailW = Math.floor(w * 0.2)
const bodyW = w - tailW
const eyeX = facing === 'right' ? bodyW - 3 : tailW + 1
const tailX = facing === 'right' ? 0 : bodyW
return `<svg xmlns='http://www.w3.org/2000/svg' width='${w}' height='${h}' viewBox='0 0 ${w} ${h}' shape-rendering='crispEdges'>`
// Tail
+ `<rect x='${tailX}' y='1' width='${tailW}' height='${Math.floor(h * 0.3)}' fill='${accent}' opacity='0.6'/>`
+ `<rect x='${tailX}' y='${h - Math.floor(h * 0.3) - 1}' width='${tailW}' height='${Math.floor(h * 0.3)}' fill='${accent}' opacity='0.6'/>`
// Body
+ `<rect x='${facing === 'right' ? tailW : 0}' y='1' width='${bodyW}' height='${h - 2}' fill='${body}' opacity='0.55'/>`
// Stripe
+ `<rect x='${hw - 1}' y='1' width='2' height='${h - 2}' fill='${accent}' opacity='0.35'/>`
// Eye
+ `<rect x='${eyeX}' y='${hh - 1}' width='2' height='2' fill='${eye}' opacity='0.5'/>`
+ `<rect x='${eyeX + (facing === 'right' ? 1 : 0)}' y='${hh - 1}' width='1' height='1' fill='white' opacity='0.35'/>`
+ `</svg>`
}
/** Generate a coral SVG of a given type */
export function coralSvg(type: 'branching' | 'brain' | 'fan', color: string, accent: string): string {
if (type === 'branching') {
return `<svg xmlns='http://www.w3.org/2000/svg' width='16' height='20' viewBox='0 0 16 20' shape-rendering='crispEdges'>`
+ `<rect x='7' y='8' width='2' height='12' fill='${color}' opacity='0.55'/>`
+ `<rect x='5' y='6' width='2' height='8' fill='${color}' opacity='0.5'/>`
+ `<rect x='9' y='5' width='2' height='9' fill='${color}' opacity='0.5'/>`
+ `<rect x='3' y='3' width='2' height='6' fill='${accent}' opacity='0.45'/>`
+ `<rect x='11' y='2' width='2' height='7' fill='${accent}' opacity='0.45'/>`
+ `<rect x='5' y='4' width='2' height='2' fill='${accent}' opacity='0.4'/>`
+ `<rect x='9' y='3' width='2' height='2' fill='${accent}' opacity='0.4'/>`
+ `<rect x='1' y='1' width='2' height='4' fill='${accent}' opacity='0.35'/>`
+ `<rect x='13' y='0' width='2' height='5' fill='${accent}' opacity='0.35'/>`
+ `</svg>`
}
if (type === 'brain') {
return `<svg xmlns='http://www.w3.org/2000/svg' width='12' height='10' viewBox='0 0 12 10' shape-rendering='crispEdges'>`
+ `<rect x='2' y='3' width='8' height='6' rx='0' fill='${color}' opacity='0.5'/>`
+ `<rect x='3' y='2' width='6' height='2' fill='${color}' opacity='0.45'/>`
+ `<rect x='4' y='1' width='4' height='2' fill='${accent}' opacity='0.4'/>`
+ `<rect x='3' y='4' width='2' height='2' fill='${accent}' opacity='0.3'/>`
+ `<rect x='7' y='5' width='2' height='2' fill='${accent}' opacity='0.3'/>`
+ `<rect x='5' y='7' width='2' height='2' fill='${accent}' opacity='0.25'/>`
+ `</svg>`
}
// fan
return `<svg xmlns='http://www.w3.org/2000/svg' width='14' height='18' viewBox='0 0 14 18' shape-rendering='crispEdges'>`
+ `<rect x='6' y='12' width='2' height='6' fill='${color}' opacity='0.5'/>`
+ `<rect x='4' y='8' width='6' height='5' fill='${color}' opacity='0.45'/>`
+ `<rect x='3' y='5' width='8' height='4' fill='${accent}' opacity='0.4'/>`
+ `<rect x='2' y='2' width='10' height='4' fill='${accent}' opacity='0.35'/>`
+ `<rect x='4' y='0' width='6' height='3' fill='${accent}' opacity='0.3'/>`
+ `<rect x='5' y='6' width='2' height='2' fill='${color}' opacity='0.25'/>`
+ `<rect x='8' y='4' width='2' height='2' fill='${color}' opacity='0.25'/>`
+ `</svg>`
}
/** Generate a seaweed stalk SVG */
export function seaweedSvg(height: number, color: string, accent: string): string {
const segments: string[] = []
for (let y = 0; y < height; y += 4) {
const xOff = (y / 4) % 2 === 0 ? 0 : 1
const c = y % 8 === 0 ? color : accent
const op = 0.3 + (y / height) * 0.25
segments.push(`<rect x='${1 + xOff}' y='${y}' width='2' height='4' fill='${c}' opacity='${op.toFixed(2)}'/>`)
}
// Leaf accents every 12px
for (let y = 6; y < height - 4; y += 12) {
const side = (y / 12) % 2 === 0 ? 0 : 3
segments.push(`<rect x='${side}' y='${y}' width='2' height='3' fill='${accent}' opacity='0.3'/>`)
}
return `<svg xmlns='http://www.w3.org/2000/svg' width='5' height='${height}' viewBox='0 0 5 ${height}' shape-rendering='crispEdges'>`
+ segments.join('')
+ `</svg>`
}
/** Generate a jellyfish SVG */
export function jellyfishSvg(bell: string, tentacle: string): string {
return `<svg xmlns='http://www.w3.org/2000/svg' width='16' height='22' viewBox='0 0 16 22' shape-rendering='crispEdges'>`
// Bell
+ `<rect x='4' y='0' width='8' height='2' fill='${bell}' opacity='0.4'/>`
+ `<rect x='2' y='2' width='12' height='2' fill='${bell}' opacity='0.45'/>`
+ `<rect x='1' y='4' width='14' height='4' fill='${bell}' opacity='0.5'/>`
+ `<rect x='2' y='8' width='12' height='2' fill='${bell}' opacity='0.45'/>`
// Inner glow
+ `<rect x='5' y='4' width='6' height='3' fill='white' opacity='0.15'/>`
// Tentacles
+ `<rect x='3' y='10' width='1' height='8' fill='${tentacle}' opacity='0.35'/>`
+ `<rect x='6' y='10' width='1' height='10' fill='${tentacle}' opacity='0.3'/>`
+ `<rect x='9' y='10' width='1' height='9' fill='${tentacle}' opacity='0.32'/>`
+ `<rect x='12' y='10' width='1' height='7' fill='${tentacle}' opacity='0.28'/>`
// Tentacle wiggles
+ `<rect x='2' y='14' width='1' height='2' fill='${tentacle}' opacity='0.25'/>`
+ `<rect x='7' y='16' width='1' height='2' fill='${tentacle}' opacity='0.22'/>`
+ `<rect x='10' y='15' width='1' height='2' fill='${tentacle}' opacity='0.22'/>`
+ `</svg>`
}
/** Generate a starfish SVG */
export function starfishSvg(color: string): string {
return `<svg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 8' shape-rendering='crispEdges'>`
+ `<rect x='3' y='0' width='2' height='3' fill='${color}' opacity='0.45'/>`
+ `<rect x='0' y='3' width='3' height='2' fill='${color}' opacity='0.45'/>`
+ `<rect x='5' y='3' width='3' height='2' fill='${color}' opacity='0.45'/>`
+ `<rect x='2' y='2' width='4' height='4' fill='${color}' opacity='0.5'/>`
+ `<rect x='1' y='5' width='2' height='2' fill='${color}' opacity='0.4'/>`
+ `<rect x='5' y='5' width='2' height='2' fill='${color}' opacity='0.4'/>`
+ `<rect x='3' y='3' width='2' height='2' fill='white' opacity='0.15'/>`
+ `</svg>`
}
/** Generate a small shell SVG */
export function shellSvg(color: string): string {
return `<svg xmlns='http://www.w3.org/2000/svg' width='6' height='4' viewBox='0 0 6 4' shape-rendering='crispEdges'>`
+ `<rect x='1' y='2' width='4' height='2' fill='${color}' opacity='0.4'/>`
+ `<rect x='2' y='1' width='2' height='1' fill='${color}' opacity='0.35'/>`
+ `<rect x='0' y='3' width='1' height='1' fill='${color}' opacity='0.3'/>`
+ `<rect x='5' y='3' width='1' height='1' fill='${color}' opacity='0.3'/>`
+ `<rect x='2' y='2' width='1' height='1' fill='white' opacity='0.15'/>`
+ `</svg>`
}