@ciridae/ui 1.0.2 → 1.0.4

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 (209) hide show
  1. package/dist/app/auth.d.ts +10 -0
  2. package/dist/app/auth.js +60 -0
  3. package/dist/app/error-boundary.d.ts +18 -0
  4. package/dist/app/error-boundary.js +45 -0
  5. package/dist/app/feedback.d.ts +12 -0
  6. package/dist/app/feedback.js +128 -0
  7. package/dist/app/index.d.ts +8 -0
  8. package/dist/app/index.js +8 -0
  9. package/dist/app/request-errors.d.ts +63 -0
  10. package/dist/app/request-errors.js +172 -0
  11. package/dist/app/sentry.d.ts +18 -0
  12. package/dist/app/sentry.js +52 -0
  13. package/dist/app/telemetry.d.ts +7 -0
  14. package/dist/app/telemetry.js +28 -0
  15. package/dist/app/trail.d.ts +9 -0
  16. package/dist/app/trail.js +18 -0
  17. package/dist/chat/chat-query-keys.d.ts +3 -3
  18. package/dist/chat/contract.d.ts +15 -15
  19. package/dist/chat/conversation-items-query-options.d.ts +114 -114
  20. package/dist/chat/conversation-source.d.ts +89 -0
  21. package/dist/chat/conversation-source.js +1 -0
  22. package/dist/chat/describe-tool-call.d.ts +3 -3
  23. package/dist/chat/describe-tool-call.js +3 -3
  24. package/dist/chat/message-text.d.ts +1 -1
  25. package/dist/chat/message-text.js +116 -48
  26. package/dist/chat/use-conversation-list.d.ts +6 -6
  27. package/dist/ciridae.css +16 -4
  28. package/dist/components/activity-feed.d.ts +3 -2
  29. package/dist/components/activity-feed.js +1 -0
  30. package/dist/components/agent-work.d.ts +3 -3
  31. package/dist/components/app-frame.d.ts +91 -0
  32. package/dist/components/app-frame.js +134 -0
  33. package/dist/components/app-navigation.d.ts +30 -3
  34. package/dist/components/app-navigation.js +105 -14
  35. package/dist/components/badge.d.ts +30 -5
  36. package/dist/components/badge.js +134 -15
  37. package/dist/components/brand-icons.d.ts +3 -3
  38. package/dist/components/bulk-action-bar.d.ts +1 -1
  39. package/dist/components/bulk-action-bar.js +3 -3
  40. package/dist/components/button.d.ts +12 -5
  41. package/dist/components/button.js +77 -44
  42. package/dist/components/calendar.d.ts +1 -1
  43. package/dist/components/calendar.js +44 -17
  44. package/dist/components/centered-page.d.ts +1 -1
  45. package/dist/components/centered-page.js +2 -2
  46. package/dist/components/chart.d.ts +22 -2
  47. package/dist/components/chart.js +251 -51
  48. package/dist/components/chat-client-source.d.ts +16 -0
  49. package/dist/components/chat-client-source.js +297 -0
  50. package/dist/components/chat-page.d.ts +9 -8
  51. package/dist/components/chat-page.js +7 -11
  52. package/dist/components/chat-sheet-controller.d.ts +11 -14
  53. package/dist/components/chat-sheet-controller.js +3 -3
  54. package/dist/components/chat-sheet.d.ts +18 -11
  55. package/dist/components/chat-sheet.js +188 -289
  56. package/dist/components/chat.d.ts +109 -19
  57. package/dist/components/chat.js +520 -124
  58. package/dist/components/checkbox-item.d.ts +6 -3
  59. package/dist/components/checkbox-item.js +9 -5
  60. package/dist/components/checkbox-list.d.ts +1 -1
  61. package/dist/components/chip.d.ts +6 -1
  62. package/dist/components/chip.js +27 -1
  63. package/dist/components/collapsible.d.ts +1 -1
  64. package/dist/components/color-theme.d.ts +1 -1
  65. package/dist/components/column-settings.d.ts +1 -1
  66. package/dist/components/column-settings.js +1 -1
  67. package/dist/components/command-menu.d.ts +5 -1
  68. package/dist/components/command-menu.js +98 -32
  69. package/dist/components/command-palette.d.ts +7 -3
  70. package/dist/components/command-palette.js +20 -11
  71. package/dist/components/copyable-id.d.ts +22 -5
  72. package/dist/components/copyable-id.js +46 -6
  73. package/dist/components/data-display.d.ts +39 -8
  74. package/dist/components/data-display.js +138 -19
  75. package/dist/components/data-grid.d.ts +23 -11
  76. package/dist/components/data-grid.js +61 -26
  77. package/dist/components/date-range-picker.d.ts +1 -1
  78. package/dist/components/date-range-picker.js +9 -20
  79. package/dist/components/date-time.d.ts +1 -1
  80. package/dist/components/dialog-height-context.d.ts +1 -1
  81. package/dist/components/document-viewer.d.ts +2 -2
  82. package/dist/components/drawer.d.ts +11 -6
  83. package/dist/components/drawer.js +91 -54
  84. package/dist/components/dropzone.d.ts +4 -4
  85. package/dist/components/editable-field.d.ts +12 -3
  86. package/dist/components/editable-field.js +57 -27
  87. package/dist/components/email-input.d.ts +1 -1
  88. package/dist/components/email-input.js +6 -5
  89. package/dist/components/environment-frame.d.ts +2 -2
  90. package/dist/components/environment-frame.js +37 -6
  91. package/dist/components/expandable-text.d.ts +1 -1
  92. package/dist/components/feedback-screenshot.d.ts +1 -1
  93. package/dist/components/feedback-sheet.d.ts +18 -3
  94. package/dist/components/feedback-sheet.js +216 -55
  95. package/dist/components/field-context.d.ts +1 -1
  96. package/dist/components/filter-bar.d.ts +3 -3
  97. package/dist/components/filter-bar.js +43 -29
  98. package/dist/components/form-controls.d.ts +12 -3
  99. package/dist/components/form-controls.js +46 -22
  100. package/dist/components/form-layout.d.ts +14 -10
  101. package/dist/components/form-layout.js +68 -23
  102. package/dist/components/glass-menu.d.ts +3 -0
  103. package/dist/components/glass-menu.js +63 -0
  104. package/dist/components/gradient-edge.d.ts +9 -0
  105. package/dist/components/gradient-edge.js +31 -0
  106. package/dist/components/hotkeys.d.ts +2 -2
  107. package/dist/components/icon.d.ts +1 -1
  108. package/dist/components/input.d.ts +7 -5
  109. package/dist/components/input.js +29 -17
  110. package/dist/components/keyboard-shortcuts.d.ts +2 -2
  111. package/dist/components/keycap.d.ts +10 -6
  112. package/dist/components/keycap.js +57 -19
  113. package/dist/components/layout.d.ts +13 -13
  114. package/dist/components/lightbox.d.ts +9 -3
  115. package/dist/components/lightbox.js +13 -9
  116. package/dist/components/link.d.ts +1 -1
  117. package/dist/components/locale.d.ts +42 -2
  118. package/dist/components/locale.js +43 -3
  119. package/dist/components/log-entry.d.ts +40 -0
  120. package/dist/components/log-entry.js +146 -0
  121. package/dist/components/log-list.d.ts +3 -3
  122. package/dist/components/mention-input.d.ts +2 -2
  123. package/dist/components/mention-input.js +4 -3
  124. package/dist/components/menu.d.ts +7 -3
  125. package/dist/components/menu.js +34 -37
  126. package/dist/components/meter.d.ts +10 -2
  127. package/dist/components/meter.js +64 -6
  128. package/dist/components/navigation.d.ts +21 -4
  129. package/dist/components/navigation.js +84 -19
  130. package/dist/components/notification-tray.d.ts +1 -1
  131. package/dist/components/notification-tray.js +5 -4
  132. package/dist/components/overlays.d.ts +6 -4
  133. package/dist/components/overlays.js +69 -40
  134. package/dist/components/page-frame.d.ts +4 -4
  135. package/dist/components/page-frame.js +1 -1
  136. package/dist/components/panel.d.ts +4 -4
  137. package/dist/components/panel.js +18 -12
  138. package/dist/components/profile-header.d.ts +1 -1
  139. package/dist/components/progress.d.ts +1 -1
  140. package/dist/components/project-avatar.d.ts +1 -1
  141. package/dist/components/radio-card.d.ts +2 -2
  142. package/dist/components/radio-card.js +10 -9
  143. package/dist/components/record.d.ts +29 -5
  144. package/dist/components/record.js +147 -18
  145. package/dist/components/resolved-file-url.d.ts +19 -0
  146. package/dist/components/resolved-file-url.js +49 -0
  147. package/dist/components/scroll-area.d.ts +1 -1
  148. package/dist/components/secret-dialog.d.ts +1 -1
  149. package/dist/components/segmented-bar.d.ts +4 -2
  150. package/dist/components/segmented-bar.js +16 -6
  151. package/dist/components/select.d.ts +9 -6
  152. package/dist/components/select.js +103 -127
  153. package/dist/components/settings.d.ts +4 -4
  154. package/dist/components/shell-skeleton.d.ts +1 -1
  155. package/dist/components/shell-skeleton.js +1 -1
  156. package/dist/components/shell.d.ts +21 -21
  157. package/dist/components/shell.js +43 -43
  158. package/dist/components/side-panel.d.ts +3 -3
  159. package/dist/components/sidebar.d.ts +13 -8
  160. package/dist/components/sidebar.js +55 -27
  161. package/dist/components/single-line-text.d.ts +2 -0
  162. package/dist/components/single-line-text.js +16 -0
  163. package/dist/components/site-banner.d.ts +5 -2
  164. package/dist/components/site-banner.js +17 -6
  165. package/dist/components/skeleton.d.ts +8 -7
  166. package/dist/components/skeleton.js +28 -16
  167. package/dist/components/skeletons.d.ts +17 -5
  168. package/dist/components/skeletons.js +71 -8
  169. package/dist/components/stages.d.ts +83 -11
  170. package/dist/components/stages.js +512 -156
  171. package/dist/components/stat-card.d.ts +12 -4
  172. package/dist/components/stat-card.js +92 -37
  173. package/dist/components/status-banner.d.ts +1 -1
  174. package/dist/components/status-banner.js +1 -1
  175. package/dist/components/status.d.ts +2 -2
  176. package/dist/components/steps.d.ts +1 -1
  177. package/dist/components/switch.d.ts +1 -1
  178. package/dist/components/tabs.d.ts +8 -8
  179. package/dist/components/tabs.js +26 -13
  180. package/dist/components/theme-customizer.d.ts +2 -2
  181. package/dist/components/theme-toggle.d.ts +4 -0
  182. package/dist/components/theme-toggle.js +67 -0
  183. package/dist/components/toast.d.ts +10 -3
  184. package/dist/components/toast.js +78 -29
  185. package/dist/components/use-new-version-available.d.ts +6 -0
  186. package/dist/components/use-new-version-available.js +29 -0
  187. package/dist/components/user-chip.d.ts +3 -3
  188. package/dist/components/wizard.d.ts +2 -2
  189. package/dist/feedback/contract.d.ts +136 -136
  190. package/dist/feedback/feedback-controller.d.ts +6 -6
  191. package/dist/feedback/generated/zod.gen.d.ts +28 -28
  192. package/dist/feedback/operator-contract.d.ts +11 -11
  193. package/dist/feedback/operator-controller.d.ts +32 -13
  194. package/dist/feedback/operator-controller.js +44 -11
  195. package/dist/preset.js +289 -33
  196. package/dist/styled-system/styles.css +3294 -1079
  197. package/dist/styled-system/tokens/index.mjs +598 -22
  198. package/dist/styled-system/tokens/tokens.d.ts +11 -11
  199. package/dist/styled-system/types/prop-type.d.ts +2 -2
  200. package/dist/styles.css +16 -4
  201. package/dist/theme/color.d.ts +3 -0
  202. package/dist/theme/color.js +5 -0
  203. package/dist/theme/derive.d.ts +9 -2
  204. package/dist/theme/derive.js +72 -12
  205. package/dist/theme/palettes.d.ts +49 -6
  206. package/dist/theme/palettes.js +84 -15
  207. package/dist/theme/tokens.d.ts +3 -3
  208. package/dist/theme/tokens.js +107 -32
  209. package/package.json +40 -3
@@ -2,13 +2,16 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Checkbox as BaseCheckbox } from "@base-ui/react/checkbox";
3
3
  import { Radio as BaseRadio } from "@base-ui/react/radio";
4
4
  import { RadioGroup as BaseRadioGroup } from "@base-ui/react/radio-group";
5
- import { CheckIcon, MinusIcon } from "@phosphor-icons/react";
5
+ import { CheckIcon } from "@phosphor-icons/react";
6
6
  import { css, cva, cx } from "#primitives/css";
7
7
  import { useFieldControl } from "./field-context.js";
8
8
  /*
9
- * The one mark for a checkbox and a radio: a glass well in dark, a near-white one in light, that
10
- * fills with the primary face once on. Hover darkens the edge of an empty mark and the face of a
11
- * filled one. The ring is a variant, because a row that holds the mark draws its own ring instead.
9
+ * The one mark for a checkbox and a radio: the selected tint in dark, a near-white well in light,
10
+ * that fills with the primary button's face and gradient edge once on. The edge is the `[data-edge]`
11
+ * ring the element carries through `checkMarkEdge`: the selected face's gradient in dark, the
12
+ * control hairline in light. Hover darkens the edge of an empty mark, washes it in light, and fades
13
+ * a filled one in dark. The ring is a variant, because a row that holds the mark draws its own
14
+ * ring instead.
12
15
  */
13
16
  export const checkMark = cva({
14
17
  base: {
@@ -18,25 +21,31 @@ export const checkMark = cva({
18
21
  display: "inline-grid",
19
22
  placeItems: "center",
20
23
  padding: "none",
21
- border: "1px solid token(colors.border.mark)",
22
- background: "bg.check",
24
+ border: "1px solid transparent",
25
+ background: "token(gradients.selected)",
23
26
  color: "bg.canvas",
24
- _light: { color: "fg.onAccent" },
27
+ _light: { color: "fg.onAccent", background: "bg.check" },
25
28
  cursor: "pointer",
26
- transitionProperty: "background, border-color",
29
+ transitionProperty: "background, opacity",
27
30
  transitionDuration: "fast",
28
- // Pointing anywhere at a row's label marks the box it would tick.
29
- "&:is(label:hover > *, label:hover *, :hover):not([data-disabled])": {
30
- borderColor: "fg.muted",
31
+ _before: {
32
+ background: "token(gradients.border.selected)",
33
+ _light: { background: "border.control" },
31
34
  },
32
- "&[data-checked], &[data-indeterminate]": {
33
- background: "bg.primary",
34
- borderColor: "transparent",
35
+ // Pointing anywhere at a row's label marks the box it would tick, while it is empty: a filled
36
+ // mark, or a radio card's once chosen, keeps its face.
37
+ "&:not([data-checked], [data-indeterminate], [data-checked] > *):is(label:hover > *, label:hover *, :hover):not([data-disabled])": {
38
+ _before: { background: "fg.muted", _light: { background: "fg.secondary" } },
39
+ _light: {
40
+ background: "linear-gradient(token(colors.bg.controlHover), token(colors.bg.controlHover)), token(colors.bg.check)",
41
+ },
35
42
  },
36
- "&:is([data-checked], [data-indeterminate]):is(label:hover > *, label:hover *, :hover):not([data-disabled])": {
37
- background: "bg.primaryHover",
38
- borderColor: "transparent",
43
+ "&[data-checked], &[data-indeterminate]": {
44
+ background: "token(gradients.primary)",
45
+ _light: { background: "token(gradients.primary)" },
46
+ _before: { background: "token(gradients.border.primary)" },
39
47
  },
48
+ "&:is([data-checked], [data-indeterminate]):is(label:hover > *, label:hover *, :hover):not([data-disabled])": { opacity: "checkedHover", _light: { opacity: "1" } },
40
49
  "&[data-disabled]": { opacity: "disabled", cursor: "not-allowed" },
41
50
  },
42
51
  variants: {
@@ -58,13 +67,25 @@ const label = css({
58
67
  fontWeight: "normal",
59
68
  cursor: "pointer",
60
69
  });
70
+ /** What a check mark's element carries, so its filled face can draw the gradient edge. */
71
+ export const checkMarkEdge = { "data-ciridae-ui": "", "data-edge": "gradient" };
61
72
  export const checkIndicator = css({
62
73
  display: "inline-flex",
63
74
  lineHeight: "1",
64
- fontSize: "icon.compact",
75
+ fontSize: "icon.small",
76
+ });
77
+ // A mixed checkbox's mark: a short dash rather than a minus glyph.
78
+ const mixedDash = css({
79
+ width: "indicator.swatch",
80
+ height: "border.hairline",
81
+ background: "currentColor",
65
82
  });
83
+ /** The tick, or the dash of a mixed checkbox. */
84
+ export function CheckGlyph({ mixed }) {
85
+ return mixed ? _jsx("span", { className: mixedDash }) : _jsx(CheckIcon, { weight: "bold" });
86
+ }
66
87
  export function Checkbox({ children, className, wrapperClassName, ...props }) {
67
- return (_jsxs("label", { className: cx(label, wrapperClassName), "data-ciridae-ui": "", children: [_jsx(BaseCheckbox.Root, { ...props, className: cx(checkMark(), className), children: _jsx(BaseCheckbox.Indicator, { className: checkIndicator, children: props.indeterminate ? _jsx(MinusIcon, { weight: "bold" }) : _jsx(CheckIcon, { weight: "bold" }) }) }), children] }));
88
+ return (_jsxs("label", { className: cx(label, wrapperClassName), "data-ciridae-ui": "", children: [_jsx(BaseCheckbox.Root, { ...props, ...checkMarkEdge, className: cx(checkMark(), className), children: _jsx(BaseCheckbox.Indicator, { className: checkIndicator, children: _jsx(CheckGlyph, { mixed: props.indeterminate }) }) }), children] }));
68
89
  }
69
90
  const textarea = css({
70
91
  width: "100%",
@@ -74,7 +95,7 @@ const textarea = css({
74
95
  paddingInline: "control.compactInline",
75
96
  border: "1px solid token(colors.border.control)",
76
97
  borderRadius: "control",
77
- background: "bg.field",
98
+ background: "bg.formField",
78
99
  color: "fg.primary",
79
100
  fontFamily: "sans",
80
101
  fontSize: "body",
@@ -108,7 +129,10 @@ const menuRadio = css({
108
129
  fontWeight: "medium",
109
130
  letterSpacing: "compact",
110
131
  cursor: "pointer",
111
- _hover: { background: "bg.hover" },
132
+ _hover: {
133
+ background: "bg.hover",
134
+ boxShadow: "inset 0 0 0 1px token(colors.border.menuHover)",
135
+ },
112
136
  "&:has([data-disabled])": { opacity: "disabled", cursor: "not-allowed" },
113
137
  });
114
138
  const menuRadioMeta = css({ marginInlineStart: "auto", color: "fg.muted" });
@@ -119,5 +143,5 @@ export const radioDot = css({
119
143
  background: "currentColor",
120
144
  });
121
145
  export function RadioGroup({ name, options, label: groupLabel, defaultValue, value, onValueChange, layout = "inline", className, ...props }) {
122
- return (_jsx(BaseRadioGroup, { ...props, className: cx(layout === "menu" ? menuRadios : radios, className), "aria-label": props["aria-label"] ?? groupLabel, "data-ciridae-ui": "", defaultValue: defaultValue, name: name, onValueChange: (nextValue) => onValueChange?.(String(nextValue)), value: value, children: options.map((option) => (_jsxs("label", { className: layout === "menu" ? menuRadio : label, children: [_jsx(BaseRadio.Root, { className: checkMark({ shape: "round" }), disabled: option.disabled, value: option.value, children: _jsx(BaseRadio.Indicator, { className: radioDot }) }), option.label, option.meta !== undefined && _jsx("span", { className: menuRadioMeta, children: option.meta })] }, option.value))) }));
146
+ return (_jsx(BaseRadioGroup, { ...props, className: cx(layout === "menu" ? menuRadios : radios, className), "aria-label": props["aria-label"] ?? groupLabel, "data-ciridae-ui": "", defaultValue: defaultValue, name: name, onValueChange: (nextValue) => onValueChange?.(String(nextValue)), value: value, children: options.map((option) => (_jsxs("label", { className: layout === "menu" ? menuRadio : label, children: [_jsx(BaseRadio.Root, { ...checkMarkEdge, className: checkMark({ shape: "round" }), disabled: option.disabled, value: option.value, children: _jsx(BaseRadio.Indicator, { className: radioDot }) }), option.label, option.meta !== undefined && _jsx("span", { className: menuRadioMeta, children: option.meta })] }, option.value))) }));
123
147
  }
@@ -4,29 +4,33 @@ import type { ComponentProps, ReactNode } from "react";
4
4
  * Stacked form fields. Validation is the form library's, never the browser's, so this always
5
5
  * carries `noValidate`.
6
6
  */
7
- export declare function Form({ className, ...props }: ComponentProps<"form">): import("@types/react").JSX.Element;
8
- export declare function FormActions({ className, ...props }: ComponentProps<"div">): import("@types/react").JSX.Element;
7
+ export declare function Form({ className, ...props }: ComponentProps<"form">): import("react").JSX.Element;
8
+ export declare function FormActions({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
9
+ /** Fields in one row of a `Form`, sharing its width equally. */
10
+ export declare function FieldRow({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
9
11
  /**
10
12
  * A label round a `FieldLabel`, its control and any `FieldError`: the label names the control it
11
13
  * holds, and while `invalid` the control is marked and described by the error, so `Input`,
12
- * `Textarea`, `Select` and `Combobox` inside need no ids of their own.
14
+ * `Textarea`, `Select` and `Combobox` inside need no ids of their own. A `hint` sits under the
15
+ * control and describes it until the error takes its place.
13
16
  */
14
- export declare function Field({ className, htmlFor, invalid, ...props }: ComponentProps<"label"> & {
17
+ export declare function Field({ className, htmlFor, invalid, hint, children, ...props }: ComponentProps<"label"> & {
15
18
  invalid?: boolean;
16
- }): import("@types/react").JSX.Element;
19
+ hint?: ReactNode;
20
+ }): import("react").JSX.Element;
17
21
  export declare function FieldLabel({ children }: {
18
22
  children: ReactNode;
19
- }): import("@types/react").JSX.Element;
23
+ }): import("react").JSX.Element;
20
24
  /** The field's error; its id is the one the field's control is described by. */
21
25
  export declare function FieldError({ id, children }: {
22
26
  id?: string;
23
27
  children: ReactNode;
24
- }): import("@types/react").JSX.Element;
25
- export declare function SegmentedControl({ className, ...props }: ComponentProps<"div">): import("@types/react").JSX.Element;
28
+ }): import("react").JSX.Element;
29
+ export declare function SegmentedControl({ className, ...props }: ComponentProps<"div">): import("react").JSX.Element;
26
30
  export type SegmentedOptionProps = Omit<ButtonProps, "variant" | "type" | "aria-pressed"> & {
27
31
  selected: boolean;
28
32
  /** Shown muted after the label, such as how many items the option lists. */
29
33
  count?: number;
30
34
  };
31
- /** One option of a `SegmentedControl`. */
32
- export declare function SegmentedOption({ selected, count, children, ...props }: SegmentedOptionProps): import("@types/react").JSX.Element;
35
+ /** One option of a `SegmentedControl`: the selected one is raised, the others bare. */
36
+ export declare function SegmentedOption({ selected, count, children, ...props }: SegmentedOptionProps): import("react").JSX.Element;
@@ -3,31 +3,65 @@ import { useContext, useId, useMemo } from "react";
3
3
  import { css, cx } from "#primitives/css";
4
4
  import { Button } from "./button.js";
5
5
  import { FieldControlContext } from "./field-context.js";
6
+ import { faceEdge } from "./gradient-edge.js";
6
7
  import { useInToolbar } from "./layout.js";
7
8
  const field = css({ display: "grid", gap: "control.iconGap", minWidth: "0", fontSize: "caption" });
8
- const labelText = css({ color: "fg.muted" });
9
- const errorText = css({ margin: "none", color: "accent.red", fontSize: "caption" });
9
+ // A single line, its box the type's own 12px, so the gap to the control is the field's 6px alone.
10
+ const labelText = css({
11
+ color: "fg.primary",
12
+ fontSize: "code",
13
+ fontWeight: "medium",
14
+ lineHeight: "tight",
15
+ letterSpacing: "compact",
16
+ });
17
+ // Help and error lines under the control, which can wrap.
18
+ const noteText = css({
19
+ margin: "none",
20
+ fontSize: "code",
21
+ fontWeight: "medium",
22
+ lineHeight: "body",
23
+ letterSpacing: "compact",
24
+ });
25
+ const hintText = css({ color: "fg.muted" });
26
+ const errorText = css({ color: "accent.red" });
27
+ /*
28
+ * A grey well holding its options, the selected one raised on the secondary button's face. The
29
+ * track is a default button's height, or in a toolbar a small one's on a thinner inset, and each
30
+ * option clears it by the inset and a hairline.
31
+ */
10
32
  const segmented = css({
33
+ "--track-inset": "token(spacing.layout.tightGap)",
34
+ "--track-height": "token(sizes.control.default)",
11
35
  display: "inline-flex",
12
36
  alignSelf: "flex-start",
13
- padding: "control.trackInset",
14
- border: "1px solid token(colors.border.control)",
15
- borderRadius: "pill",
16
- // Children follow the track's own shape, so the selected pill nests inside it. Ghost text is
17
- // white in dark, so the track mutes every option and lets only the selected one, on the accent
18
- // wash, read fully.
19
- "& > *": { borderColor: "transparent!", borderRadius: "pill", color: "fg.muted!" },
20
- "& > [aria-pressed=true]": {
21
- background: "bg.selected",
22
- color: "fg.primary!",
37
+ alignItems: "center",
38
+ gap: "control.trackInset",
39
+ height: "var(--track-height)",
40
+ padding: "var(--track-inset)",
41
+ borderRadius: "inset",
42
+ // The well grey in light; in dark the panel's, where the field's white wash would read as glass.
43
+ background: "bg.panel",
44
+ _light: { background: "bg.field" },
45
+ "&[data-density=compact]": {
46
+ "--track-inset": "token(spacing.control.trackInset)",
47
+ "--track-height": "token(sizes.control.compact)",
23
48
  },
24
- // In a toolbar the whole track, not each option, is a small button's height.
25
- "&[data-density=compact] > *": {
26
- height: "calc(token(sizes.control.compact) - 2 * (token(spacing.control.trackInset) + token(sizes.border.hairline)))",
49
+ "& > *": {
50
+ height: "calc(var(--track-height) - 2 * (var(--track-inset) + token(sizes.border.hairline)))!",
51
+ borderRadius: "action!",
27
52
  },
53
+ "& > [aria-pressed=false]:not(:hover)": { color: "fg.muted" },
54
+ "& > [aria-pressed=true]": { boxShadow: "soft" },
28
55
  });
29
56
  const countText = css({ color: "fg.muted", fontVariantNumeric: "tabular-nums" });
30
57
  const formStack = css({ display: "grid", gap: "layout.clusterGap", minWidth: "0" });
58
+ // Fields side by side sit closer than stacked ones.
59
+ const fieldRow = css({
60
+ display: "grid",
61
+ gridAutoFlow: "column",
62
+ gridAutoColumns: "minmax(0, 1fr)",
63
+ gap: "layout.contentGap",
64
+ });
31
65
  const formActions = css({
32
66
  display: "flex",
33
67
  justifyContent: "flex-end",
@@ -43,16 +77,26 @@ export function Form({ className, ...props }) {
43
77
  export function FormActions({ className, ...props }) {
44
78
  return _jsx("div", { ...props, className: cx(formActions, className), "data-ciridae-ui": "" });
45
79
  }
80
+ /** Fields in one row of a `Form`, sharing its width equally. */
81
+ export function FieldRow({ className, ...props }) {
82
+ return _jsx("div", { ...props, className: cx(fieldRow, className), "data-ciridae-ui": "" });
83
+ }
46
84
  /**
47
85
  * A label round a `FieldLabel`, its control and any `FieldError`: the label names the control it
48
86
  * holds, and while `invalid` the control is marked and described by the error, so `Input`,
49
- * `Textarea`, `Select` and `Combobox` inside need no ids of their own.
87
+ * `Textarea`, `Select` and `Combobox` inside need no ids of their own. A `hint` sits under the
88
+ * control and describes it until the error takes its place.
50
89
  */
51
- export function Field({ className, htmlFor, invalid = false, ...props }) {
90
+ export function Field({ className, htmlFor, invalid = false, hint, children, ...props }) {
52
91
  const generatedId = useId();
53
92
  const controlId = htmlFor ?? `${generatedId}-control`;
54
- const fieldControl = useMemo(() => ({ controlId, descriptionId: invalid ? `${controlId}-error` : undefined, invalid }), [controlId, invalid]);
55
- return (_jsx(FieldControlContext, { value: fieldControl, children: _jsx("label", { ...props, htmlFor: htmlFor, className: cx(field, className), "data-ciridae-ui": "" }) }));
93
+ const showsHint = hint !== undefined && !invalid;
94
+ const fieldControl = useMemo(() => ({
95
+ controlId,
96
+ descriptionId: invalid ? `${controlId}-error` : showsHint ? `${controlId}-hint` : undefined,
97
+ invalid,
98
+ }), [controlId, invalid, showsHint]);
99
+ return (_jsx(FieldControlContext, { value: fieldControl, children: _jsxs("label", { ...props, htmlFor: htmlFor, className: cx(field, className), "data-ciridae-ui": "", children: [children, showsHint && (_jsx("span", { id: `${controlId}-hint`, className: cx(noteText, hintText), children: hint }))] }) }));
56
100
  }
57
101
  export function FieldLabel({ children }) {
58
102
  return _jsx("span", { className: labelText, children: children });
@@ -60,13 +104,14 @@ export function FieldLabel({ children }) {
60
104
  /** The field's error; its id is the one the field's control is described by. */
61
105
  export function FieldError({ id, children }) {
62
106
  const field = useContext(FieldControlContext);
63
- return (_jsx("p", { id: id ?? (field ? `${field.controlId}-error` : undefined), role: "alert", className: errorText, children: children }));
107
+ return (_jsx("p", { id: id ?? (field ? `${field.controlId}-error` : undefined), role: "alert", className: cx(noteText, errorText), children: children }));
64
108
  }
65
109
  export function SegmentedControl({ className, ...props }) {
66
110
  const inToolbar = useInToolbar();
67
- return (_jsx("div", { ...props, className: cx(segmented, className), "data-density": inToolbar ? "compact" : undefined, "data-ciridae-ui": "" }));
111
+ const edge = faceEdge("raised");
112
+ return (_jsx("div", { ...props, className: cx(segmented, edge.className, className), "data-density": inToolbar ? "compact" : undefined, "data-ciridae-ui": "", "data-edge": edge["data-edge"] }));
68
113
  }
69
- /** One option of a `SegmentedControl`. */
114
+ /** One option of a `SegmentedControl`: the selected one is raised, the others bare. */
70
115
  export function SegmentedOption({ selected, count, children, ...props }) {
71
- return (_jsxs(Button, { ...props, type: "button", variant: "ghost", "aria-pressed": selected, children: [children, count !== undefined && _jsx("span", { className: countText, children: count })] }));
116
+ return (_jsxs(Button, { ...props, type: "button", variant: selected ? "secondary" : "ghost", "aria-pressed": selected, children: [children, count !== undefined && _jsx("span", { className: countText, children: count })] }));
72
117
  }
@@ -0,0 +1,3 @@
1
+ export declare const glassMenuSurface: import("../styled-system/types/index.js").SystemStyleObject;
2
+ export declare const glassMenuRow: import("../styled-system/types/index.js").SystemStyleObject;
3
+ export declare const glassMenuDivider: import("../styled-system/types/index.js").SystemStyleObject;
@@ -0,0 +1,63 @@
1
+ import { css } from "#primitives/css";
2
+ /*
3
+ * The glass sheet every menu, popover and select list floats on, and the rows inside it, as raw
4
+ * styles for `css()` to merge with each popup's own: an 8-round sheet padded 4px all round that
5
+ * blurs what lies under it, holding 4-round rows 32px tall.
6
+ */
7
+ export const glassMenuSurface = css.raw({
8
+ padding: "menu.inset",
9
+ border: "1px solid token(colors.border.control)",
10
+ borderRadius: "action",
11
+ background: "bg.popover",
12
+ backdropFilter: "blur(token(blurs.popover))",
13
+ boxShadow: "soft",
14
+ color: "fg.primary",
15
+ textStyle: "body",
16
+ // The popup takes focus as it opens; it is a container, not a control, so it draws no ring.
17
+ outline: "none",
18
+ });
19
+ /*
20
+ * A row: hovered, it takes a wash inside the menu hover hairline, the raised glass in dark and the
21
+ * row's hover wash in light. A row that is a plain button has no highlight state, so it takes the
22
+ * wash on hover. Reached by the keyboard, away from the pointer, it draws the focus ring inside its
23
+ * own edge in place of the wash and the hairline.
24
+ */
25
+ export const glassMenuRow = css.raw({
26
+ display: "flex",
27
+ alignItems: "center",
28
+ gap: "layout.controlGap",
29
+ minHeight: "control.compact",
30
+ paddingInline: "menu.itemInline",
31
+ borderRadius: "control",
32
+ color: "fg.primary",
33
+ fontSize: "body",
34
+ fontWeight: "medium",
35
+ lineHeight: "tight",
36
+ letterSpacing: "compact",
37
+ cursor: "pointer",
38
+ outline: "none",
39
+ "&[data-highlighted], &:is(button):hover": {
40
+ background: "token(gradients.raised)",
41
+ boxShadow: "inset 0 0 0 1px token(colors.border.menuHover)",
42
+ _light: { background: "bg.rowHover" },
43
+ },
44
+ // Under the pointer the row keeps the wash, so the ring marks only the row the keys reached.
45
+ "&:focus-visible:not(:hover), &[data-highlighted]:focus-visible:not(:hover)": {
46
+ focusRing: "inset",
47
+ background: "transparent",
48
+ boxShadow: "none",
49
+ _light: { background: "transparent" },
50
+ },
51
+ "&[data-disabled]": { opacity: "disabled", cursor: "not-allowed" },
52
+ });
53
+ /*
54
+ * The rule between groups of rows, inset 8px from the sheet's sides with 4px above and below: a
55
+ * hairline along the top of a 2px band, as the design draws it.
56
+ */
57
+ export const glassMenuDivider = css.raw({
58
+ flexShrink: "0",
59
+ height: "calc(token(sizes.border.hairline) * 2)",
60
+ marginBlock: "layout.tightGap",
61
+ marginInline: "menu.itemInline",
62
+ borderTop: "1px solid token(colors.border.divider)",
63
+ });
@@ -0,0 +1,9 @@
1
+ type GradientEdge = "raised" | "primary" | "danger" | "tinted" | "toolbar";
2
+ /** A face's edge: a flat hairline ciridae.css paints by name, or one of the gradient edges. */
3
+ export type FaceEdge = "glass" | "control" | GradientEdge;
4
+ /** The class and `data-edge` that draw `edge`; nothing for a face with no edge. */
5
+ export declare function faceEdge(edge: FaceEdge | undefined): {
6
+ className: string | undefined;
7
+ "data-edge": string | undefined;
8
+ };
9
+ export {};
@@ -0,0 +1,31 @@
1
+ import { cva } from "#primitives/css";
2
+ /*
3
+ * The 1px edge a face draws as a gradient. A gradient cannot be a border color, so it paints the
4
+ * `[data-edge]` ring ciridae.css draws inside the element's box, which also keeps its corners:
5
+ * `faceEdge` gives the class that paints the ring and the `data-edge` that turns it on. Dark draws
6
+ * no edge on the primary or destructive face. A toolbar control's edge is the selected face's: the
7
+ * raised gradient in dark and a paler grey gradient in light.
8
+ */
9
+ const gradientEdge = cva({
10
+ base: {},
11
+ variants: {
12
+ edge: {
13
+ raised: { _before: { background: "token(gradients.border.raised)" } },
14
+ primary: { _before: { background: "token(gradients.border.primary)" } },
15
+ danger: { _before: { background: "token(gradients.border.danger)" } },
16
+ tinted: { _before: { background: "token(gradients.border.tinted)" } },
17
+ toolbar: { _before: { background: "token(gradients.border.selected)" } },
18
+ },
19
+ },
20
+ });
21
+ function isGradientEdge(edge) {
22
+ return edge !== "glass" && edge !== "control";
23
+ }
24
+ /** The class and `data-edge` that draw `edge`; nothing for a face with no edge. */
25
+ export function faceEdge(edge) {
26
+ if (edge === undefined)
27
+ return { className: undefined, "data-edge": undefined };
28
+ if (isGradientEdge(edge))
29
+ return { className: gradientEdge({ edge }), "data-edge": "gradient" };
30
+ return { className: undefined, "data-edge": edge };
31
+ }
@@ -7,7 +7,7 @@ export declare const PAGE_HOTKEY_SCOPE = "page";
7
7
  /** Gives the hotkeys registered inside it to the page, paused while an overlay is open. */
8
8
  export declare function PageHotkeys({ children }: {
9
9
  children: ReactNode;
10
- }): import("@types/react").JSX.Element;
10
+ }): import("react").JSX.Element;
11
11
  /**
12
12
  * An overlay's keyboard: while mounted (and `enabled`), only the hotkeys registered inside it and
13
13
  * the global ones fire, so the top overlay owns the keyboard and the page under it pauses.
@@ -15,7 +15,7 @@ export declare function PageHotkeys({ children }: {
15
15
  export declare function HotkeyLayer({ enabled, children }: {
16
16
  enabled?: boolean;
17
17
  children: ReactNode;
18
- }): import("@types/react").JSX.Element;
18
+ }): import("react").JSX.Element;
19
19
  /**
20
20
  * The person's rebound hotkeys, kept in `localStorage` as JSON under `storageKey`, as the root
21
21
  * `HotkeysProvider`'s `customHotkeys` and `onCustomHotkeysChange`. Entries that are no hotkey are
@@ -55,4 +55,4 @@ export type SpinnerProps = Omit<IconProps, "size"> & {
55
55
  label?: string;
56
56
  };
57
57
  /** The one in-progress glyph: every pending state spins this, never a refresh or notch icon. */
58
- export declare function Spinner({ size, label, className, ...props }: SpinnerProps): import("@types/react").JSX.Element;
58
+ export declare function Spinner({ size, label, className, ...props }: SpinnerProps): import("react").JSX.Element;
@@ -1,11 +1,11 @@
1
1
  import { type ComponentProps, type MouseEvent } from "react";
2
2
  import type { Hotkey } from "./hotkeys.js";
3
3
  export type InputProps = Omit<ComponentProps<"input">, "size"> & {
4
- size?: "small" | "medium" | "large";
4
+ size?: "inline" | "small" | "medium" | "large";
5
5
  };
6
- export declare function Input({ className, size, ...props }: InputProps): import("@types/react").JSX.Element;
6
+ export declare function Input({ className, size, ...props }: InputProps): import("react").JSX.Element;
7
7
  export type SearchInputProps = Omit<ComponentProps<"input">, "size" | "value" | "defaultValue"> & {
8
- variant?: "regular" | "flat" | "sidebar" | "dropdown";
8
+ variant?: "regular" | "flat" | "sidebar" | "ghost";
9
9
  /** Defaults to `compact` in a `Toolbar`, `regular` elsewhere. */
10
10
  size?: "regular" | "compact";
11
11
  /** `fill` takes the row's width; `field` grows from a name's width to a search field's. */
@@ -23,7 +23,7 @@ export type SearchInputProps = Omit<ComponentProps<"input">, "size" | "value" |
23
23
  shortcut?: Hotkey;
24
24
  wrapperClassName?: string;
25
25
  };
26
- export declare function SearchInput({ variant, size: ownSize, width, value, defaultValue, onValueCommit, debounceMs, shortcut, wrapperClassName, className, onChange, onKeyDown, ...props }: SearchInputProps): import("@types/react").JSX.Element;
26
+ export declare function SearchInput({ variant, size: ownSize, width, value, defaultValue, onValueCommit, debounceMs, shortcut, wrapperClassName, className, onChange, onKeyDown, ...props }: SearchInputProps): import("react").JSX.Element;
27
27
  export type SearchFieldProps = Omit<ComponentProps<"input">, "size" | "type"> & {
28
28
  /** Defaults to `compact` in a `Toolbar`, `regular` elsewhere. */
29
29
  size?: "regular" | "compact";
@@ -31,6 +31,8 @@ export type SearchFieldProps = Omit<ComponentProps<"input">, "size" | "type"> &
31
31
  onClear?: (event: MouseEvent<HTMLButtonElement>) => void;
32
32
  /** Shows this hotkey's hint while the field is empty, such as "/"; the host binds it. */
33
33
  shortcut?: Hotkey;
34
+ /** In a menu or popover: the hairline alone, as the sheet's divider, with no hover or ring. */
35
+ quiet?: boolean;
34
36
  wrapperClassName?: string;
35
37
  };
36
- export declare function SearchField({ size: ownSize, onClear, shortcut, wrapperClassName, className, disabled, ...props }: SearchFieldProps): import("@types/react").JSX.Element;
38
+ export declare function SearchField({ size: ownSize, onClear, quiet, shortcut, wrapperClassName, className, disabled, ...props }: SearchFieldProps): import("react").JSX.Element;
@@ -16,7 +16,7 @@ import { useCiridaeLocale } from "./locale.js";
16
16
  const fieldFace = {
17
17
  border: "1px solid token(colors.border.control)",
18
18
  borderRadius: "control",
19
- background: "bg.field",
19
+ background: "bg.formField",
20
20
  color: "fg.primary",
21
21
  fontFamily: "sans",
22
22
  fontWeight: "medium",
@@ -34,13 +34,19 @@ const field = cva({
34
34
  base: { ...fieldFace, width: "100%", lineHeight: "tight" },
35
35
  variants: {
36
36
  size: {
37
- /** Matches the small button, for forms and filters. */
37
+ /** The editor an inline-edit row opens in place of its value. */
38
+ inline: {
39
+ height: "field.inline",
40
+ paddingInline: "menu.itemInline",
41
+ fontSize: "body",
42
+ },
43
+ /** For a row of filters, beside compact selects and small buttons. */
38
44
  small: {
39
45
  height: "control.compact",
40
46
  paddingInline: "control.compactInline",
41
47
  fontSize: "caption",
42
48
  },
43
- /** The Round 2 field: the height of the default button. */
49
+ /** The field: the height of the default button. */
44
50
  medium: {
45
51
  height: "control.default",
46
52
  paddingInline: "control.compactInline",
@@ -55,9 +61,9 @@ const field = cva({
55
61
  },
56
62
  },
57
63
  },
58
- defaultVariants: { size: "small" },
64
+ defaultVariants: { size: "medium" },
59
65
  });
60
- export function Input({ className, size = "small", ...props }) {
66
+ export function Input({ className, size = "medium", ...props }) {
61
67
  const fieldIds = useFieldControl(props);
62
68
  return (_jsx(BaseInput, { ...props, ...fieldIds, className: cx(field({ size }), className), "data-ciridae-ui": "" }));
63
69
  }
@@ -89,7 +95,8 @@ const search = cva({
89
95
  border: "1px solid token(colors.border.control)",
90
96
  background: "bg.field",
91
97
  },
92
- dropdown: {
98
+ // No face: the field reads as text until focused, beside ghost buttons in a toolbar.
99
+ ghost: {
93
100
  borderRadius: "action",
94
101
  paddingInline: "control.compactInline",
95
102
  border: "0",
@@ -142,7 +149,7 @@ const searchText = cva({
142
149
  regular: {},
143
150
  sidebar: {},
144
151
  flat: {},
145
- dropdown: { fontSize: "caption", fontWeight: "regular" },
152
+ ghost: { fontSize: "caption", fontWeight: "regular" },
146
153
  },
147
154
  size: { regular: {}, compact: { fontSize: "caption", fontWeight: "regular" } },
148
155
  },
@@ -178,12 +185,13 @@ export function SearchInput({ variant = "regular", size: ownSize, width = "fill"
178
185
  onValueCommit(event.currentTarget.value);
179
186
  }
180
187
  onKeyDown?.(event);
181
- }, className: cx(searchText({ variant, size }), className) }), shortcut && _jsx(ShortcutHint, { hotkey: shortcut })] }));
188
+ }, className: cx(searchText({ variant, size }), className) }), shortcut && _jsx(ShortcutHint, { boxed: true, hotkey: shortcut })] }));
182
189
  }
183
190
  /*
184
191
  * The Round 2 search field is an underline, not a box: an icon, the text, and a clear button on
185
- * one hairline. Focus turns that hairline into a 2px accent line, as the frames draw it, rather
186
- * than boxing a field that has no box.
192
+ * one hairline. Hovered it takes the hover wash, and focused the ring, both on its own nearly
193
+ * square corners. Quiet, in a menu or popover, the hairline is the sheet's divider and nothing
194
+ * else changes: the popup opens with the field focused, and its caret shows that.
187
195
  */
188
196
  const underline = cva({
189
197
  base: {
@@ -194,10 +202,6 @@ const underline = cva({
194
202
  paddingInline: "search.inline",
195
203
  borderBottom: "1px solid token(colors.border.control)",
196
204
  color: "fg.muted",
197
- "&:has(:focus-visible)": {
198
- borderBottomColor: "border.focus",
199
- boxShadow: "inset 0 -1px 0 0 token(colors.border.focus)",
200
- },
201
205
  "&:has(:disabled)": { opacity: "disabled" },
202
206
  },
203
207
  variants: {
@@ -205,8 +209,16 @@ const underline = cva({
205
209
  regular: { height: "control.default" },
206
210
  compact: { height: "control.compact" },
207
211
  },
212
+ quiet: {
213
+ false: {
214
+ borderRadius: "control",
215
+ _hover: { background: "bg.controlHover" },
216
+ "&:has(:focus-visible)": { focusRing: "outer" },
217
+ },
218
+ true: { paddingInline: "none", borderBottomColor: "border.divider" },
219
+ },
208
220
  },
209
- defaultVariants: { size: "regular" },
221
+ defaultVariants: { size: "regular", quiet: false },
210
222
  });
211
223
  const underlineText = css({
212
224
  flex: "1",
@@ -241,9 +253,9 @@ const clear = css({
241
253
  _hover: { color: "fg.primary", background: "bg.hover" },
242
254
  _focusVisible: { focusRing: "inset" },
243
255
  });
244
- export function SearchField({ size: ownSize, onClear, shortcut, wrapperClassName, className, disabled, ...props }) {
256
+ export function SearchField({ size: ownSize, onClear, quiet = false, shortcut, wrapperClassName, className, disabled, ...props }) {
245
257
  const inToolbar = useInToolbar();
246
258
  const { labels } = useCiridaeLocale();
247
259
  const size = ownSize ?? (inToolbar ? "compact" : "regular");
248
- return (_jsxs("label", { className: cx(underline({ size }), wrapperClassName), "data-ciridae-ui": "", children: [_jsx(MagnifyingGlassIcon, { className: icon({ size: "small" }) }), _jsx(BaseInput, { ...props, type: "search", disabled: disabled, className: cx(underlineText, className) }), onClear && (_jsx("button", { type: "button", className: clear, "aria-label": labels.common.clear, disabled: disabled, onClick: onClear, children: _jsx(XIcon, { className: icon({ size: "small" }) }) })), shortcut && !onClear && _jsx(ShortcutHint, { hotkey: shortcut })] }));
260
+ return (_jsxs("label", { className: cx(underline({ size, quiet }), wrapperClassName), "data-ciridae-ui": "", children: [_jsx(MagnifyingGlassIcon, { className: icon({ size: "small" }) }), _jsx(BaseInput, { ...props, type: "search", disabled: disabled, className: cx(underlineText, className) }), onClear && (_jsx("button", { type: "button", className: clear, "aria-label": labels.common.clear, disabled: disabled, onClick: onClear, children: _jsx(XIcon, { className: icon({ size: "small" }) }) })), shortcut && !onClear && _jsx(ShortcutHint, { boxed: true, hotkey: shortcut })] }));
249
261
  }
@@ -5,6 +5,6 @@ export type KeyboardShortcutsProps = {
5
5
  open?: boolean;
6
6
  onOpenChange?: (open: boolean) => void;
7
7
  };
8
- export declare function KeyboardShortcuts({ hotkey, open: ownOpen, onOpenChange, }: KeyboardShortcutsProps): import("@types/react").JSX.Element;
8
+ export declare function KeyboardShortcuts({ hotkey, open: ownOpen, onOpenChange, }: KeyboardShortcutsProps): import("react").JSX.Element;
9
9
  /** The app's rebindable hotkeys under their groups, each with Change and, once changed, Reset. */
10
- export declare function KeyboardShortcutSettings(): import("@types/react").JSX.Element[];
10
+ export declare function KeyboardShortcutSettings(): import("react").JSX.Element[];
@@ -1,12 +1,16 @@
1
1
  import { type Hotkey } from "./hotkeys.js";
2
2
  import type { ComponentProps } from "react";
3
- /** One key as a 24px cap, such as the `K` after a search field. */
4
- export declare function Keycap({ className, ...props }: ComponentProps<"kbd">): import("@types/react").JSX.Element;
3
+ /**
4
+ * One key as a 22px square cap, such as the `K` after a search field. A label of more than one
5
+ * character, such as `Esc`, takes the wider word cap on its own.
6
+ */
7
+ export declare function Keycap({ className, children, ...props }: ComponentProps<"kbd">): import("react").JSX.Element;
5
8
  /**
6
9
  * A hotkey as one quiet line, as the hotkeys layer writes it: `Mod` is "⌘" on a Mac and "Ctrl"
7
- * elsewhere, and a sequence's steps are spaced ("G I"). `Keycap` is the boxed key for a place
8
- * that wants it.
10
+ * elsewhere, and a sequence's steps are spaced ("G I"). `boxed` sets each key in its own
11
+ * `Keycap`, 2px apart, as a menu row or a search field shows it.
9
12
  */
10
- export declare function ShortcutHint({ hotkey, className, ...props }: ComponentProps<"kbd"> & {
13
+ export declare function ShortcutHint({ hotkey, boxed, className, ...props }: ComponentProps<"kbd"> & {
11
14
  hotkey: Hotkey;
12
- }): import("@types/react").JSX.Element;
15
+ boxed?: boolean;
16
+ }): import("react").JSX.Element;