@crisp-ui-kit/crisp 0.20.2 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.onboardingRecipe = exports.OnboardingChecklist = void 0;
3
+ exports.onboardingRecipe = exports.onboardingLabels = exports.OnboardingChecklist = void 0;
4
4
  var onboarding_1 = require("./onboarding");
5
5
  Object.defineProperty(exports, "OnboardingChecklist", { enumerable: true, get: function () { return onboarding_1.OnboardingChecklist; } });
6
+ Object.defineProperty(exports, "onboardingLabels", { enumerable: true, get: function () { return onboarding_1.onboardingLabels; } });
6
7
  var onboarding_recipe_1 = require("./onboarding.recipe");
7
8
  Object.defineProperty(exports, "onboardingRecipe", { enumerable: true, get: function () { return onboarding_recipe_1.onboardingRecipe; } });
@@ -1,17 +1,27 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.onboardingLabels = void 0;
3
4
  exports.OnboardingChecklist = OnboardingChecklist;
4
5
  const jsx_runtime_1 = require("react/jsx-runtime");
5
6
  const lucide_react_1 = require("lucide-react");
6
7
  const onboarding_recipe_1 = require("./onboarding.recipe");
8
+ /**
9
+ * The English defaults for the two pieces of copy that are formatters rather
10
+ * than strings. Exported so a consumer can wrap one instead of restating it,
11
+ * and so the strings live in one place rather than inside the signature.
12
+ */
13
+ exports.onboardingLabels = {
14
+ progressText: (done, total) => ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("b", { children: [done, "/", total] }), " ", "steps completed"] })),
15
+ completedStepLabel: (label) => `${String(label)} — completed`,
16
+ };
7
17
  /**
8
18
  * An onboarding checklist card — measured from Attio's floating "Getting
9
19
  * started" widget. Title + optional menu/minimize, a segmented progress bar,
10
20
  * and step rows (completed rows strike through with a brand check).
11
21
  */
12
- function OnboardingChecklist({ title = "Getting started", steps, encouragement = "Keep it up!", onStepClick, onMenu, onMinimize, className, optionsLabel = "Checklist options", minimizeLabel = "Minimize", progressLabel = "Onboarding progress", }) {
22
+ function OnboardingChecklist({ title = "Getting started", steps, encouragement = "Keep it up!", onStepClick, onMenu, onMinimize, className, optionsLabel = "Checklist options", minimizeLabel = "Minimize", progressLabel = "Onboarding progress", progressText = exports.onboardingLabels.progressText, completedStepLabel = exports.onboardingLabels.completedStepLabel, }) {
13
23
  const total = steps.length;
14
24
  const done = steps.filter((s) => s.done).length;
15
- return ((0, jsx_runtime_1.jsxs)("div", { className: (0, onboarding_recipe_1.onboardingRecipe)({ className }), children: [(0, jsx_runtime_1.jsxs)("div", { className: "onboarding-head", children: [(0, jsx_runtime_1.jsx)("span", { className: "onboarding-title", children: title }), onMenu && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "onboarding-iconbtn", "aria-label": optionsLabel, onClick: onMenu, children: (0, jsx_runtime_1.jsx)(lucide_react_1.MoreHorizontal, { size: 16, strokeWidth: 1.75 }) })), onMinimize && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "onboarding-iconbtn", "aria-label": minimizeLabel, onClick: onMinimize, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { size: 16, strokeWidth: 1.75 }) }))] }), (0, jsx_runtime_1.jsxs)("span", { className: "onboarding-sub", children: [(0, jsx_runtime_1.jsxs)("b", { children: [done, "/", total] }), " ", "steps completed", encouragement ? (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [" \u00B7 ", encouragement] }) : null] }), (0, jsx_runtime_1.jsx)("div", { className: "onboarding-progress", role: "progressbar", "aria-valuenow": done, "aria-valuemin": 0, "aria-valuemax": total, "aria-label": progressLabel, children: Array.from({ length: total }, (_, i) => ((0, jsx_runtime_1.jsx)("span", { className: "onboarding-seg", "data-done": i < done ? "" : undefined }, i))) }), (0, jsx_runtime_1.jsx)("div", { className: "onboarding-steps", children: steps.map((s) => ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "onboarding-step", "data-done": s.done ? "" : undefined, disabled: s.done, onClick: s.done ? undefined : () => onStepClick?.(s.id), children: [(0, jsx_runtime_1.jsx)("span", { className: "onboarding-step-icon", children: s.icon }), (0, jsx_runtime_1.jsx)("span", { className: "onboarding-step-label", children: s.label }), (0, jsx_runtime_1.jsx)("span", { className: "onboarding-step-check", children: s.done && (0, jsx_runtime_1.jsx)(lucide_react_1.Check, { size: 12, strokeWidth: 3 }) })] }, s.id))) })] }));
25
+ return ((0, jsx_runtime_1.jsxs)("div", { className: (0, onboarding_recipe_1.onboardingRecipe)({ className }), children: [(0, jsx_runtime_1.jsxs)("div", { className: "onboarding-head", children: [(0, jsx_runtime_1.jsx)("span", { className: "onboarding-title", children: title }), onMenu && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "onboarding-iconbtn", "aria-label": optionsLabel, onClick: onMenu, children: (0, jsx_runtime_1.jsx)(lucide_react_1.MoreHorizontal, { size: 16, strokeWidth: 1.75 }) })), onMinimize && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "onboarding-iconbtn", "aria-label": minimizeLabel, onClick: onMinimize, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { size: 16, strokeWidth: 1.75 }) }))] }), (0, jsx_runtime_1.jsxs)("span", { className: "onboarding-sub", children: [progressText(done, total), encouragement ? (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [" \u00B7 ", encouragement] }) : null] }), (0, jsx_runtime_1.jsx)("div", { className: "onboarding-progress", role: "progressbar", "aria-valuenow": done, "aria-valuemin": 0, "aria-valuemax": total, "aria-label": progressLabel, children: Array.from({ length: total }, (_, i) => ((0, jsx_runtime_1.jsx)("span", { className: "onboarding-seg", "data-done": i < done ? "" : undefined }, i))) }), (0, jsx_runtime_1.jsx)("div", { className: "onboarding-steps", children: steps.map((s) => ((0, jsx_runtime_1.jsxs)("button", { type: "button", className: "onboarding-step", "data-done": s.done ? "" : undefined, "aria-label": s.done ? completedStepLabel(s.label) : undefined, disabled: s.done, onClick: s.done ? undefined : () => onStepClick?.(s.id), children: [(0, jsx_runtime_1.jsx)("span", { className: "onboarding-step-icon", children: s.icon }), (0, jsx_runtime_1.jsx)("span", { className: "onboarding-step-label", children: s.label }), (0, jsx_runtime_1.jsx)("span", { className: "onboarding-step-check", children: s.done && (0, jsx_runtime_1.jsx)(lucide_react_1.Check, { size: 12, strokeWidth: 3 }) })] }, s.id))) })] }));
16
26
  }
17
27
  OnboardingChecklist.displayName = "OnboardingChecklist";
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.recordpageRecipe = exports.RecordPage = void 0;
3
+ exports.recordpageRecipe = exports.recordPageLabels = exports.RecordPage = void 0;
4
4
  var recordpage_1 = require("./recordpage");
5
5
  Object.defineProperty(exports, "RecordPage", { enumerable: true, get: function () { return recordpage_1.RecordPage; } });
6
+ Object.defineProperty(exports, "recordPageLabels", { enumerable: true, get: function () { return recordpage_1.recordPageLabels; } });
6
7
  var recordpage_recipe_1 = require("./recordpage.recipe");
7
8
  Object.defineProperty(exports, "recordpageRecipe", { enumerable: true, get: function () { return recordpage_recipe_1.recordpageRecipe; } });
@@ -119,6 +119,8 @@ exports.recordPageLabels = {
119
119
  },
120
120
  domainSetTo: (value) => ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["Domain set to ", (0, jsx_runtime_1.jsx)("b", { children: value })] })),
121
121
  taggedCategories: (count) => ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["Tagged ", (0, jsx_runtime_1.jsx)("b", { children: count }), " categories"] })),
122
+ recordCreated: (name) => ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("b", { children: name }), " record was created"] })),
123
+ noEmailsWith: (name) => (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: ["No emails logged with ", name, " yet."] }),
122
124
  };
123
125
  function TagsValue({ values }) {
124
126
  const [expanded, setExpanded] = React.useState(false);
@@ -367,13 +369,13 @@ function RecordPage({ record, columns, collection = "Records", labels, onBack, o
367
369
  : null,
368
370
  {
369
371
  id: "created",
370
- text: ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)("b", { children: name }), " record was created"] })),
372
+ text: t.recordCreated(name),
371
373
  time: "1 day ago",
372
374
  },
373
375
  ].filter(Boolean);
374
376
  const shownActivity = showAllActivity ? activity : activity.slice(0, 2);
375
377
  const activityFeed = ((0, jsx_runtime_1.jsxs)("section", { children: [(0, jsx_runtime_1.jsx)(SectionHead, { title: t.tabs.activity, action: activity.length > 2 ? ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "recordpage-viewall recordpage-viewall--inline", onClick: () => setShowAllActivity((s) => !s), children: showAllActivity ? t.showLess : t.viewAll })) : undefined }), (0, jsx_runtime_1.jsx)("div", { className: "recordpage-feed", children: shownActivity.map((a) => ((0, jsx_runtime_1.jsxs)("div", { className: "recordpage-feed-item", children: [(0, jsx_runtime_1.jsx)("span", { className: "recordpage-feed-dot", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Activity, { size: 13, strokeWidth: 2 }) }), (0, jsx_runtime_1.jsxs)("div", { className: "recordpage-feed-body", children: [a.text, (0, jsx_runtime_1.jsx)("div", { className: "recordpage-feed-time", children: a.time })] })] }, a.id))) })] }));
376
- const emailsEmpty = ((0, jsx_runtime_1.jsxs)(EmptyCard, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Inbox, { size: 22, strokeWidth: 1.5 }), children: ["No emails logged with ", name, " yet."] }));
378
+ const emailsEmpty = ((0, jsx_runtime_1.jsx)(EmptyCard, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Inbox, { size: 22, strokeWidth: 1.5 }), children: t.noEmailsWith(name) }));
377
379
  const panel = (active) => {
378
380
  switch (active) {
379
381
  case "overview":
@@ -1,2 +1,2 @@
1
- export { OnboardingChecklist, } from "./onboarding.js";
1
+ export { OnboardingChecklist, onboardingLabels, } from "./onboarding.js";
2
2
  export { onboardingRecipe } from "./onboarding.recipe.js";
@@ -1,14 +1,23 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Check, ChevronDown, MoreHorizontal } from "lucide-react";
3
3
  import { onboardingRecipe } from "./onboarding.recipe.js";
4
+ /**
5
+ * The English defaults for the two pieces of copy that are formatters rather
6
+ * than strings. Exported so a consumer can wrap one instead of restating it,
7
+ * and so the strings live in one place rather than inside the signature.
8
+ */
9
+ export const onboardingLabels = {
10
+ progressText: (done, total) => (_jsxs(_Fragment, { children: [_jsxs("b", { children: [done, "/", total] }), " ", "steps completed"] })),
11
+ completedStepLabel: (label) => `${String(label)} — completed`,
12
+ };
4
13
  /**
5
14
  * An onboarding checklist card — measured from Attio's floating "Getting
6
15
  * started" widget. Title + optional menu/minimize, a segmented progress bar,
7
16
  * and step rows (completed rows strike through with a brand check).
8
17
  */
9
- export function OnboardingChecklist({ title = "Getting started", steps, encouragement = "Keep it up!", onStepClick, onMenu, onMinimize, className, optionsLabel = "Checklist options", minimizeLabel = "Minimize", progressLabel = "Onboarding progress", }) {
18
+ export function OnboardingChecklist({ title = "Getting started", steps, encouragement = "Keep it up!", onStepClick, onMenu, onMinimize, className, optionsLabel = "Checklist options", minimizeLabel = "Minimize", progressLabel = "Onboarding progress", progressText = onboardingLabels.progressText, completedStepLabel = onboardingLabels.completedStepLabel, }) {
10
19
  const total = steps.length;
11
20
  const done = steps.filter((s) => s.done).length;
12
- return (_jsxs("div", { className: onboardingRecipe({ className }), children: [_jsxs("div", { className: "onboarding-head", children: [_jsx("span", { className: "onboarding-title", children: title }), onMenu && (_jsx("button", { type: "button", className: "onboarding-iconbtn", "aria-label": optionsLabel, onClick: onMenu, children: _jsx(MoreHorizontal, { size: 16, strokeWidth: 1.75 }) })), onMinimize && (_jsx("button", { type: "button", className: "onboarding-iconbtn", "aria-label": minimizeLabel, onClick: onMinimize, children: _jsx(ChevronDown, { size: 16, strokeWidth: 1.75 }) }))] }), _jsxs("span", { className: "onboarding-sub", children: [_jsxs("b", { children: [done, "/", total] }), " ", "steps completed", encouragement ? _jsxs(_Fragment, { children: [" \u00B7 ", encouragement] }) : null] }), _jsx("div", { className: "onboarding-progress", role: "progressbar", "aria-valuenow": done, "aria-valuemin": 0, "aria-valuemax": total, "aria-label": progressLabel, children: Array.from({ length: total }, (_, i) => (_jsx("span", { className: "onboarding-seg", "data-done": i < done ? "" : undefined }, i))) }), _jsx("div", { className: "onboarding-steps", children: steps.map((s) => (_jsxs("button", { type: "button", className: "onboarding-step", "data-done": s.done ? "" : undefined, disabled: s.done, onClick: s.done ? undefined : () => onStepClick?.(s.id), children: [_jsx("span", { className: "onboarding-step-icon", children: s.icon }), _jsx("span", { className: "onboarding-step-label", children: s.label }), _jsx("span", { className: "onboarding-step-check", children: s.done && _jsx(Check, { size: 12, strokeWidth: 3 }) })] }, s.id))) })] }));
21
+ return (_jsxs("div", { className: onboardingRecipe({ className }), children: [_jsxs("div", { className: "onboarding-head", children: [_jsx("span", { className: "onboarding-title", children: title }), onMenu && (_jsx("button", { type: "button", className: "onboarding-iconbtn", "aria-label": optionsLabel, onClick: onMenu, children: _jsx(MoreHorizontal, { size: 16, strokeWidth: 1.75 }) })), onMinimize && (_jsx("button", { type: "button", className: "onboarding-iconbtn", "aria-label": minimizeLabel, onClick: onMinimize, children: _jsx(ChevronDown, { size: 16, strokeWidth: 1.75 }) }))] }), _jsxs("span", { className: "onboarding-sub", children: [progressText(done, total), encouragement ? _jsxs(_Fragment, { children: [" \u00B7 ", encouragement] }) : null] }), _jsx("div", { className: "onboarding-progress", role: "progressbar", "aria-valuenow": done, "aria-valuemin": 0, "aria-valuemax": total, "aria-label": progressLabel, children: Array.from({ length: total }, (_, i) => (_jsx("span", { className: "onboarding-seg", "data-done": i < done ? "" : undefined }, i))) }), _jsx("div", { className: "onboarding-steps", children: steps.map((s) => (_jsxs("button", { type: "button", className: "onboarding-step", "data-done": s.done ? "" : undefined, "aria-label": s.done ? completedStepLabel(s.label) : undefined, disabled: s.done, onClick: s.done ? undefined : () => onStepClick?.(s.id), children: [_jsx("span", { className: "onboarding-step-icon", children: s.icon }), _jsx("span", { className: "onboarding-step-label", children: s.label }), _jsx("span", { className: "onboarding-step-check", children: s.done && _jsx(Check, { size: 12, strokeWidth: 3 }) })] }, s.id))) })] }));
13
22
  }
14
23
  OnboardingChecklist.displayName = "OnboardingChecklist";
@@ -1,2 +1,2 @@
1
- export { RecordPage } from "./recordpage.js";
1
+ export { RecordPage, recordPageLabels, } from "./recordpage.js";
2
2
  export { recordpageRecipe } from "./recordpage.recipe.js";
@@ -82,6 +82,8 @@ export const recordPageLabels = {
82
82
  },
83
83
  domainSetTo: (value) => (_jsxs(_Fragment, { children: ["Domain set to ", _jsx("b", { children: value })] })),
84
84
  taggedCategories: (count) => (_jsxs(_Fragment, { children: ["Tagged ", _jsx("b", { children: count }), " categories"] })),
85
+ recordCreated: (name) => (_jsxs(_Fragment, { children: [_jsx("b", { children: name }), " record was created"] })),
86
+ noEmailsWith: (name) => _jsxs(_Fragment, { children: ["No emails logged with ", name, " yet."] }),
85
87
  };
86
88
  function TagsValue({ values }) {
87
89
  const [expanded, setExpanded] = React.useState(false);
@@ -330,13 +332,13 @@ export function RecordPage({ record, columns, collection = "Records", labels, on
330
332
  : null,
331
333
  {
332
334
  id: "created",
333
- text: (_jsxs(_Fragment, { children: [_jsx("b", { children: name }), " record was created"] })),
335
+ text: t.recordCreated(name),
334
336
  time: "1 day ago",
335
337
  },
336
338
  ].filter(Boolean);
337
339
  const shownActivity = showAllActivity ? activity : activity.slice(0, 2);
338
340
  const activityFeed = (_jsxs("section", { children: [_jsx(SectionHead, { title: t.tabs.activity, action: activity.length > 2 ? (_jsx("button", { type: "button", className: "recordpage-viewall recordpage-viewall--inline", onClick: () => setShowAllActivity((s) => !s), children: showAllActivity ? t.showLess : t.viewAll })) : undefined }), _jsx("div", { className: "recordpage-feed", children: shownActivity.map((a) => (_jsxs("div", { className: "recordpage-feed-item", children: [_jsx("span", { className: "recordpage-feed-dot", children: _jsx(ActivityIcon, { size: 13, strokeWidth: 2 }) }), _jsxs("div", { className: "recordpage-feed-body", children: [a.text, _jsx("div", { className: "recordpage-feed-time", children: a.time })] })] }, a.id))) })] }));
339
- const emailsEmpty = (_jsxs(EmptyCard, { icon: _jsx(Inbox, { size: 22, strokeWidth: 1.5 }), children: ["No emails logged with ", name, " yet."] }));
341
+ const emailsEmpty = (_jsx(EmptyCard, { icon: _jsx(Inbox, { size: 22, strokeWidth: 1.5 }), children: t.noEmailsWith(name) }));
340
342
  const panel = (active) => {
341
343
  switch (active) {
342
344
  case "overview":
@@ -1,2 +1,2 @@
1
- export { OnboardingChecklist, type OnboardingChecklistProps, type OnboardingStep, } from "./onboarding";
1
+ export { OnboardingChecklist, type OnboardingChecklistProps, type OnboardingStep, onboardingLabels, } from "./onboarding";
2
2
  export { onboardingRecipe } from "./onboarding.recipe";
@@ -14,6 +14,18 @@ export type OnboardingChecklistProps = {
14
14
  minimizeLabel?: string;
15
15
  /** Accessible name for the progress bar. */
16
16
  progressLabel?: string;
17
+ /**
18
+ * The "N/total steps completed" line. A formatter, not a string: the count
19
+ * moves within the sentence between languages, so a template with holes
20
+ * cannot be translated (#216).
21
+ */
22
+ progressText?: (done: number, total: number) => React.ReactNode;
23
+ /**
24
+ * Accessible name for a completed step. Without one, a done step announces
25
+ * as "…, button, dimmed" and the strikethrough and check glyph — its only
26
+ * other signals — are invisible to a screen reader.
27
+ */
28
+ completedStepLabel?: (label: React.ReactNode) => string;
17
29
  title?: React.ReactNode;
18
30
  steps: OnboardingStep[];
19
31
  /** Subtitle after "N/total steps completed" (default "Keep it up!"). */
@@ -26,12 +38,21 @@ export type OnboardingChecklistProps = {
26
38
  onMinimize?: () => void;
27
39
  className?: string;
28
40
  };
41
+ /**
42
+ * The English defaults for the two pieces of copy that are formatters rather
43
+ * than strings. Exported so a consumer can wrap one instead of restating it,
44
+ * and so the strings live in one place rather than inside the signature.
45
+ */
46
+ export declare const onboardingLabels: {
47
+ progressText: (done: number, total: number) => React.ReactNode;
48
+ completedStepLabel: (label: React.ReactNode) => string;
49
+ };
29
50
  /**
30
51
  * An onboarding checklist card — measured from Attio's floating "Getting
31
52
  * started" widget. Title + optional menu/minimize, a segmented progress bar,
32
53
  * and step rows (completed rows strike through with a brand check).
33
54
  */
34
- export declare function OnboardingChecklist({ title, steps, encouragement, onStepClick, onMenu, onMinimize, className, optionsLabel, minimizeLabel, progressLabel, }: OnboardingChecklistProps): React.JSX.Element;
55
+ export declare function OnboardingChecklist({ title, steps, encouragement, onStepClick, onMenu, onMinimize, className, optionsLabel, minimizeLabel, progressLabel, progressText, completedStepLabel, }: OnboardingChecklistProps): React.JSX.Element;
35
56
  export declare namespace OnboardingChecklist {
36
57
  var displayName: string;
37
58
  }
@@ -1,2 +1,2 @@
1
- export { RecordPage, type RecordPageProps } from "./recordpage";
1
+ export { RecordPage, type RecordPageLabels, type RecordPageLabelsInput, type RecordPageProps, recordPageLabels, } from "./recordpage";
2
2
  export { recordpageRecipe } from "./recordpage.recipe";
@@ -77,6 +77,10 @@ export type RecordPageLabels = {
77
77
  domainSetTo: (value: React.ReactNode) => React.ReactNode;
78
78
  /** Activity line for added categories, given how many. */
79
79
  taggedCategories: (count: number) => React.ReactNode;
80
+ /** Activity line for the record's creation, given its name. */
81
+ recordCreated: (name: React.ReactNode) => React.ReactNode;
82
+ /** Empty state on the Emails tab, given the record's name. */
83
+ noEmailsWith: (name: React.ReactNode) => React.ReactNode;
80
84
  };
81
85
  export declare const recordPageLabels: RecordPageLabels;
82
86
  /** Partial override of any chrome string; the keyed maps nest. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crisp-ui-kit/crisp",
3
- "version": "0.20.2",
3
+ "version": "0.21.0",
4
4
  "description": "A lean React design system, measured pixel-for-pixel from Attio.",
5
5
  "license": "MIT",
6
6
  "type": "module",