@flesler/cursor-canvas-web 1.3.0 → 1.5.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.
Files changed (42) hide show
  1. package/dist/cursor-canvas-web.css +1 -0
  2. package/dist/cursor-canvas.cjs +1 -1
  3. package/dist/cursor-canvas.js +753 -766
  4. package/dist/pie-donut-DNbA0GeH.cjs +1 -0
  5. package/dist/pie-donut-qRBFP0tT.js +63 -0
  6. package/package.json +10 -3
  7. package/src/runtime/index.tsx +64 -0
  8. package/src/shim/__tests__/barChart.test.ts +61 -0
  9. package/src/shim/__tests__/canvas.smoke.test.tsx +209 -0
  10. package/src/shim/__tests__/category-theme.test.tsx +58 -0
  11. package/src/shim/__tests__/dag.test.ts +49 -0
  12. package/src/shim/__tests__/host-tokens.test.ts +106 -0
  13. package/src/shim/__tests__/lineChart.test.ts +28 -0
  14. package/src/shim/__tests__/useCanvasState.test.tsx +35 -0
  15. package/src/shim/__tests__/useHostTheme.test.tsx +43 -0
  16. package/src/shim/__tests__/valueAxis.test.ts +39 -0
  17. package/src/shim/bar-chart.tsx +77 -0
  18. package/src/shim/canvas-hooks.tsx +57 -0
  19. package/src/shim/canvas-mantine-root.tsx +129 -0
  20. package/src/shim/chart-shared.tsx +252 -0
  21. package/src/shim/cursor-canvas.tsx +136 -0
  22. package/src/shim/dag.ts +179 -0
  23. package/src/shim/diff.tsx +161 -0
  24. package/src/shim/host-tokens.ts +614 -0
  25. package/src/shim/line-chart.tsx +62 -0
  26. package/src/shim/pie-chart.tsx +127 -0
  27. package/src/shim/pie-donut.tsx +79 -0
  28. package/src/shim/theme.ts +77 -0
  29. package/src/shim/todo.tsx +184 -0
  30. package/src/shim/ui/actions.tsx +58 -0
  31. package/src/shim/ui/card-context.tsx +11 -0
  32. package/src/shim/ui/card.tsx +79 -0
  33. package/src/shim/ui/chevron.tsx +23 -0
  34. package/src/shim/ui/display.tsx +83 -0
  35. package/src/shim/ui/form.tsx +102 -0
  36. package/src/shim/ui/layout.tsx +67 -0
  37. package/src/shim/ui/rich.tsx +136 -0
  38. package/src/shim/ui/table.tsx +71 -0
  39. package/src/shim/ui/typography.tsx +129 -0
  40. package/src/shim/use-tokens.ts +13 -0
  41. package/src/superset/chart-extra.d.ts +13 -7
  42. package/src/superset/index.d.ts +4 -4
@@ -0,0 +1,83 @@
1
+ import { Alert, Badge, Text as MantineText } from '@mantine/core'
2
+ import type { CalloutProps, PillProps, StatProps } from '../../vendor/canvas-sdk/ui-primitives.js'
3
+ import { toneColor, toneHex } from '../theme'
4
+ import { useHostTheme } from '../use-tokens'
5
+
6
+ export function Stat({ value, label, tone, style }: StatProps) {
7
+ const host = useHostTheme()
8
+ return (
9
+ <div style={style}>
10
+ <MantineText
11
+ fz={22}
12
+ fw={700}
13
+ lh={1.2}
14
+ style={{
15
+ color: tone ? toneHex[tone] : host.text.primary,
16
+ fontFamily: host.typography.fontFamily,
17
+ }}
18
+ >
19
+ {value}
20
+ </MantineText>
21
+ <MantineText
22
+ fz={12}
23
+ style={{
24
+ color: host.text.tertiary,
25
+ fontFamily: host.typography.fontFamily,
26
+ }}
27
+ >
28
+ {label}
29
+ </MantineText>
30
+ </div>
31
+ )
32
+ }
33
+
34
+ export function Pill({
35
+ children,
36
+ active,
37
+ tone = 'neutral',
38
+ size = 'md',
39
+ leadingContent,
40
+ keyboardHint,
41
+ title,
42
+ style,
43
+ onClick,
44
+ }: PillProps) {
45
+ return (
46
+ <Badge
47
+ color={toneColor[tone]}
48
+ variant={active ? 'filled' : 'light'}
49
+ size={size === 'sm' ? 'sm' : 'md'}
50
+ leftSection={leadingContent}
51
+ rightSection={keyboardHint}
52
+ title={title}
53
+ onClick={onClick}
54
+ style={{
55
+ cursor: onClick ? 'pointer' : undefined,
56
+ textTransform: 'none',
57
+ ...style,
58
+ }}
59
+ >
60
+ {children}
61
+ </Badge>
62
+ )
63
+ }
64
+
65
+ export function Callout({
66
+ children,
67
+ tone = 'neutral',
68
+ title,
69
+ icon,
70
+ style,
71
+ }: CalloutProps) {
72
+ return (
73
+ <Alert
74
+ color={toneColor[tone]}
75
+ title={title}
76
+ icon={icon}
77
+ variant="light"
78
+ style={style}
79
+ >
80
+ {children}
81
+ </Alert>
82
+ )
83
+ }
@@ -0,0 +1,102 @@
1
+ import { Checkbox as MantineCheckbox, Select as MantineSelect, Textarea as MantineTextarea, TextInput as MantineTextInput, Switch } from '@mantine/core'
2
+ import type { CheckboxProps, SelectProps, TextAreaProps, TextInputProps, ToggleProps } from '../../vendor/canvas-sdk/form-primitives.js'
3
+
4
+ export function Select({
5
+ value,
6
+ onChange,
7
+ options,
8
+ placeholder,
9
+ disabled,
10
+ style,
11
+ }: SelectProps) {
12
+ return (
13
+ <MantineSelect
14
+ value={value}
15
+ data={options}
16
+ placeholder={placeholder}
17
+ disabled={disabled}
18
+ allowDeselect={false}
19
+ onChange={(next) => {
20
+ if (next !== null) onChange?.(next)
21
+ }}
22
+ style={style}
23
+ />
24
+ )
25
+ }
26
+
27
+ export function Checkbox({
28
+ checked,
29
+ onChange,
30
+ disabled,
31
+ label,
32
+ style,
33
+ }: CheckboxProps) {
34
+ return (
35
+ <MantineCheckbox
36
+ checked={checked}
37
+ disabled={disabled}
38
+ label={label}
39
+ onChange={event => onChange?.(event.currentTarget.checked)}
40
+ style={style}
41
+ />
42
+ )
43
+ }
44
+
45
+ export function Toggle({
46
+ checked,
47
+ onChange,
48
+ disabled,
49
+ size = 'sm',
50
+ style,
51
+ }: ToggleProps) {
52
+ return (
53
+ <Switch
54
+ checked={checked}
55
+ disabled={disabled}
56
+ size={size === 'sm' ? 'sm' : 'md'}
57
+ onChange={event => onChange?.(event.currentTarget.checked)}
58
+ style={style}
59
+ />
60
+ )
61
+ }
62
+
63
+ export function TextInput({
64
+ value,
65
+ onChange,
66
+ placeholder,
67
+ disabled,
68
+ type,
69
+ style,
70
+ }: TextInputProps) {
71
+ return (
72
+ <MantineTextInput
73
+ value={value}
74
+ placeholder={placeholder}
75
+ disabled={disabled}
76
+ type={type}
77
+ onChange={event => onChange?.(event.currentTarget.value)}
78
+ style={style}
79
+ />
80
+ )
81
+ }
82
+
83
+ export function TextArea({
84
+ value,
85
+ onChange,
86
+ placeholder,
87
+ disabled,
88
+ rows = 3,
89
+ style,
90
+ }: TextAreaProps) {
91
+ return (
92
+ <MantineTextarea
93
+ value={value}
94
+ placeholder={placeholder}
95
+ disabled={disabled}
96
+ autosize
97
+ minRows={rows}
98
+ onChange={event => onChange?.(event.currentTarget.value)}
99
+ style={style}
100
+ />
101
+ )
102
+ }
@@ -0,0 +1,67 @@
1
+ import { Group, Divider as MantineDivider, Stack as MantineStack } from '@mantine/core'
2
+ import type { DividerProps, GridProps, RowProps, StackProps } from '../../vendor/canvas-sdk/ui-primitives.js'
3
+
4
+ export function Stack({ children, gap, style }: StackProps) {
5
+ return (
6
+ <MantineStack gap={gap} style={style}>
7
+ {children}
8
+ </MantineStack>
9
+ )
10
+ }
11
+
12
+ const FLEX: Record<string, string> = {
13
+ start: 'flex-start',
14
+ center: 'center',
15
+ end: 'flex-end',
16
+ stretch: 'stretch',
17
+ 'space-between': 'space-between',
18
+ }
19
+
20
+ export function Row({
21
+ children,
22
+ gap,
23
+ align = 'center',
24
+ justify = 'start',
25
+ wrap = false,
26
+ style,
27
+ }: RowProps) {
28
+ return (
29
+ <Group
30
+ gap={gap}
31
+ align={FLEX[align]}
32
+ justify={FLEX[justify]}
33
+ wrap={wrap ? 'wrap' : 'nowrap'}
34
+ style={style}
35
+ >
36
+ {children}
37
+ </Group>
38
+ )
39
+ }
40
+
41
+ export function Spacer() {
42
+ return <div style={{ flex: 1 }} />
43
+ }
44
+
45
+ export function Grid({ children, columns, gap = 16, align, style }: GridProps) {
46
+ const gridTemplateColumns =
47
+ typeof columns === 'number'
48
+ ? `repeat(${columns}, minmax(0, 1fr))`
49
+ : columns
50
+ return (
51
+ <div
52
+ style={{
53
+ display: 'grid',
54
+ gridTemplateColumns,
55
+ gap,
56
+ alignItems: align,
57
+ ...style,
58
+ }}
59
+ >
60
+ {children}
61
+ </div>
62
+ )
63
+ }
64
+
65
+ export function Divider({ style }: DividerProps) {
66
+ return <MantineDivider style={style} />
67
+ }
@@ -0,0 +1,136 @@
1
+ import { useState } from 'react'
2
+ import type { CollapsibleSectionProps } from '../../vendor/canvas-sdk/collapsible-section.js'
3
+ import type { SwatchProps } from '../../vendor/canvas-sdk/swatch.js'
4
+ import type { UsageBarProps } from '../../vendor/canvas-sdk/usage-bar.js'
5
+ import { usageColorSequence } from '../theme'
6
+ import { useHostTheme } from '../use-tokens'
7
+ import { Chevron } from './chevron'
8
+
9
+ export function Swatch({ color, style }: SwatchProps) {
10
+ const theme = useHostTheme()
11
+ return (
12
+ <span
13
+ style={{
14
+ display: 'inline-block',
15
+ width: 14,
16
+ height: 14,
17
+ borderRadius: 4,
18
+ background: theme.category[color],
19
+ flex: '0 0 auto',
20
+ ...style,
21
+ }}
22
+ />
23
+ )
24
+ }
25
+
26
+ export function UsageBar({
27
+ segments,
28
+ total,
29
+ topLeftLabel,
30
+ topRightLabel,
31
+ style,
32
+ }: UsageBarProps) {
33
+ const theme = useHostTheme()
34
+ const safeTotal = total > 0 ? total : 1
35
+ const used = segments.reduce(
36
+ (sum, s) => sum + (Number.isFinite(s.value) && s.value > 0 ? s.value : 0),
37
+ 0,
38
+ )
39
+ const remainder = Math.max(0, safeTotal - used)
40
+ return (
41
+ <div style={style}>
42
+ {(topLeftLabel != null || topRightLabel != null) && (
43
+ <div
44
+ style={{
45
+ display: 'flex',
46
+ justifyContent: 'space-between',
47
+ fontSize: 12,
48
+ color: theme.text.secondary,
49
+ marginBottom: 6,
50
+ }}
51
+ >
52
+ <span>{topLeftLabel}</span>
53
+ <span>{topRightLabel}</span>
54
+ </div>
55
+ )}
56
+ <div style={{ display: 'flex', gap: 2, height: 8 }}>
57
+ {segments.map((segment, index) => {
58
+ const value =
59
+ Number.isFinite(segment.value) && segment.value > 0
60
+ ? segment.value
61
+ : 0
62
+ const color =
63
+ theme.category[
64
+ segment.color ??
65
+ usageColorSequence[index % usageColorSequence.length]
66
+ ]
67
+ return (
68
+ <div
69
+ key={segment.id}
70
+ style={{
71
+ width: `${(value / safeTotal) * 100}%`,
72
+ background: color,
73
+ borderRadius: 3,
74
+ }}
75
+ />
76
+ )
77
+ })}
78
+ {remainder > 0 && (
79
+ <div
80
+ style={{
81
+ width: `${(remainder / safeTotal) * 100}%`,
82
+ background: theme.fill.tertiary,
83
+ borderRadius: 3,
84
+ }}
85
+ />
86
+ )}
87
+ </div>
88
+ </div>
89
+ )
90
+ }
91
+
92
+ export function CollapsibleSection({
93
+ title,
94
+ leading,
95
+ count,
96
+ trailing,
97
+ children,
98
+ defaultOpen = false,
99
+ style,
100
+ }: CollapsibleSectionProps) {
101
+ const theme = useHostTheme()
102
+ const [open, setOpen] = useState(defaultOpen)
103
+ return (
104
+ <div style={style}>
105
+ <div
106
+ onClick={() => setOpen(o => !o)}
107
+ style={{
108
+ display: 'flex',
109
+ alignItems: 'center',
110
+ gap: 6,
111
+ padding: '4px 0',
112
+ cursor: 'pointer',
113
+ userSelect: 'none',
114
+ color: theme.text.primary,
115
+ fontSize: 14,
116
+ }}
117
+ >
118
+ <Chevron expanded={open} />
119
+ {leading}
120
+ <span style={{ fontWeight: 500 }}>{title}</span>
121
+ {count != null && (
122
+ <span style={{ color: theme.text.tertiary, fontSize: 12 }}>
123
+ {count}
124
+ </span>
125
+ )}
126
+ <span style={{ flex: 1 }} />
127
+ {trailing != null && (
128
+ <span style={{ color: theme.text.tertiary }}>{trailing}</span>
129
+ )}
130
+ </div>
131
+ {open && (
132
+ <div style={{ paddingLeft: 18, paddingBottom: 4 }}>{children}</div>
133
+ )}
134
+ </div>
135
+ )
136
+ }
@@ -0,0 +1,71 @@
1
+ import { Table as MantineTable } from '@mantine/core'
2
+ import type { TableProps, TableRowTone } from '../../vendor/canvas-sdk/ui-primitives.js'
3
+
4
+ const ROW_TONE_BG: Record<TableRowTone, string> = {
5
+ success: 'rgba(31,138,101,0.12)',
6
+ danger: 'rgba(207,45,86,0.12)',
7
+ warning: 'rgba(232,163,61,0.14)',
8
+ info: 'rgba(46,121,181,0.12)',
9
+ neutral: 'rgba(136,136,168,0.12)',
10
+ }
11
+
12
+ export function Table({
13
+ headers,
14
+ rows,
15
+ columnAlign,
16
+ rowTone,
17
+ framed = true,
18
+ striped,
19
+ stickyHeader,
20
+ style,
21
+ emptyMessage,
22
+ }: TableProps) {
23
+ return (
24
+ <MantineTable
25
+ withTableBorder={framed}
26
+ withColumnBorders={false}
27
+ striped={striped}
28
+ stickyHeader={stickyHeader}
29
+ highlightOnHover
30
+ style={style}
31
+ >
32
+ <MantineTable.Thead>
33
+ <MantineTable.Tr>
34
+ {headers.map((header, index) => (
35
+ <MantineTable.Th key={index} ta={columnAlign?.[index] ?? 'left'}>
36
+ {header}
37
+ </MantineTable.Th>
38
+ ))}
39
+ </MantineTable.Tr>
40
+ </MantineTable.Thead>
41
+ <MantineTable.Tbody>
42
+ {rows.length === 0 ? (
43
+ <MantineTable.Tr>
44
+ <MantineTable.Td colSpan={headers.length} ta="center" c="dimmed">
45
+ {emptyMessage ?? 'No data'}
46
+ </MantineTable.Td>
47
+ </MantineTable.Tr>
48
+ ) : (
49
+ rows.map((row, rowIndex) => {
50
+ const tone = rowTone?.[rowIndex]
51
+ return (
52
+ <MantineTable.Tr
53
+ key={rowIndex}
54
+ style={tone ? { background: ROW_TONE_BG[tone] } : undefined}
55
+ >
56
+ {headers.map((_, colIndex) => (
57
+ <MantineTable.Td
58
+ key={colIndex}
59
+ ta={columnAlign?.[colIndex] ?? 'left'}
60
+ >
61
+ {row[colIndex]}
62
+ </MantineTable.Td>
63
+ ))}
64
+ </MantineTable.Tr>
65
+ )
66
+ })
67
+ )}
68
+ </MantineTable.Tbody>
69
+ </MantineTable>
70
+ )
71
+ }
@@ -0,0 +1,129 @@
1
+ import { Text as MantineText, Title } from '@mantine/core'
2
+ import { createContext, useContext, type CSSProperties } from 'react'
3
+ import type { CodeProps, H1Props, H2Props, H3Props, LinkProps, TextProps, TextWeight } from '../../vendor/canvas-sdk/ui-primitives.js'
4
+ import { useHostTheme } from '../use-tokens'
5
+
6
+ function headingStyle(
7
+ host: ReturnType<typeof useHostTheme>,
8
+ level: 'h1' | 'h2' | 'h3',
9
+ style?: CSSProperties,
10
+ ): CSSProperties {
11
+ const block = host.typography[level]
12
+ return {
13
+ fontFamily: host.typography.fontFamily,
14
+ fontSize: block.fontSize,
15
+ lineHeight: block.lineHeight,
16
+ fontWeight: block.fontWeight,
17
+ color: host.text.primary,
18
+ margin: 0,
19
+ ...style,
20
+ }
21
+ }
22
+
23
+ export function H1({ children, style }: H1Props) {
24
+ const host = useHostTheme()
25
+ return (
26
+ <Title order={1} style={headingStyle(host, 'h1', style)}>
27
+ {children}
28
+ </Title>
29
+ )
30
+ }
31
+
32
+ export function H2({ children, style }: H2Props) {
33
+ const host = useHostTheme()
34
+ return (
35
+ <Title order={2} style={headingStyle(host, 'h2', style)}>
36
+ {children}
37
+ </Title>
38
+ )
39
+ }
40
+
41
+ export function H3({ children, style }: H3Props) {
42
+ const host = useHostTheme()
43
+ return (
44
+ <Title order={3} style={headingStyle(host, 'h3', style)}>
45
+ {children}
46
+ </Title>
47
+ )
48
+ }
49
+
50
+ const TEXT_WEIGHT: Record<TextWeight, number> = {
51
+ normal: 400,
52
+ medium: 500,
53
+ semibold: 600,
54
+ bold: 700,
55
+ }
56
+
57
+ const TextParentContext = createContext(false)
58
+
59
+ export function Text({
60
+ children,
61
+ tone = 'primary',
62
+ size = 'body',
63
+ as = 'p',
64
+ weight = 'normal',
65
+ italic,
66
+ truncate,
67
+ style,
68
+ }: TextProps) {
69
+ const theme = useHostTheme()
70
+ const nestedInText = useContext(TextParentContext)
71
+ const component = nestedInText && as === 'p' ? 'span' : as
72
+ const blockParagraph = as === 'p'
73
+ return (
74
+ <TextParentContext.Provider value={true}>
75
+ <MantineText
76
+ component={component}
77
+ size={size === 'small' ? 'xs' : 'sm'}
78
+ fw={TEXT_WEIGHT[weight]}
79
+ c={tone === 'primary' ? undefined : theme.text[tone]}
80
+ truncate={truncate}
81
+ style={{
82
+ fontStyle: italic ? 'italic' : undefined,
83
+ ...(tone === 'primary' ? { color: theme.text.primary } : undefined),
84
+ ...(blockParagraph && component === 'span'
85
+ ? { display: 'block' }
86
+ : undefined),
87
+ fontFamily: theme.typography.fontFamily,
88
+ ...style,
89
+ }}
90
+ >
91
+ {children}
92
+ </MantineText>
93
+ </TextParentContext.Provider>
94
+ )
95
+ }
96
+
97
+ export function Code({ children, style }: CodeProps) {
98
+ const host = useHostTheme()
99
+ return (
100
+ <code
101
+ style={{
102
+ fontFamily:
103
+ 'ui-monospace, SFMono-Regular, Menlo, Monaco, "Cascadia Code", monospace',
104
+ fontSize: '0.92em',
105
+ color: host.text.primary,
106
+ background: host.fill.secondary,
107
+ borderRadius: host.radius.sm,
108
+ padding: '1px 4px',
109
+ ...style,
110
+ }}
111
+ >
112
+ {children}
113
+ </code>
114
+ )
115
+ }
116
+
117
+ export function Link({ children, href, style }: LinkProps) {
118
+ const host = useHostTheme()
119
+ return (
120
+ <a
121
+ href={href}
122
+ target="_blank"
123
+ rel="noreferrer"
124
+ style={{ color: host.text.link, ...style }}
125
+ >
126
+ {children}
127
+ </a>
128
+ )
129
+ }
@@ -0,0 +1,13 @@
1
+ import { useComputedColorScheme } from '@mantine/core'
2
+ import { resolveThemeForColorScheme, type ShimHostTheme } from './host-tokens'
3
+
4
+ /**
5
+ * Returns the host theme tokens for the active color scheme. Reads
6
+ * `window.__cursorCanvas.state.theme` when present (IDE bridge); otherwise
7
+ * Mantine color scheme + `prefers-color-scheme` defaults.
8
+ */
9
+ export function useHostTheme(): ShimHostTheme {
10
+ const scheme = useComputedColorScheme('light')
11
+ const polarity = scheme === 'dark' ? 'dark' : 'light'
12
+ return resolveThemeForColorScheme(polarity)
13
+ }
@@ -12,7 +12,7 @@ export type CanvasChartScale = NonNullable<YAxisProps['scale']>
12
12
  * horizontal bar charts. Non-linear scales default `domain` to `[1, 'dataMax']`.
13
13
  */
14
14
  export type CanvasChartValueAxisExtra = Pick<
15
- Omit<YAxisProps, 'ref'>,
15
+ Omit<YAxisProps, 'ref'>,
16
16
  | 'scale'
17
17
  | 'domain'
18
18
  | 'type'
@@ -26,14 +26,20 @@ export type CanvasChartValueAxisExtra = Pick<
26
26
  >
27
27
 
28
28
  export type CanvasChartExtra = {
29
- valueAxis?: CanvasChartValueAxisExtra
29
+ valueAxis?: CanvasChartValueAxisExtra
30
30
  }
31
31
 
32
- export type CanvasBarChartExtra = CanvasChartExtra
32
+ export type CanvasBarChartExtra = CanvasChartExtra & {
33
+ /** Category (y) axis on `horizontal` bar charts. */
34
+ categoryAxis?: {
35
+ /** Label column width in px; labels truncate to fit. Default 96. */
36
+ width?: number
37
+ }
38
+ }
33
39
 
34
40
  export type CanvasLineChartExtra = CanvasChartExtra & {
35
- /** Curved (`monotone`) segments; the IDE draws straight segments. */
36
- smooth?: boolean;
37
- /** Stack the filled areas. Requires `fill`. */
38
- stacked?: boolean
41
+ /** Curved (`monotone`) segments; the IDE draws straight segments. */
42
+ smooth?: boolean;
43
+ /** Stack the filled areas. Requires `fill`. */
44
+ stacked?: boolean
39
45
  }
@@ -12,13 +12,13 @@ export * from '../vendor/canvas-sdk/index.js'
12
12
  export type * from './chart-extra.js'
13
13
 
14
14
  export type BarChartProps = SdkBarChartProps & {
15
- extra?: CanvasBarChartExtra
15
+ extra?: CanvasBarChartExtra
16
16
  }
17
17
 
18
18
  export type LineChartProps = SdkLineChartProps & {
19
- extra?: CanvasLineChartExtra;
20
- /** @deprecated Use `extra.smooth`. */
21
- smooth?: boolean
19
+ extra?: CanvasLineChartExtra;
20
+ /** @deprecated Use `extra.smooth`. */
21
+ smooth?: boolean
22
22
  }
23
23
 
24
24
  export declare function BarChart(props: BarChartProps): JSX.Element