first commit

This commit is contained in:
ray zhou
2026-05-21 18:16:26 +08:00
commit 033aa0ae69
324 changed files with 24705 additions and 0 deletions

View File

@@ -0,0 +1,153 @@
---
name: canvas
description: >-
A Cursor Canvas is a live React app that the user can open beside the chat.
You MUST use a canvas when the agent produces a standalone analytical artifact
— quantitative analyses, billing investigations, security audits, architecture
reviews, data-heavy content, timelines, charts, tables, interactive
explorations, repeatable tools, or any response that benefits from visual
layout. Especially prefer a canvas when presenting results from MCP tools
(Datadog, Databricks, Linear, Sentry, Slack, etc.) where the data is the
deliverable — render it in a rich canvas rather than dumping it into a
markdown table or code block. If you catch yourself about to write a markdown
table, stop and use a canvas instead. You MUST also read this skill whenever
you create, edit, or debug any .canvas.tsx file.
metadata:
surfaces:
- ide
---
A canvas is a single `.canvas.tsx` file the IDE compiles so the user can open it beside the chat. Follow the workflow below in order.
## Workflow
### 1. Decide whether to use a canvas
The trigger is **user intent**, not response shape. Ask: would the user benefit from viewing this output as its **own standalone artifact**, separate from the chat? If the output is a means to an end (a drafted message, a code fix, a dashboard in another tool), skip the canvas.
**Use a canvas when the agent produces new standalone analytical output:**
- Quantitative analyses and metrics breakdowns (e.g. "send 500 requests and tell me how many fail")
- Billing or account investigations that surface structured findings from database queries
- Security audits or architecture reviews with categorized findings
- Cross-system data analyses and overlap reports
- Structured data from MCP tools (Databricks, Datadog, etc.) where the data IS the deliverable
- Financial analyses, margin decompositions, usage trend reports
- Tables with more than a handful of rows that the user asked to see
**Do NOT use a canvas when:**
- The user asks for work in a **specific tool** — "create a Datadog dashboard" means give them a Datadog dashboard, not a canvas
- The user has a **specific deliverable** — "draft a support response", "fix this code", "make this PR"
- The user is **working within an existing artifact** — improving an HTML dashboard, editing an existing file
- The user is doing **targeted debugging** or active development, even if structured findings emerge along the way
- Short factual answers, one-off file edits, or quick clarifying questions
- MCP tools are queried as an **intermediate step** for a different deliverable (e.g. querying Stripe to draft a support reply)
### 2. Write the canvas
**Location.** Canvases live at `/Users/<user>/.cursor/projects/<workspace>/canvases/<name>.canvas.tsx`. The IDE only detects canvases written directly inside that exact directory — subfolders, alternate extensions, and other locations are not picked up. For a new canvas, always use the write file tool to create the `.canvas.tsx` file at that exact path; do not stop after telling the user the path or showing code in chat. Treat that managed `canvases/` directory as pre-provisioned by Cursor itself: write the canvas file directly there and do **not** spend turns creating the directory with `mkdir` or checking whether it exists before writing. Listing its contents for other purposes (e.g. checking for existing canvases) is fine. If you can't determine the workspace directory from absolute paths already in your environment (terminals, transcripts, recently-viewed files), list `~/.cursor/projects/` rather than guessing. Use a descriptive kebab-case filename ending in `.canvas.tsx`; preserve acronym capitalization and lowercase the rest.
**File rules:**
- Exactly one `.canvas.tsx` file per canvas. Never create helper files, style files, or supporting modules.
- Import **only** from `cursor/canvas`. No relative imports, no npm packages, no Node built-ins.
- Default-export the top-level component.
- Embed all data inline. **No `fetch()`, no network calls.**
**Never render empty states.** A canvas exists to show real content. If a section, chart, table, or component has no data to display, **omit it** — do not render it with placeholder text ("Add header here", "TODO", "Example"), a "No data" message, an empty array, zeroed rows, or an empty chart frame. If the entire canvas would be empty because you don't have the underlying data, do not produce a canvas — tell the user what's missing and ask for it instead.
**Label every plot.** Charts and tables must be self-describing — a reader looking at the canvas alone should know exactly what they're seeing. For every plot include:
- A title naming the **specific metric** (not "Metrics" — "API error rate by service").
- **Axis labels with units** on both axes (e.g. "Date", "Latency (ms)").
- A **legend** when more than one series is shown, with the exact series names from the source data.
- The **source and time range** in a small caption (e.g. "Source: Datadog · last 7 days"). If a value is a transformation (mean, p95, normalized, smoothed), say so in the label.
**Component discovery:** prefer built-in `cursor/canvas` components over hand-rolled markup. The full public surface (components, hooks, prop types, tokens) is declared in `~/.cursor/skills-cursor/canvas/sdk/index.d.ts` and its sibling `.d.ts` files — read them when you need exact exports, prop shapes, or hook signatures rather than guessing. Referencing an export that does not exist is the most common runtime error.
Apply the Canvas generation policy below as you write, and complete its pre-delivery self-check (section 6) before returning the canvas.
## Design guidance
Be creative. The SDK gives you expressive building blocks — use them in whatever combination best serves the content. But avoid slop: no gradients, no emojis, no box-shadows, no rainbow coloring. Cursor canvases are flat, minimal, and purposeful.
### Visual hierarchy
Not everything deserves equal treatment. Primary content gets more space, larger headings, and accent color. Supporting content stays compact. Squint test: blur your eyes — can you tell what matters?
**Color.** All colors from `useHostTheme()` tokens — read its JSDoc in the SDK declarations for the return shape and usage pattern. No hardcoded hex. Use accent color deliberately, not on everything.
### Slop patterns — forbidden
These specific patterns produce low-quality output. If 2+ are present, redesign.
- **Gradients** — no `linear-gradient`, `radial-gradient`, `background-clip: text`.
- **Emojis** — no emoji as icons, status indicators, bullets, or section markers.
- **Box shadows** — no `box-shadow`. Flat surfaces only.
- **Wall of identical cards** — every section wrapped in the same card style with no variation. Mix open sections with cards.
- **Rainbow coloring** — a different color on every element. Most elements are neutral; color is used sparingly with purpose.
- **Giant text** — font sizes above H1 (24px), or bold text stuffed in CardHeader.
- **Decorative borders** — colored borders on every element. Borders are structural (subtle stroke tokens), not decorative.
### Pre-delivery self-check
Before returning canvas code, verify:
1. Does the layout have visual hierarchy? One thing should stand out.
2. Is there variety in the composition? Not just a single column of uniform blocks.
3. Slop check: scan for the forbidden patterns above.
## Introducing the canvas
When you create a canvas, add a short note in your chat response telling the user you created a canvas they can open beside the chat:
- **First canvas** — if no other `.canvas.tsx` files exist in the workspace's `canvases/` directory, include one sentence explaining what a canvas is.
- **Unsolicited canvas** — if the user didn't ask for a canvas, include one sentence explaining why you chose it over plain text.
Both can apply at once; one or two sentences total is enough. Skip the intro for subsequent canvases.
## Troubleshooting
If a canvas appears blank or missing, the most common cause is that it was not written under `/Users/<user>/.cursor/projects/<workspace>/canvases/` exactly — re-save it to that path. Do not debug this by trying to create the managed directory manually; focus on correcting the file path instead. Users can click the canvas file path in the response to open it, just like any other file path in Cursor. When present, the canvas server writes a `<name>.canvas.status.json` sidecar after each build with `status`, `diagnostics`, or `error` fields you can read; the file is best-effort and may not exist, so don't block on it.
## Good example
```tsx
import { Divider, Grid, H1, H2, Stack, Stat, Table, Text } from 'cursor/canvas';
export default function ServiceOverview() {
return (
<Stack gap={20}>
<H1>Service Overview</H1>
<Grid columns={3} gap={16}>
<Stat value="6" label="Total Services" />
<Stat value="5" label="Healthy" tone="success" />
<Stat value="1" label="Degraded" tone="warning" />
</Grid>
<Divider />
<H2>Service Status</H2>
<Table
headers={["Service", "Status", "Uptime", "Latency"]}
rows={[
["api-gateway", "Operational", "99.99%", "12ms"],
["auth-service", "Degraded", "99.2%", "340ms"],
["billing", "Operational", "99.8%", "45ms"],
]}
rowTone={[undefined, "warning", undefined]}
/>
<Divider />
<H2>Recent Changes</H2>
<Text>Auth service latency increased after the 14:30 deploy.</Text>
<Text tone="secondary" size="small">Last checked: Apr 7, 2026 14:52 UTC</Text>
</Stack>
);
}
```
Stats in a Grid, Table directly under H2, text sections without cards.
## Bad example — do not imitate
```tsx
// BAD — every section wrapped in Card, no hierarchy, Table unnecessarily boxed
<Stack gap={12}>
<Card><CardHeader>Summary</CardHeader><CardBody><Text>6 services.</Text></CardBody></Card>
<Card><CardHeader>Status</CardHeader><CardBody><Table headers={[...]} rows={[...]} /></CardBody></Card>
<Card><CardHeader>Changes</CardHeader><CardBody><Text>Latency increased.</Text></CardBody></Card>
</Stack>
```

View File

@@ -0,0 +1,264 @@
/**
* Design tokens for `cursor/canvas` (standalone; no UI framework dependency).
*
* Color values are aligned with the Cursor app dark theme (`packages/ui` `cursor-dark` sources).
*/
export declare const canvasPaletteDark: {
readonly foreground: "#E4E4E4EB";
readonly foregroundSecondary: "#E4E4E48D";
readonly foregroundTertiary: "#E4E4E45E";
readonly foregroundQuaternary: "#E4E4E442";
readonly editor: "#181818";
readonly chrome: "#141414";
readonly sidebar: "#141414";
readonly elevated: "#181818";
readonly fillPrimary: "#E4E4E430";
readonly fillSecondary: "#E4E4E41E";
readonly fillTertiary: "#E4E4E411";
readonly fillQuaternary: "#E4E4E40A";
readonly strokePrimary: "#E4E4E433";
readonly strokeSecondary: "#E4E4E41F";
readonly strokeTertiary: "#E4E4E414";
readonly accent: "#599CE7";
readonly buttonBackground: "#599CE7";
readonly buttonForeground: "#191c22";
readonly buttonHoverBackground: "#6AABE9";
readonly link: "#87c3ff";
readonly diffInsertedLine: "#3FA26633";
readonly diffRemovedLine: "#B8004933";
readonly diffStripAdded: "#3FA2668F";
readonly diffStripRemoved: "#FC6B838F";
};
/**
* Light-mode palette derived from `packages/ui/src/tokens/themes/cursor-core/light.ts`.
* Base color: #141414. Same percentages as dark (regular light has no overrides
* in CURSOR_SEMANTIC_OVERRIDES — only high-contrast does).
*/
export declare const canvasPaletteLight: {
readonly foreground: "#141414F0";
readonly foregroundSecondary: "#141414BD";
readonly foregroundTertiary: "#1414148A";
readonly foregroundQuaternary: "#1414145C";
readonly editor: "#FCFCFC";
readonly chrome: "#F8F8F8";
readonly sidebar: "#F3F3F3";
readonly elevated: "#FCFCFC";
readonly fillPrimary: "#14141433";
readonly fillSecondary: "#14141424";
readonly fillTertiary: "#14141414";
readonly fillQuaternary: "#1414140F";
readonly strokePrimary: "#14141433";
readonly strokeSecondary: "#1414141F";
readonly strokeTertiary: "#14141414";
readonly accent: "#3685BF";
readonly buttonBackground: "#3685BF";
readonly buttonForeground: "#FCFCFC";
readonly buttonHoverBackground: "#2E76AB";
readonly link: "#3685BF";
readonly diffInsertedLine: "#1F8A651F";
readonly diffRemovedLine: "#CF2D5614";
readonly diffStripAdded: "#1F8A65CC";
readonly diffStripRemoved: "#CF2D56CC";
};
export interface CanvasPalette {
readonly foreground: string;
readonly foregroundSecondary: string;
readonly foregroundTertiary: string;
readonly foregroundQuaternary: string;
readonly editor: string;
readonly chrome: string;
readonly sidebar: string;
readonly elevated: string;
readonly fillPrimary: string;
readonly fillSecondary: string;
readonly fillTertiary: string;
readonly fillQuaternary: string;
readonly strokePrimary: string;
readonly strokeSecondary: string;
readonly strokeTertiary: string;
readonly accent: string;
readonly buttonBackground: string;
readonly buttonForeground: string;
readonly buttonHoverBackground: string;
readonly link: string;
readonly diffInsertedLine: string;
readonly diffRemovedLine: string;
readonly diffStripAdded: string;
readonly diffStripRemoved: string;
}
/**
* Chart color palette — distilled from portal-website analytics charts.
* 88% opacity (E0) softens vibrancy without dulling; palette maximizes
* hue + luminosity spread for distinguishable multi-series charts.
*/
export declare const chartPalette: {
readonly green: "#1F8A65E8";
readonly darkGreen: "#0D855AE0";
readonly lightGreen: "#52B896E0";
readonly mintGreen: "#7DCAB0E0";
readonly blue: "#2E79B5E0";
readonly lightBlue: "#70B0D8E0";
readonly indigo: "#5A6CC0F0";
readonly lightIndigo: "#9AAADCE0";
readonly purple: "#7B64B8F0";
readonly lightPurple: "#AA98D8E0";
readonly warmPink: "#C85898E0";
readonly lightPink: "#E8A0C4E0";
readonly brightOrange: "#F0A040E0";
readonly deepOrange: "#C06028E0";
readonly goldenYellow: "#E8C030E0";
readonly darkAmber: "#C04848E0";
readonly warmPeach: "#F0A088E0";
readonly vibrantTeal: "#2A9A8AE0";
readonly muted: "#8888A8E0";
readonly neutralLine: "#888899D0";
};
/**
* Shared category palette for canvas primitives that show categorical tints
* (`Swatch`, `UsageBar` segments, etc.). Uses a coherent subset of
* `chartPalette` so a category's color reads consistently across any
* primitive that consumes it.
*
* The insertion order here is the canonical category order — primitives
* that auto-assign colors (e.g. `UsageBar` segments without an explicit
* `color`) cycle through these keys in order.
*/
export declare const colorPalette: {
readonly gray: "#8888A8E0";
readonly purple: "#7B64B8F0";
readonly green: "#1F8A65E8";
readonly yellow: "#E8C030E0";
readonly pink: "#C85898E0";
readonly blue: "#2E79B5E0";
readonly orange: "#F0A040E0";
};
export type Color = keyof typeof colorPalette;
/**
* Auto-color rotation for `UsageBar` segments without an explicit `color`.
* Decoupled from `colorPalette`'s declaration order so the palette can grow
* or be reordered without changing how unspecified segments cycle.
*
* Matches the original `packages/ui` `ContextUsageTray` order so the same
* segment index lands on the same hue as the source.
*/
export declare const usageColorSequence: readonly Color[];
/**
* Ordered array for automatic series coloring — alternates dark/light across
* distinct hue families for maximum perceptual separation.
*/
export declare const chartColorSequence: readonly ["#1F8A65E8", "#70B0D8E0", "#5A6CC0F0", "#F0A040E0", "#C06028E0", "#E8C030E0", "#C85898E0", "#F0A088E0", "#7B64B8F0", "#7DCAB0E0", "#8888A8E0", "#2A9A8AE0"];
declare function buildTokens(palette: CanvasPalette): {
bg: {
editor: string;
chrome: string;
elevated: string;
};
text: {
primary: string;
secondary: string;
tertiary: string;
quaternary: string;
link: string;
onAccent: string;
};
stroke: {
primary: string;
secondary: string;
tertiary: string;
};
fill: {
primary: string;
secondary: string;
tertiary: string;
quaternary: string;
};
accent: {
primary: string;
control: string;
controlHover: string;
};
diff: {
insertedLine: string;
removedLine: string;
stripAdded: string;
stripRemoved: string;
};
};
/** Semantic colors for components (spacing and radius live in `theme.ts`). */
export declare const canvasTokens: {
bg: {
editor: string;
chrome: string;
elevated: string;
};
text: {
primary: string;
secondary: string;
tertiary: string;
quaternary: string;
link: string;
onAccent: string;
};
stroke: {
primary: string;
secondary: string;
tertiary: string;
};
fill: {
primary: string;
secondary: string;
tertiary: string;
quaternary: string;
};
accent: {
primary: string;
control: string;
controlHover: string;
};
diff: {
insertedLine: string;
removedLine: string;
stripAdded: string;
stripRemoved: string;
};
};
export declare const canvasTokensLight: {
bg: {
editor: string;
chrome: string;
elevated: string;
};
text: {
primary: string;
secondary: string;
tertiary: string;
quaternary: string;
link: string;
onAccent: string;
};
stroke: {
primary: string;
secondary: string;
tertiary: string;
};
fill: {
primary: string;
secondary: string;
tertiary: string;
quaternary: string;
};
accent: {
primary: string;
control: string;
controlHover: string;
};
diff: {
insertedLine: string;
removedLine: string;
stripAdded: string;
stripRemoved: string;
};
};
export type CanvasTokens = ReturnType<typeof buildTokens>;
export {};
//# sourceMappingURL=canvas-tokens.d.ts.map

View File

@@ -0,0 +1,200 @@
/**
* Chart primitives for `cursor/canvas` — multi-series, stacked, and pie charts
* rendered as pure inline SVG with zero external dependencies.
*
* Distilled from the portal-website Highcharts analytics charting layer.
*/
import type { CSSProperties, JSX } from "react";
/**
* Semantic tone for a chart series or slice. Mirrors the tone vocabulary
* used by `Stat`, `Pill`, `Table`, and other SDK primitives so colors
* match across a canvas — e.g. a `Stat tone="success"` and a
* `ChartSeries tone="success"` render in the same green.
*
* Omit `tone` to let the chart auto-assign a distinct color from the
* chart palette; supply `tone` only when the value carries semantic
* meaning that should match other tonal elements on the page.
*/
export type ChartTone = "success" | "danger" | "warning" | "info" | "neutral";
/** A single labeled value, used by `PieChart`. */
export type ChartDataPoint = {
label: string;
/** Non-negative numeric value. */
value: number;
};
/**
* A named data series for `BarChart` and `LineChart`.
* The `data` array aligns by index with the parent component's `categories`.
* If `tone` is omitted, a color is auto-assigned from the chart palette.
*/
export type ChartSeries = {
name: string;
data: number[];
tone?: ChartTone;
};
export type BarChartProps = {
/** Category labels along the independent axis. */
categories: string[];
/** One or more data series. Values align by index with `categories`. */
series: ChartSeries[];
height?: number;
/** Stack series on top of each other instead of grouping side-by-side. */
stacked?: boolean;
/** Render horizontal bars instead of vertical columns. */
horizontal?: boolean;
/** Show as 100% stacked (implies `stacked`). */
normalized?: boolean;
/** Suffix for y-axis tick labels (e.g. "%"). */
valueSuffix?: string;
style?: CSSProperties;
};
export type LineChartProps = {
categories: string[];
series: ChartSeries[];
height?: number;
/** Fill the area under each line with a soft tint. */
fill?: boolean;
valueSuffix?: string;
style?: CSSProperties;
};
export type PieChartProps = {
data: Array<ChartDataPoint & {
tone?: ChartTone;
}>;
size?: number;
donut?: boolean;
style?: CSSProperties;
};
/**
* Multi-series bar/column chart with optional stacking and normalization.
* Distilled from the portal-website Highcharts analytics charts.
*
* Pass `categories` for x-axis labels and one or more `series` whose `data`
* arrays align by index. With a single series you get simple bars; with
* multiple series the default is grouped (side-by-side) — set `stacked` for
* stacked columns or `normalized` for 100%-stacked share-mode.
*
* Colors are auto-assigned from the chart palette. With a **single series**,
* each bar gets a different color by category (so a chart of 5 categories
* shows 5 colors out of the box). With **multiple series**, each series gets
* its own color. A legend appears when there are 2+ series.
*
* For semantic coloring, pass `tone` on a series — it maps to the same
* palette entries used by `Stat`, `Pill`, and `Table` so your chart matches
* tonal elements elsewhere on the page.
*
* @example
* ```tsx
* // Simple single-series
* <BarChart
* categories={["Mon", "Tue", "Wed"]}
* series={[{ name: "Requests", data: [120, 90, 150] }]}
* />
*
* // Stacked multi-series (like portal AI commit chart)
* <BarChart
* categories={["Mon", "Tue", "Wed"]}
* series={[
* { name: "IDE", data: [120, 90, 150] },
* { name: "CLI", data: [30, 40, 25] },
* { name: "Cloud", data: [50, 60, 70] },
* ]}
* stacked
* />
*
* // Semantic tones — "accepted" renders in the same green as
* // <Stat tone="success"> elsewhere on the page.
* <BarChart
* categories={["Mon", "Tue", "Wed"]}
* series={[
* { name: "Accepted", data: [70, 80, 60], tone: "success" },
* { name: "Rejected", data: [30, 20, 40], tone: "danger" },
* ]}
* stacked
* />
* ```
*/
export declare function BarChart({ categories, series, height, stacked, horizontal, normalized, valueSuffix, style }: BarChartProps): JSX.Element;
/**
* Multi-series line chart with optional area fill. Distilled from the
* portal-website Highcharts analytics charts.
*
* Each series draws a polyline with dot markers at each data point.
* Set `fill` to shade the area under every line. Hover over any category
* column to see a tooltip with all series values at that point.
*
* This is **not** a time-series component — it does not parse dates.
* Pass pre-formatted date strings as `categories` if plotting over time.
*
* Colors are auto-assigned from the chart palette. For semantic coloring,
* pass `tone` on a series — it maps to the same palette entries used by
* `Stat`, `Pill`, and `Table`.
*
* @example
* ```tsx
* // Single line
* <LineChart
* categories={["Jan", "Feb", "Mar", "Apr"]}
* series={[{ name: "Revenue", data: [100, 140, 120, 180] }]}
* />
*
* // Multi-series with area fill
* <LineChart
* categories={["Jan", "Feb", "Mar", "Apr"]}
* series={[
* { name: "Accepted", data: [50, 70, 60, 90] },
* { name: "Suggested", data: [120, 140, 130, 160] },
* ]}
* fill
* />
*
* // Semantic tones — "errors" renders in the same red as a
* // <Pill tone="danger"> elsewhere on the page.
* <LineChart
* categories={["00:00", "06:00", "12:00", "18:00"]}
* series={[
* { name: "p95 latency", data: [80, 95, 110, 90], tone: "info" },
* { name: "errors", data: [2, 4, 9, 3], tone: "danger" },
* ]}
* />
* ```
*/
export declare function LineChart({ categories, series, height, fill, valueSuffix, style }: LineChartProps): JSX.Element;
/**
* Pie (or donut) chart with hover highlighting. Distilled from the
* portal-website Highcharts analytics charts.
*
* Unlike `BarChart` and `LineChart`, `PieChart` takes a flat `data` array of
* `{ label, value }` points — each slice is its own category. Colors are
* auto-assigned from the chart palette; pass `tone` on a point to give a
* slice a semantic color that matches other tonal elements on the page.
*
* Hovering a slice expands it outward and dims the others; hovering a legend
* item does the same. A tooltip with value and percentage appears below the
* chart. Set `donut` for a hollow center.
*
* **Do not** use for bar-style comparisons — use `BarChart` instead.
*
* @example
* ```tsx
* // Basic pie
* <PieChart
* data={[
* { label: "IDE", value: 120 },
* { label: "CLI", value: 30 },
* { label: "Cloud", value: 50 },
* ]}
* />
*
* // Donut with semantic tones
* <PieChart
* data={[
* { label: "Passing", value: 70, tone: "success" },
* { label: "Failing", value: 30, tone: "danger" },
* ]}
* donut
* />
* ```
*/
export declare function PieChart({ data, size, donut, style }: PieChartProps): JSX.Element;
//# sourceMappingURL=chart-primitives.d.ts.map

View File

@@ -0,0 +1,65 @@
/**
* Borderless disclosure row — chevron + structured header (title, optional
* leading slot, count, trailing slot) with a body that toggles open. Distilled
* from baby-glass `ContextTreeRow`'s lightweight list-row chrome (no card
* border, no background fill).
*
* For a bordered surface that collapses, use `<Card collapsible>` instead.
*/
import { type CSSProperties, type JSX, type ReactNode } from "react";
export type CollapsibleSectionProps = {
/** Plain-text title rendered next to the disclosure chevron. */
title: string;
/**
* Optional leading visual (e.g. `<Swatch>`) shown between the chevron and
* the title.
*/
leading?: ReactNode;
/**
* Optional small count rendered after the title (e.g. number of children).
*/
count?: number;
/**
* Optional trailing node, right-aligned (e.g. token readout, badge, button).
* Rendered with `t.text.tertiary` color hint via the wrapper; the slot can
* override.
*/
trailing?: ReactNode;
/** Body shown when expanded. */
children?: ReactNode;
style?: CSSProperties;
};
/**
* Borderless collapsible row with a structured header. Always starts closed
* (uncontrolled, no `defaultOpen`).
*
* Compose with `<Swatch>` in the `leading` slot for a colored category icon,
* and put a token readout / pill / button in `trailing`. Body content is
* indented under the row so nested `CollapsibleSection`s read as a tree.
*
* For a bordered, card-shaped collapsible surface, use `<Card collapsible>`
* instead — `CollapsibleSection` has no border or background and is meant to
* sit in a list of similar rows.
*
* @example
* ```tsx
* // Basic
* <CollapsibleSection title="Conversation">
* <Text>Messages go here.</Text>
* </CollapsibleSection>
*
* // With a colored category swatch + count + trailing token readout
* <CollapsibleSection
* title="Tools"
* count={4}
* leading={<Swatch color="purple" />}
* trailing={<Text size="small" tone="tertiary">12.3k</Text>}
* >
* <CollapsibleSection title="Grep">
* <Text>Search results.</Text>
* </CollapsibleSection>
* </CollapsibleSection>
* ```
*/
export declare function CollapsibleSection({ title, leading, count, trailing, children, style }: CollapsibleSectionProps): JSX.Element;
//# sourceMappingURL=collapsible-section.d.ts.map

102
skills-cursor/canvas/sdk/dag-layout.d.ts vendored Normal file
View File

@@ -0,0 +1,102 @@
/**
* Pure layout math for directed acyclic graphs. Returns positioned node
* coordinates, edge anchor points, rank bounding boxes, and back-edge flags.
* Rendering is the caller's responsibility.
*
* Handles cycles gracefully: back-edges are detected via DFS, excluded from
* ranking, and flagged in the output so the caller can render them differently
* (e.g. dashed arcs).
*/
export type DAGLayoutOptions = {
/** Nodes to lay out. Only `id` is required. */
nodes: Array<{
id: string;
}>;
/** Directed edges. */
edges: Array<{
from: string;
to: string;
}>;
/** Flow direction. Default `"vertical"` (top-to-bottom). */
direction?: "vertical" | "horizontal";
/** Node box width in px. Default 160. */
nodeWidth?: number;
/** Node box height in px. Default 40. */
nodeHeight?: number;
/** Gap between ranks (layers) in px. Default 64. */
rankGap?: number;
/** Gap between sibling nodes in the same rank in px. Default 48. */
nodeGap?: number;
/** Padding around the bounding box in px. Default 24. */
padding?: number;
};
export type DAGLayoutNode = {
id: string;
/** Left edge of the node box. */
x: number;
/** Top edge of the node box. */
y: number;
/** Layer index (0 = root). */
rank: number;
/** Position within the rank (0-indexed). */
order: number;
};
export type DAGLayoutEdge = {
from: string;
to: string;
/** Suggested source anchor point (center of the outgoing side). */
sourceX: number;
sourceY: number;
/** Suggested target anchor point (center of the incoming side). */
targetX: number;
targetY: number;
/** True when this edge was identified as a back-edge (part of a cycle). */
isBackEdge: boolean;
};
export type DAGLayoutRank = {
/** Rank index (0 = root). */
rank: number;
/** Left edge of the rank bounding box. */
x: number;
/** Top edge of the rank bounding box. */
y: number;
/** Width of the rank bounding box. */
width: number;
/** Height of the rank bounding box. */
height: number;
/** Node ids in this rank, in order. */
nodeIds: string[];
};
export type DAGLayoutResult = {
nodes: DAGLayoutNode[];
edges: DAGLayoutEdge[];
/** Bounding box per rank — useful for drawing layer bands. */
ranks: DAGLayoutRank[];
/** The direction used for this layout. */
direction: "vertical" | "horizontal";
/** Total width of the bounding box. */
width: number;
/** Total height of the bounding box. */
height: number;
};
/**
* Compute a hierarchical layout for a directed graph.
*
* Returns node positions, edge anchor points, rank bounding boxes, and
* back-edge flags. The caller handles all rendering.
*
* @example
* ```ts
* const layout = computeDAGLayout({
* nodes: [{ id: "a" }, { id: "b" }, { id: "c" }],
* edges: [{ from: "a", to: "b" }, { from: "b", to: "c" }],
* });
*
* // layout.nodes[i].x / .y → position your own SVG/HTML elements
* // layout.edges[i].sourceX/Y, targetX/Y → draw lines between them
* // layout.edges[i].isBackEdge → style cycle edges differently
* // layout.ranks[i] → draw layer bands behind each rank
* ```
*/
export declare function computeDAGLayout(options: DAGLayoutOptions): DAGLayoutResult;
//# sourceMappingURL=dag-layout.d.ts.map

130
skills-cursor/canvas/sdk/diff-view.d.ts vendored Normal file
View File

@@ -0,0 +1,130 @@
/**
* Low-level diff primitives for the canvas SDK.
*
* The canvas diff surface is intentionally minimal — two components that
* compose with the generic `Card` / `CardHeader` / `CardBody` / `Pill` /
* `Text` primitives to build any diff layout an agent can imagine:
*
* - `DiffView` — a monospaced, syntax-highlighted unified diff renderer.
* Drops into any container (a `Card`, a table cell, a bare layout,
* nothing at all). No card chrome, no header, no path display. Pass
* `path` to auto-detect the language for highlighting, or `language`
* to override.
*
* - `DiffStats` — the canonical `+N` green / `-N` red glyph pair. Use
* it anywhere a small "added/deleted" summary makes sense — in a
* `CardHeader`'s `trailing` slot, next to a filename in a file tree,
* inside a status row, etc.
*
* For file-level metadata the preferred composition is to use the
* generic `Card` family:
*
* ```tsx
* <Card collapsible>
* <CardHeader trailing={<DiffStats additions={5} deletions={2} />}>
* src/utils.ts
* </CardHeader>
* <CardBody style={{ padding: 0 }}>
* <DiffView path="src/utils.ts" lines={lines} />
* </CardBody>
* </Card>
* ```
*/
import type { CSSProperties, JSX } from "react";
export type DiffStatsProps = {
additions?: number;
deletions?: number;
style?: CSSProperties;
};
/**
* Inline `+N` / `-N` glyph pair. Green additions, red deletions, with
* tabular numerals so columns of stats line up. Renders nothing when
* both counts are zero.
*
* @example
* ```tsx
* <CardHeader trailing={<DiffStats additions={12} deletions={3} />}>
* src/utils.ts
* </CardHeader>
*
* <Row gap={8}>
* <Text>Refactor pass</Text>
* <DiffStats additions={42} deletions={17} />
* </Row>
* ```
*/
export declare function DiffStats({ additions, deletions, style }: DiffStatsProps): JSX.Element | null;
export type DiffLineType = "added" | "removed" | "unchanged";
export type DiffLineData = {
type: DiffLineType;
content: string;
lineNumber?: number;
};
export type DiffViewProps = {
lines: DiffLineData[];
/**
* File path used to infer the syntax-highlighting language from the
* extension (e.g. `"src/utils.ts"` → `typescript`). The most ergonomic
* way to enable highlighting — pass the same path you show in the
* enclosing card header. Unknown extensions silently render as plain
* text.
*
* If both `path` and `language` are provided, `language` wins.
*/
path?: string;
/**
* Explicit language override for syntax highlighting (e.g.
* `"typescript"`, `"python"`, `"tsx"`). Use this when no file path is
* available, when the path's extension is misleading, or when the
* content is a snippet rather than a real file. Accepts common
* aliases (`ts`, `py`, `rs`, `md`, etc.). Unknown languages silently
* fall back to plain text.
*
* Highlighting is applied per line, so multi-line constructs (block
* comments, template literals) may not colorize perfectly across line
* boundaries. For typical diff-sized inputs this is fine.
*/
language?: string;
/** Show line numbers in the gutter. Default `true`. */
showLineNumbers?: boolean;
/** Color line numbers green/red for added/removed lines. Default `true`. */
coloredLineNumbers?: boolean;
/** Show a 3px accent strip on the left edge for changed lines. Default `true`. */
showAccentStrip?: boolean;
style?: CSSProperties;
};
/**
* Unified diff body renderer with monospaced type, colored line
* backgrounds, line-number gutter, accent strip, and optional Shiki
* syntax highlighting.
*
* `DiffView` does not provide any surrounding chrome — place it inside
* a `Card` + `CardBody` (with `padding: 0`) when you want the standard
* bordered "file diff" look, or drop it anywhere else if you want the
* bare renderer.
*
* Pass `path` to enable syntax highlighting from the file extension.
*
* @example
* ```tsx
* <Card>
* <CardHeader trailing={<DiffStats additions={2} deletions={1} />}>
* src/utils.ts
* </CardHeader>
* <CardBody style={{ padding: 0 }}>
* <DiffView
* path="src/utils.ts"
* lines={[
* { type: "unchanged", content: "export function add(a: number, b: number): number {", lineNumber: 1 },
* { type: "removed", content: " return a + b;", lineNumber: 2 },
* { type: "added", content: " const result = a + b;", lineNumber: 2 },
* { type: "added", content: " return result;", lineNumber: 3 },
* { type: "unchanged", content: "}", lineNumber: 4 },
* ]}
* />
* </CardBody>
* </Card>
* ```
*/
export declare function DiffView({ lines, path, language, showLineNumbers, coloredLineNumbers, showAccentStrip, style }: DiffViewProps): JSX.Element;
//# sourceMappingURL=diff-view.d.ts.map

View File

@@ -0,0 +1,194 @@
/**
* Form primitives for `cursor/canvas`. Provides themed, controlled form controls
* for interactive canvas apps with persistent state. All `onChange` callbacks
* receive the **value directly** (not a DOM event), so they pair naturally
* with `useCanvasState`:
*
* ```tsx
* const [name, setName] = useCanvasState("name", "");
* <TextInput value={name} onChange={setName} placeholder="Enter name…" />
* ```
*/
import { type CSSProperties, type JSX, type ReactNode } from "react";
export type TextInputProps = {
value?: string;
/** Called with the new string value on every keystroke. */
onChange?: (value: string) => void;
placeholder?: string;
disabled?: boolean;
type?: "text" | "email" | "password" | "number" | "url" | "search";
style?: CSSProperties;
};
/**
* Single-line text input (28px height). Use for names, titles, search
* queries, and short text fields.
*
* `onChange` receives the **string value**, not a DOM event — this pairs
* directly with `useCanvasState` setters.
*
* @example
* ```tsx
* const [name, setName] = useCanvasState("name", "");
*
* <TextInput value={name} onChange={setName} placeholder="Task title…" />
* ```
*/
export declare function TextInput({ value, onChange, placeholder, disabled, type, style }: TextInputProps): JSX.Element;
export type TextAreaProps = {
value?: string;
/** Called with the new string value on every keystroke. */
onChange?: (value: string) => void;
placeholder?: string;
disabled?: boolean;
/** Minimum visible rows. Defaults to 3. */
rows?: number;
style?: CSSProperties;
};
/**
* Multi-line text input that auto-resizes to fit its content.
* Use for notes, descriptions, comments, and multi-line text fields.
*
* The textarea grows as the user types. Set `rows` for the minimum visible
* height (defaults to 3). Override with `style={{ height: "100px" }}` for a
* fixed size.
*
* @example
* ```tsx
* const [notes, setNotes] = useCanvasState("notes", "");
*
* <TextArea value={notes} onChange={setNotes} placeholder="Add notes…" rows={4} />
* ```
*/
export declare function TextArea({ value, onChange, placeholder, disabled, rows, style }: TextAreaProps): JSX.Element;
export type CheckboxProps = {
checked?: boolean;
/** Called with the new boolean value when toggled. */
onChange?: (checked: boolean) => void;
disabled?: boolean;
/** Optional label rendered beside the checkbox. Clicking the label toggles the checkbox. */
label?: ReactNode;
style?: CSSProperties;
};
/**
* Checkbox with optional label (accent blue when checked).
*
* Pass `label` to render a clickable text label beside the checkbox. Without a
* label, provide `title` or wrap in your own `<label>` for accessibility.
*
* @example
* ```tsx
* const [agreed, setAgreed] = useCanvasState("agreed", false);
*
* <Checkbox checked={agreed} onChange={setAgreed} label="I agree to the terms" />
* ```
*
* @example
* ```tsx
* // Checkbox in a list — no label, parent handles layout
* <Checkbox checked={item.done} onChange={(v) => toggleItem(item.id, v)} />
* ```
*/
export declare function Checkbox({ checked, onChange, disabled, label, style }: CheckboxProps): JSX.Element;
export type ToggleProps = {
checked?: boolean;
/** Called with the new boolean value when toggled. */
onChange?: (checked: boolean) => void;
disabled?: boolean;
/** `sm` = 16px track, `md` = 20px track (default `sm`). */
size?: "sm" | "md";
style?: CSSProperties;
};
/**
* Boolean toggle switch. Uses the accent color for the "on" state and a
* neutral fill for "off".
*
* @example
* ```tsx
* const [enabled, setEnabled] = useCanvasState("enabled", false);
*
* <Row gap={8} align="center">
* <Text>Notifications</Text>
* <Spacer />
* <Toggle checked={enabled} onChange={setEnabled} />
* </Row>
* ```
*/
export declare function Toggle({ checked, onChange, disabled, size, style }: ToggleProps): JSX.Element;
export type SelectOption = {
value: string;
label: string;
disabled?: boolean;
};
export type SelectProps = {
value?: string;
/** Called with the new selected value. */
onChange?: (value: string) => void;
/** List of options. Each must have a unique `value`. */
options: SelectOption[];
/** Placeholder shown when no value is selected. */
placeholder?: string;
disabled?: boolean;
style?: CSSProperties;
};
/**
* Dropdown select (native `<select>` with themed styling).
*
* Uses a native `<select>` under the hood for reliable keyboard, screen-reader,
* and mobile support. The dropdown list uses OS-native styling.
*
* @example
* ```tsx
* const [priority, setPriority] = useCanvasState("priority", "medium");
*
* <Select
* value={priority}
* onChange={setPriority}
* options={[
* { value: "low", label: "Low" },
* { value: "medium", label: "Medium" },
* { value: "high", label: "High" },
* ]}
* />
* ```
*/
export declare function Select({ value, onChange, options, placeholder, disabled, style }: SelectProps): JSX.Element;
export type IconButtonProps = {
/** Icon content: an SVG element, emoji, or unicode character. */
children: ReactNode;
onClick?: () => void;
disabled?: boolean;
/** Tooltip text. Always provide for accessibility since there is no text label. */
title?: string;
/**
* `"default"` is transparent until hovered; `"circle"` has a permanent
* background fill.
*/
variant?: "default" | "circle";
/** `sm` = 16px, `md` = 20px (default `md`). */
size?: "sm" | "md";
style?: CSSProperties;
};
/**
* Compact icon-only button for inline actions on list items (delete, edit,
* expand, etc.). Accepts **any** `children` as the icon — use an inline SVG,
* an emoji, or a unicode character.
*
* Always provide `title` for accessibility (screen-reader label + tooltip).
*
* Canvas has no icon font, so pass icon content directly.
*
* @example
* ```tsx
* // Delete button on a card
* <IconButton title="Delete" onClick={() => remove(id)}>✕</IconButton>
*
* // Edit button with an SVG icon
* <IconButton title="Edit" variant="circle" size="sm" onClick={edit}>
* <svg width={12} height={12} viewBox="0 0 12 12" fill="none">
* <path d="M8.5 1.5l2 2L4 10H2V8z" stroke="currentColor" strokeWidth={1.2} />
* </svg>
* </IconButton>
* ```
*/
export declare function IconButton({ children, onClick, disabled, title, variant, size, style }: IconButtonProps): JSX.Element;
//# sourceMappingURL=form-primitives.d.ts.map

117
skills-cursor/canvas/sdk/hooks.d.ts vendored Normal file
View File

@@ -0,0 +1,117 @@
import type { CanvasPalette, CanvasTokens } from "./canvas-tokens.js";
import { type CanvasAction } from "./internal/canvas-action-dispatch.js";
/**
* Host theme for the current canvas. Semantic color groups (`text`, `bg`,
* `fill`, `stroke`, `accent`, `diff`) live at the top level for ergonomic
* inline-style access; `tokens` is also present as a self-reference for
* callers that prefer a namespaced form.
*/
export interface CanvasHostTheme extends CanvasTokens {
readonly kind: string;
readonly tokens: CanvasTokens;
readonly palette: CanvasPalette;
}
/**
* Returns the current host theme. Falls back to dark mode when no host
* state is available.
*
* Semantic color groups are available directly on the returned object —
* `accent`, `text`, `bg`, `fill`, `stroke`, `diff` — as well as `kind`
* (`"dark"` | `"light"` | …) and `palette` (the flat color palette).
*
* Call `useHostTheme()` inside each component that needs theme access —
* the returned object is scoped to that component, not shared across
* function boundaries.
*
* Stable paths for `style={{ ... }}` usage:
* - `text.primary / secondary / tertiary / quaternary` — text hierarchy
* - `bg.editor / chrome / elevated` — surface backgrounds
* - `fill.primary / secondary / tertiary / quaternary` — tinted fills
* - `stroke.primary / secondary / tertiary` — borders and dividers
* - `accent.primary / control` — accent blue and button background
* - `text.link` — link color
* - `text.onAccent` — text on accent-colored surfaces
*
* Prefer built-in components (`Card`, `Button`, `Text`, etc.) over raw
* token usage. Reach for tokens only when no component covers the case,
* and stick to flat solid colors — no gradients, no box-shadows.
*
* @example
* ```tsx
* function Overview() {
* const theme = useHostTheme();
* return (
* <div style={{ background: theme.fill.tertiary, color: theme.text.secondary, padding: 8 }}>
* <span style={{ color: theme.accent.primary }}>Accent text</span>
* </div>
* );
* }
* ```
*/
export declare function useHostTheme(): CanvasHostTheme;
/**
* Setter for `useCanvasState`. Accepts either a new value or an updater
* function that receives the previous value.
*/
export type SetCanvasState<T> = (action: T | ((prev: T) => T)) => void;
/**
* Persistent state hook for canvas applications. Works like `React.useState`
* but the value survives rebuilds, reloads, and IDE restarts — it is stored
* in a `.canvas.data.json` sidecar file next to the canvas source.
*
* Each key is a unique string chosen by the canvas author. Keys are stable
* regardless of hook call order or component tree structure.
*
* @param key - Unique string identifier for this piece of state.
* @param defaultValue - Value returned when no persisted value exists for `key`.
* @returns A `[value, setValue]` tuple, same shape as `React.useState`.
*
* @example
* ```tsx
* function Counter() {
* const [count, setCount] = useCanvasState("count", 0);
* return <Button onClick={() => setCount(c => c + 1)}>{count}</Button>;
* }
* ```
*
* @example
* ```tsx
* interface Column { id: string; title: string; cardIds: string[] }
* function KanbanBoard() {
* const [columns, setColumns] = useCanvasState<Column[]>("columns", [
* { id: "todo", title: "To Do", cardIds: [] },
* { id: "doing", title: "In Progress", cardIds: [] },
* { id: "done", title: "Done", cardIds: [] },
* ]);
* // ...
* }
* ```
*/
export declare function useCanvasState<T>(key: string, defaultValue: T): [T, SetCanvasState<T>];
export type { CanvasAction };
/**
* Returns a stable `dispatch` function for triggering IDE actions from
* canvas buttons. Actions are fire-and-forget — the canvas does not
* receive a response.
*
* ## Available actions
*
* **`openAgent`** — Navigate the IDE to an agent conversation.
* `agentId` is the conversation UUID (the filename stem from the
* `agent-transcripts/` directory). Works for both local and
* cloud/background agents in Glass and classic IDE modes.
*
* @example
* ```tsx
* function AgentLink({ agentId, title }: { agentId: string; title: string }) {
* const dispatch = useCanvasAction();
* return (
* <Button onClick={() => dispatch({ type: "openAgent", agentId })}>
* {title}
* </Button>
* );
* }
* ```
*/
export declare function useCanvasAction(): (action: CanvasAction) => void;
//# sourceMappingURL=hooks.d.ts.map

59
skills-cursor/canvas/sdk/index.d.ts vendored Normal file
View File

@@ -0,0 +1,59 @@
/**
* Public API for authoring `.canvas.tsx` files via `cursor/canvas`.
*
* Be creative with layout — use Grid, Row, cards, charts, tables, and raw SVG
* in whatever combination serves the content. Read the canvas skill for full
* design guidance. Key constraints:
*
* - Colors from `useHostTheme()` tokens. No hardcoded hex.
* - No gradients, no box-shadows, no emojis as decoration.
* - Don't wrap every section in Card — mix open sections with cards.
* - Run the pre-delivery self-check before returning code.
*/
/** Shared category color palette used by `Swatch`, `UsageBar`, etc. */
export type { Color } from "./canvas-tokens.js";
export { colorPalette, usageColorSequence } from "./canvas-tokens.js";
/** Charts. */
export type { BarChartProps, ChartDataPoint, ChartSeries, ChartTone, LineChartProps, PieChartProps, } from "./chart-primitives.js";
export { BarChart, LineChart, PieChart } from "./chart-primitives.js";
/** Borderless collapsible disclosure row with a structured header. */
export type { CollapsibleSectionProps } from "./collapsible-section.js";
export { CollapsibleSection } from "./collapsible-section.js";
/** DAG layout. */
export type { DAGLayoutEdge, DAGLayoutNode, DAGLayoutOptions, DAGLayoutRank, DAGLayoutResult, } from "./dag-layout.js";
export { computeDAGLayout } from "./dag-layout.js";
/**
* Diff rendering. Compose with the generic `Card` family for file-level
* chrome: use `DiffView` inside a `CardBody` (with `padding: 0`) and put
* `DiffStats` in the enclosing `CardHeader`'s `trailing` slot.
*/
export type { DiffLineData, DiffLineType, DiffStatsProps, DiffViewProps, } from "./diff-view.js";
export { DiffStats, DiffView } from "./diff-view.js";
/** Form controls. */
export type { CheckboxProps, IconButtonProps, SelectOption, SelectProps, TextAreaProps, TextInputProps, ToggleProps, } from "./form-primitives.js";
export { Checkbox, IconButton, Select, TextArea, TextInput, Toggle, } from "./form-primitives.js";
/** Host state hooks. */
export type { CanvasAction, CanvasHostTheme, SetCanvasState } from "./hooks.js";
export { useCanvasAction, useCanvasState, useHostTheme } from "./hooks.js";
/** Colored category swatch (uses the shared `Color` palette). */
export type { SwatchProps } from "./swatch.js";
export { Swatch } from "./swatch.js";
/** Semantic design tokens for custom styling. */
export type { CanvasPalette, CanvasTokens } from "./theme.js";
export { canvasPaletteDark, canvasPaletteLight, canvasTokens, canvasTokensLight, } from "./theme.js";
export type { TodoItem, TodoListCardProps, TodoListProps, TodoStatus, } from "./todo-list.js";
export { TodoList, TodoListCard } from "./todo-list.js";
/** Component props types. */
export type { ButtonProps, CalloutProps, CalloutTone, CardBodyProps, CardHeaderProps, CardProps, CardSize, CardVariant, CodeProps, DividerProps, GridProps, H1Props, H2Props, H3Props, LinkProps, PillProps, PillSize, PillTone, RowProps, StackProps, StatProps, StatTone, TableColumnAlign, TableProps, TableRowTone, TextProps, TextWeight, } from "./ui-primitives.js";
/** Layout. */
/** Typography. */
/** Surfaces. */
/** Actions. */
/** Feedback. */
export { Button, Callout, Card, CardBody, CardHeader, Code, Divider, Grid, H1, H2, H3, Link,
/** Shallow-merge two style objects. Useful for combining tokens with overrides. */
mergeStyle, Pill, Row, Spacer, Stack, Stat, Table, Text, } from "./ui-primitives.js";
/** Usage bar — segmented progress meter with optional labels above. */
export type { UsageBarProps, UsageBarSegment } from "./usage-bar.js";
export { UsageBar } from "./usage-bar.js";
//# sourceMappingURL=index.d.ts.map

40
skills-cursor/canvas/sdk/swatch.d.ts vendored Normal file
View File

@@ -0,0 +1,40 @@
/**
* Colored category swatch — a small filled rounded box. Intended for inline
* list/row decoration (category badges, the leading slot of
* `CollapsibleSection`, etc.).
*
* Pulls colors from the shared `Color` palette so a category's swatch and
* its `UsageBar` segment for the same `color` stay visually coherent.
*/
import type { CSSProperties, JSX } from "react";
import { type Color } from "./canvas-tokens.js";
export type SwatchProps = {
/** One of the 7 shared category hues. Matches `UsageBar` segment colors. */
color: Color;
style?: CSSProperties;
};
/**
* Filled, rounded category swatch (24px). Use as the leading visual on
* category rows, list items, or as the `leading` slot of a
* `CollapsibleSection`.
*
* Colors come from the shared 7-hue `Color` palette, so a category's swatch
* matches its `UsageBar` segment for the same `color`.
*
* @example
* ```tsx
* // Standalone — purple "tools" swatch
* <Swatch color="purple" />
*
* // As the `leading` slot on a CollapsibleSection
* <CollapsibleSection
* title="Tools"
* count={4}
* leading={<Swatch color="purple" />}
* >
* <Text>Tool calls go here.</Text>
* </CollapsibleSection>
* ```
*/
export declare function Swatch({ color, style }: SwatchProps): JSX.Element;
//# sourceMappingURL=swatch.d.ts.map

61
skills-cursor/canvas/sdk/theme.d.ts vendored Normal file
View File

@@ -0,0 +1,61 @@
export type { CanvasPalette, CanvasTokens } from "./canvas-tokens.js";
export { canvasPaletteDark, canvasPaletteLight, canvasTokens, canvasTokensLight, } from "./canvas-tokens.js";
/** Typography presets used by the built-in `cursor/canvas` components. */
export declare const canvasTypography: {
readonly h1: {
readonly fontSize: "24px";
readonly lineHeight: "30px";
readonly fontWeight: 590;
};
readonly h2: {
readonly fontSize: "18px";
readonly lineHeight: "24px";
readonly fontWeight: 590;
};
readonly h3: {
readonly fontSize: "16px";
readonly lineHeight: "22px";
readonly fontWeight: 590;
};
readonly body: {
readonly fontSize: "14px";
readonly lineHeight: "20px";
readonly fontWeight: 400;
};
readonly small: {
readonly fontSize: "12px";
readonly lineHeight: "16px";
readonly fontWeight: 400;
};
};
/** Spacing scale (px). */
export declare const canvasSpacing: {
readonly "0.5": 2;
readonly "1": 4;
readonly "1.5": 6;
readonly "2": 8;
readonly "2.5": 10;
readonly "3": 12;
readonly "3.5": 14;
readonly "4": 16;
readonly "4.5": 18;
readonly "5": 20;
readonly "6": 24;
readonly "7": 28;
readonly "8": 32;
readonly "9": 36;
readonly "10": 40;
};
export type CanvasSpacing = typeof canvasSpacing;
/** Border radius (px). */
export declare const canvasRadius: {
readonly none: 0;
readonly xs: 2;
readonly sm: 4;
readonly md: 6;
readonly lg: 8;
readonly xl: 12;
readonly full: 9999;
};
export type CanvasRadius = typeof canvasRadius;
//# sourceMappingURL=theme.d.ts.map

49
skills-cursor/canvas/sdk/todo-list.d.ts vendored Normal file
View File

@@ -0,0 +1,49 @@
import type { CSSProperties, JSX } from "react";
export type TodoStatus = "pending" | "in_progress" | "completed" | "cancelled";
export interface TodoItem {
readonly id: string;
readonly content: string;
readonly status: TodoStatus;
}
export type TodoListProps = {
todos: readonly TodoItem[];
dimmedTodoIds?: ReadonlySet<string>;
/** Called when a todo row is clicked (entire row is a button). */
onTodoClick?: (todo: TodoItem) => void;
style?: CSSProperties;
};
/**
* Task list with status icons and wrapping text. Each row is a **clickable**
* button; use `onTodoClick` to handle selection or navigation.
*
* @example
* ```tsx
* <TodoList
* todos={items}
* onTodoClick={(todo) => setActiveId(todo.id)}
* />
* ```
*/
export declare function TodoList({ todos, dimmedTodoIds, onTodoClick, style }: TodoListProps): JSX.Element | null;
export type TodoListCardProps = {
todos: readonly TodoItem[];
dimmedTodoIds?: ReadonlySet<string>;
defaultExpanded?: boolean;
onTodoClick?: (todo: TodoItem) => void;
style?: CSSProperties;
};
/**
* Bordered, collapsible todo list with summary header (N of M Done). Compose
* with `onTodoClick` for row actions.
*
* @example
* ```tsx
* <TodoListCard
* todos={items}
* defaultExpanded
* onTodoClick={(todo) => console.log(todo.id)}
* />
* ```
*/
export declare function TodoListCard({ todos, dimmedTodoIds, defaultExpanded, onTodoClick, style }: TodoListCardProps): JSX.Element | null;
//# sourceMappingURL=todo-list.d.ts.map

View File

@@ -0,0 +1,549 @@
/**
* UI primitives for `cursor/canvas`. Styling follows the Cursor dark theme; no extra packages required.
*/
import { type CSSProperties, type JSX, type ReactNode } from "react";
/**
* Shallow-merge style objects with `override` taking precedence.
*
* Use for small tweaks on built-in components (e.g. extra padding or width).
* **Do not** use this to build elaborate custom chrome — prefer the built-in
* components and flat solid token colors. No gradients, no box-shadows.
*
* @example
* ```tsx
* // Good — minor override on a built-in component
* <CardBody style={mergeStyle({ padding: 16 })}>…</CardBody>
*
* // Bad — hand-rolled decorative styling
* <div style={mergeStyle(base, { background: "linear-gradient(…)" })}>…</div>
* ```
*/
export declare function mergeStyle(base: CSSProperties, override?: CSSProperties): CSSProperties;
export type StackProps = {
children?: ReactNode;
gap?: number;
style?: CSSProperties;
};
/**
* Vertical flex column. Use as the top-level page wrapper or to stack cards/sections.
*
* @example
* ```tsx
* <Stack gap={16}>
* <H1>Dashboard</H1>
* <Card>…</Card>
* <Card>…</Card>
* </Stack>
* ```
*/
export declare function Stack({ children, gap, style }: StackProps): JSX.Element;
export type RowProps = {
children?: ReactNode;
gap?: number;
align?: "start" | "center" | "end" | "stretch";
justify?: "start" | "center" | "end" | "space-between";
wrap?: boolean;
style?: CSSProperties;
};
/**
* Horizontal flex row. Use for inline groups of buttons, badges, or metadata.
*
* @example
* ```tsx
* <Row gap={8} align="center">
* <Button variant="primary">Save</Button>
* <Button variant="ghost">Cancel</Button>
* </Row>
* ```
*/
export declare function Row({ children, gap, align, justify, wrap, style }: RowProps): JSX.Element;
/**
* CSS Grid with tokenized gap. Prefer this over `Row` + `wrap` when you need a
* fixed number of equal-width columns: wrapped flex items can land on their
* own row and grow to full width (`flex-grow`), which is often surprising for
* boards and dashboards.
*/
export type GridProps = {
children?: ReactNode;
/**
* Equal columns: pass a number (uses `repeat(n, minmax(0, 1fr))`), or a CSS
* `grid-template-columns` string (e.g. `"1fr 2fr"` or `"minmax(0, 200px) 1fr"`).
*/
columns: number | string;
gap?: number;
align?: "start" | "center" | "end" | "stretch";
style?: CSSProperties;
};
export declare function Grid({ children, columns, gap, align, style }: GridProps): JSX.Element;
export type DividerProps = {
style?: CSSProperties;
};
/**
* Horizontal line for visually separating sections. Uses `stroke.tertiary`
* to match the Card/Table hairline weight.
*
* @example
* ```tsx
* <Stack>
* <Text>Section one</Text>
* <Divider />
* <Text>Section two</Text>
* </Stack>
* ```
*/
export declare function Divider({ style }: DividerProps): JSX.Element;
/**
* Flex spacer that pushes siblings apart. Place inside a `Row` to push
* trailing content to the right edge.
*
* @example
* ```tsx
* <Row>
* <Text>Title</Text>
* <Spacer />
* <Button variant="primary">Save</Button>
* </Row>
* ```
*/
export declare function Spacer(): JSX.Element;
/** Horizontal alignment for a table column. */
export type TableColumnAlign = "left" | "center" | "right";
/** Semantic tone for a table row — renders a translucent tinted background. */
export type TableRowTone = "success" | "danger" | "warning" | "info" | "neutral";
export type TableProps = {
/** Column titles, left to right. Column count is fixed by this array. */
headers: ReactNode[];
/**
* Body rows. Each row is an array of cells in the same order as `headers`.
* Shorter rows are padded with empty cells; extra cells are ignored.
*/
rows: ReactNode[][];
/** Optional alignment per column index (headers/rows). Defaults to left. */
columnAlign?: Array<TableColumnAlign | undefined>;
/**
* Optional semantic tone per row index. Applies a translucent tinted
* background — use for status highlighting (e.g. failing services, warnings).
* Sparse: `undefined` entries are uncolored.
*/
rowTone?: Array<TableRowTone | undefined>;
/** When true (default), bordered rounded shell with horizontal scroll if needed. */
framed?: boolean;
/** Alternate subtle fill on even rows for easier scanning in large tables. */
striped?: boolean;
/** Stick the header row when the framed container scrolls vertically. */
stickyHeader?: boolean;
style?: CSSProperties;
/** Shown in a single spanning cell when `rows` is empty. */
emptyMessage?: ReactNode;
};
/**
* Data table with column headers and rows. Framed by default with its own
* bordered container — **do not wrap in a Card** unless the card itself is
* a named entity that happens to contain a table. Render directly under a
* heading in the normal case.
*
* @example
* ```tsx
* // Good — table directly under a heading
* <H2>Active services</H2>
* <Table
* headers={["Service", "Status", "RPS"]}
* rows={[
* ["api-gateway", "Steady", "3.2k"],
* ["workers", "Hot", "8.1k"],
* ]}
* columnAlign={["left", "left", "right"]}
* />
*
* // Allowed — table inside a named-entity card
* <Card>
* <CardHeader>billing-service</CardHeader>
* <CardBody><Table headers={…} rows={…} /></CardBody>
* </Card>
* ```
*/
export declare function Table({ headers, rows, columnAlign, rowTone, framed, striped, stickyHeader, style, emptyMessage }: TableProps): JSX.Element;
export type TextWeight = "normal" | "medium" | "semibold" | "bold";
export type TextProps = {
children?: ReactNode;
tone?: "primary" | "secondary" | "tertiary" | "quaternary";
size?: "body" | "small";
/**
* Element tag to render. Defaults to `"p"` for top-level body copy and
* automatically switches to `"span"` when nested inside another typography
* container so inline emphasis stays valid HTML.
*/
as?: "p" | "span";
/** Font weight. Default is `"normal"` (400). Use `"semibold"` or `"bold"` for emphasis. */
weight?: TextWeight;
/** Render as italic. */
italic?: boolean;
/**
* Truncate overflowing text with an ellipsis on a single line.
* - `true` / `"end"` — ellipsis at the end (default truncation).
* - `"start"` — ellipsis at the start. Useful for file paths where the
* filename matters more than the directory prefix.
*
* Requires the parent to have a bounded width (flex child with
* `minWidth: 0`, fixed width, etc.) — otherwise the text just expands
* and never overflows.
*/
truncate?: boolean | "start" | "end";
style?: CSSProperties;
};
/**
* Body text with tone, size, weight, and italic variants.
*
* Top-level `Text` renders a `<p>`. Nested `Text` automatically renders a
* `<span>` so inline emphasis like `<Text>Use <Text weight="semibold">this</Text></Text>`
* does not emit invalid nested paragraphs. Use `as` to override when needed.
*
* Compose with `<Code>` for inline code and `<Link>` for hyperlinks inside
* the text flow.
*
* @example
* ```tsx
* <Text>Primary body text.</Text>
* <Text weight="semibold">Important note.</Text>
* <Text italic tone="secondary">Supplementary remark.</Text>
* <Text>Run <Code>npm install</Code> to get started.</Text>
* <Text>See the <Link href="https://example.com">docs</Link> for details.</Text>
* ```
*/
export declare function Text({ children, tone, size, as, weight, italic, truncate, style }: TextProps): JSX.Element;
export type H1Props = {
children?: ReactNode;
style?: CSSProperties;
};
/**
* Page-level heading. Use once at the top of a canvas.
* **Do not** place inside `CardHeader` — card headers use their own label.
*
* @example
* ```tsx
* <Stack>
* <H1>Performance Report</H1>
* <Card>…</Card>
* </Stack>
* ```
*/
export declare function H1({ children, style }: H1Props): JSX.Element;
export type H2Props = {
children?: ReactNode;
style?: CSSProperties;
};
/**
* Section heading. Use between groups of cards or sections.
* **Do not** place inside `CardHeader` — card headers use their own label.
*
* @example
* ```tsx
* <Stack>
* <H2>Recent activity</H2>
* <Card>…</Card>
* <Card>…</Card>
* </Stack>
* ```
*/
export declare function H2({ children, style }: H2Props): JSX.Element;
export type H3Props = {
children?: ReactNode;
style?: CSSProperties;
};
/**
* Sub-section heading. Use below `H2` for finer hierarchy.
*
* @example
* ```tsx
* <Stack>
* <H2>API Reference</H2>
* <H3>Authentication</H3>
* <Text>All requests require a bearer token.</Text>
* </Stack>
* ```
*/
export declare function H3({ children, style }: H3Props): JSX.Element;
export type CodeProps = {
children?: ReactNode;
style?: CSSProperties;
};
/**
* Inline `<code>` span for identifiers, file names, or short snippets.
* Uses `0.92em` so it scales with surrounding text (headings, body, etc.).
*
* Prefer writing backtick markdown inside `Text` — e.g. `` <Text>Run `npm install`</Text> `` —
* which is automatically parsed. Use `<Code>` only when you need an explicit element.
*
* @example
* ```tsx
* <Text>Run <Code>npm install</Code> to get started.</Text>
* ```
*/
export declare function Code({ children, style }: CodeProps): JSX.Element;
export type LinkProps = {
children?: ReactNode;
href: string;
style?: CSSProperties;
};
/**
* Inline link that opens in the user's default browser.
*
* Prefer writing markdown links inside `Text` — e.g. `<Text>See the [docs](url)</Text>` —
* which are automatically parsed. Use `<Link>` when you need an explicit anchor
* outside of a text flow or when composing with other elements.
*
* @example
* ```tsx
* <Link href="https://docs.example.com">View documentation</Link>
* ```
*/
export declare function Link({ children, href, style }: LinkProps): JSX.Element;
export type CardSize = "base" | "lg";
export type CardVariant = "default" | "borderless";
/**
* Inline chevron SVG used by disclosure-style controls (collapsible cards,
* expandable list items, etc.). Shared by `Card` and `todo-list.tsx` so
* every disclosure in the canvas SDK uses the same glyph.
*/
export declare function CanvasChevron({ expanded }: {
expanded: boolean;
}): JSX.Element;
export type CardProps = {
children?: ReactNode;
/** Default: bordered surface with radius; `borderless` removes both. */
variant?: CardVariant;
/** `lg` uses a taller header and roomier title padding (matches packages/ui). */
size?: CardSize;
/**
* When true, the header uses `position: sticky` so it stays visible while
* the card body scrolls. Requires the card (or a parent) to have a
* constrained height and `overflow: auto` — the canvas host controls this,
* so sticky behavior depends on the host viewport.
*/
stickyHeader?: boolean;
/**
* Make the card collapsible. The header becomes a clickable toggle with
* a leading chevron; `CardBody` renders nothing while the card is closed.
*/
collapsible?: boolean;
/** Initial open state in uncontrolled mode. Ignored when `open` is set. */
defaultOpen?: boolean;
/** Controlled open state. Pair with `onOpenChange`. */
open?: boolean;
/** Fires on every toggle with the next open state. */
onOpenChange?: (open: boolean) => void;
style?: CSSProperties;
};
/**
* Bordered surface for a **labeled, self-contained unit** — a file, a service,
* a config block, or a table with a title. Compose with `CardHeader` + `CardBody`.
*
* **When to use Card:**
* - Displaying a named entity (file path, service name, resource).
* - Wrapping a `<Table>` or `<DiffView>` that needs a title.
* - A distinct, bounded section the user might scan by header label.
*
* **When NOT to use Card:**
* - General text sections — use `<H2>` + `<Text>` instead. Not every section
* needs a border.
* - Page-level layout — use `<Stack>` with headings. A canvas should not be a
* wall of stacked cards.
* - Nesting — do not put cards inside cards. Use `<Divider>` within a card body.
*
* Pass **plain text** as `CardHeader` children — the header provides its own
* 12px font. Do **not** put `<H1>` or `<H2>` inside a card header.
*
* Set `collapsible` to make the header a toggle that shows/hides `CardBody`.
*
* @example
* ```tsx
* // Card wraps a titled diff
* <Card>
* <CardHeader trailing={<DiffStats additions={5} deletions={2} />}>
* src/utils.ts
* </CardHeader>
* <CardBody style={{ padding: 0 }}>
* <DiffView path="src/utils.ts" lines={lines} />
* </CardBody>
* </Card>
*
* // Collapsible card
* <Card collapsible defaultOpen={false}>
* <CardHeader>deploy-service.ts</CardHeader>
* <CardBody>Service handles rolling deployments across regions.</CardBody>
* </Card>
*
* // Bad — card wrapping plain text that should just be a heading
* // Use <H2>Overview</H2><Text>…</Text> instead.
* ```
*/
export declare function Card({ children, variant, size, stickyHeader, collapsible, defaultOpen, open: openProp, onOpenChange, style }: CardProps): JSX.Element;
export type CardHeaderProps = {
/** Plain text title. Do **not** pass headings, buttons, pills, or layout rows. */
children?: ReactNode;
/** Small trailing content aligned to the right edge — a status label, a
* single pill, or a short metadata string. Keep it compact. */
trailing?: ReactNode;
style?: CSSProperties;
};
/**
* 28px header row (32px at `size="lg"`). A compact label for the card.
*
* **`children`** — plain text only. This is a 12px label, not a toolbar.
* Do **not** pass `<H1>`, `<H2>`, `<Text weight="bold">`, `<Pill>`,
* `<Button>`, `<Row>`, or any interactive/layout elements as children.
*
* **`trailing`** — optional right-aligned slot for small status indicators
* (a short label, a single `<Pill>`, a metadata string like a timestamp).
*
* @example
* ```tsx
* // Good — plain title
* <CardHeader>config.yaml</CardHeader>
*
* // Good — title with trailing status
* <CardHeader trailing={<Pill active>Healthy</Pill>}>
* billing-service
* </CardHeader>
*
* // Bad — heading in header (use CardHeader text, not H2)
* // Bad — buttons in header (no room, wrong context)
* // Bad — multiple pills in header (use trailing for one, or move to CardBody)
* ```
*/
export declare function CardHeader({ children, trailing, style }: CardHeaderProps): JSX.Element;
export type CardBodyProps = {
children?: ReactNode;
style?: CSSProperties;
};
/**
* Padded content area inside a Card.
* Override `style` to adjust padding for custom layouts.
*
* @example
* ```tsx
* <Card>
* <CardHeader>Overview</CardHeader>
* <CardBody>This service is currently healthy.</CardBody>
* </Card>
* ```
*/
export declare function CardBody({ children, style }: CardBodyProps): JSX.Element | null;
export type ButtonProps = {
children?: ReactNode;
variant?: "primary" | "secondary" | "ghost";
disabled?: boolean;
type?: "button" | "submit" | "reset";
style?: CSSProperties;
onClick?: () => void;
};
/**
* Action button (24px height, sized to its label). **Never stretch to full
* width** — buttons are always inline and hug their text.
*
* @example
* ```tsx
* <Row gap={8}>
* <Button variant="primary" onClick={handleSave}>Save</Button>
* <Button variant="secondary">Export</Button>
* <Button variant="ghost">Cancel</Button>
* </Row>
* ```
*/
export declare function Button({ children, variant, disabled, type, style, onClick }: ButtonProps): JSX.Element;
export type PillTone = "neutral" | "added" | "deleted" | "renamed" | "success" | "warning" | "info";
export type PillSize = "sm" | "md";
export type PillProps = {
children?: ReactNode;
/** Whether the pill is in its selected/active state (filled background). */
active?: boolean;
/**
* Semantic tone. Recolors the border and text. When `active` is also
* set, fills the background with the tone color at low opacity.
* Defaults to `neutral` (current stroke/text tokens).
*/
tone?: PillTone;
/**
* Visual size. `"md"` (default) is the standard pill. `"sm"` is a
* compact variant with smaller text, tighter padding, and no border —
* designed for tight spaces like `CardHeader` trailing slots.
*/
size?: PillSize;
/** Shown before the label (icon, emoji, etc.). */
leadingContent?: ReactNode;
/** e.g. shortcut hint — matches ui `Pill` ghost keyboard hint (muted primary). */
keyboardHint?: string;
disabled?: boolean;
title?: string;
style?: CSSProperties;
onClick?: () => void;
};
/**
* Pill-shaped label or toggle button. Use for tab bars, filter groups, or
* action suggestions. Set `active` for the selected state (filled background).
*
* @example
* ```tsx
* // Tab-style selector
* <Row gap={8}>
* {tabs.map(tab => (
* <Pill key={tab} active={tab === selected} onClick={() => setSelected(tab)}>
* {tab}
* </Pill>
* ))}
* </Row>
*
* // Action suggestion with shortcut hint
* <Pill onClick={handlePlan} keyboardHint="⇧Tab">Plan new idea</Pill>
* ```
*/
export declare function Pill({ children, active, tone, size, leadingContent, keyboardHint, disabled, title, style, onClick }: PillProps): JSX.Element;
export type StatTone = "success" | "danger" | "warning" | "info";
export type StatProps = {
/** The primary metric value (number, percentage, short string). */
value: ReactNode;
/** Label below the value. */
label: string;
/** Semantic color for the value. Omit for default primary text. */
tone?: StatTone;
style?: CSSProperties;
};
/**
* Single metric display — a large value with a compact label beneath it.
* Use inside `<Grid>` for dashboard summary strips.
*
* @example
* ```tsx
* <Grid columns={3} gap={16}>
* <Stat value="4" label="Healthy" tone="success" />
* <Stat value="1" label="Degraded" tone="warning" />
* <Stat value="99.2%" label="Avg Uptime" />
* </Grid>
* ```
*/
export declare function Stat({ value, label, tone, style }: StatProps): JSX.Element;
export type CalloutTone = "info" | "success" | "warning" | "danger" | "neutral";
export type CalloutProps = {
/** Body content. Plain strings, `<Text>`, `<Code>`, `<Link>`, or short lists. */
children?: ReactNode;
/** Semantic tone. Recolors the border, background tint, and title text. */
tone?: CalloutTone;
/** Optional bold title line, shown above the body in the tone color. */
title?: ReactNode;
/** Optional leading icon (emoji, inline SVG, or short text glyph). */
icon?: ReactNode;
style?: CSSProperties;
};
/**
* Tinted, bordered notice block for warnings, tips, or short status messages
* inline within a section.
*
* @example
* ```tsx
* <Callout tone="warning" title="Heads up">
* Rolling deploy is in progress. Metrics may be noisy for 10 minutes.
* </Callout>
* ```
*/
export declare function Callout({ children, tone, title, icon, style }: CalloutProps): JSX.Element;
//# sourceMappingURL=ui-primitives.d.ts.map

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=ui-primitives.test.d.ts.map

56
skills-cursor/canvas/sdk/usage-bar.d.ts vendored Normal file
View File

@@ -0,0 +1,56 @@
/**
* Segmented usage bar primitive — proportional pills + remainder, with an
* optional one-line label row above. Visually matches `packages/ui`
* `ContextUsageTray`'s usage bar, themed for canvas via the shared `Color`
* palette and `useHostTheme()` semantic tokens.
*/
import type { CSSProperties, JSX, ReactNode } from "react";
import { type Color } from "./canvas-tokens.js";
export interface UsageBarSegment {
/** Stable identifier (used as React key). */
readonly id: string;
/** Proportional weight for this segment. Non-finite or `<= 0` is treated as 0. */
readonly value: number;
/**
* Optional explicit color. Defaults to a rotation through
* `usageColorSequence` by index, matching the original `packages/ui`
* `ContextUsageTray` order so the same index lands on the same hue.
*/
readonly color?: Color;
}
export type UsageBarProps = {
/** Segments rendered left-to-right; widths are proportional to `value`. */
readonly segments: readonly UsageBarSegment[];
/** Total weight of the bar. The remainder span fills `max(0, total - sum(values))`. */
readonly total: number;
/** Optional small label rendered above the bar, left-aligned. */
readonly topLeftLabel?: ReactNode;
/** Optional small label rendered above the bar, right-aligned. */
readonly topRightLabel?: ReactNode;
readonly style?: CSSProperties;
};
/**
* Segmented horizontal usage bar — proportional category pills with a
* remainder span. Use to visualize a fixed-budget breakdown (context window
* tokens, storage usage, etc.).
*
* @example
* ```tsx
* <UsageBar
* total={120_000}
* topLeftLabel="64% Full"
* topRightLabel="76.8K / 120K Tokens"
* segments={[
* { id: "system", value: 8_000, color: "gray" },
* { id: "tools", value: 24_000, color: "purple" },
* { id: "rules", value: 12_000, color: "green" },
* { id: "skills", value: 6_000, color: "yellow" },
* { id: "mcp", value: 4_000, color: "pink" },
* { id: "subagents", value: 8_800, color: "blue" },
* { id: "conversation", value: 14_000, color: "orange" },
* ]}
* />
* ```
*/
export declare function UsageBar({ segments, total, topLeftLabel, topRightLabel, style }: UsageBarProps): JSX.Element;
//# sourceMappingURL=usage-bar.d.ts.map