@crisp-ui-kit/crisp 0.54.5 → 0.54.6

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 (34) hide show
  1. package/dist/authshell.css +4 -0
  2. package/dist/authshell.layered.css +4 -0
  3. package/dist/cjs/components/datatable/datatable.js +28 -5
  4. package/dist/cjs/components/datatable/datatable.recipe.js +6 -0
  5. package/dist/cjs/components/personchip/personchip.js +7 -3
  6. package/dist/cjs/components/tooltip/tooltip.js +2 -2
  7. package/dist/cjs/components/tooltip/tooltip.recipe.js +37 -0
  8. package/dist/cjs/product/authshell/authshell.js +1 -1
  9. package/dist/cjs/product/authshell/authshell.recipe.js +18 -0
  10. package/dist/cjs/product/recordstable/recordstable.js +12 -3
  11. package/dist/cjs/product/richtext/richtext.js +31 -4
  12. package/dist/datatable.css +8 -4
  13. package/dist/datatable.layered.css +8 -4
  14. package/dist/esm/components/datatable/datatable.js +28 -5
  15. package/dist/esm/components/datatable/datatable.recipe.js +6 -0
  16. package/dist/esm/components/personchip/personchip.js +7 -3
  17. package/dist/esm/components/tooltip/tooltip.js +2 -2
  18. package/dist/esm/components/tooltip/tooltip.recipe.js +37 -0
  19. package/dist/esm/product/authshell/authshell.js +1 -1
  20. package/dist/esm/product/authshell/authshell.recipe.js +18 -0
  21. package/dist/esm/product/recordstable/recordstable.js +12 -3
  22. package/dist/esm/product/richtext/richtext.js +31 -4
  23. package/dist/styles.css +19 -4
  24. package/dist/styles.layered.css +19 -4
  25. package/dist/tooltip.css +7 -0
  26. package/dist/tooltip.d.ts +2 -2
  27. package/dist/tooltip.layered.css +7 -0
  28. package/dist/types/components/datatable/datatable.d.ts +50 -11
  29. package/dist/types/components/personchip/personchip.d.ts +14 -2
  30. package/dist/types/components/tooltip/tooltip.d.ts +16 -0
  31. package/dist/types/components/tooltip/tooltip.recipe.d.ts +30 -2
  32. package/dist/types/product/authshell/authshell.d.ts +4 -1
  33. package/dist/types/product/recordstable/recordstable.d.ts +12 -0
  34. package/package.json +1 -1
@@ -11,6 +11,16 @@ import { contract as c } from "../../tokens/index.js";
11
11
  // (#101112, the same "dark pill" ink NavItem's own text uses) and
12
12
  // line100's light hex (#eeeff1) — both theme-constant, same as before,
13
13
  // just the wrong pair.
14
+ //
15
+ // crisp#563: this `boxShadow` is Attio's PLAIN tooltip (a truncated header
16
+ // label, measured on "Twitter follower count") — still an uncorroborated
17
+ // single sample, flagged for its own re-measurement pass, not touched here.
18
+ // Attio has a SECOND, visually similar but structurally different tooltip
19
+ // for a reference-column header (icon + the referenced type's name after
20
+ // the label, crisp#580) with its own measured shadow — an inset ring plus
21
+ // two offset shadows, `variant: "reference"` below. Background, text
22
+ // colour, radius, padding and font are identical between the two measured
23
+ // samples; only the shadow differs.
14
24
  const spec = defineSpec({
15
25
  name: "tooltip",
16
26
  base: {
@@ -35,7 +45,34 @@ const spec = defineSpec({
35
45
  // An at-rule whose body is declarations rather than rules, so a part
36
46
  // expresses it directly: the entry state the overlay animates from.
37
47
  { selector: "@starting-style", decls: { opacity: 0 } },
48
+ // #580: the icon + referenced-type-name row a `variant="reference"`
49
+ // consumer (DataTable/RecordsTable's `ThLabel`) composes into `content`.
50
+ // Lives here, not in the caller's inline style, so it reads its gap from
51
+ // the token contract like every other measured value on this surface —
52
+ // `content` is portaled straight into this bubble regardless of which
53
+ // component built the JSX, so a nested selector reaches it the same way
54
+ // it reaches anything else rendered inside `.crisp-tooltip`.
55
+ {
56
+ selector: "& .crisp-tooltip-reference-row",
57
+ decls: {
58
+ display: "inline-flex",
59
+ alignItems: "center",
60
+ gap: c.space["1"],
61
+ },
62
+ },
38
63
  ],
64
+ variants: {
65
+ variant: {
66
+ // Inherits `base`'s boxShadow untouched -- crisp#563 updates that
67
+ // single value when it re-measures the plain tooltip, and this
68
+ // variant picks the correction up for free.
69
+ plain: {},
70
+ reference: {
71
+ boxShadow: "rgba(0,0,0,0) 0 0 0 1px inset, rgba(28,40,64,0.18) 0 0 2px 0, rgba(0,0,0,0.04) 0 1px 3px 0",
72
+ },
73
+ },
74
+ },
75
+ defaultVariants: { variant: "plain" },
39
76
  });
40
77
  const config = specToRecipe(spec);
41
78
  export const tooltipRecipe = resolveRecipe(config);
@@ -13,6 +13,6 @@ export function AuthShell({ progress, logo, title, children, preview, footer, cl
13
13
  const pct = hasProgress
14
14
  ? Math.round(Math.max(0, Math.min(1, progress)) * 100)
15
15
  : 0;
16
- return (_jsxs("div", { className: authshellRecipe({ className }), ...rest, children: [hasProgress && (_jsx("div", { className: "crisp-authshell-progress", role: "progressbar", "aria-valuenow": pct, "aria-valuemin": 0, "aria-valuemax": 100, children: _jsx("div", { className: "crisp-authshell-progress-fill", style: { width: `${pct}%` } }) })), logo != null && _jsx("div", { className: "crisp-authshell-logo", children: logo }), _jsxs("div", { className: "crisp-authshell-card", children: [_jsx("div", { className: "crisp-authshell-pane", children: _jsxs("div", { className: "crisp-authshell-body", children: [title != null && (_jsx("div", { className: "crisp-authshell-title", children: title })), _jsx("div", { className: "crisp-authshell-form", children: children })] }) }), preview != null && (_jsx("div", { className: "crisp-authshell-preview", children: preview }))] }), footer != null && _jsx("div", { className: "crisp-authshell-footer", children: footer })] }));
16
+ return (_jsxs("div", { className: authshellRecipe({ className }), "data-single-pane": preview == null ? "" : undefined, ...rest, children: [hasProgress && (_jsx("div", { className: "crisp-authshell-progress", role: "progressbar", "aria-valuenow": pct, "aria-valuemin": 0, "aria-valuemax": 100, children: _jsx("div", { className: "crisp-authshell-progress-fill", style: { width: `${pct}%` } }) })), logo != null && _jsx("div", { className: "crisp-authshell-logo", children: logo }), _jsxs("div", { className: "crisp-authshell-card", children: [_jsx("div", { className: "crisp-authshell-pane", children: _jsxs("div", { className: "crisp-authshell-body", children: [title != null && (_jsx("div", { className: "crisp-authshell-title", children: title })), _jsx("div", { className: "crisp-authshell-form", children: children })] }) }), preview != null && (_jsx("div", { className: "crisp-authshell-preview", children: preview }))] }), footer != null && _jsx("div", { className: "crisp-authshell-footer", children: footer })] }));
17
17
  }
18
18
  AuthShell.displayName = "AuthShell";
@@ -7,25 +7,43 @@ import { contract as c } from "../../tokens/index.js";
7
7
  // from the live app, EXCEPT the 1200px preview-hiding breakpoint (called out
8
8
  // at the container query below) — the pane drops out in JS somewhere between a
9
9
  // measured 954px and 1393px viewport width, so 1200px is chosen, not measured.
10
+ //
11
+ // crisp#653: that two-pane card's own 692px height, border and 420px pane
12
+ // width were measured for a SPECIFIC screen with a real product-preview
13
+ // pane -- omitting `preview` (`data-single-pane`, set by the component) used
14
+ // to keep all three anyway, leaving a bordered box with a huge empty area
15
+ // under short content. Attio's own single-pane screen (`app.attio.com/login`,
16
+ // reachable in a fresh logged-out browser context -- no session needed) has
17
+ // NO card at all: every ancestor of its "Sign in" heading measured
18
+ // `border: 0px none`, `box-shadow: none`, `background: transparent`, a
19
+ // 360-364px content column (not 420px), sized to its own content and
20
+ // roughly centred in the viewport rather than pinned near the top.
21
+ // `data-single-pane` now drops the card chrome and narrows the body to
22
+ // match; the two-pane card is unchanged.
10
23
  const config = defineRecipe({
11
24
  name: "authshell",
12
25
  base: `min-height: 100%; box-sizing: border-box; display: flex; flex-direction: column; align-items: center; position: relative;
13
26
  background: ${c.bgSurface}; font-family: ${c.font.sans}; font-size: ${c.text.lg}; line-height: 20px; color: ${c.fgPrimary};
14
27
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
15
28
 
29
+ &[data-single-pane] { justify-content: center; }
30
+
16
31
  & .crisp-authshell-progress { position: absolute; top: 0; left: 0; width: 100%; height: 2px; background: ${c.borderDefault}; }
17
32
  & .crisp-authshell-progress-fill { height: 2px; background: ${c.fgPrimary}; border-radius: 4px; transition: width .2s; }
18
33
 
19
34
  & .crisp-authshell-logo { margin-top: ${c.space["8"]}; height: 24px; display: flex; align-items: center; }
20
35
 
21
36
  & .crisp-authshell-card { margin-top: ${c.space["6"]}; display: flex; height: 692px; border-radius: 20px; border: 1px solid ${c.borderSubtle}; overflow: hidden; }
37
+ &[data-single-pane] .crisp-authshell-card { height: auto; border: none; border-radius: 0; overflow: visible; }
22
38
 
23
39
  & .crisp-authshell-pane { flex: 0 0 564px; padding: 72px; display: flex; flex-direction: column; box-sizing: border-box; }
40
+ &[data-single-pane] .crisp-authshell-pane { flex: 0 0 auto; padding: 0; }
24
41
 
25
42
  & .crisp-authshell-preview { flex: 0 0 564px; padding: 96px 64px; background: ${c.bgSubtle}; border-inline-start: 1px solid color-mix(in srgb, ${c.fgPrimary} 5%, transparent); position: relative; overflow: hidden; box-sizing: border-box; }
26
43
  & .crisp-authshell-preview::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 120px; background: linear-gradient(color-mix(in srgb, ${c.bgSubtle} 0%, transparent) 0%, ${c.bgSubtle} 100%); pointer-events: none; }
27
44
 
28
45
  & .crisp-authshell-body { width: 420px; margin: 0 auto; display: flex; flex-direction: column; gap: ${c.space["8"]}; align-items: center; }
46
+ &[data-single-pane] .crisp-authshell-body { width: 360px; }
29
47
 
30
48
  & .crisp-authshell-title { font-size: 20px; line-height: 24px; font-weight: ${c.weight.semibold}; letter-spacing: -0.4px; color: ${c.fgPrimary}; align-self: stretch; }
31
49
 
@@ -37,8 +37,15 @@ export function isLabelTruncated(el) {
37
37
  * viewport change, label change) via `ResizeObserver`; SSR-safe the same way
38
38
  * `MiddleTruncate` is -- the DOM is only touched inside an effect, so the
39
39
  * server (and the first client paint) renders the plain span.
40
+ *
41
+ * #580: a `referencedType` column (Attio's own `type: "reference"`) appends
42
+ * that type's icon + name after the label and switches to `Tooltip`'s
43
+ * `"reference"` chrome variant (Attio's second, structurally distinct
44
+ * tooltip — same background/colour/radius/padding/font as the plain one, a
45
+ * different `box-shadow`, crisp#563). Every other column stays label-only,
46
+ * the `"plain"` variant #534/#558 shipped.
40
47
  */
41
- function ThLabel({ label }) {
48
+ function ThLabel({ label, referencedType, }) {
42
49
  const ref = React.useRef(null);
43
50
  const [overflowing, setOverflowing] = React.useState(false);
44
51
  // biome-ignore lint/correctness/useExhaustiveDependencies: `label` isn't read in the body, but a rename (col.label changing with the column's own width unchanged) needs a re-measure a ResizeObserver on `el` won't fire for -- same reasoning as Tabs' own re-measure effect.
@@ -67,7 +74,9 @@ function ThLabel({ label }) {
67
74
  // actually show. `open={false}` when it fits keeps the Tooltip mounted
68
75
  // (asChild costs no extra DOM either way) but pins it permanently closed,
69
76
  // so hovering a non-overflowing header still opens nothing.
70
- return (_jsx(Tooltip, { content: label, asChild: true, open: overflowing ? undefined : false, children: _jsx("span", { ref: ref, className: "crisp-recordstable-th-label", children: label }) }));
77
+ const ReferenceIcon = referencedType?.icon;
78
+ const content = referencedType ? (_jsxs("span", { className: "crisp-tooltip-reference-row", children: [label, ReferenceIcon && _jsx(ReferenceIcon, { size: 16, strokeWidth: 1.75 }), referencedType.label] })) : (label);
79
+ return (_jsx(Tooltip, { content: content, asChild: true, variant: referencedType ? "reference" : "plain", open: overflowing ? undefined : false, children: _jsx("span", { ref: ref, className: "crisp-recordstable-th-label", children: label }) }));
71
80
  }
72
81
  const EDITABLE = new Set(["text", "number", "url", "currency"]);
73
82
  // types whose cell is an always-interactive picker (not double-click-to-edit)
@@ -1173,7 +1182,7 @@ export function RecordsTable({ labels, columns: columnsProp = defaultColumns, ro
1173
1182
  setDragCol(col.key);
1174
1183
  e.dataTransfer.effectAllowed = "move";
1175
1184
  }
1176
- : undefined, onDragEnd: () => setDragCol(null), onClick: (e) => openColumnMenu(col.key, e.currentTarget), children: [Icon ? _jsx(Icon, { size: 16, strokeWidth: 1.75 }) : null, _jsx(ThLabel, { label: col.label }), _jsx("span", { className: "crisp-recordstable-sort", children: _jsx("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: _jsx("path", { d: "m4 6 4 4 4-4" }) }) })] })), openMenu === `col:${col.key}` &&
1185
+ : undefined, onDragEnd: () => setDragCol(null), onClick: (e) => openColumnMenu(col.key, e.currentTarget), children: [Icon ? _jsx(Icon, { size: 16, strokeWidth: 1.75 }) : null, _jsx(ThLabel, { label: col.label, referencedType: col.referencedType }), _jsx("span", { className: "crisp-recordstable-sort", children: _jsx("svg", { width: "12", height: "12", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": "true", children: _jsx("path", { d: "m4 6 4 4 4-4" }) }) })] })), openMenu === `col:${col.key}` &&
1177
1186
  rootRef.current &&
1178
1187
  createPortal(_jsxs("div", { ref: menuRef, className: "crisp-recordstable-menu crisp-recordstable-menu--fixed crisp-recordstable-menu--hdr", role: "menu", "aria-label": `${col.label} column`, style: menuPos ?? undefined, onKeyDown: onMenuKeyDown, children: [_jsxs("button", { type: "button", className: "crisp-recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => sortBy(col.key, "asc"), children: [_jsx(ArrowUp, { size: 15, strokeWidth: 1.75 }), t.sortAscending] }), _jsxs("button", { type: "button", className: "crisp-recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => sortBy(col.key, "desc"), children: [_jsx(ArrowDown, { size: 15, strokeWidth: 1.75 }), t.sortDescending] }), _jsxs("button", { type: "button", className: "crisp-recordstable-menu-item", role: "menuitem", tabIndex: -1, disabled: i <= 1, onClick: () => moveColumn(col.key, -1), children: [_jsx(ArrowLeft, { "data-rtl-flip": "", size: 15, strokeWidth: 1.75 }), t.moveLeft] }), _jsxs("button", { type: "button", className: "crisp-recordstable-menu-item", role: "menuitem", tabIndex: -1, disabled: i === 0 || i >= cols.length - 1, onClick: () => moveColumn(col.key, 1), children: [_jsx(ArrowRight, { "data-rtl-flip": "", size: 15, strokeWidth: 1.75 }), t.moveRight] }), _jsx("div", { className: "crisp-recordstable-menu-sep" }), _jsxs("button", { type: "button", className: "crisp-recordstable-menu-item", role: "menuitem", tabIndex: -1, onClick: () => {
1179
1188
  setRenamingCol(col.key);
@@ -123,6 +123,17 @@ export const RichTextEditor = React.forwardRef(function RichTextEditor({ default
123
123
  // The Range covering a live "/query" (trigger included), mirroring the
124
124
  // mention primitive below — only set in "slash" mode.
125
125
  const slashRangeRef = React.useRef(null);
126
+ // #672: the selection the user actually had when they opened the toolbar's
127
+ // "Body ▾" menu — only set in "toolbar" mode. That menu's own search
128
+ // input steals DOM focus from the body the moment it opens, so by the
129
+ // time an item is picked, `window.getSelection()` no longer points at
130
+ // the block the user meant; without this, `run()`'s `body.focus()`
131
+ // resets the caret to the start of the document (the FIRST block)
132
+ // instead, and every conversion landed there regardless of what was
133
+ // selected. Cloned, since the live Range would otherwise track DOM
134
+ // mutations (and the selection moving to the search input) between now
135
+ // and when it's restored.
136
+ const toolbarSelectionRef = React.useRef(null);
126
137
  const blockSearchRef = React.useRef(null);
127
138
  const [mention, setMention] = React.useState(null);
128
139
  // The Range covering the live "@query" text (trigger included) — kept
@@ -228,6 +239,7 @@ export const RichTextEditor = React.forwardRef(function RichTextEditor({ default
228
239
  }, [readOnly]);
229
240
  const closeBlockMenu = React.useCallback(() => {
230
241
  slashRangeRef.current = null;
242
+ toolbarSelectionRef.current = null;
231
243
  setBlockMenu(null);
232
244
  }, []);
233
245
  /** Mirrors the mention-trigger detection below: only handles the caret
@@ -406,6 +418,14 @@ export const RichTextEditor = React.forwardRef(function RichTextEditor({ default
406
418
  sel?.removeAllRanges();
407
419
  sel?.addRange(range);
408
420
  }
421
+ else if (blockMenu?.mode === "toolbar" && toolbarSelectionRef.current) {
422
+ // #672: no trigger text to delete here (nothing was typed to open
423
+ // this menu) -- just put the selection back where the user actually
424
+ // had it, before `applyBlockItem` -> `run()` refocuses the body.
425
+ const sel = window.getSelection();
426
+ sel?.removeAllRanges();
427
+ sel?.addRange(toolbarSelectionRef.current);
428
+ }
409
429
  applyBlockItem(item.id);
410
430
  closeBlockMenu();
411
431
  };
@@ -428,14 +448,21 @@ export const RichTextEditor = React.forwardRef(function RichTextEditor({ default
428
448
  const toggleBlockMenuFromToolbar = () => {
429
449
  if (!bar)
430
450
  return;
431
- setBlockMenu((cur) => cur?.mode === "toolbar"
432
- ? null
433
- : {
451
+ setBlockMenu((cur) => {
452
+ if (cur?.mode === "toolbar")
453
+ return null;
454
+ // #672: capture the live selection NOW, before the menu's own search
455
+ // input (focused by the effect below) steals it away.
456
+ const sel = window.getSelection();
457
+ toolbarSelectionRef.current =
458
+ sel && sel.rangeCount > 0 ? sel.getRangeAt(0).cloneRange() : null;
459
+ return {
434
460
  anchor: { x: bar.x, y: bar.y },
435
461
  query: "",
436
462
  highlighted: 0,
437
463
  mode: "toolbar",
438
- });
464
+ };
465
+ });
439
466
  };
440
467
  // biome-ignore lint/correctness/useExhaustiveDependencies: syncEmpty only reads/writes stable refs, same as run()'s own call to it just below
441
468
  const insertMention = React.useCallback((item) => {
package/dist/styles.css CHANGED
@@ -679,10 +679,14 @@
679
679
  & .crisp-datatable-tag[data-tone="orange"] { background: color-mix(in srgb, var(--crisp-bg-orange) 16%, transparent); color: color-mix(in srgb, var(--crisp-bg-orange) 82%, var(--crisp-fg-primary)); }
680
680
  & .crisp-datatable-num { font-variant-numeric: tabular-nums; }
681
681
  & .crisp-datatable-muted { color: var(--crisp-fg-muted); }
682
- /* row drag-to-reorder -- a Settings > Objects > Attributes screen measures
683
- a leading 20px grip column, a 16px grip icon, and the row's own hairline doubling as the drop indicator (it tints toward the brand colour as the active drop target). The affordance + geometry are measured; the drag MECHANISM is crisp's existing house idiom -- HTML5
684
- draggable + dataTransfer, the same one column reorder already uses
685
- (RecordsTable's reorderColumn) -- not a proprietary pointer-events implementation. */
682
+ /* url/email cell types (#654) -- same measured values as RecordsTable's own .crisp-recordstable-link, which this mirrors: no underline at rest, underline on hover, own-colour text rather than a browser-blue link. */
683
+ & .crisp-datatable-link { color: var(--crisp-fg-primary); text-decoration: none; overflow: hidden; text-overflow: ellipsis; }
684
+ & .crisp-datatable-link:hover { text-decoration: underline; }
685
+ /* row drag-to-reorder -- a Settings > Objects > Attributes screen measures a leading 20px grip column, a 16px grip icon, and the row's own
686
+ hairline doubling as the drop indicator (it tints toward the brand
687
+ colour as the active drop target). The affordance + geometry are
688
+ measured; the drag MECHANISM is crisp's existing house idiom -- HTML5 draggable + dataTransfer, the same one column reorder already uses (RecordsTable's reorderColumn) -- not a proprietary
689
+ pointer-events implementation. */
686
690
  & .crisp-datatable-col-grip { width: 20px; padding: 0; text-align: center; }
687
691
  & .crisp-datatable-grip { display: inline-flex; align-items: center; justify-content: center; width: 100%; height: 100%; margin: 0; border: none; border-radius: 0; background: none; font-family: inherit; color: var(--crisp-fg-muted); cursor: grab; }
688
692
  & .crisp-datatable-grip:active { cursor: grabbing; }
@@ -1892,6 +1896,13 @@
1892
1896
  .crisp-tooltip {
1893
1897
  position: fixed; z-index: 70; box-sizing: border-box; pointer-events: none; max-width: 260px; padding: var(--crisp-space-1-5) var(--crisp-space-2); border-radius: var(--crisp-radius-md); background: #101112; color: #eeeff1; font-family: var(--crisp-font-sans); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); line-height: 1.45; box-shadow: 0 4px 14px -2px rgba(28,40,64,0.28), 0 0 0 1px rgba(255,255,255,0.04); opacity: 1; transition: opacity 0.12s ease;
1894
1898
  @starting-style { opacity: 0; }
1899
+ & .crisp-tooltip-reference-row { display: inline-flex; align-items: center; gap: var(--crisp-space-1); }
1900
+ }
1901
+ .crisp-tooltip--variant_plain {
1902
+
1903
+ }
1904
+ .crisp-tooltip--variant_reference {
1905
+ box-shadow: rgba(0,0,0,0) 0 0 0 1px inset, rgba(28,40,64,0.18) 0 0 2px 0, rgba(0,0,0,0.04) 0 1px 3px 0;
1895
1906
  }
1896
1907
 
1897
1908
  @media (prefers-reduced-motion: reduce) {
@@ -1966,14 +1977,18 @@
1966
1977
 
1967
1978
  .crisp-authshell {
1968
1979
  min-height: 100%; box-sizing: border-box; display: flex; flex-direction: column; align-items: center; position: relative; background: var(--crisp-bg-surface); font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); line-height: 20px; color: var(--crisp-fg-primary); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
1980
+ &[data-single-pane] { justify-content: center; }
1969
1981
  & .crisp-authshell-progress { position: absolute; top: 0; left: 0; width: 100%; height: 2px; background: var(--crisp-border-default); }
1970
1982
  & .crisp-authshell-progress-fill { height: 2px; background: var(--crisp-fg-primary); border-radius: 4px; transition: width .2s; }
1971
1983
  & .crisp-authshell-logo { margin-top: var(--crisp-space-8); height: 24px; display: flex; align-items: center; }
1972
1984
  & .crisp-authshell-card { margin-top: var(--crisp-space-6); display: flex; height: 692px; border-radius: 20px; border: 1px solid var(--crisp-border-subtle); overflow: hidden; }
1985
+ &[data-single-pane] .crisp-authshell-card { height: auto; border: none; border-radius: 0; overflow: visible; }
1973
1986
  & .crisp-authshell-pane { flex: 0 0 564px; padding: 72px; display: flex; flex-direction: column; box-sizing: border-box; }
1987
+ &[data-single-pane] .crisp-authshell-pane { flex: 0 0 auto; padding: 0; }
1974
1988
  & .crisp-authshell-preview { flex: 0 0 564px; padding: 96px 64px; background: var(--crisp-bg-subtle); border-inline-start: 1px solid color-mix(in srgb, var(--crisp-fg-primary) 5%, transparent); position: relative; overflow: hidden; box-sizing: border-box; }
1975
1989
  & .crisp-authshell-preview::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 120px; background: linear-gradient(color-mix(in srgb, var(--crisp-bg-subtle) 0%, transparent) 0%, var(--crisp-bg-subtle) 100%); pointer-events: none; }
1976
1990
  & .crisp-authshell-body { width: 420px; margin: 0 auto; display: flex; flex-direction: column; gap: var(--crisp-space-8); align-items: center; }
1991
+ &[data-single-pane] .crisp-authshell-body { width: 360px; }
1977
1992
  & .crisp-authshell-title { font-size: 20px; line-height: 24px; font-weight: var(--crisp-weight-semibold); letter-spacing: -0.4px; color: var(--crisp-fg-primary); align-self: stretch; }
1978
1993
  & .crisp-authshell-form { align-self: stretch; display: flex; flex-direction: column; gap: 28px; }
1979
1994
  & .crisp-authshell-footer { margin-top: var(--crisp-space-6); display: flex; align-items: center; gap: var(--crisp-space-1); font-size: var(--crisp-text-sm); line-height: 16px; color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
@@ -680,10 +680,14 @@
680
680
  & .crisp-datatable-tag[data-tone="orange"] { background: color-mix(in srgb, var(--crisp-bg-orange) 16%, transparent); color: color-mix(in srgb, var(--crisp-bg-orange) 82%, var(--crisp-fg-primary)); }
681
681
  & .crisp-datatable-num { font-variant-numeric: tabular-nums; }
682
682
  & .crisp-datatable-muted { color: var(--crisp-fg-muted); }
683
- /* row drag-to-reorder -- a Settings > Objects > Attributes screen measures
684
- a leading 20px grip column, a 16px grip icon, and the row's own hairline doubling as the drop indicator (it tints toward the brand colour as the active drop target). The affordance + geometry are measured; the drag MECHANISM is crisp's existing house idiom -- HTML5
685
- draggable + dataTransfer, the same one column reorder already uses
686
- (RecordsTable's reorderColumn) -- not a proprietary pointer-events implementation. */
683
+ /* url/email cell types (#654) -- same measured values as RecordsTable's own .crisp-recordstable-link, which this mirrors: no underline at rest, underline on hover, own-colour text rather than a browser-blue link. */
684
+ & .crisp-datatable-link { color: var(--crisp-fg-primary); text-decoration: none; overflow: hidden; text-overflow: ellipsis; }
685
+ & .crisp-datatable-link:hover { text-decoration: underline; }
686
+ /* row drag-to-reorder -- a Settings > Objects > Attributes screen measures a leading 20px grip column, a 16px grip icon, and the row's own
687
+ hairline doubling as the drop indicator (it tints toward the brand
688
+ colour as the active drop target). The affordance + geometry are
689
+ measured; the drag MECHANISM is crisp's existing house idiom -- HTML5 draggable + dataTransfer, the same one column reorder already uses (RecordsTable's reorderColumn) -- not a proprietary
690
+ pointer-events implementation. */
687
691
  & .crisp-datatable-col-grip { width: 20px; padding: 0; text-align: center; }
688
692
  & .crisp-datatable-grip { display: inline-flex; align-items: center; justify-content: center; width: 100%; height: 100%; margin: 0; border: none; border-radius: 0; background: none; font-family: inherit; color: var(--crisp-fg-muted); cursor: grab; }
689
693
  & .crisp-datatable-grip:active { cursor: grabbing; }
@@ -1893,6 +1897,13 @@
1893
1897
  .crisp-tooltip {
1894
1898
  position: fixed; z-index: 70; box-sizing: border-box; pointer-events: none; max-width: 260px; padding: var(--crisp-space-1-5) var(--crisp-space-2); border-radius: var(--crisp-radius-md); background: #101112; color: #eeeff1; font-family: var(--crisp-font-sans); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); line-height: 1.45; box-shadow: 0 4px 14px -2px rgba(28,40,64,0.28), 0 0 0 1px rgba(255,255,255,0.04); opacity: 1; transition: opacity 0.12s ease;
1895
1899
  @starting-style { opacity: 0; }
1900
+ & .crisp-tooltip-reference-row { display: inline-flex; align-items: center; gap: var(--crisp-space-1); }
1901
+ }
1902
+ .crisp-tooltip--variant_plain {
1903
+
1904
+ }
1905
+ .crisp-tooltip--variant_reference {
1906
+ box-shadow: rgba(0,0,0,0) 0 0 0 1px inset, rgba(28,40,64,0.18) 0 0 2px 0, rgba(0,0,0,0.04) 0 1px 3px 0;
1896
1907
  }
1897
1908
 
1898
1909
  @media (prefers-reduced-motion: reduce) {
@@ -1967,14 +1978,18 @@
1967
1978
 
1968
1979
  .crisp-authshell {
1969
1980
  min-height: 100%; box-sizing: border-box; display: flex; flex-direction: column; align-items: center; position: relative; background: var(--crisp-bg-surface); font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); line-height: 20px; color: var(--crisp-fg-primary); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
1981
+ &[data-single-pane] { justify-content: center; }
1970
1982
  & .crisp-authshell-progress { position: absolute; top: 0; left: 0; width: 100%; height: 2px; background: var(--crisp-border-default); }
1971
1983
  & .crisp-authshell-progress-fill { height: 2px; background: var(--crisp-fg-primary); border-radius: 4px; transition: width .2s; }
1972
1984
  & .crisp-authshell-logo { margin-top: var(--crisp-space-8); height: 24px; display: flex; align-items: center; }
1973
1985
  & .crisp-authshell-card { margin-top: var(--crisp-space-6); display: flex; height: 692px; border-radius: 20px; border: 1px solid var(--crisp-border-subtle); overflow: hidden; }
1986
+ &[data-single-pane] .crisp-authshell-card { height: auto; border: none; border-radius: 0; overflow: visible; }
1974
1987
  & .crisp-authshell-pane { flex: 0 0 564px; padding: 72px; display: flex; flex-direction: column; box-sizing: border-box; }
1988
+ &[data-single-pane] .crisp-authshell-pane { flex: 0 0 auto; padding: 0; }
1975
1989
  & .crisp-authshell-preview { flex: 0 0 564px; padding: 96px 64px; background: var(--crisp-bg-subtle); border-inline-start: 1px solid color-mix(in srgb, var(--crisp-fg-primary) 5%, transparent); position: relative; overflow: hidden; box-sizing: border-box; }
1976
1990
  & .crisp-authshell-preview::after { content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 120px; background: linear-gradient(color-mix(in srgb, var(--crisp-bg-subtle) 0%, transparent) 0%, var(--crisp-bg-subtle) 100%); pointer-events: none; }
1977
1991
  & .crisp-authshell-body { width: 420px; margin: 0 auto; display: flex; flex-direction: column; gap: var(--crisp-space-8); align-items: center; }
1992
+ &[data-single-pane] .crisp-authshell-body { width: 360px; }
1978
1993
  & .crisp-authshell-title { font-size: 20px; line-height: 24px; font-weight: var(--crisp-weight-semibold); letter-spacing: -0.4px; color: var(--crisp-fg-primary); align-self: stretch; }
1979
1994
  & .crisp-authshell-form { align-self: stretch; display: flex; flex-direction: column; gap: 28px; }
1980
1995
  & .crisp-authshell-footer { margin-top: var(--crisp-space-6); display: flex; align-items: center; gap: var(--crisp-space-1); font-size: var(--crisp-text-sm); line-height: 16px; color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
package/dist/tooltip.css CHANGED
@@ -1,6 +1,13 @@
1
1
  .crisp-tooltip {
2
2
  position: fixed; z-index: 70; box-sizing: border-box; pointer-events: none; max-width: 260px; padding: var(--crisp-space-1-5) var(--crisp-space-2); border-radius: var(--crisp-radius-md); background: #101112; color: #eeeff1; font-family: var(--crisp-font-sans); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); line-height: 1.45; box-shadow: 0 4px 14px -2px rgba(28,40,64,0.28), 0 0 0 1px rgba(255,255,255,0.04); opacity: 1; transition: opacity 0.12s ease;
3
3
  @starting-style { opacity: 0; }
4
+ & .crisp-tooltip-reference-row { display: inline-flex; align-items: center; gap: var(--crisp-space-1); }
5
+ }
6
+ .crisp-tooltip--variant_plain {
7
+
8
+ }
9
+ .crisp-tooltip--variant_reference {
10
+ box-shadow: rgba(0,0,0,0) 0 0 0 1px inset, rgba(28,40,64,0.18) 0 0 2px 0, rgba(0,0,0,0.04) 0 1px 3px 0;
4
11
  }
5
12
 
6
13
  @media (prefers-reduced-motion: reduce) {
package/dist/tooltip.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { RecipeFn } from "../core/types";
2
2
 
3
3
  export interface TooltipVariants {
4
-
4
+ variant?: "plain" | "reference";
5
5
  }
6
6
 
7
- export declare const tooltipRecipe: RecipeFn<{ }>;
7
+ export declare const tooltipRecipe: RecipeFn<{ variant: { plain: string; reference: string } }>;
@@ -2,6 +2,13 @@
2
2
  .crisp-tooltip {
3
3
  position: fixed; z-index: 70; box-sizing: border-box; pointer-events: none; max-width: 260px; padding: var(--crisp-space-1-5) var(--crisp-space-2); border-radius: var(--crisp-radius-md); background: #101112; color: #eeeff1; font-family: var(--crisp-font-sans); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); line-height: 1.45; box-shadow: 0 4px 14px -2px rgba(28,40,64,0.28), 0 0 0 1px rgba(255,255,255,0.04); opacity: 1; transition: opacity 0.12s ease;
4
4
  @starting-style { opacity: 0; }
5
+ & .crisp-tooltip-reference-row { display: inline-flex; align-items: center; gap: var(--crisp-space-1); }
6
+ }
7
+ .crisp-tooltip--variant_plain {
8
+
9
+ }
10
+ .crisp-tooltip--variant_reference {
11
+ box-shadow: rgba(0,0,0,0) 0 0 0 1px inset, rgba(28,40,64,0.18) 0 0 2px 0, rgba(0,0,0,0.04) 0 1px 3px 0;
5
12
  }
6
13
 
7
14
  @media (prefers-reduced-motion: reduce) {
@@ -22,7 +22,20 @@ export type DataEditableOption = {
22
22
  * `editor: "select"`. */
23
23
  avatar?: string;
24
24
  };
25
- export type DataCellType = "text" | "entity" | "tags" | "status" | "date" | "datetime" | "number" | "currency";
25
+ export type DataCellType = "text" | "entity" | "tags" | "status" | "date" | "datetime" | "number" | "currency"
26
+ /** Opens `https://{value}` in a new tab -- matches RecordsTable's own
27
+ * `url` type exactly (crisp/#654): `color: fgPrimary`, no underline at
28
+ * rest, underline on hover. A click stops propagation, same as an
29
+ * `entity` column's `onEntityClick` link, so it doesn't also fire the
30
+ * row's own `onRowClick`. */
31
+ | "url"
32
+ /** Opens `mailto:{value}`, same link styling as `url`. Attio confirms
33
+ * this is a distinct column type ("Primary email address", checklist
34
+ * G-042), but its editor was never opened live in the measured
35
+ * workspace -- this type is display-only for now; editing still falls
36
+ * through to the plain-text editor every other non-number/date type
37
+ * gets, not a dedicated one (see #597 for when that's measured). */
38
+ | "email";
26
39
  export type DataTableColumn<T> = {
27
40
  key: string;
28
41
  /** Truncates with an ellipsis when it's wider than the column (#534: a
@@ -35,6 +48,17 @@ export type DataTableColumn<T> = {
35
48
  type?: DataCellType;
36
49
  /** A leading field-type icon in the header (matches RecordsTable). */
37
50
  icon?: IconType;
51
+ /** This column's values reference another type of record (e.g. a
52
+ * "Primary location" column of Country records). When the header
53
+ * overflows, its `Tooltip` appends this type's icon and name after the
54
+ * label and switches to Attio's second, reference-column tooltip chrome
55
+ * (crisp#580) — distinct from `icon` above, which is the column's own
56
+ * field-type glyph shown in the header row itself, not inside the
57
+ * tooltip. Omit for a plain column; the tooltip stays label-only (#534). */
58
+ referencedType?: {
59
+ icon: IconType;
60
+ label: string;
61
+ };
38
62
  /** Currency symbol for `currency` columns (default "$"). */
39
63
  symbol?: string;
40
64
  /** Allow drag-to-resize on this column (default true). */
@@ -51,17 +75,25 @@ export type DataTableColumn<T> = {
51
75
  editable?: boolean;
52
76
  /** The inline editor an `editable` column opens. Omitted: a plain field
53
77
  * inferred from `type` (number/date/datetime/text). `"select"`/
54
- * `"reference"` render crisp's own `Select` the same way — at rest the
55
- * cell shows the column's `render`/formatted value like every other
56
- * editable type (#453), and double-click (or Enter on the selected
78
+ * `"reference"`/`"actor"` render crisp's own `Select` the same way — at
79
+ * rest the cell shows the column's `render`/formatted value like every
80
+ * other editable type (#453), and double-click (or Enter on the selected
57
81
  * cell) opens the picker; choosing a value commits and closes it,
58
- * Escape cancels. `select` reads `options`; `reference` reads
59
- * `refOptions` and is search-enabled, the same searchable-`Select`
82
+ * Escape cancels. `select` reads `options`; `reference`/`actor` both
83
+ * read `refOptions` and are search-enabled, the same searchable-`Select`
60
84
  * shape as `RecordsTable`'s own `ReferenceCell`. `"switch"` is the one
61
85
  * exception — ALWAYS its own live widget, no double-click gate, since
62
86
  * flipping the toggle IS the commit gesture and there's no meaningfully
63
- * different "rest" display for a boolean. */
64
- editor?: "select" | "switch" | "reference";
87
+ * different "rest" display for a boolean.
88
+ *
89
+ * `"actor"` (crisp/#654, checklist G-039) is `"reference"` with Attio's
90
+ * own measured person-picker copy as the default instead of a generic
91
+ * one: `refSearchPlaceholder` defaults to "Search people…" and the
92
+ * empty-search state reads "No users" rather than `Select`'s own
93
+ * generic "No results" — both still overridable the normal way.
94
+ * Otherwise identical to `reference`: same `refOptions` shape, same
95
+ * `onCreateReference`/`createLabel` "Create X" row. */
96
+ editor?: "select" | "switch" | "reference" | "actor";
65
97
  /** Options for `editor: "select"` (value/label, an optional `color` a
66
98
  * caller's own `render` can read) — same shape as RecordsTable's
67
99
  * `Column.options` for its `status` picker. */
@@ -72,10 +104,17 @@ export type DataTableColumn<T> = {
72
104
  * RecordsTable's own `ReferenceCell`, which this picker mirrors. */
73
105
  refOptions?: DataEditableOption[];
74
106
  /** Placeholder for `editor: "reference"`'s search input. Defaults to
75
- * `Select`'s own generic "Search…" — a generic primitive doesn't hardcode
76
- * Attio's own copy ("Search records..."), which is workspace/object
77
- * text, not a crisp-measured value. */
107
+ * `Select`'s own generic "Search…" for `reference` — a generic primitive
108
+ * doesn't hardcode Attio's own copy ("Search records..."), which is
109
+ * workspace/object text, not a crisp-measured value. `editor: "actor"`
110
+ * defaults this to the measured "Search people…" instead (still
111
+ * overridable here). */
78
112
  refSearchPlaceholder?: string;
113
+ /** `editor: "reference"`/`"actor"`'s empty-search-results text. Defaults
114
+ * to `Select`'s own generic "No results" for `reference`; `"actor"`
115
+ * defaults this to the measured "No users" (checklist G-039 — a real
116
+ * empty-search state on an empty query, not an error). */
117
+ refEmptyLabel?: string;
79
118
  /** Adds a trailing "Create {createLabel}" row to `editor: "reference"`'s
80
119
  * picker (Attio's own always has one, #449) — rendered only when this is
81
120
  * provided. Picking it hands back the picker's current search text, e.g.
@@ -11,6 +11,14 @@ export type PersonChipProps = Omit<React.ComponentPropsWithoutRef<"span">, "chil
11
11
  * Applied to the name only; the avatar keeps its full-strength tone so
12
12
  * the chip still reads as a distinct person, not a disabled control. */
13
13
  muted?: boolean;
14
+ /** One more thing after the name that must never shrink or wrap out of
15
+ * view (an icon, a small badge) — a consumer needing this used to wrap
16
+ * `PersonChip` in its own flex row for it, which needed `min-width: 0`
17
+ * of its own to keep shrinking correctly and, missing that, overflowed
18
+ * its container the same way #668 did before that fix (#683). Rendered
19
+ * inside the chip, after the name, so it shares `Chip`'s own flex/
20
+ * shrink layout instead of wrapping it in a second, unconstrained one. */
21
+ trailing?: React.ReactNode;
14
22
  className?: string;
15
23
  };
16
24
  /**
@@ -29,9 +37,13 @@ export type PersonChipProps = Omit<React.ComponentPropsWithoutRef<"span">, "chil
29
37
  * Composes `Chip`'s `truncate` CSS directly rather than its string-only
30
38
  * auto-wrap (children here is an avatar plus a name, not a single string),
31
39
  * so the name gets its own `.crisp-chip-label` span while the avatar stays
32
- * fixed-size.
40
+ * fixed-size. `trailing` (#683) is the same idea for a consumer's own
41
+ * icon/badge after the name — inside the chip's own flex row rather than a
42
+ * second one a consumer would otherwise wrap this in, which needs its own
43
+ * `min-width: 0` to keep shrinking correctly and, missing that, overflows
44
+ * its container the same way #668 did before `Chip` itself was fixed.
33
45
  */
34
- export declare function PersonChip({ name, avatar, muted, className, ...props }: PersonChipProps): React.JSX.Element;
46
+ export declare function PersonChip({ name, avatar, muted, trailing, className, ...props }: PersonChipProps): React.JSX.Element;
35
47
  export declare namespace PersonChip {
36
48
  var displayName: string;
37
49
  }
@@ -33,6 +33,14 @@ export type TooltipProps = Omit<React.ComponentPropsWithoutRef<"span">, "content
33
33
  * `ContextMenu`'s.
34
34
  */
35
35
  asChild?: boolean;
36
+ /**
37
+ * Which of Attio's two measured tooltip shadows to use (crisp#580) —
38
+ * background/colour/radius/padding/font are identical between them, only
39
+ * the `box-shadow` differs. `"reference"` is for a reference-column
40
+ * header's tooltip (icon + the referenced type's name); every other
41
+ * tooltip in crisp is `"plain"`, the default.
42
+ */
43
+ variant?: "plain" | "reference";
36
44
  };
37
45
  /**
38
46
  * A hover/focus tooltip — a dark hint chip measured live. Wraps its
@@ -85,4 +93,12 @@ export declare const Tooltip: React.ForwardRefExoticComponent<Omit<Omit<React.De
85
93
  * `ContextMenu`'s.
86
94
  */
87
95
  asChild?: boolean;
96
+ /**
97
+ * Which of Attio's two measured tooltip shadows to use (crisp#580) —
98
+ * background/colour/radius/padding/font are identical between them, only
99
+ * the `box-shadow` differs. `"reference"` is for a reference-column
100
+ * header's tooltip (icon + the referenced type's name); every other
101
+ * tooltip in crisp is `"plain"`, the default.
102
+ */
103
+ variant?: "plain" | "reference";
88
104
  } & React.RefAttributes<HTMLElement>>;
@@ -23,9 +23,37 @@ declare const spec: {
23
23
  readonly decls: {
24
24
  readonly opacity: 0;
25
25
  };
26
+ }, {
27
+ readonly selector: "& .crisp-tooltip-reference-row";
28
+ readonly decls: {
29
+ readonly display: "inline-flex";
30
+ readonly alignItems: "center";
31
+ readonly gap: `var(--${string})`;
32
+ };
26
33
  }];
34
+ readonly variants: {
35
+ readonly variant: {
36
+ readonly plain: {};
37
+ readonly reference: {
38
+ readonly boxShadow: "rgba(0,0,0,0) 0 0 0 1px inset, rgba(28,40,64,0.18) 0 0 2px 0, rgba(0,0,0,0.04) 0 1px 3px 0";
39
+ };
40
+ };
41
+ };
42
+ readonly defaultVariants: {
43
+ readonly variant: "plain";
44
+ };
27
45
  };
28
- declare const config: import("../..").RecipeConfig<Record<string, Record<string, string>>>;
29
- export declare const tooltipRecipe: import("../..").RecipeFn<Record<string, Record<string, string>>>;
46
+ declare const config: import("../..").RecipeConfig<{
47
+ readonly variant: {
48
+ readonly plain: string;
49
+ readonly reference: string;
50
+ };
51
+ }>;
52
+ export declare const tooltipRecipe: import("../..").RecipeFn<{
53
+ readonly variant: {
54
+ readonly plain: string;
55
+ readonly reference: string;
56
+ };
57
+ }>;
30
58
  export { spec };
31
59
  export default config;
@@ -8,7 +8,10 @@ export type AuthShellProps = Omit<React.ComponentPropsWithoutRef<"div">, "title"
8
8
  title?: React.ReactNode;
9
9
  /** The form / body content. */
10
10
  children: React.ReactNode;
11
- /** Right-hand product preview. Hidden under 1200px; omit for a single-pane card. */
11
+ /** Right-hand product preview. Hidden under 1200px; omit for a single-pane
12
+ * layout — no card chrome (border/shadow/fixed height), a narrower
13
+ * 360px content column, and the whole shell vertically centred, matching
14
+ * the real single-pane sign-in screen (crisp#653). */
12
15
  preview?: React.ReactNode;
13
16
  /** Legal / support links under the card. */
14
17
  footer?: React.ReactNode;
@@ -28,6 +28,18 @@ export type Column = {
28
28
  * enriched values are washed (#9b69ff at 4%). */
29
29
  enriched?: boolean;
30
30
  icon?: IconType;
31
+ /** This column's values reference another type of record (e.g. a
32
+ * "Primary location" column of Country records). When the header
33
+ * overflows, its `Tooltip` appends this type's icon and name after the
34
+ * label and switches to Attio's second, reference-column tooltip chrome
35
+ * (crisp#580) — distinct from `icon` above (the column's own field-type
36
+ * glyph shown in the header row) and from `refOptions` (the picker's
37
+ * selectable values). Omit for a plain column; the tooltip stays
38
+ * label-only (#534). */
39
+ referencedType?: {
40
+ icon: IconType;
41
+ label: string;
42
+ };
31
43
  width?: number;
32
44
  /** Currency symbol for `currency` columns (default "$"). */
33
45
  symbol?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crisp-ui-kit/crisp",
3
- "version": "0.54.5",
3
+ "version": "0.54.6",
4
4
  "description": "A lean React design system, measured pixel-for-pixel from Attio.",
5
5
  "license": "MIT",
6
6
  "type": "module",