@flesler/cursor-canvas-web 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -8
- package/dist/cursor-canvas.cjs +1 -1
- package/dist/cursor-canvas.js +530 -701
- package/dist/host-tokens-Cjzmp0z_.cjs +1 -0
- package/dist/host-tokens-Dh2BFFjv.js +412 -0
- package/dist/runtime/index.d.ts +5 -7
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.js +101 -14
- package/package.json +22 -12
- package/src/vendor/canvas/SKILL.md +113 -0
- package/src/vendor/canvas-sdk/callout-tone-icons.d.ts +10 -0
- package/src/vendor/canvas-sdk/canvas-tokens.d.ts +259 -0
- package/src/vendor/canvas-sdk/chart-primitives.d.ts +272 -0
- package/src/vendor/canvas-sdk/collapsible-section.d.ts +66 -0
- package/src/vendor/canvas-sdk/dag-layout.d.ts +101 -0
- package/src/vendor/canvas-sdk/diff-view.d.ts +129 -0
- package/src/vendor/canvas-sdk/form-primitives.d.ts +194 -0
- package/src/vendor/canvas-sdk/hooks.d.ts +173 -0
- package/src/vendor/canvas-sdk/index.d.ts +65 -0
- package/src/vendor/canvas-sdk/internal/canvas-action-dispatch.d.ts +17 -0
- package/src/vendor/canvas-sdk/manifest.json +5 -0
- package/src/vendor/canvas-sdk/swatch.d.ts +39 -0
- package/src/vendor/canvas-sdk/theme.d.ts +107 -0
- package/src/vendor/canvas-sdk/todo-list.d.ts +48 -0
- package/src/vendor/canvas-sdk/ui-primitives.d.ts +569 -0
- package/src/vendor/canvas-sdk/usage-bar.d.ts +55 -0
- package/dist/shim/charts.d.ts +0 -62
- package/dist/shim/cursor-canvas.d.ts +0 -278
- package/dist/shim/dag.d.ts +0 -55
- package/dist/shim/diff.d.ts +0 -23
- package/dist/shim/theme.d.ts +0 -319
- package/dist/shim/todo.d.ts +0 -22
- package/dist/shim/use-tokens.d.ts +0 -6
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Borderless disclosure row — chevron + structured header (title, optional
|
|
3
|
+
* leading slot, count, trailing slot) with a body that toggles open. Distilled
|
|
4
|
+
* from baby-glass `ContextTreeRow`'s lightweight list-row chrome (no card
|
|
5
|
+
* border, no background fill).
|
|
6
|
+
*
|
|
7
|
+
* For a bordered surface that collapses, use `<Card collapsible>` instead.
|
|
8
|
+
*/
|
|
9
|
+
import { type CSSProperties, type JSX, type ReactNode } from "react";
|
|
10
|
+
export type CollapsibleSectionProps = {
|
|
11
|
+
/** Plain-text title rendered next to the disclosure chevron. */
|
|
12
|
+
title: string;
|
|
13
|
+
/**
|
|
14
|
+
* Optional leading visual (e.g. `<Swatch>`) shown between the chevron and
|
|
15
|
+
* the title.
|
|
16
|
+
*/
|
|
17
|
+
leading?: ReactNode;
|
|
18
|
+
/**
|
|
19
|
+
* Optional small count rendered after the title (e.g. number of children).
|
|
20
|
+
*/
|
|
21
|
+
count?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Optional trailing node, right-aligned (e.g. token readout, badge, button).
|
|
24
|
+
* Rendered with `t.text.tertiary` color hint via the wrapper; the slot can
|
|
25
|
+
* override.
|
|
26
|
+
*/
|
|
27
|
+
trailing?: ReactNode;
|
|
28
|
+
/** Body shown when expanded. */
|
|
29
|
+
children?: ReactNode;
|
|
30
|
+
/** When true, the section starts expanded (uncontrolled). */
|
|
31
|
+
defaultOpen?: boolean;
|
|
32
|
+
style?: CSSProperties;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Borderless collapsible row with a structured header. Starts closed unless
|
|
36
|
+
* `defaultOpen` is set.
|
|
37
|
+
*
|
|
38
|
+
* Compose with `<Swatch>` in the `leading` slot for a colored category icon,
|
|
39
|
+
* and put a token readout / pill / button in `trailing`. Body content is
|
|
40
|
+
* indented under the row so nested `CollapsibleSection`s read as a tree.
|
|
41
|
+
*
|
|
42
|
+
* For a bordered, card-shaped collapsible surface, use `<Card collapsible>`
|
|
43
|
+
* instead — `CollapsibleSection` has no border or background and is meant to
|
|
44
|
+
* sit in a list of similar rows.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```tsx
|
|
48
|
+
* // Basic
|
|
49
|
+
* <CollapsibleSection title="Conversation">
|
|
50
|
+
* <Text>Messages go here.</Text>
|
|
51
|
+
* </CollapsibleSection>
|
|
52
|
+
*
|
|
53
|
+
* // With a colored category swatch + count + trailing token readout
|
|
54
|
+
* <CollapsibleSection
|
|
55
|
+
* title="Tools"
|
|
56
|
+
* count={4}
|
|
57
|
+
* leading={<Swatch color="purple" />}
|
|
58
|
+
* trailing={<Text size="small" tone="tertiary">12.3k</Text>}
|
|
59
|
+
* >
|
|
60
|
+
* <CollapsibleSection title="Grep">
|
|
61
|
+
* <Text>Search results.</Text>
|
|
62
|
+
* </CollapsibleSection>
|
|
63
|
+
* </CollapsibleSection>
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export declare function CollapsibleSection({ title, leading, count, trailing, children, defaultOpen, style, }: CollapsibleSectionProps): JSX.Element;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure layout math for directed acyclic graphs. Returns positioned node
|
|
3
|
+
* coordinates, edge anchor points, rank bounding boxes, and back-edge flags.
|
|
4
|
+
* Rendering is the caller's responsibility.
|
|
5
|
+
*
|
|
6
|
+
* Handles cycles gracefully: back-edges are detected via DFS, excluded from
|
|
7
|
+
* ranking, and flagged in the output so the caller can render them differently
|
|
8
|
+
* (e.g. dashed arcs).
|
|
9
|
+
*/
|
|
10
|
+
export type DAGLayoutOptions = {
|
|
11
|
+
/** Nodes to lay out. Only `id` is required. */
|
|
12
|
+
nodes: Array<{
|
|
13
|
+
id: string;
|
|
14
|
+
}>;
|
|
15
|
+
/** Directed edges. */
|
|
16
|
+
edges: Array<{
|
|
17
|
+
from: string;
|
|
18
|
+
to: string;
|
|
19
|
+
}>;
|
|
20
|
+
/** Flow direction. Default `"vertical"` (top-to-bottom). */
|
|
21
|
+
direction?: "vertical" | "horizontal";
|
|
22
|
+
/** Node box width in px. Default 160. */
|
|
23
|
+
nodeWidth?: number;
|
|
24
|
+
/** Node box height in px. Default 40. */
|
|
25
|
+
nodeHeight?: number;
|
|
26
|
+
/** Gap between ranks (layers) in px. Default 64. */
|
|
27
|
+
rankGap?: number;
|
|
28
|
+
/** Gap between sibling nodes in the same rank in px. Default 48. */
|
|
29
|
+
nodeGap?: number;
|
|
30
|
+
/** Padding around the bounding box in px. Default 24. */
|
|
31
|
+
padding?: number;
|
|
32
|
+
};
|
|
33
|
+
export type DAGLayoutNode = {
|
|
34
|
+
id: string;
|
|
35
|
+
/** Left edge of the node box. */
|
|
36
|
+
x: number;
|
|
37
|
+
/** Top edge of the node box. */
|
|
38
|
+
y: number;
|
|
39
|
+
/** Layer index (0 = root). */
|
|
40
|
+
rank: number;
|
|
41
|
+
/** Position within the rank (0-indexed). */
|
|
42
|
+
order: number;
|
|
43
|
+
};
|
|
44
|
+
export type DAGLayoutEdge = {
|
|
45
|
+
from: string;
|
|
46
|
+
to: string;
|
|
47
|
+
/** Suggested source anchor point (center of the outgoing side). */
|
|
48
|
+
sourceX: number;
|
|
49
|
+
sourceY: number;
|
|
50
|
+
/** Suggested target anchor point (center of the incoming side). */
|
|
51
|
+
targetX: number;
|
|
52
|
+
targetY: number;
|
|
53
|
+
/** True when this edge was identified as a back-edge (part of a cycle). */
|
|
54
|
+
isBackEdge: boolean;
|
|
55
|
+
};
|
|
56
|
+
export type DAGLayoutRank = {
|
|
57
|
+
/** Rank index (0 = root). */
|
|
58
|
+
rank: number;
|
|
59
|
+
/** Left edge of the rank bounding box. */
|
|
60
|
+
x: number;
|
|
61
|
+
/** Top edge of the rank bounding box. */
|
|
62
|
+
y: number;
|
|
63
|
+
/** Width of the rank bounding box. */
|
|
64
|
+
width: number;
|
|
65
|
+
/** Height of the rank bounding box. */
|
|
66
|
+
height: number;
|
|
67
|
+
/** Node ids in this rank, in order. */
|
|
68
|
+
nodeIds: string[];
|
|
69
|
+
};
|
|
70
|
+
export type DAGLayoutResult = {
|
|
71
|
+
nodes: DAGLayoutNode[];
|
|
72
|
+
edges: DAGLayoutEdge[];
|
|
73
|
+
/** Bounding box per rank — useful for drawing layer bands. */
|
|
74
|
+
ranks: DAGLayoutRank[];
|
|
75
|
+
/** The direction used for this layout. */
|
|
76
|
+
direction: "vertical" | "horizontal";
|
|
77
|
+
/** Total width of the bounding box. */
|
|
78
|
+
width: number;
|
|
79
|
+
/** Total height of the bounding box. */
|
|
80
|
+
height: number;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Compute a hierarchical layout for a directed graph.
|
|
84
|
+
*
|
|
85
|
+
* Returns node positions, edge anchor points, rank bounding boxes, and
|
|
86
|
+
* back-edge flags. The caller handles all rendering.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts
|
|
90
|
+
* const layout = computeDAGLayout({
|
|
91
|
+
* nodes: [{ id: "a" }, { id: "b" }, { id: "c" }],
|
|
92
|
+
* edges: [{ from: "a", to: "b" }, { from: "b", to: "c" }],
|
|
93
|
+
* });
|
|
94
|
+
*
|
|
95
|
+
* // layout.nodes[i].x / .y → position your own SVG/HTML elements
|
|
96
|
+
* // layout.edges[i].sourceX/Y, targetX/Y → draw lines between them
|
|
97
|
+
* // layout.edges[i].isBackEdge → style cycle edges differently
|
|
98
|
+
* // layout.ranks[i] → draw layer bands behind each rank
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
export declare function computeDAGLayout(options: DAGLayoutOptions): DAGLayoutResult;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Low-level diff primitives for the canvas SDK.
|
|
3
|
+
*
|
|
4
|
+
* The canvas diff surface is intentionally minimal — two components that
|
|
5
|
+
* compose with the generic `Card` / `CardHeader` / `CardBody` / `Pill` /
|
|
6
|
+
* `Text` primitives to build any diff layout an agent can imagine:
|
|
7
|
+
*
|
|
8
|
+
* - `DiffView` — a monospaced, syntax-highlighted unified diff renderer.
|
|
9
|
+
* Drops into any container (a `Card`, a table cell, a bare layout,
|
|
10
|
+
* nothing at all). No card chrome, no header, no path display. Pass
|
|
11
|
+
* `path` to auto-detect the language for highlighting, or `language`
|
|
12
|
+
* to override.
|
|
13
|
+
*
|
|
14
|
+
* - `DiffStats` — the canonical `+N` green / `-N` red glyph pair. Use
|
|
15
|
+
* it anywhere a small "added/deleted" summary makes sense — in a
|
|
16
|
+
* `CardHeader`'s `trailing` slot, next to a filename in a file tree,
|
|
17
|
+
* inside a status row, etc.
|
|
18
|
+
*
|
|
19
|
+
* For file-level metadata the preferred composition is to use the
|
|
20
|
+
* generic `Card` family:
|
|
21
|
+
*
|
|
22
|
+
* ```tsx
|
|
23
|
+
* <Card collapsible>
|
|
24
|
+
* <CardHeader trailing={<DiffStats additions={5} deletions={2} />}>
|
|
25
|
+
* src/utils.ts
|
|
26
|
+
* </CardHeader>
|
|
27
|
+
* <CardBody style={{ padding: 0 }}>
|
|
28
|
+
* <DiffView path="src/utils.ts" lines={lines} />
|
|
29
|
+
* </CardBody>
|
|
30
|
+
* </Card>
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
import type { CSSProperties, JSX } from "react";
|
|
34
|
+
export type DiffStatsProps = {
|
|
35
|
+
additions?: number;
|
|
36
|
+
deletions?: number;
|
|
37
|
+
style?: CSSProperties;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Inline `+N` / `-N` glyph pair. Green additions, red deletions, with
|
|
41
|
+
* tabular numerals so columns of stats line up. Renders nothing when
|
|
42
|
+
* both counts are zero.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```tsx
|
|
46
|
+
* <CardHeader trailing={<DiffStats additions={12} deletions={3} />}>
|
|
47
|
+
* src/utils.ts
|
|
48
|
+
* </CardHeader>
|
|
49
|
+
*
|
|
50
|
+
* <Row gap={8}>
|
|
51
|
+
* <Text>Refactor pass</Text>
|
|
52
|
+
* <DiffStats additions={42} deletions={17} />
|
|
53
|
+
* </Row>
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export declare function DiffStats({ additions, deletions, style, }: DiffStatsProps): JSX.Element | null;
|
|
57
|
+
export type DiffLineType = "added" | "removed" | "unchanged";
|
|
58
|
+
export type DiffLineData = {
|
|
59
|
+
type: DiffLineType;
|
|
60
|
+
content: string;
|
|
61
|
+
lineNumber?: number;
|
|
62
|
+
};
|
|
63
|
+
export type DiffViewProps = {
|
|
64
|
+
lines: DiffLineData[];
|
|
65
|
+
/**
|
|
66
|
+
* File path used to infer the syntax-highlighting language from the
|
|
67
|
+
* extension (e.g. `"src/utils.ts"` → `typescript`). The most ergonomic
|
|
68
|
+
* way to enable highlighting — pass the same path you show in the
|
|
69
|
+
* enclosing card header. Unknown extensions silently render as plain
|
|
70
|
+
* text.
|
|
71
|
+
*
|
|
72
|
+
* If both `path` and `language` are provided, `language` wins.
|
|
73
|
+
*/
|
|
74
|
+
path?: string;
|
|
75
|
+
/**
|
|
76
|
+
* Explicit language override for syntax highlighting (e.g.
|
|
77
|
+
* `"typescript"`, `"python"`, `"tsx"`). Use this when no file path is
|
|
78
|
+
* available, when the path's extension is misleading, or when the
|
|
79
|
+
* content is a snippet rather than a real file. Accepts common
|
|
80
|
+
* aliases (`ts`, `py`, `rs`, `md`, etc.). Unknown languages silently
|
|
81
|
+
* fall back to plain text.
|
|
82
|
+
*
|
|
83
|
+
* Highlighting is applied per line, so multi-line constructs (block
|
|
84
|
+
* comments, template literals) may not colorize perfectly across line
|
|
85
|
+
* boundaries. For typical diff-sized inputs this is fine.
|
|
86
|
+
*/
|
|
87
|
+
language?: string;
|
|
88
|
+
/** Show line numbers in the gutter. Default `true`. */
|
|
89
|
+
showLineNumbers?: boolean;
|
|
90
|
+
/** Color line numbers green/red for added/removed lines. Default `true`. */
|
|
91
|
+
coloredLineNumbers?: boolean;
|
|
92
|
+
/** Show a 3px accent strip on the left edge for changed lines. Default `true`. */
|
|
93
|
+
showAccentStrip?: boolean;
|
|
94
|
+
style?: CSSProperties;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Unified diff body renderer with monospaced type, colored line
|
|
98
|
+
* backgrounds, line-number gutter, accent strip, and optional Shiki
|
|
99
|
+
* syntax highlighting.
|
|
100
|
+
*
|
|
101
|
+
* `DiffView` does not provide any surrounding chrome — place it inside
|
|
102
|
+
* a `Card` + `CardBody` (with `padding: 0`) when you want the standard
|
|
103
|
+
* bordered "file diff" look, or drop it anywhere else if you want the
|
|
104
|
+
* bare renderer.
|
|
105
|
+
*
|
|
106
|
+
* Pass `path` to enable syntax highlighting from the file extension.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```tsx
|
|
110
|
+
* <Card>
|
|
111
|
+
* <CardHeader trailing={<DiffStats additions={2} deletions={1} />}>
|
|
112
|
+
* src/utils.ts
|
|
113
|
+
* </CardHeader>
|
|
114
|
+
* <CardBody style={{ padding: 0 }}>
|
|
115
|
+
* <DiffView
|
|
116
|
+
* path="src/utils.ts"
|
|
117
|
+
* lines={[
|
|
118
|
+
* { type: "unchanged", content: "export function add(a: number, b: number): number {", lineNumber: 1 },
|
|
119
|
+
* { type: "removed", content: " return a + b;", lineNumber: 2 },
|
|
120
|
+
* { type: "added", content: " const result = a + b;", lineNumber: 2 },
|
|
121
|
+
* { type: "added", content: " return result;", lineNumber: 3 },
|
|
122
|
+
* { type: "unchanged", content: "}", lineNumber: 4 },
|
|
123
|
+
* ]}
|
|
124
|
+
* />
|
|
125
|
+
* </CardBody>
|
|
126
|
+
* </Card>
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
export declare function DiffView({ lines, path, language, showLineNumbers, coloredLineNumbers, showAccentStrip, style, }: DiffViewProps): JSX.Element;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Form primitives for `cursor/canvas`. Provides themed, controlled form controls
|
|
3
|
+
* for interactive canvas apps with persistent state. All `onChange` callbacks
|
|
4
|
+
* receive the **value directly** (not a DOM event), so they pair naturally
|
|
5
|
+
* with `useCanvasState`:
|
|
6
|
+
*
|
|
7
|
+
* ```tsx
|
|
8
|
+
* const [name, setName] = useCanvasState("name", "");
|
|
9
|
+
* <TextInput value={name} onChange={setName} placeholder="Enter name…" />
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
import { type CSSProperties, type JSX, type ReactNode } from "react";
|
|
13
|
+
export type TextInputProps = {
|
|
14
|
+
value?: string;
|
|
15
|
+
/** Called with the new string value on every keystroke. */
|
|
16
|
+
onChange?: (value: string) => void;
|
|
17
|
+
placeholder?: string;
|
|
18
|
+
disabled?: boolean;
|
|
19
|
+
type?: "text" | "email" | "password" | "number" | "url" | "search";
|
|
20
|
+
style?: CSSProperties;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Single-line text input (28px height). Use for names, titles, search
|
|
24
|
+
* queries, and short text fields.
|
|
25
|
+
*
|
|
26
|
+
* `onChange` receives the **string value**, not a DOM event — this pairs
|
|
27
|
+
* directly with `useCanvasState` setters.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```tsx
|
|
31
|
+
* const [name, setName] = useCanvasState("name", "");
|
|
32
|
+
*
|
|
33
|
+
* <TextInput value={name} onChange={setName} placeholder="Task title…" />
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare function TextInput({ value, onChange, placeholder, disabled, type, style, }: TextInputProps): JSX.Element;
|
|
37
|
+
export type TextAreaProps = {
|
|
38
|
+
value?: string;
|
|
39
|
+
/** Called with the new string value on every keystroke. */
|
|
40
|
+
onChange?: (value: string) => void;
|
|
41
|
+
placeholder?: string;
|
|
42
|
+
disabled?: boolean;
|
|
43
|
+
/** Minimum visible rows. Defaults to 3. */
|
|
44
|
+
rows?: number;
|
|
45
|
+
style?: CSSProperties;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Multi-line text input that auto-resizes to fit its content.
|
|
49
|
+
* Use for notes, descriptions, comments, and multi-line text fields.
|
|
50
|
+
*
|
|
51
|
+
* The textarea grows as the user types. Set `rows` for the minimum visible
|
|
52
|
+
* height (defaults to 3). Override with `style={{ height: "100px" }}` for a
|
|
53
|
+
* fixed size.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```tsx
|
|
57
|
+
* const [notes, setNotes] = useCanvasState("notes", "");
|
|
58
|
+
*
|
|
59
|
+
* <TextArea value={notes} onChange={setNotes} placeholder="Add notes…" rows={4} />
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export declare function TextArea({ value, onChange, placeholder, disabled, rows, style, }: TextAreaProps): JSX.Element;
|
|
63
|
+
export type CheckboxProps = {
|
|
64
|
+
checked?: boolean;
|
|
65
|
+
/** Called with the new boolean value when toggled. */
|
|
66
|
+
onChange?: (checked: boolean) => void;
|
|
67
|
+
disabled?: boolean;
|
|
68
|
+
/** Optional label rendered beside the checkbox. Clicking the label toggles the checkbox. */
|
|
69
|
+
label?: ReactNode;
|
|
70
|
+
style?: CSSProperties;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Checkbox with optional label (accent blue when checked).
|
|
74
|
+
*
|
|
75
|
+
* Pass `label` to render a clickable text label beside the checkbox. Without a
|
|
76
|
+
* label, provide `title` or wrap in your own `<label>` for accessibility.
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```tsx
|
|
80
|
+
* const [agreed, setAgreed] = useCanvasState("agreed", false);
|
|
81
|
+
*
|
|
82
|
+
* <Checkbox checked={agreed} onChange={setAgreed} label="I agree to the terms" />
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```tsx
|
|
87
|
+
* // Checkbox in a list — no label, parent handles layout
|
|
88
|
+
* <Checkbox checked={item.done} onChange={(v) => toggleItem(item.id, v)} />
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
export declare function Checkbox({ checked, onChange, disabled, label, style, }: CheckboxProps): JSX.Element;
|
|
92
|
+
export type ToggleProps = {
|
|
93
|
+
checked?: boolean;
|
|
94
|
+
/** Called with the new boolean value when toggled. */
|
|
95
|
+
onChange?: (checked: boolean) => void;
|
|
96
|
+
disabled?: boolean;
|
|
97
|
+
/** `sm` = 16px track, `md` = 20px track (default `sm`). */
|
|
98
|
+
size?: "sm" | "md";
|
|
99
|
+
style?: CSSProperties;
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Boolean toggle switch. Uses the accent color for the "on" state and a
|
|
103
|
+
* neutral fill for "off".
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```tsx
|
|
107
|
+
* const [enabled, setEnabled] = useCanvasState("enabled", false);
|
|
108
|
+
*
|
|
109
|
+
* <Row gap={8} align="center">
|
|
110
|
+
* <Text>Notifications</Text>
|
|
111
|
+
* <Spacer />
|
|
112
|
+
* <Toggle checked={enabled} onChange={setEnabled} />
|
|
113
|
+
* </Row>
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
export declare function Toggle({ checked, onChange, disabled, size, style, }: ToggleProps): JSX.Element;
|
|
117
|
+
export type SelectOption = {
|
|
118
|
+
value: string;
|
|
119
|
+
label: string;
|
|
120
|
+
disabled?: boolean;
|
|
121
|
+
};
|
|
122
|
+
export type SelectProps = {
|
|
123
|
+
value?: string;
|
|
124
|
+
/** Called with the new selected value. */
|
|
125
|
+
onChange?: (value: string) => void;
|
|
126
|
+
/** List of options. Each must have a unique `value`. */
|
|
127
|
+
options: SelectOption[];
|
|
128
|
+
/** Placeholder shown when no value is selected. */
|
|
129
|
+
placeholder?: string;
|
|
130
|
+
disabled?: boolean;
|
|
131
|
+
style?: CSSProperties;
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Dropdown select (native `<select>` with themed styling).
|
|
135
|
+
*
|
|
136
|
+
* Uses a native `<select>` under the hood for reliable keyboard, screen-reader,
|
|
137
|
+
* and mobile support. The OS-rendered option list is themed for dark mode via
|
|
138
|
+
* `color-scheme` and per-option colors.
|
|
139
|
+
*
|
|
140
|
+
* @example
|
|
141
|
+
* ```tsx
|
|
142
|
+
* const [priority, setPriority] = useCanvasState("priority", "medium");
|
|
143
|
+
*
|
|
144
|
+
* <Select
|
|
145
|
+
* value={priority}
|
|
146
|
+
* onChange={setPriority}
|
|
147
|
+
* options={[
|
|
148
|
+
* { value: "low", label: "Low" },
|
|
149
|
+
* { value: "medium", label: "Medium" },
|
|
150
|
+
* { value: "high", label: "High" },
|
|
151
|
+
* ]}
|
|
152
|
+
* />
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
export declare function Select({ value, onChange, options, placeholder, disabled, style, }: SelectProps): JSX.Element;
|
|
156
|
+
export type IconButtonProps = {
|
|
157
|
+
/** Icon content: an SVG element, emoji, or unicode character. */
|
|
158
|
+
children: ReactNode;
|
|
159
|
+
onClick?: () => void;
|
|
160
|
+
disabled?: boolean;
|
|
161
|
+
/** Tooltip text. Always provide for accessibility since there is no text label. */
|
|
162
|
+
title?: string;
|
|
163
|
+
/**
|
|
164
|
+
* `"default"` is transparent until hovered; `"circle"` has a permanent
|
|
165
|
+
* background fill.
|
|
166
|
+
*/
|
|
167
|
+
variant?: "default" | "circle";
|
|
168
|
+
/** `sm` = 16px, `md` = 20px (default `md`). */
|
|
169
|
+
size?: "sm" | "md";
|
|
170
|
+
style?: CSSProperties;
|
|
171
|
+
};
|
|
172
|
+
/**
|
|
173
|
+
* Compact icon-only button for inline actions on list items (delete, edit,
|
|
174
|
+
* expand, etc.). Accepts **any** `children` as the icon — use an inline SVG,
|
|
175
|
+
* an emoji, or a unicode character.
|
|
176
|
+
*
|
|
177
|
+
* Always provide `title` for accessibility (screen-reader label + tooltip).
|
|
178
|
+
*
|
|
179
|
+
* Canvas has no icon font, so pass icon content directly.
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```tsx
|
|
183
|
+
* // Delete button on a card
|
|
184
|
+
* <IconButton title="Delete" onClick={() => remove(id)}>✕</IconButton>
|
|
185
|
+
*
|
|
186
|
+
* // Edit button with an SVG icon
|
|
187
|
+
* <IconButton title="Edit" variant="circle" size="sm" onClick={edit}>
|
|
188
|
+
* <svg width={12} height={12} viewBox="0 0 12 12" fill="none">
|
|
189
|
+
* <path d="M8.5 1.5l2 2L4 10H2V8z" stroke="currentColor" strokeWidth={1.2} />
|
|
190
|
+
* </svg>
|
|
191
|
+
* </IconButton>
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
export declare function IconButton({ children, onClick, disabled, title, variant, size, style, }: IconButtonProps): JSX.Element;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import type { CanvasHostThemeBrand, CanvasPalette, CanvasTokens, ChartPalette } from "./canvas-tokens.js";
|
|
2
|
+
import { type CanvasAction } from "./internal/canvas-action-dispatch.js";
|
|
3
|
+
import { type CanvasRadius, type CanvasTypography } from "./theme.js";
|
|
4
|
+
/**
|
|
5
|
+
* Host theme for the current canvas. Semantic color groups (`text`, `bg`,
|
|
6
|
+
* `fill`, `stroke`, `accent`, `diff`) live at the top level for ergonomic
|
|
7
|
+
* inline-style access; `tokens` is also present as a self-reference for
|
|
8
|
+
* callers that prefer a namespaced form. There is no `colors` bag —
|
|
9
|
+
* `theme.colors.bg.primary` is not a path.
|
|
10
|
+
*/
|
|
11
|
+
export interface CanvasHostTheme extends CanvasTokens {
|
|
12
|
+
readonly kind: string;
|
|
13
|
+
readonly brand: CanvasHostThemeBrand;
|
|
14
|
+
readonly tokens: CanvasTokens;
|
|
15
|
+
readonly palette: CanvasPalette;
|
|
16
|
+
readonly radius: CanvasRadius;
|
|
17
|
+
readonly typography: CanvasTypography;
|
|
18
|
+
readonly chart: {
|
|
19
|
+
readonly palette: ChartPalette;
|
|
20
|
+
readonly sequence: readonly string[];
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Raw theme payload read from the host `theme` channel. Every field is typed
|
|
25
|
+
* `unknown` on purpose: the value crosses an untrusted JSON/SSE boundary
|
|
26
|
+
* (`window.__cursorCanvas.state`), so it is validated per-field at read time
|
|
27
|
+
* rather than trusted from the wire. Mirrors `CanvasHostThemeState` from
|
|
28
|
+
* `@anysphere/constants` (the producer side); kept local so this bundled SDK
|
|
29
|
+
* stays dependency-free.
|
|
30
|
+
*/
|
|
31
|
+
interface RawHostThemeState {
|
|
32
|
+
readonly kind?: unknown;
|
|
33
|
+
readonly brand?: unknown;
|
|
34
|
+
readonly primary?: unknown;
|
|
35
|
+
readonly editorBackground?: unknown;
|
|
36
|
+
readonly editorForeground?: unknown;
|
|
37
|
+
}
|
|
38
|
+
/** Exported for unit tests only; not part of the SDK surface (see index.ts). */
|
|
39
|
+
export declare function resolveTheme(raw: RawHostThemeState | undefined): CanvasHostTheme;
|
|
40
|
+
/**
|
|
41
|
+
* Returns the current host theme. When no host state is available (first
|
|
42
|
+
* paint, or a dead state bridge) it falls back to the page's
|
|
43
|
+
* `prefers-color-scheme` polarity instead of assuming dark.
|
|
44
|
+
*
|
|
45
|
+
* Semantic color groups are available directly on the returned object —
|
|
46
|
+
* `accent`, `text`, `bg`, `fill`, `stroke`, `diff` — as well as `kind`
|
|
47
|
+
* (`"dark"` | `"light"` | …), `brand` (`"cursor"` | `"grok"`, default
|
|
48
|
+
* `"cursor"` when the host omits it), and `palette` (the flat color
|
|
49
|
+
* palette). There is no `theme.colors` group.
|
|
50
|
+
*
|
|
51
|
+
* Call `useHostTheme()` inside each component that needs theme access —
|
|
52
|
+
* the returned object is scoped to that component, not shared across
|
|
53
|
+
* function boundaries.
|
|
54
|
+
*
|
|
55
|
+
* Stable paths for `style={{ ... }}` usage:
|
|
56
|
+
* - `text.primary / secondary / tertiary / quaternary` — text hierarchy
|
|
57
|
+
* - `bg.editor / chrome / elevated` — surface backgrounds
|
|
58
|
+
* - `fill.primary / secondary / tertiary / quaternary` — tinted fills
|
|
59
|
+
* - `stroke.primary / secondary / tertiary` — borders and dividers
|
|
60
|
+
* - `accent.primary / control` — accent blue and button background
|
|
61
|
+
* - `text.link` — link color
|
|
62
|
+
* - `text.onAccent` — text on accent-colored surfaces
|
|
63
|
+
*
|
|
64
|
+
* Prefer built-in components (`Card`, `Button`, `Text`, etc.) over raw
|
|
65
|
+
* token usage. Reach for tokens only when no component covers the case,
|
|
66
|
+
* and stick to flat solid colors — no gradients, no box-shadows.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```tsx
|
|
70
|
+
* function Overview() {
|
|
71
|
+
* const theme = useHostTheme();
|
|
72
|
+
* return (
|
|
73
|
+
* <div style={{ background: theme.fill.tertiary, color: theme.text.secondary, padding: 8 }}>
|
|
74
|
+
* <span style={{ color: theme.accent.primary }}>Accent text</span>
|
|
75
|
+
* </div>
|
|
76
|
+
* );
|
|
77
|
+
* }
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export declare function useHostTheme(): CanvasHostTheme;
|
|
81
|
+
/**
|
|
82
|
+
* Setter for `useCanvasState`. Accepts either a new value or an updater
|
|
83
|
+
* function that receives the previous value.
|
|
84
|
+
*/
|
|
85
|
+
export type SetCanvasState<T> = (action: T | ((prev: T) => T)) => void;
|
|
86
|
+
/**
|
|
87
|
+
* Persistent state hook for canvas applications. Works like `React.useState`
|
|
88
|
+
* but the value survives rebuilds, reloads, and IDE restarts — it is stored
|
|
89
|
+
* in a `.canvas.data.json` sidecar file next to the canvas source.
|
|
90
|
+
*
|
|
91
|
+
* Each key is a unique string chosen by the canvas author. Keys are stable
|
|
92
|
+
* regardless of hook call order or component tree structure.
|
|
93
|
+
*
|
|
94
|
+
* @param key - Unique string identifier for this piece of state.
|
|
95
|
+
* @param defaultValue - Value returned when no persisted value exists for `key`.
|
|
96
|
+
* @returns A `[value, setValue]` tuple, same shape as `React.useState`.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```tsx
|
|
100
|
+
* function Counter() {
|
|
101
|
+
* const [count, setCount] = useCanvasState("count", 0);
|
|
102
|
+
* return <Button onClick={() => setCount(c => c + 1)}>{count}</Button>;
|
|
103
|
+
* }
|
|
104
|
+
* ```
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```tsx
|
|
108
|
+
* interface Column { id: string; title: string; cardIds: string[] }
|
|
109
|
+
* function KanbanBoard() {
|
|
110
|
+
* const [columns, setColumns] = useCanvasState<Column[]>("columns", [
|
|
111
|
+
* { id: "todo", title: "To Do", cardIds: [] },
|
|
112
|
+
* { id: "doing", title: "In Progress", cardIds: [] },
|
|
113
|
+
* { id: "done", title: "Done", cardIds: [] },
|
|
114
|
+
* ]);
|
|
115
|
+
* // ...
|
|
116
|
+
* }
|
|
117
|
+
* ```
|
|
118
|
+
*/
|
|
119
|
+
export declare function useCanvasState<T>(key: string, defaultValue: T): [T, SetCanvasState<T>];
|
|
120
|
+
export type { CanvasAction };
|
|
121
|
+
/**
|
|
122
|
+
* Returns a stable `dispatch` function for triggering IDE actions from
|
|
123
|
+
* canvas buttons. Actions are fire-and-forget — the canvas does not
|
|
124
|
+
* receive a response.
|
|
125
|
+
*
|
|
126
|
+
* ## Available actions
|
|
127
|
+
*
|
|
128
|
+
* **`openAgent`** — Navigate the IDE to an agent conversation.
|
|
129
|
+
* `agentId` is the conversation UUID (the filename stem from the
|
|
130
|
+
* `agent-transcripts/` directory). Works for both local and
|
|
131
|
+
* cloud/background agents in Glass and classic IDE modes.
|
|
132
|
+
*
|
|
133
|
+
* **`newComposerChat`** — Open a new local agent chat with the
|
|
134
|
+
* dispatching canvas file as an @mention. Optional `userPrompt` is
|
|
135
|
+
* appended after the mention. The canvas file is resolved from the
|
|
136
|
+
* action route's `canvasId`; do not pass the path from canvas UI.
|
|
137
|
+
*
|
|
138
|
+
* **`openFile`** — Open an existing workspace file in the host IDE.
|
|
139
|
+
* `path` may be workspace-relative or absolute. Optional `selection`
|
|
140
|
+
* follows VS Code editor line/column fields.
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```tsx
|
|
144
|
+
* function AgentLink({ agentId, title }: { agentId: string; title: string }) {
|
|
145
|
+
* const dispatch = useCanvasAction();
|
|
146
|
+
* return (
|
|
147
|
+
* <Button onClick={() => dispatch({ type: "openAgent", agentId })}>
|
|
148
|
+
* {title}
|
|
149
|
+
* </Button>
|
|
150
|
+
* );
|
|
151
|
+
* }
|
|
152
|
+
* ```
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```tsx
|
|
156
|
+
* function AskAboutCanvas() {
|
|
157
|
+
* const dispatch = useCanvasAction();
|
|
158
|
+
* return (
|
|
159
|
+
* <Button
|
|
160
|
+
* onClick={() =>
|
|
161
|
+
* dispatch({
|
|
162
|
+
* type: "newComposerChat",
|
|
163
|
+
* userPrompt: "please explain what is using the most context",
|
|
164
|
+
* })
|
|
165
|
+
* }
|
|
166
|
+
* >
|
|
167
|
+
* Ask in chat
|
|
168
|
+
* </Button>
|
|
169
|
+
* );
|
|
170
|
+
* }
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
export declare function useCanvasAction(): (action: CanvasAction) => void;
|