@ciridae/ui 1.0.3 → 1.0.5

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 (124) hide show
  1. package/dist/chat/conversation-source.d.ts +15 -1
  2. package/dist/chat/describe-tool-call.d.ts +3 -3
  3. package/dist/chat/describe-tool-call.js +3 -3
  4. package/dist/chat/message-text.js +2 -2
  5. package/dist/ciridae.css +16 -4
  6. package/dist/components/activity-feed.d.ts +1 -0
  7. package/dist/components/activity-feed.js +1 -0
  8. package/dist/components/app-frame.d.ts +13 -1
  9. package/dist/components/app-frame.js +45 -3
  10. package/dist/components/app-navigation.d.ts +28 -1
  11. package/dist/components/app-navigation.js +110 -14
  12. package/dist/components/badge.d.ts +29 -4
  13. package/dist/components/badge.js +134 -15
  14. package/dist/components/bulk-action-bar.js +3 -3
  15. package/dist/components/button.d.ts +10 -3
  16. package/dist/components/button.js +77 -44
  17. package/dist/components/calendar.js +44 -17
  18. package/dist/components/centered-page.js +2 -2
  19. package/dist/components/chart.d.ts +1 -1
  20. package/dist/components/chart.js +16 -11
  21. package/dist/components/chat-client-source.js +47 -6
  22. package/dist/components/chat-sheet-controller.d.ts +1 -0
  23. package/dist/components/chat-sheet-controller.js +1 -0
  24. package/dist/components/chat-sheet.d.ts +10 -2
  25. package/dist/components/chat-sheet.js +74 -37
  26. package/dist/components/chat.d.ts +90 -14
  27. package/dist/components/chat.js +401 -101
  28. package/dist/components/checkbox-item.d.ts +5 -2
  29. package/dist/components/checkbox-item.js +9 -5
  30. package/dist/components/chip.d.ts +5 -0
  31. package/dist/components/chip.js +27 -1
  32. package/dist/components/column-settings.js +1 -1
  33. package/dist/components/command-menu.d.ts +5 -1
  34. package/dist/components/command-menu.js +98 -32
  35. package/dist/components/command-palette.d.ts +6 -2
  36. package/dist/components/command-palette.js +20 -10
  37. package/dist/components/data-display.d.ts +35 -4
  38. package/dist/components/data-display.js +138 -19
  39. package/dist/components/data-grid.d.ts +14 -2
  40. package/dist/components/data-grid.js +61 -26
  41. package/dist/components/date-range-picker.js +9 -20
  42. package/dist/components/drawer.d.ts +5 -0
  43. package/dist/components/drawer.js +91 -54
  44. package/dist/components/editable-field.d.ts +17 -3
  45. package/dist/components/editable-field.js +57 -27
  46. package/dist/components/email-input.js +6 -5
  47. package/dist/components/environment-frame.d.ts +1 -1
  48. package/dist/components/environment-frame.js +37 -6
  49. package/dist/components/filter-bar.js +39 -28
  50. package/dist/components/form-controls.d.ts +9 -0
  51. package/dist/components/form-controls.js +46 -22
  52. package/dist/components/form-layout.d.ts +7 -3
  53. package/dist/components/form-layout.js +68 -23
  54. package/dist/components/glass-menu.d.ts +3 -0
  55. package/dist/components/glass-menu.js +63 -0
  56. package/dist/components/gradient-edge.d.ts +9 -0
  57. package/dist/components/gradient-edge.js +31 -0
  58. package/dist/components/input.d.ts +4 -2
  59. package/dist/components/input.js +26 -15
  60. package/dist/components/keycap.d.ts +9 -5
  61. package/dist/components/keycap.js +57 -19
  62. package/dist/components/lightbox.js +2 -1
  63. package/dist/components/locale.d.ts +10 -0
  64. package/dist/components/locale.js +18 -2
  65. package/dist/components/log-entry.d.ts +40 -0
  66. package/dist/components/log-entry.js +146 -0
  67. package/dist/components/mention-input.js +4 -3
  68. package/dist/components/menu.d.ts +6 -2
  69. package/dist/components/menu.js +34 -37
  70. package/dist/components/meter.d.ts +9 -1
  71. package/dist/components/meter.js +64 -6
  72. package/dist/components/navigation.d.ts +21 -4
  73. package/dist/components/navigation.js +84 -19
  74. package/dist/components/notification-tray.js +5 -4
  75. package/dist/components/overlays.d.ts +3 -1
  76. package/dist/components/overlays.js +69 -40
  77. package/dist/components/page-frame.js +1 -1
  78. package/dist/components/panel.d.ts +1 -1
  79. package/dist/components/panel.js +18 -12
  80. package/dist/components/radio-card.js +10 -9
  81. package/dist/components/record.d.ts +25 -1
  82. package/dist/components/record.js +147 -18
  83. package/dist/components/segmented-bar.d.ts +3 -1
  84. package/dist/components/segmented-bar.js +16 -6
  85. package/dist/components/select.d.ts +5 -2
  86. package/dist/components/select.js +103 -127
  87. package/dist/components/shell-skeleton.js +1 -1
  88. package/dist/components/shell.d.ts +2 -2
  89. package/dist/components/shell.js +43 -45
  90. package/dist/components/sidebar.d.ts +7 -2
  91. package/dist/components/sidebar.js +55 -27
  92. package/dist/components/single-line-text.d.ts +2 -0
  93. package/dist/components/single-line-text.js +16 -0
  94. package/dist/components/site-banner.d.ts +5 -2
  95. package/dist/components/site-banner.js +17 -6
  96. package/dist/components/skeleton.d.ts +7 -6
  97. package/dist/components/skeleton.js +28 -16
  98. package/dist/components/skeletons.d.ts +12 -0
  99. package/dist/components/skeletons.js +71 -8
  100. package/dist/components/stages.d.ts +78 -6
  101. package/dist/components/stages.js +512 -156
  102. package/dist/components/stat-card.d.ts +10 -2
  103. package/dist/components/stat-card.js +92 -37
  104. package/dist/components/status-banner.js +1 -1
  105. package/dist/components/tabs.js +26 -13
  106. package/dist/components/theme-toggle.d.ts +4 -0
  107. package/dist/components/theme-toggle.js +67 -0
  108. package/dist/components/toast.d.ts +9 -2
  109. package/dist/components/toast.js +78 -29
  110. package/dist/preset.js +291 -33
  111. package/dist/styled-system/styles.css +3107 -1039
  112. package/dist/styled-system/tokens/index.mjs +602 -22
  113. package/dist/styled-system/tokens/tokens.d.ts +11 -11
  114. package/dist/styled-system/types/prop-type.d.ts +2 -2
  115. package/dist/styles.css +16 -4
  116. package/dist/theme/color.d.ts +3 -0
  117. package/dist/theme/color.js +5 -0
  118. package/dist/theme/derive.d.ts +9 -2
  119. package/dist/theme/derive.js +72 -12
  120. package/dist/theme/palettes.d.ts +49 -6
  121. package/dist/theme/palettes.js +84 -15
  122. package/dist/theme/tokens.d.ts +3 -3
  123. package/dist/theme/tokens.js +108 -32
  124. package/package.json +9 -1
@@ -4,8 +4,11 @@ export type CheckboxItemProps = Omit<ComponentProps<typeof BaseCheckbox.Root>, "
4
4
  children: ReactNode;
5
5
  /** Shown muted at the row's end, such as where the item comes from. */
6
6
  meta?: ReactNode;
7
- /** Shows a drag handle on hover, for a row that can be reordered. */
8
- grip?: boolean;
7
+ /**
8
+ * Shows a drag handle on hover, for a row that can be reordered; `locked` keeps the handle's
9
+ * room empty, for a row that stays put among ones that move.
10
+ */
11
+ grip?: boolean | "locked";
9
12
  className?: string;
10
13
  };
11
14
  export declare function CheckboxItem({ children, meta, grip: showGrip, className, ...props }: CheckboxItemProps): import("react").JSX.Element;
@@ -1,8 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Checkbox as BaseCheckbox } from "@base-ui/react/checkbox";
3
- import { CheckIcon, DotsSixVerticalIcon, MinusIcon } from "@phosphor-icons/react";
3
+ import { DotsSixVerticalIcon } from "@phosphor-icons/react";
4
4
  import { css, cx } from "#primitives/css";
5
- import { checkIndicator, checkMark } from "./form-controls.js";
5
+ import { CheckGlyph, checkIndicator, checkMark, checkMarkEdge } from "./form-controls.js";
6
6
  import { icon } from "./icon.js";
7
7
  /*
8
8
  * A checkbox as a whole row: the mark, its label, and what else the row has to say, with the hover
@@ -23,8 +23,10 @@ const row = css({
23
23
  cursor: "pointer",
24
24
  transitionProperty: "background",
25
25
  transitionDuration: "fast",
26
- _hover: { background: "bg.glass" },
27
- "&:has(:focus-visible)": { focusRing: "outer" },
26
+ // The selected tint in dark and the row wash in light, as a menu row hovers; focus rings the row
27
+ // inside its own edge.
28
+ _hover: { background: "token(gradients.selected)", _light: { background: "bg.rowHover" } },
29
+ "&:has(:focus-visible)": { focusRing: "inset" },
28
30
  "&:has([data-disabled])": { opacity: "disabled", cursor: "not-allowed" },
29
31
  });
30
32
  // The grip only shows once the row is pointed at, so a list of rows is not a column of grips.
@@ -37,9 +39,11 @@ const grip = css({
37
39
  transitionProperty: "opacity",
38
40
  transitionDuration: "fast",
39
41
  "label:hover > &, label:focus-within > &": { opacity: "1" },
42
+ // A locked row keeps the grip's room, so its label lines up with the rows that move.
43
+ "&[data-locked]": { visibility: "hidden" },
40
44
  });
41
45
  const labelText = css({ flex: "1", minWidth: "0" });
42
46
  const metaText = css({ flexShrink: "0", color: "fg.subtle", fontSize: "code" });
43
47
  export function CheckboxItem({ children, meta, grip: showGrip, className, ...props }) {
44
- return (_jsxs("label", { className: cx(row, className), "data-ciridae-ui": "", children: [showGrip && (_jsx("span", { className: grip, "aria-hidden": "true", children: _jsx(DotsSixVerticalIcon, { className: icon({ size: "small" }) }) })), _jsx(BaseCheckbox.Root, { ...props, className: checkMark({ focus: "row" }), children: _jsx(BaseCheckbox.Indicator, { className: checkIndicator, children: props.indeterminate ? _jsx(MinusIcon, { weight: "bold" }) : _jsx(CheckIcon, { weight: "bold" }) }) }), _jsx("span", { className: labelText, children: children }), meta !== undefined && _jsx("span", { className: metaText, children: meta })] }));
48
+ return (_jsxs("label", { className: cx(row, className), "data-ciridae-ui": "", children: [showGrip && (_jsx("span", { className: grip, "aria-hidden": "true", "data-locked": showGrip === "locked" || undefined, children: _jsx(DotsSixVerticalIcon, { className: icon({ size: "small" }) }) })), _jsx(BaseCheckbox.Root, { ...props, ...checkMarkEdge, className: checkMark({ focus: "row" }), children: _jsx(BaseCheckbox.Indicator, { className: checkIndicator, children: _jsx(CheckGlyph, { mixed: props.indeterminate }) }) }), _jsx("span", { className: labelText, children: children }), meta !== undefined && _jsx("span", { className: metaText, children: meta })] }));
45
49
  }
@@ -1,5 +1,10 @@
1
1
  import type { Tone } from "./badge.js";
2
2
  import type { ComponentProps, ReactNode } from "react";
3
+ /** A small neutral label naming a category, such as "Prefilled". A status takes a `Badge`. */
4
+ export declare function Tag({ icon, children, className, ...props }: Omit<ComponentProps<"span">, "children"> & {
5
+ children: ReactNode;
6
+ icon?: ReactNode;
7
+ }): import("react").JSX.Element;
3
8
  export type ChipProps = Omit<ComponentProps<"span">, "children"> & {
4
9
  children: ReactNode;
5
10
  /** A mark before the label. */
@@ -1,7 +1,8 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { XIcon } from "@phosphor-icons/react";
3
3
  import { css, cx } from "#primitives/css";
4
4
  import { IconButton } from "./button.js";
5
+ import { faceEdge } from "./gradient-edge.js";
5
6
  import { icon as iconStyle } from "./icon.js";
6
7
  import { useCiridaeLocale } from "./locale.js";
7
8
  const chip = css({
@@ -36,6 +37,31 @@ const chip = css({
36
37
  "&[data-tone=warning]": { background: "bg.warningSubtle", color: "accent.yellow" },
37
38
  "&[data-tone=danger], &[data-invalid]": { borderColor: "accent.red", color: "accent.red" },
38
39
  });
40
+ /* An 18px label on the raised face: 12px text over a 2px inset and the edge's 1px. */
41
+ const tag = css({
42
+ display: "inline-flex",
43
+ alignItems: "center",
44
+ gap: "layout.tightGap",
45
+ paddingBlock: "control.trackInset",
46
+ paddingInlineStart: "control.iconGap",
47
+ paddingInlineEnd: "layout.controlGap",
48
+ border: "1px solid transparent",
49
+ borderRadius: "round",
50
+ background: "token(gradients.raised)",
51
+ color: "fg.label",
52
+ fontFamily: "sans",
53
+ fontSize: "code",
54
+ fontWeight: "medium",
55
+ lineHeight: "tight",
56
+ letterSpacing: "compact",
57
+ whiteSpace: "nowrap",
58
+ "& > svg": { flexShrink: "0", fontSize: "icon.small" },
59
+ });
60
+ /** A small neutral label naming a category, such as "Prefilled". A status takes a `Badge`. */
61
+ export function Tag({ icon, children, className, ...props }) {
62
+ const edge = faceEdge("raised");
63
+ return (_jsxs("span", { ...props, className: cx(tag, edge.className, className), "data-ciridae-ui": "", "data-edge": edge["data-edge"], children: [icon, children] }));
64
+ }
39
65
  export function Chip({ children, icon, tone = "neutral", onRemove, removeLabel, disabled = false, invalid = false, size = "regular", className, ...props }) {
40
66
  const { labels } = useCiridaeLocale();
41
67
  const name = removeLabel ?? (typeof children === "string" ? children : "");
@@ -102,7 +102,7 @@ export function ColumnSettings({ columns, columnVisibility, onColumnVisibilityCh
102
102
  };
103
103
  const setShown = (id, visible) => onColumnVisibilityChange({ ...columnVisibility, [id]: visible });
104
104
  const dropMarker = (_jsx("span", { className: dropSlot, "aria-hidden": "true", children: _jsx("span", {}) }));
105
- return (_jsx(Popover, { open: open, onOpenChange: onOpenChange, density: "compact", trigger: trigger ?? (_jsx(Button, { variant: "secondary", size: "medium", icon: _jsx(ColumnsIcon, { className: icon({ size: "small" }) }), children: labels.columns })), children: _jsxs("div", { className: panel, children: [_jsxs("div", { className: heading, children: [labels.columns, onReset && (_jsx(Button, { variant: "ghost", onClick: onReset, children: labels.reset }))] }), _jsxs("div", { className: list, onDragEnd: finishDrag, children: [locked.map((column) => (_jsx(CheckboxItem, { checked: true, disabled: true, meta: labels.alwaysShown, children: column.label }, column.id))), shown.map((column, index) => (_jsxs("span", { className: css({ display: "contents" }), children: [dropIndex === index && dragging !== column.id && dropMarker, _jsx("span", { className: draggable, draggable: reorderable, "data-dragging": dragging === column.id || undefined, onDragStart: (event) => {
105
+ return (_jsx(Popover, { open: open, onOpenChange: onOpenChange, density: "compact", trigger: trigger ?? (_jsx(Button, { variant: "secondary", size: "medium", icon: _jsx(ColumnsIcon, { className: icon({ size: "small" }) }), children: labels.columns })), children: _jsxs("div", { className: panel, children: [_jsxs("div", { className: heading, children: [labels.columns, onReset && (_jsx(Button, { variant: "ghost", onClick: onReset, children: labels.reset }))] }), _jsxs("div", { className: list, onDragEnd: finishDrag, children: [locked.map((column) => (_jsx(CheckboxItem, { checked: true, disabled: true, grip: reorderable && "locked", meta: labels.alwaysShown, children: column.label }, column.id))), shown.map((column, index) => (_jsxs("span", { className: css({ display: "contents" }), children: [dropIndex === index && dragging !== column.id && dropMarker, _jsx("span", { className: draggable, draggable: reorderable, "data-dragging": dragging === column.id || undefined, onDragStart: (event) => {
106
106
  event.dataTransfer.effectAllowed = "move";
107
107
  setDragging(column.id);
108
108
  }, onDragOver: (event) => dragOver(event, index), onDrop: (event) => {
@@ -6,6 +6,8 @@ export type CommandMenuItem = {
6
6
  icon?: ReactNode;
7
7
  /** Muted text after the label, such as a record's title. */
8
8
  detail?: string;
9
+ /** What the row is, at its end, such as "Invoice" or "Job". */
10
+ kind?: string;
9
11
  /** The hotkey that runs it while the menu is open, such as "Mod+J", shown at the row's end. */
10
12
  shortcut?: HotkeyChord;
11
13
  /** More text the query matches, such as a job's customer. */
@@ -39,5 +41,7 @@ export type CommandMenuProps = {
39
41
  placeholder?: string;
40
42
  /** Shown when no row matches, above any `unfiltered` row; the locale's "No results" by default. */
41
43
  empty?: ReactNode;
44
+ /** What the menu searches, at the end of its footer, such as "Records, people and actions". */
45
+ searchScope?: ReactNode;
42
46
  };
43
- export declare function CommandMenu({ open, onOpenChange, query, onQueryChange, groups, title: ownTitle, placeholder: ownPlaceholder, empty: ownEmpty, }: CommandMenuProps): import("react").JSX.Element;
47
+ export declare function CommandMenu({ open, onOpenChange, query, onQueryChange, groups, title: ownTitle, placeholder: ownPlaceholder, empty: ownEmpty, searchScope, }: CommandMenuProps): import("react").JSX.Element;
@@ -12,9 +12,9 @@ import { Command } from "cmdk";
12
12
  import { useLayoutEffect, useRef, useState, } from "react";
13
13
  import { css } from "#primitives/css";
14
14
  import { scoreCommandMatch } from "./command-match.js";
15
- import { HotkeyLayer, useHotkeys } from "./hotkeys.js";
15
+ import { formatForDisplay, HotkeyLayer, useHotkeys } from "./hotkeys.js";
16
16
  import { icon } from "./icon.js";
17
- import { ShortcutHint } from "./keycap.js";
17
+ import { Keycap, ShortcutHint } from "./keycap.js";
18
18
  import { useCiridaeLocale } from "./locale.js";
19
19
  const backdrop = css({
20
20
  position: "fixed",
@@ -26,22 +26,23 @@ const backdrop = css({
26
26
  "&[data-starting-style], &[data-ending-style]": { opacity: "0" },
27
27
  _motionReduce: { transition: "none" },
28
28
  });
29
+ // The palette is a sheet in a glass shell, as a modal is: the shell blurs the page in dark, the
30
+ // sheet blurs it in light.
29
31
  const popup = css({
30
32
  zIndex: "dialog",
31
33
  position: "fixed",
32
- top: "15dvh",
34
+ top: "18dvh",
33
35
  left: "50%",
34
36
  display: "flex",
35
- flexDirection: "column",
36
37
  width: "calc(100vw - token(spacing.layout.pageGap) * 2)",
37
- maxWidth: "overlay.dialog",
38
- overflow: "hidden",
39
- border: "1px solid token(colors.border.control)",
40
- borderRadius: "action",
41
- background: "bg.card",
38
+ maxWidth: "calc(token(sizes.overlay.palette) + token(spacing.menu.inset) * 2 + 2px)",
39
+ padding: "menu.inset",
40
+ border: "1px solid token(colors.border.divider)",
41
+ borderRadius: "shell",
42
+ background: "bg.modalShell",
43
+ backdropFilter: "blur(token(blurs.modalShell))",
42
44
  color: "fg.primary",
43
45
  textStyle: "body",
44
- boxShadow: "card",
45
46
  outline: "none",
46
47
  transform: "translateX(-50%)",
47
48
  transitionProperty: "opacity",
@@ -49,6 +50,18 @@ const popup = css({
49
50
  "&[data-starting-style], &[data-ending-style]": { opacity: "0" },
50
51
  _motionReduce: { transition: "none" },
51
52
  _phone: { top: "layout.pageGap" },
53
+ "& > [cmdk-root]": {
54
+ display: "flex",
55
+ flexDirection: "column",
56
+ flex: "1",
57
+ minWidth: "0",
58
+ overflow: "hidden",
59
+ border: "1px solid token(colors.border.divider)",
60
+ borderRadius: "inset",
61
+ background: "bg.modalSheet",
62
+ backdropFilter: "blur(token(blurs.modalSheet))",
63
+ boxShadow: "soft",
64
+ },
52
65
  });
53
66
  const visuallyHidden = css({
54
67
  position: "absolute",
@@ -65,9 +78,9 @@ const field = css({
65
78
  display: "flex",
66
79
  alignItems: "center",
67
80
  gap: "layout.controlGap",
68
- height: "control.search",
69
- paddingInline: "control.compactInline",
70
- borderBottom: "1px solid token(colors.border.control)",
81
+ height: "commandPalette.query",
82
+ flexShrink: "0",
83
+ paddingInline: "control.inline",
71
84
  color: "fg.muted",
72
85
  "& > input": {
73
86
  flex: "1",
@@ -78,35 +91,52 @@ const field = css({
78
91
  background: "transparent",
79
92
  color: "fg.primary",
80
93
  font: "inherit",
94
+ fontWeight: "medium",
95
+ letterSpacing: "compact",
81
96
  _placeholder: { color: "fg.muted" },
82
97
  },
83
98
  });
99
+ // The rules between the query, the results and the footer stop short of the sheet's edges.
100
+ const divider = css({
101
+ flexShrink: "0",
102
+ height: "border.hairline",
103
+ marginInline: "panel.compact",
104
+ background: "border.divider",
105
+ });
84
106
  const list = css({
85
107
  maxHeight: "min(60dvh, 420px)",
86
108
  overflowY: "auto",
87
109
  overscrollBehavior: "contain",
88
- "& [cmdk-group] + [cmdk-group]": { borderTop: "1px solid token(colors.border.subtle)" },
110
+ padding: "panel.compact",
111
+ "& [cmdk-group-items]": { display: "grid", gap: "control.trackInset" },
89
112
  "& [cmdk-group-heading]": {
90
113
  paddingInline: "control.compactInline",
91
114
  paddingTop: "control.block",
92
- paddingBottom: "layout.tightGap",
93
- color: "fg.muted",
115
+ paddingBottom: "control.compactBlock",
116
+ color: "fg.subtle",
94
117
  fontSize: "code",
95
118
  fontWeight: "medium",
119
+ letterSpacing: "compact",
120
+ lineHeight: "tight",
96
121
  },
97
- // The last group's rows run to the popup's foot, with the field's inset under them.
98
- "& [cmdk-group]:last-child": { paddingBottom: "layout.tightGap" },
122
+ "& [cmdk-group] + [cmdk-group]": { marginTop: "control.trackInset" },
99
123
  });
100
124
  const row = css({
101
125
  display: "flex",
102
126
  alignItems: "center",
103
- gap: "layout.controlGap",
104
- minHeight: "menu.item",
127
+ gap: "layout.contentGap",
128
+ minHeight: "commandPalette.row",
105
129
  paddingInline: "control.compactInline",
106
- fontSize: "caption",
107
- lineHeight: "body",
130
+ borderRadius: "control",
131
+ fontSize: "body",
132
+ fontWeight: "medium",
133
+ letterSpacing: "compact",
134
+ lineHeight: "tight",
108
135
  cursor: "pointer",
109
- "&[data-selected=true]": { background: "bg.rowHover" },
136
+ "&[data-selected=true]": {
137
+ background: "token(gradients.selected)",
138
+ boxShadow: "inset 0 0 0 1px token(colors.border.divider)",
139
+ },
110
140
  "& > svg": { flexShrink: "0", color: "fg.muted" },
111
141
  "& > [data-label]": {
112
142
  flexShrink: "0",
@@ -121,7 +151,14 @@ const row = css({
121
151
  overflow: "hidden",
122
152
  textOverflow: "ellipsis",
123
153
  whiteSpace: "nowrap",
124
- color: "fg.muted",
154
+ color: "fg.subtle",
155
+ fontSize: "code",
156
+ },
157
+ "& > [data-kind]": {
158
+ flexShrink: "0",
159
+ marginInlineStart: "auto",
160
+ color: "fg.subtle",
161
+ fontSize: "code",
125
162
  },
126
163
  "& > [data-shortcut]": { marginInlineStart: "auto" },
127
164
  // A link row's anchor fills the row, so its content lays out as a plain row's does. Choosing
@@ -150,7 +187,14 @@ const row = css({
150
187
  overflow: "hidden",
151
188
  textOverflow: "ellipsis",
152
189
  whiteSpace: "nowrap",
153
- color: "fg.muted",
190
+ color: "fg.subtle",
191
+ fontSize: "code",
192
+ },
193
+ "& > [data-kind]": {
194
+ flexShrink: "0",
195
+ marginInlineStart: "auto",
196
+ color: "fg.subtle",
197
+ fontSize: "code",
154
198
  },
155
199
  },
156
200
  });
@@ -160,6 +204,24 @@ const emptyRow = css({
160
204
  color: "fg.muted",
161
205
  fontSize: "caption",
162
206
  });
207
+ // How to drive the palette from the keyboard, and what it searches.
208
+ const footer = css({
209
+ display: "flex",
210
+ alignItems: "center",
211
+ justifyContent: "space-between",
212
+ gap: "layout.clusterGap",
213
+ minHeight: "commandPalette.footer",
214
+ flexShrink: "0",
215
+ paddingInline: "control.inline",
216
+ color: "fg.subtle",
217
+ fontSize: "code",
218
+ fontWeight: "medium",
219
+ letterSpacing: "compact",
220
+ _phone: { display: "none" },
221
+ });
222
+ const footerHints = css({ display: "flex", alignItems: "center", gap: "layout.clusterGap" });
223
+ const footerHint = css({ display: "flex", alignItems: "center", gap: "control.iconGap" });
224
+ const footerKeys = css({ display: "flex", alignItems: "center", gap: "layout.tightGap" });
163
225
  /**
164
226
  * The shown rows' hotkeys, in the menu's layer: it is the newest scope, so a row's hotkey wins over
165
227
  * the page's own binding for the same keys, such as Cmd+J opening the chat empty.
@@ -176,10 +238,14 @@ function CommandMenuShortcuts({ items, run, }) {
176
238
  : []));
177
239
  return null;
178
240
  }
241
+ /** The keys the palette's footer names, as its keycaps write them. */
242
+ const escapeKey = formatForDisplay("Escape");
243
+ const moveKeys = [formatForDisplay("ArrowUp"), formatForDisplay("ArrowDown")];
244
+ const openKey = formatForDisplay("Enter");
179
245
  /** Past this many rows the menu draws only those near the view. */
180
246
  const virtualizeRowsPast = 50;
181
- /** A row's height before it is measured: the menu's item height. */
182
- const menuRowHeight = 30;
247
+ /** A row's height before it is measured: the palette's row height, `sizes.commandPalette.row`. */
248
+ const menuRowHeight = 40;
183
249
  // The rows lie over a box as tall as all of them, each moved down to its place.
184
250
  const virtualLines = css({
185
251
  position: "relative",
@@ -189,7 +255,7 @@ const virtualLines = css({
189
255
  function matchesQuery(item, query) {
190
256
  return item.unfiltered === true || scoreCommandMatch(item, query) > 0;
191
257
  }
192
- export function CommandMenu({ open, onOpenChange, query, onQueryChange, groups, title: ownTitle, placeholder: ownPlaceholder, empty: ownEmpty, }) {
258
+ export function CommandMenu({ open, onOpenChange, query, onQueryChange, groups, title: ownTitle, placeholder: ownPlaceholder, empty: ownEmpty, searchScope, }) {
193
259
  const { labels } = useCiridaeLocale();
194
260
  const title = ownTitle ?? labels.commandMenu.title;
195
261
  const placeholder = ownPlaceholder ?? labels.common.search;
@@ -306,8 +372,8 @@ export function CommandMenu({ open, onOpenChange, query, onQueryChange, groups,
306
372
  links.current.set(item.id, element);
307
373
  else
308
374
  links.current.delete(item.id);
309
- } })) : (_jsx(CommandMenuRowContent, { item: item })), item.shortcut && _jsx(ShortcutHint, { "data-shortcut": "", hotkey: item.shortcut })] }, item.id));
310
- return (_jsx(BaseDialog.Root, { open: open, onOpenChange: setOpen, children: _jsx(BaseDialog.Portal, { children: _jsxs(HotkeyLayer, { children: [_jsx(CommandMenuShortcuts, { items: shownGroups.flatMap((group) => group.items), run: run }), _jsx(BaseDialog.Backdrop, { className: backdrop, "data-ciridae-ui": "" }), _jsxs(BaseDialog.Popup, { className: popup, "data-ciridae-ui": "", ref: popupRef, finalFocus: restoreFocus, children: [_jsx(BaseDialog.Title, { className: visuallyHidden, children: title }), _jsxs(Command, { label: title, shouldFilter: false, loop: true, ...(virtualized ? { value: selectedId, onValueChange: setChosenId } : {}), onKeyDown: moveSelection, children: [_jsxs("div", { className: field, children: [_jsx(MagnifyingGlassIcon, { className: icon({ size: "small" }) }), _jsx(Command.Input, { autoFocus: true, placeholder: placeholder, value: query, onValueChange: onQueryChange })] }), _jsxs(Command.List, { ref: setListElement, className: list, children: [!matched && (_jsx("div", { className: emptyRow, role: "presentation", children: empty })), virtualized ? (_jsx("div", { className: virtualLines, style: { height: virtualizer.getTotalSize() }, children: virtualizer.getVirtualItems().map((virtualItem) => {
375
+ } })) : (_jsx(CommandMenuRowContent, { item: item })), item.shortcut && _jsx(ShortcutHint, { boxed: true, "data-shortcut": "", hotkey: item.shortcut })] }, item.id));
376
+ return (_jsx(BaseDialog.Root, { open: open, onOpenChange: setOpen, children: _jsx(BaseDialog.Portal, { children: _jsxs(HotkeyLayer, { children: [_jsx(CommandMenuShortcuts, { items: shownGroups.flatMap((group) => group.items), run: run }), _jsx(BaseDialog.Backdrop, { className: backdrop, "data-ciridae-ui": "" }), _jsxs(BaseDialog.Popup, { className: popup, "data-ciridae-ui": "", ref: popupRef, finalFocus: restoreFocus, children: [_jsx(BaseDialog.Title, { className: visuallyHidden, children: title }), _jsxs(Command, { label: title, shouldFilter: false, loop: true, ...(virtualized ? { value: selectedId, onValueChange: setChosenId } : {}), onKeyDown: moveSelection, children: [_jsxs("div", { className: field, children: [_jsx(MagnifyingGlassIcon, { className: icon({ size: "small" }) }), _jsx(Command.Input, { autoFocus: true, placeholder: placeholder, value: query, onValueChange: onQueryChange }), _jsx(Keycap, { "aria-hidden": "true", children: escapeKey })] }), _jsx("div", { className: divider }), _jsxs(Command.List, { ref: setListElement, className: list, children: [!matched && (_jsx("div", { className: emptyRow, role: "presentation", children: empty })), virtualized ? (_jsx("div", { className: virtualLines, style: { height: virtualizer.getTotalSize() }, children: virtualizer.getVirtualItems().map((virtualItem) => {
311
377
  const line = lines[virtualItem.index];
312
378
  if (!line)
313
379
  return null;
@@ -317,10 +383,10 @@ export function CommandMenu({ open, onOpenChange, query, onQueryChange, groups,
317
383
  style: { transform: `translateY(${virtualItem.start}px)` },
318
384
  };
319
385
  return line.kind === "heading" ? (_jsx("div", { ref: measured.ref, "data-index": measured.index, style: measured.style, "cmdk-group-heading": "", role: "presentation", children: line.heading }, line.key)) : (drawItem(line.item, measured));
320
- }) })) : (shownGroups.map((group, groupIndex) => (_jsx(Command.Group, { heading: group.heading, children: group.items.map((item) => drawItem(item)) }, group.id ?? group.heading ?? groupIndex))))] })] })] })] }) }) }));
386
+ }) })) : (shownGroups.map((group, groupIndex) => (_jsx(Command.Group, { heading: group.heading, children: group.items.map((item) => drawItem(item)) }, group.id ?? group.heading ?? groupIndex))))] }), _jsx("div", { className: divider }), _jsxs("div", { className: footer, children: [_jsxs("div", { className: footerHints, children: [_jsxs("span", { className: footerHint, children: [_jsx("span", { className: footerKeys, children: moveKeys.map((key) => (_jsx(Keycap, { "aria-hidden": "true", children: key }, key))) }), labels.commandMenu.move] }), _jsxs("span", { className: footerHint, children: [_jsx(Keycap, { "aria-hidden": "true", children: openKey }), labels.commandMenu.open] }), _jsxs("span", { className: footerHint, children: [_jsx(Keycap, { "aria-hidden": "true", children: escapeKey }), labels.common.close] })] }), searchScope] })] })] })] }) }) }));
321
387
  }
322
388
  function CommandMenuRowContent({ item }) {
323
- return (_jsxs(_Fragment, { children: [item.icon, _jsx("span", { "data-label": "", children: item.label }), item.detail && _jsx("span", { "data-detail": "", children: item.detail })] }));
389
+ return (_jsxs(_Fragment, { children: [item.icon, _jsx("span", { "data-label": "", children: item.label }), item.detail && _jsx("span", { "data-detail": "", children: item.detail }), item.kind && _jsx("span", { "data-kind": "", children: item.kind })] }));
324
390
  }
325
391
  /** A link row's anchor; the row's own selection clicks it, so it never takes focus itself. */
326
392
  function CommandMenuLink({ item, register, }) {
@@ -18,6 +18,8 @@ export type CommandPaletteProps = {
18
18
  title?: string;
19
19
  placeholder?: string;
20
20
  empty?: ReactNode;
21
+ /** What the palette searches, at the end of its footer, such as "Records, people and actions". */
22
+ searchScope?: ReactNode;
21
23
  };
22
24
  /** Whether the palette is open; `hotkey` (Cmd/Ctrl+K by default) toggles it from anywhere. */
23
25
  export declare function useCommandPalette({ hotkey }?: {
@@ -27,10 +29,12 @@ export declare function useCommandPalette({ hotkey }?: {
27
29
  setOpen: import("react").Dispatch<import("react").SetStateAction<boolean>>;
28
30
  toggle: () => void;
29
31
  };
30
- export declare function CommandPalette({ open, onOpenChange, groups, onQueryChange, footerItem, title, placeholder, empty, }: CommandPaletteProps): import("react").JSX.Element;
32
+ export declare function CommandPalette({ open, onOpenChange, groups, onQueryChange, footerItem, title, placeholder, empty, searchScope, }: CommandPaletteProps): import("react").JSX.Element;
31
33
  export type CommandPaletteTriggerProps = ComponentProps<"button"> & {
32
34
  /** The hotkey `useCommandPalette` opens it with, shown at the end; Cmd/Ctrl+K by default. */
33
35
  hotkey?: HotkeyChord;
36
+ /** The icon alone, round, for the rail; it is named "Search" unless given an `aria-label`. */
37
+ compact?: boolean;
34
38
  };
35
39
  /** A search field's face, as a button that opens the palette; reads "Search" unless given text. */
36
- export declare function CommandPaletteTrigger({ hotkey, className, children, ...props }: CommandPaletteTriggerProps): import("react").JSX.Element;
40
+ export declare function CommandPaletteTrigger({ hotkey, compact, className, children, ...props }: CommandPaletteTriggerProps): import("react").JSX.Element;
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /*
3
3
  * The app's Cmd+K palette on `CommandMenu`: groups of pages, records, people and actions the app
4
4
  * supplies, matched loosely on label, detail and keywords and ranked closest first within each
@@ -25,7 +25,7 @@ export function useCommandPalette({ hotkey = "Mod+K" } = {}) {
25
25
  const toggle = useCallback(() => setOpen((current) => !current), []);
26
26
  return { open, setOpen, toggle };
27
27
  }
28
- export function CommandPalette({ open, onOpenChange, groups, onQueryChange, footerItem, title, placeholder, empty, }) {
28
+ export function CommandPalette({ open, onOpenChange, groups, onQueryChange, footerItem, title, placeholder, empty, searchScope, }) {
29
29
  const [query, setQuery] = useState("");
30
30
  const changeQuery = (next) => {
31
31
  setQuery(next);
@@ -47,30 +47,40 @@ export function CommandPalette({ open, onOpenChange, groups, onQueryChange, foot
47
47
  const footer = typed && footerItem ? footerItem(typed) : null;
48
48
  return (_jsx(CommandMenu, { open: open, onOpenChange: onOpenChange, query: query, onQueryChange: changeQuery, groups: footer
49
49
  ? [...rankedGroups, { id: "footer", items: [{ ...footer, unfiltered: true }] }]
50
- : rankedGroups, title: title, placeholder: placeholder, empty: empty }));
50
+ : rankedGroups, title: title, placeholder: placeholder, empty: empty, searchScope: searchScope }));
51
51
  }
52
+ // A form field's well on the canvas, as the sidebar draws it; in the rail, a round icon.
52
53
  const trigger = css({
53
54
  display: "flex",
54
55
  alignItems: "center",
55
56
  gap: "control.iconGap",
56
57
  width: "100%",
57
- height: "control.compact",
58
+ height: "control.sidebarSearch",
58
59
  paddingInlineStart: "control.compactInline",
59
- paddingInlineEnd: "control.iconGap",
60
- border: "1px solid token(colors.border.control)",
61
- borderRadius: "action",
62
- background: "bg.field",
60
+ paddingInlineEnd: "control.block",
61
+ border: "1px solid token(colors.border.canvas)",
62
+ borderRadius: "control",
63
+ background: "bg.formField",
63
64
  color: "fg.muted",
65
+ _light: { color: "fg.primary" },
64
66
  font: "inherit",
65
67
  fontWeight: "medium",
66
68
  letterSpacing: "compact",
69
+ lineHeight: "tight",
67
70
  cursor: "pointer",
68
71
  _hover: { color: "fg.primary" },
69
72
  _focusVisible: { focusRing: "outer" },
70
73
  "& > [data-shortcut]": { marginInlineStart: "auto", _phone: { display: "none" } },
74
+ "&[data-compact]": {
75
+ justifyContent: "center",
76
+ width: "control.compact",
77
+ height: "control.compact",
78
+ padding: "none",
79
+ borderRadius: "round",
80
+ },
71
81
  });
72
82
  /** A search field's face, as a button that opens the palette; reads "Search" unless given text. */
73
- export function CommandPaletteTrigger({ hotkey = "Mod+K", className, children, ...props }) {
83
+ export function CommandPaletteTrigger({ hotkey = "Mod+K", compact = false, className, children, ...props }) {
74
84
  const { labels } = useCiridaeLocale();
75
- return (_jsxs("button", { type: "button", "aria-haspopup": "dialog", ...props, className: cx(trigger, className), "data-ciridae-ui": "", children: [_jsx(MagnifyingGlassIcon, { className: icon({ size: "small" }) }), children ?? labels.common.search, _jsx(ShortcutHint, { "data-shortcut": "", hotkey: hotkey })] }));
85
+ return (_jsxs("button", { type: "button", "aria-haspopup": "dialog", "aria-label": compact ? labels.common.search : undefined, ...props, className: cx(trigger, className), "data-ciridae-ui": "", "data-compact": compact || undefined, children: [_jsx(MagnifyingGlassIcon, { className: icon({ size: "small" }) }), !compact && (_jsxs(_Fragment, { children: [children ?? labels.common.search, _jsx(ShortcutHint, { boxed: true, "data-shortcut": "", hotkey: hotkey })] }))] }));
76
86
  }
@@ -1,8 +1,11 @@
1
1
  import { Separator as BaseSeparator } from "@base-ui/react/separator";
2
2
  import type { ComponentProps, ReactNode } from "react";
3
3
  export type AvatarColor = "neutral" | "blue" | "violet" | "green" | "teal" | "yellow" | "orange" | "pink" | "red";
4
- /** 12, 16, 20, 24, 26, 32, and 44 pixels; the Round 2 set is `menu` through `large`. */
5
- export type AvatarSize = "micro" | "mark" | "small" | "menu" | "row" | "medium" | "large";
4
+ /**
5
+ * 12, 14, 20, 24, 26, 28, 32 (`row` and `medium`) and 44 pixels; the kit's set is `menu` through
6
+ * `large`.
7
+ */
8
+ export type AvatarSize = "micro" | "mark" | "small" | "menu" | "agent" | "owner" | "row" | "medium" | "large";
6
9
  /**
7
10
  * The one mark for a person, an organization, a project, or the agent: `src` shows the photo or
8
11
  * logo, and the initials derived from `label` stand in until it loads or when there is none. A
@@ -20,6 +23,10 @@ export declare function Avatar({ label, src, size, kind, icon, badge, color, cla
20
23
  badge?: ReactNode;
21
24
  color?: AvatarColor;
22
25
  }): import("react").JSX.Element;
26
+ /** The slot where an owner's avatar goes, before anyone owns the record. */
27
+ export declare function UnassignedAvatar({ size, className, ...props }: ComponentProps<"span"> & {
28
+ size?: "small" | "owner" | "row";
29
+ }): import("react").JSX.Element;
23
30
  export declare function Divider({ inset, className, ...props }: Omit<ComponentProps<typeof BaseSeparator>, "className"> & {
24
31
  className?: string;
25
32
  /** `menu` insets it to a menu's or a popover's rows. */
@@ -28,13 +35,15 @@ export declare function Divider({ inset, className, ...props }: Omit<ComponentPr
28
35
  export type DescriptionRow = [label: string, value: ReactNode];
29
36
  /**
30
37
  * Label-value pairs in two columns. Rows whose value is `null` or `undefined` are left out, so
31
- * callers can list every field a record may carry. `compact` tightens the rows for dense output.
38
+ * callers can list every field a record may carry. `compact` sets them as key-value pairs;
39
+ * `layout="facts"` as fact rows, each value at the far edge over a hairline.
32
40
  * `loading` keeps every row's label and draws a placeholder for its value, so the list arrives at
33
41
  * the size it loads to; build the rows from the record that may not have loaded yet.
34
42
  */
35
- export declare function DescriptionList({ rows, density, loading, className, ...props }: ComponentProps<"dl"> & {
43
+ export declare function DescriptionList({ rows, density, layout, loading, className, ...props }: ComponentProps<"dl"> & {
36
44
  rows: DescriptionRow[];
37
45
  density?: "default" | "compact";
46
+ layout?: "columns" | "facts";
38
47
  loading?: boolean;
39
48
  }): import("react").JSX.Element;
40
49
  /**
@@ -45,3 +54,25 @@ export declare function CodeBlock({ className, copy, copyLabel, ...props }: Comp
45
54
  copy?: string;
46
55
  copyLabel?: string;
47
56
  }): import("react").JSX.Element;
57
+ /** Where a field's value came from, or who vouches for it: a 14px icon and a line. */
58
+ export type DetailFieldNote = {
59
+ icon?: ReactNode;
60
+ label: ReactNode;
61
+ };
62
+ export type DetailFieldProps = Omit<ComponentProps<"div">, "children"> & {
63
+ label: ReactNode;
64
+ value: ReactNode;
65
+ /** Where the value came from, such as "Net 30 from Aug 22". */
66
+ source?: DetailFieldNote;
67
+ /** Who or what vouches for it, such as "Checked by Dana R.". */
68
+ assurance?: DetailFieldNote;
69
+ /** Beside the value, such as a Contested badge; its line under the value is `description`. */
70
+ badge?: ReactNode;
71
+ description?: ReactNode;
72
+ /** A trailing button, such as Resolve. */
73
+ action?: ReactNode;
74
+ state?: "default" | "selected" | "inset" | "highlighted";
75
+ /** A hairline under the row; the last row of a list leaves it out. Defaults to true. */
76
+ divider?: boolean;
77
+ };
78
+ export declare function DetailField({ label, value, source, assurance, badge, description, action, state, divider, className, ...props }: DetailFieldProps): import("react").JSX.Element;