Customization Guide

Ink is your source code. Here's where to look for the most common tweaks.

Repo layout

src/
  components/         # UI components (Sidebar, PageHeader, etc.)
  editor/             # Block editor (blocks, commands, keyboard)
  features/           # Feature modules (spaces, sharing, tags)
  lib/                # Utilities (supabase client, realtime, markdown)
  pages/              # Top-level routes
  theme.css           # Tokens
supabase/
  schema.sql          # Schema
  functions/          # Edge Functions

Common customizations

Change brand colors

File: src/theme.css

:root {
  --ink-accent: #f43f5e;
  --ink-accent-contrast: #3d0614;
  --ink-bg: #0b0f1a;
}

Add a custom block type

Blocks are registered in:

File: src/editor/blocks/registry.ts

export const blockTypes = {
  paragraph: paragraphBlock,
  heading: headingBlock,
  // add:
  diagram: {
    label: "Diagram",
    slashTrigger: "diagram",
    icon: DiagramIcon,
    schema: z.object({ mermaid: z.string() }),
    render: ({ data }) => <MermaidRenderer src={data.mermaid} />,
    renderEditor: ({ data, onChange }) => (
      <MermaidEditor value={data.mermaid} onChange={onChange} />
    ),
    renderMarkdown: ({ data }) => `\`\`\`mermaid\n${data.mermaid}\n\`\`\``,
  },
};

You also need an entry in src/editor/markdown/parsers.ts for markdown import.

Add a keyboard shortcut

File: src/editor/keyboard/shortcuts.ts

Swap the storage bucket

Change VITE_INK_STORAGE_BUCKET. Existing uploads remain in the old bucket; new uploads go to the new one. Write a one-off migrate script if you need to move historical files (ask Claude Code).

Add an embed handler

File: src/editor/blocks/embed/handlers.ts

export const embedHandlers = {
  youtube: { match: /youtube\.com\/watch/, render: renderYoutube },
  figma: { match: /figma\.com\/file/, render: renderFigma },
  // add:
  whimsical: {
    match: /whimsical\.com\//,
    render: ({ url }) => <iframe src={url.replace("whimsical.com", "whimsical.com/embed")} />,
  },
};

Change the publish template

File: src/features/publish/templates/

Contains the HTML/CSS scaffold for published pages. Edit the header, footer, or layout.

Restrict what can be embedded

File: src/editor/blocks/embed/allowlist.ts

A hostname allowlist keeps iframes from turning into a security hole.

Tips for working with AI

Ink is heavy on the editor side. When asking Claude Code:

  • Think in blocks. Any new UI that renders content should fit the block system. "Add a diagram block" is better than "add a diagram panel".
  • Think about realtime. Block schema changes need to merge cleanly — tell Claude to make additions backward-compatible.
  • Think about markdown round-trip. If you add a block, it needs markdown import and export, or exports will lose information.
  • Think about published output. Blocks that rely on runtime JS need a static fallback for the publish renderer.

Example prompts

Add a Mermaid diagram block. The block stores a Mermaid source string, renders inline using @mermaid-js/mermaid, supports zooming, and round-trips to markdown as a fenced ```mermaid code block. Update the publish renderer to pre-render the SVG.

Add per-space branding: logo, favicon, and accent color at the space level. The publish renderer should use space branding instead of workspace defaults when publishing.

Extract the slash-command matching into a pure-function library so we can cover it with unit tests. Preserve the current command set and ordering.