@crisp-ui-kit/crisp 0.54.2 → 0.54.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/dist/board.css +13 -1
  2. package/dist/board.layered.css +13 -1
  3. package/dist/chip.css +7 -1
  4. package/dist/chip.d.ts +2 -1
  5. package/dist/chip.layered.css +7 -1
  6. package/dist/cjs/components/chip/chip.js +3 -2
  7. package/dist/cjs/components/chip/chip.recipe.js +37 -1
  8. package/dist/cjs/components/datatable/datatable.js +489 -111
  9. package/dist/cjs/components/emptystate/emptystate.js +3 -2
  10. package/dist/cjs/components/emptystate/emptystate.recipe.js +26 -1
  11. package/dist/cjs/components/personchip/index.js +17 -0
  12. package/dist/cjs/components/personchip/personchip.js +50 -0
  13. package/dist/cjs/components/table/table.js +2 -2
  14. package/dist/cjs/components/tabletoolbar/tabletoolbar.js +41 -4
  15. package/dist/cjs/components/tabs/tabs.js +2 -2
  16. package/dist/cjs/components/tabs/tabs.recipe.js +30 -3
  17. package/dist/cjs/components/text/index.js +7 -0
  18. package/dist/cjs/components/text/text.js +55 -0
  19. package/dist/cjs/components/text/text.recipe.js +34 -0
  20. package/dist/cjs/index.js +4 -0
  21. package/dist/cjs/product/board/board.js +60 -2
  22. package/dist/cjs/product/board/board.recipe.js +10 -0
  23. package/dist/cjs/product/comments/comments.js +426 -0
  24. package/dist/cjs/product/comments/comments.recipe.js +126 -0
  25. package/dist/cjs/product/comments/index.js +8 -0
  26. package/dist/cjs/product/headerbar/headerbar.recipe.js +9 -1
  27. package/dist/cjs/product/listpage/listpage.recipe.js +10 -1
  28. package/dist/cjs/product/objectsettings/index.js +16 -0
  29. package/dist/cjs/product/objectsettings/objectsettings.js +365 -0
  30. package/dist/cjs/product/objectsettings/objectsettings.recipe.js +65 -0
  31. package/dist/cjs/product/recordpage/recordpage.recipe.js +10 -1
  32. package/dist/cjs/product/recordstable/recordstable.js +89 -6
  33. package/dist/cjs/product/richtext/richtext.recipe.js +26 -1
  34. package/dist/cjs/product/taskstable/taskstable.recipe.js +10 -1
  35. package/dist/comments.css +80 -0
  36. package/dist/comments.d.ts +7 -0
  37. package/dist/comments.layered.css +82 -0
  38. package/dist/emptystate.css +15 -1
  39. package/dist/emptystate.d.ts +2 -2
  40. package/dist/emptystate.layered.css +15 -1
  41. package/dist/esm/components/chip/chip.js +3 -2
  42. package/dist/esm/components/chip/chip.recipe.js +37 -1
  43. package/dist/esm/components/datatable/datatable.js +488 -111
  44. package/dist/esm/components/emptystate/emptystate.js +4 -3
  45. package/dist/esm/components/emptystate/emptystate.recipe.js +26 -1
  46. package/dist/esm/components/emptystate/index.js +1 -1
  47. package/dist/esm/components/personchip/index.js +1 -0
  48. package/dist/esm/components/personchip/personchip.js +47 -0
  49. package/dist/esm/components/table/table.js +2 -2
  50. package/dist/esm/components/tabletoolbar/tabletoolbar.js +43 -6
  51. package/dist/esm/components/tabs/tabs.js +2 -2
  52. package/dist/esm/components/tabs/tabs.recipe.js +30 -3
  53. package/dist/esm/components/text/index.js +2 -0
  54. package/dist/esm/components/text/text.js +19 -0
  55. package/dist/esm/components/text/text.recipe.js +31 -0
  56. package/dist/esm/index.js +4 -0
  57. package/dist/esm/product/board/board.js +61 -3
  58. package/dist/esm/product/board/board.recipe.js +10 -0
  59. package/dist/esm/product/comments/comments.js +388 -0
  60. package/dist/esm/product/comments/comments.recipe.js +123 -0
  61. package/dist/esm/product/comments/index.js +2 -0
  62. package/dist/esm/product/headerbar/headerbar.recipe.js +9 -1
  63. package/dist/esm/product/listpage/listpage.recipe.js +10 -1
  64. package/dist/esm/product/objectsettings/index.js +2 -0
  65. package/dist/esm/product/objectsettings/objectsettings.js +322 -0
  66. package/dist/esm/product/objectsettings/objectsettings.recipe.js +62 -0
  67. package/dist/esm/product/recordpage/recordpage.recipe.js +10 -1
  68. package/dist/esm/product/recordstable/recordstable.js +90 -8
  69. package/dist/esm/product/richtext/richtext.recipe.js +26 -1
  70. package/dist/esm/product/taskstable/taskstable.recipe.js +10 -1
  71. package/dist/headerbar.css +1 -1
  72. package/dist/headerbar.layered.css +1 -1
  73. package/dist/listpage.css +8 -1
  74. package/dist/listpage.layered.css +8 -1
  75. package/dist/objectsettings.css +49 -0
  76. package/dist/objectsettings.d.ts +7 -0
  77. package/dist/objectsettings.layered.css +51 -0
  78. package/dist/recordpage.css +13 -2
  79. package/dist/recordpage.layered.css +13 -2
  80. package/dist/richtext.css +11 -1
  81. package/dist/richtext.layered.css +11 -1
  82. package/dist/styles.css +224 -10
  83. package/dist/styles.layered.css +224 -10
  84. package/dist/tabs.css +7 -1
  85. package/dist/tabs.d.ts +2 -2
  86. package/dist/tabs.layered.css +7 -1
  87. package/dist/taskstable.css +8 -1
  88. package/dist/taskstable.layered.css +8 -1
  89. package/dist/text.css +9 -0
  90. package/dist/text.d.ts +7 -0
  91. package/dist/text.layered.css +11 -0
  92. package/dist/types/components/chip/chip.d.ts +32 -0
  93. package/dist/types/components/chip/chip.recipe.d.ts +31 -0
  94. package/dist/types/components/datatable/datatable.d.ts +28 -4
  95. package/dist/types/components/emptystate/emptystate.d.ts +29 -4
  96. package/dist/types/components/emptystate/emptystate.recipe.d.ts +2 -0
  97. package/dist/types/components/emptystate/index.d.ts +1 -1
  98. package/dist/types/components/personchip/index.d.ts +1 -0
  99. package/dist/types/components/personchip/personchip.d.ts +37 -0
  100. package/dist/types/components/table/table.d.ts +36 -0
  101. package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +8 -2
  102. package/dist/types/components/tabs/tabs.d.ts +4 -2
  103. package/dist/types/components/tabs/tabs.recipe.d.ts +18 -3
  104. package/dist/types/components/text/index.d.ts +2 -0
  105. package/dist/types/components/text/text.d.ts +32 -0
  106. package/dist/types/components/text/text.recipe.d.ts +40 -0
  107. package/dist/types/index.d.ts +4 -0
  108. package/dist/types/product/board/board.d.ts +20 -1
  109. package/dist/types/product/comments/comments.d.ts +192 -0
  110. package/dist/types/product/comments/comments.recipe.d.ts +3 -0
  111. package/dist/types/product/comments/index.d.ts +2 -0
  112. package/dist/types/product/objectsettings/index.d.ts +2 -0
  113. package/dist/types/product/objectsettings/objectsettings.d.ts +261 -0
  114. package/dist/types/product/objectsettings/objectsettings.recipe.d.ts +3 -0
  115. package/dist/types/product/recordstable/recordstable.d.ts +24 -2
  116. package/package.json +21 -1
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.objectsettingsRecipe = exports.ObjectsList = exports.ObjectSettings = exports.ObjectGeneralSection = exports.ObjectDetailPage = exports.ObjectAppearanceSection = exports.CreateAttributeDialog = exports.AttributeTypeSelect = exports.AttributesTable = exports.ATTRIBUTE_TYPE_OPTIONS = exports.ATTRIBUTE_AI_AUTOFILL_OPTIONS = void 0;
4
+ var objectsettings_1 = require("./objectsettings");
5
+ Object.defineProperty(exports, "ATTRIBUTE_AI_AUTOFILL_OPTIONS", { enumerable: true, get: function () { return objectsettings_1.ATTRIBUTE_AI_AUTOFILL_OPTIONS; } });
6
+ Object.defineProperty(exports, "ATTRIBUTE_TYPE_OPTIONS", { enumerable: true, get: function () { return objectsettings_1.ATTRIBUTE_TYPE_OPTIONS; } });
7
+ Object.defineProperty(exports, "AttributesTable", { enumerable: true, get: function () { return objectsettings_1.AttributesTable; } });
8
+ Object.defineProperty(exports, "AttributeTypeSelect", { enumerable: true, get: function () { return objectsettings_1.AttributeTypeSelect; } });
9
+ Object.defineProperty(exports, "CreateAttributeDialog", { enumerable: true, get: function () { return objectsettings_1.CreateAttributeDialog; } });
10
+ Object.defineProperty(exports, "ObjectAppearanceSection", { enumerable: true, get: function () { return objectsettings_1.ObjectAppearanceSection; } });
11
+ Object.defineProperty(exports, "ObjectDetailPage", { enumerable: true, get: function () { return objectsettings_1.ObjectDetailPage; } });
12
+ Object.defineProperty(exports, "ObjectGeneralSection", { enumerable: true, get: function () { return objectsettings_1.ObjectGeneralSection; } });
13
+ Object.defineProperty(exports, "ObjectSettings", { enumerable: true, get: function () { return objectsettings_1.ObjectSettings; } });
14
+ Object.defineProperty(exports, "ObjectsList", { enumerable: true, get: function () { return objectsettings_1.ObjectsList; } });
15
+ var objectsettings_recipe_1 = require("./objectsettings.recipe");
16
+ Object.defineProperty(exports, "objectsettingsRecipe", { enumerable: true, get: function () { return objectsettings_recipe_1.objectsettingsRecipe; } });
@@ -0,0 +1,365 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ObjectSettings = exports.ATTRIBUTE_AI_AUTOFILL_OPTIONS = exports.ATTRIBUTE_TYPE_OPTIONS = void 0;
37
+ exports.ObjectsList = ObjectsList;
38
+ exports.ObjectDetailPage = ObjectDetailPage;
39
+ exports.ObjectGeneralSection = ObjectGeneralSection;
40
+ exports.ObjectAppearanceSection = ObjectAppearanceSection;
41
+ exports.AttributesTable = AttributesTable;
42
+ exports.AttributeTypeSelect = AttributeTypeSelect;
43
+ exports.CreateAttributeDialog = CreateAttributeDialog;
44
+ const jsx_runtime_1 = require("react/jsx-runtime");
45
+ const lucide_react_1 = require("lucide-react");
46
+ const React = __importStar(require("react"));
47
+ const badge_1 = require("../../components/badge");
48
+ const datatable_1 = require("../../components/datatable");
49
+ const formfield_1 = require("../../components/formfield");
50
+ const input_1 = require("../../components/input");
51
+ const menu_1 = require("../../components/menu");
52
+ const select_1 = require("../../components/select");
53
+ const switch_1 = require("../../components/switch");
54
+ const tabs_1 = require("../../components/tabs");
55
+ const textarea_1 = require("../../components/textarea");
56
+ const core_1 = require("../../core");
57
+ const formdialog_1 = require("../formdialog");
58
+ const settingssection_1 = require("../settingssection");
59
+ const objectsettings_recipe_1 = require("./objectsettings.recipe");
60
+ /**
61
+ * Settings → Objects (crisp#637, P-122): H1 + subtitle, a search+Filter row,
62
+ * and a DataTable of every object (icon+name, type, record count,
63
+ * attribute count, a per-row "…" menu). Measured live against
64
+ * app.attio.com/<workspace>/settings/data/objects — the plan-gate banner
65
+ * above the table and the "5/3" object-count pill in the "Object" header
66
+ * (both billing-tier chrome) are skipped by decision, not oversight.
67
+ */
68
+ function ObjectsList({ objects, onOpenObject, onEditObject, onCopyObjectId, onCreateObject, title: titleProp, subtitle: subtitleProp, newObjectLabel: newObjectLabelProp, searchPlaceholder: searchPlaceholderProp, filterLabel: filterLabelProp, objectColumnLabel: objectColumnLabelProp, typeColumnLabel: typeColumnLabelProp, recordsColumnLabel: recordsColumnLabelProp, attributesColumnLabel: attributesColumnLabelProp, noRecordsLabel: noRecordsLabelProp, editLabel: editLabelProp, copyIdLabel: copyIdLabelProp, rowMenuLabel, emptyLabel, className, ...rest }) {
69
+ const title = (0, core_1.useCrispMessage)(titleProp, "Objects");
70
+ const subtitle = (0, core_1.useCrispMessage)(subtitleProp, "Modify and add objects in your workspace");
71
+ const newObjectLabel = (0, core_1.useCrispMessage)(newObjectLabelProp, "New custom object");
72
+ const searchPlaceholder = (0, core_1.useCrispMessage)(searchPlaceholderProp, "Search objects");
73
+ const filterLabel = (0, core_1.useCrispMessage)(filterLabelProp, "Filter");
74
+ const objectColumnLabel = (0, core_1.useCrispMessage)(objectColumnLabelProp, "Object");
75
+ const typeColumnLabel = (0, core_1.useCrispMessage)(typeColumnLabelProp, "Type");
76
+ const recordsColumnLabel = (0, core_1.useCrispMessage)(recordsColumnLabelProp, "Records");
77
+ const attributesColumnLabel = (0, core_1.useCrispMessage)(attributesColumnLabelProp, "Attributes");
78
+ const noRecordsLabel = (0, core_1.useCrispMessage)(noRecordsLabelProp, "No records");
79
+ const editLabel = (0, core_1.useCrispMessage)(editLabelProp, "Edit");
80
+ const copyIdLabel = (0, core_1.useCrispMessage)(copyIdLabelProp, "Copy ID");
81
+ const [query, setQuery] = React.useState("");
82
+ const filtered = React.useMemo(() => {
83
+ if (!query.trim())
84
+ return objects;
85
+ const q = query.trim().toLowerCase();
86
+ return objects.filter((o) => String(o.name ?? "")
87
+ .toLowerCase()
88
+ .includes(q));
89
+ }, [objects, query]);
90
+ const columns = React.useMemo(() => [
91
+ {
92
+ key: "name",
93
+ header: objectColumnLabel,
94
+ render: (_v, row) => ((0, jsx_runtime_1.jsxs)("span", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-objectsettings-icon", style: { background: row.iconColor ?? "currentColor" }, children: row.icon }), row.name] })),
95
+ },
96
+ {
97
+ key: "typeLabel",
98
+ header: typeColumnLabel,
99
+ render: (_v, row) => ((0, jsx_runtime_1.jsx)("span", { className: "crisp-objectsettings-typebadge", children: row.typeLabel })),
100
+ },
101
+ {
102
+ key: "recordCount",
103
+ header: recordsColumnLabel,
104
+ align: "left",
105
+ render: (_v, row) => row.recordCount ? (row.recordCount) : ((0, jsx_runtime_1.jsx)("span", { className: "crisp-objectsettings-muted", children: noRecordsLabel })),
106
+ },
107
+ {
108
+ key: "attributeCount",
109
+ header: attributesColumnLabel,
110
+ },
111
+ {
112
+ key: "actions",
113
+ header: "",
114
+ align: "right",
115
+ render: (_v, row) => ((0, jsx_runtime_1.jsxs)(menu_1.Menu, { align: "end", trigger: (0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": rowMenuLabel?.(row) ?? `${row.name}`, style: {
116
+ display: "inline-flex",
117
+ border: "none",
118
+ background: "none",
119
+ cursor: "pointer",
120
+ color: "inherit",
121
+ padding: 4,
122
+ }, children: (0, jsx_runtime_1.jsx)(lucide_react_1.MoreHorizontal, { size: 16 }) }), children: [(0, jsx_runtime_1.jsx)(menu_1.MenuItem, { icon: (0, jsx_runtime_1.jsx)(lucide_react_1.Pencil, { size: 14 }), onSelect: () => onEditObject?.(row.id), children: editLabel }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => onCopyObjectId?.(row.id), children: copyIdLabel })] })),
123
+ },
124
+ ], [
125
+ objectColumnLabel,
126
+ typeColumnLabel,
127
+ recordsColumnLabel,
128
+ attributesColumnLabel,
129
+ noRecordsLabel,
130
+ editLabel,
131
+ copyIdLabel,
132
+ onEditObject,
133
+ onCopyObjectId,
134
+ rowMenuLabel,
135
+ ]);
136
+ return ((0, jsx_runtime_1.jsx)("div", { className: (0, objectsettings_recipe_1.objectsettingsRecipe)({ className }), ...rest, children: (0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-page", children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-head", children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-heading", children: [(0, jsx_runtime_1.jsx)("h1", { className: "crisp-objectsettings-title", children: title }), (0, jsx_runtime_1.jsx)("p", { className: "crisp-objectsettings-subtitle", children: subtitle })] }), onCreateObject && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-objectsettings-filterbtn", onClick: onCreateObject, children: newObjectLabel }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-toolbar", children: [(0, jsx_runtime_1.jsxs)("span", { className: "crisp-objectsettings-search", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Search, { size: 15, strokeWidth: 1.75, "aria-hidden": "true" }), (0, jsx_runtime_1.jsx)("input", { type: "search", placeholder: searchPlaceholder, value: query, onChange: (e) => setQuery(e.target.value), "aria-label": searchPlaceholder })] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-objectsettings-filterbtn", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.SlidersHorizontal, { size: 14 }), filterLabel] })] }), (0, jsx_runtime_1.jsx)(datatable_1.DataTable, { columns: columns, data: filtered, onRowClick: onOpenObject ? (row) => onOpenObject(row.id) : undefined, empty: emptyLabel })] }) }));
137
+ }
138
+ ObjectsList.displayName = "ObjectsList";
139
+ /**
140
+ * Settings → Objects → an object (crisp#637, P-123, verified live against
141
+ * .../objects/companies/general): 48px icon tile + name + type badge, a
142
+ * description line, then a Configuration/Attributes tab bar (Attio's own
143
+ * tab bar also carries Permissions/Templates/Notifications/Imports — out of
144
+ * scope here, see #613/#638; only the two rows this task covers are built).
145
+ * Tab content is a render prop, the same shape crisp's own `Tabs` already
146
+ * takes, so a consumer composes `ObjectGeneralSection`/
147
+ * `ObjectAppearanceSection` and `AttributesTable` underneath.
148
+ */
149
+ function ObjectDetailPage({ icon, iconColor, name, typeLabel, description, tab, defaultTab = "configuration", onTabChange, attributeCount, configurationLabel: configurationLabelProp, attributesLabel: attributesLabelProp, children, }) {
150
+ const configurationLabel = (0, core_1.useCrispMessage)(configurationLabelProp, "Configuration");
151
+ const attributesLabel = (0, core_1.useCrispMessage)(attributesLabelProp, "Attributes");
152
+ const [internalTab, setInternalTab] = React.useState(defaultTab);
153
+ const active = tab ?? internalTab;
154
+ const setTab = (v) => {
155
+ const next = v;
156
+ if (tab === undefined)
157
+ setInternalTab(next);
158
+ onTabChange?.(next);
159
+ };
160
+ return ((0, jsx_runtime_1.jsx)("div", { className: (0, objectsettings_recipe_1.objectsettingsRecipe)(), children: (0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-page", children: [(0, jsx_runtime_1.jsx)("div", { className: "crisp-objectsettings-head", children: (0, jsx_runtime_1.jsxs)("div", { style: { display: "flex", alignItems: "flex-start", gap: 12 }, children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-objectsettings-icon crisp-objectsettings-icon--lg", style: { background: iconColor ?? "currentColor" }, children: icon }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-heading", children: [(0, jsx_runtime_1.jsxs)("h1", { className: "crisp-objectsettings-title", children: [name, typeLabel != null && ((0, jsx_runtime_1.jsx)(badge_1.Badge, { tone: "neutral", size: "small", children: typeLabel }))] }), description != null && ((0, jsx_runtime_1.jsx)("p", { className: "crisp-objectsettings-subtitle", children: description }))] })] }) }), (0, jsx_runtime_1.jsx)(tabs_1.Tabs, { value: active, onValueChange: setTab, tabs: [
161
+ { value: "configuration", label: configurationLabel },
162
+ {
163
+ value: "attributes",
164
+ label: attributesLabel,
165
+ badge: attributeCount,
166
+ },
167
+ ], children: (v) => children(v) })] }) }));
168
+ }
169
+ ObjectDetailPage.displayName = "ObjectDetailPage";
170
+ /** The Configuration tab's "General" section (crisp#637, P-123): plural/
171
+ * singular noun inputs side by side, then a slug input Attio locks for
172
+ * system objects. Composes `SettingsSection` + `FormField`, no new chrome. */
173
+ function ObjectGeneralSection({ pluralNoun, onPluralNounChange, singularNoun, onSingularNounChange, slug, slugEditable = false, onSlugChange, titleLabel: titleLabelProp, descriptionLabel: descriptionLabelProp, pluralLabel: pluralLabelProp, singularLabel: singularLabelProp, slugLabel: slugLabelProp, slugHelpLabel: slugHelpLabelProp, }) {
174
+ const titleLabel = (0, core_1.useCrispMessage)(titleLabelProp, "General");
175
+ const descriptionLabel = (0, core_1.useCrispMessage)(descriptionLabelProp, "Set words to describe a single and multiple objects of this type");
176
+ const pluralLabel = (0, core_1.useCrispMessage)(pluralLabelProp, "Plural noun");
177
+ const singularLabel = (0, core_1.useCrispMessage)(singularLabelProp, "Singular noun");
178
+ const slugLabel = (0, core_1.useCrispMessage)(slugLabelProp, "Slug");
179
+ const slugHelpLabel = (0, core_1.useCrispMessage)(slugHelpLabelProp, "You can't change the slug of a system object");
180
+ const pluralId = React.useId();
181
+ const singularId = React.useId();
182
+ const slugId = React.useId();
183
+ return ((0, jsx_runtime_1.jsxs)(settingssection_1.SettingsSection, { title: titleLabel, description: descriptionLabel, children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-fieldrow", children: [(0, jsx_runtime_1.jsx)(formfield_1.FormField, { label: pluralLabel, htmlFor: pluralId, children: (0, jsx_runtime_1.jsx)(input_1.Input, { id: pluralId, value: pluralNoun, onChange: (e) => onPluralNounChange?.(e.target.value) }) }), (0, jsx_runtime_1.jsx)(formfield_1.FormField, { label: singularLabel, htmlFor: singularId, children: (0, jsx_runtime_1.jsx)(input_1.Input, { id: singularId, value: singularNoun, onChange: (e) => onSingularNounChange?.(e.target.value) }) })] }), (0, jsx_runtime_1.jsx)(formfield_1.FormField, { label: slugLabel, htmlFor: slugId, description: !slugEditable ? slugHelpLabel : undefined, children: (0, jsx_runtime_1.jsx)(input_1.Input, { id: slugId, value: slug, disabled: !slugEditable, onChange: (e) => onSlugChange?.(e.target.value) }) })] }));
184
+ }
185
+ ObjectGeneralSection.displayName = "ObjectGeneralSection";
186
+ /** The Configuration tab's "Appearance" section (crisp#637, P-123): a single
187
+ * "Record page layout" row with a "Configure record page" button — the
188
+ * target flow itself (crisp's own record-page-layout editor) is a separate,
189
+ * unbuilt surface; this only renders the row and its click. */
190
+ function ObjectAppearanceSection({ onConfigureRecordPage, titleLabel: titleLabelProp, rowLabel: rowLabelProp, rowDescriptionLabel: rowDescriptionLabelProp, actionLabel: actionLabelProp, }) {
191
+ const titleLabel = (0, core_1.useCrispMessage)(titleLabelProp, "Appearance");
192
+ const rowLabel = (0, core_1.useCrispMessage)(rowLabelProp, "Record page layout");
193
+ const rowDescriptionLabel = (0, core_1.useCrispMessage)(rowDescriptionLabelProp, "Configure tabs, widgets, and layout for this object's record pages");
194
+ const actionLabel = (0, core_1.useCrispMessage)(actionLabelProp, "Configure record page");
195
+ return ((0, jsx_runtime_1.jsx)(settingssection_1.SettingsSection, { title: titleLabel, children: (0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-fieldrow", style: { alignItems: "center" }, children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { style: { fontWeight: 500 }, children: rowLabel }), (0, jsx_runtime_1.jsx)("p", { className: "crisp-objectsettings-subtitle", children: rowDescriptionLabel })] }), onConfigureRecordPage && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-objectsettings-filterbtn", style: { justifySelf: "end" }, onClick: onConfigureRecordPage, children: actionLabel }))] }) }));
196
+ }
197
+ ObjectAppearanceSection.displayName = "ObjectAppearanceSection";
198
+ /**
199
+ * The Attributes tab's table (crisp#637, P-124, verified live): H2 +
200
+ * "Create attribute", a search+Filter row, then a DataTable of every
201
+ * attribute — drag handle (via `DataTable`'s own `onRowsReorder`) + type
202
+ * icon + name, Data type, a constraint label, and System (grey)/Enriched
203
+ * (purple) property badges. Row height is DataTable's own 36px; Attio's
204
+ * measures 40px here — a real, small divergence left to DataTable's owner
205
+ * rather than patched locally (see the PR).
206
+ */
207
+ function AttributesTable({ attributes, onOpenAttribute, onEditAttribute, onArchiveAttribute, onReorder, onCreateAttribute, title: titleProp, subtitle: subtitleProp, createLabel: createLabelProp, searchPlaceholder: searchPlaceholderProp, filterLabel: filterLabelProp, nameColumnLabel: nameColumnLabelProp, dataTypeColumnLabel: dataTypeColumnLabelProp, constraintsColumnLabel: constraintsColumnLabelProp, propertiesColumnLabel: propertiesColumnLabelProp, systemLabel: systemLabelProp, enrichedLabel: enrichedLabelProp, editLabel: editLabelProp, archiveLabel: archiveLabelProp, rowActions, emptyLabel, }) {
208
+ const title = (0, core_1.useCrispMessage)(titleProp, "Attributes");
209
+ const subtitle = (0, core_1.useCrispMessage)(subtitleProp, "Modify and add object attributes");
210
+ const createLabel = (0, core_1.useCrispMessage)(createLabelProp, "Create attribute");
211
+ const searchPlaceholder = (0, core_1.useCrispMessage)(searchPlaceholderProp, "Search attributes");
212
+ const filterLabel = (0, core_1.useCrispMessage)(filterLabelProp, "Filter");
213
+ const nameColumnLabel = (0, core_1.useCrispMessage)(nameColumnLabelProp, "Name");
214
+ const dataTypeColumnLabel = (0, core_1.useCrispMessage)(dataTypeColumnLabelProp, "Data type");
215
+ const constraintsColumnLabel = (0, core_1.useCrispMessage)(constraintsColumnLabelProp, "Constraints");
216
+ const propertiesColumnLabel = (0, core_1.useCrispMessage)(propertiesColumnLabelProp, "Properties");
217
+ const systemLabel = (0, core_1.useCrispMessage)(systemLabelProp, "System");
218
+ const enrichedLabel = (0, core_1.useCrispMessage)(enrichedLabelProp, "Enriched");
219
+ const editLabel = (0, core_1.useCrispMessage)(editLabelProp, "Edit");
220
+ const archiveLabel = (0, core_1.useCrispMessage)(archiveLabelProp, "Archive");
221
+ const [query, setQuery] = React.useState("");
222
+ const filtered = React.useMemo(() => {
223
+ if (!query.trim())
224
+ return attributes;
225
+ const q = query.trim().toLowerCase();
226
+ return attributes.filter((a) => String(a.name ?? "")
227
+ .toLowerCase()
228
+ .includes(q));
229
+ }, [attributes, query]);
230
+ const columns = React.useMemo(() => [
231
+ {
232
+ key: "name",
233
+ header: nameColumnLabel,
234
+ render: (_v, row) => ((0, jsx_runtime_1.jsxs)("span", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [(0, jsx_runtime_1.jsx)("span", { style: {
235
+ display: "inline-flex",
236
+ color: "var(--crisp-fg-muted, inherit)",
237
+ }, children: row.icon }), row.name] })),
238
+ },
239
+ { key: "dataType", header: dataTypeColumnLabel },
240
+ {
241
+ key: "constraint",
242
+ header: constraintsColumnLabel,
243
+ render: (_v, row) => row.constraint ?? null,
244
+ },
245
+ {
246
+ key: "properties",
247
+ header: propertiesColumnLabel,
248
+ render: (_v, row) => ((0, jsx_runtime_1.jsxs)("span", { style: { display: "flex", gap: 6 }, children: [row.system && ((0, jsx_runtime_1.jsx)(badge_1.Badge, { tone: "neutral", size: "small", children: systemLabel })), row.enriched && ((0, jsx_runtime_1.jsx)(badge_1.Badge, { tone: "purple", size: "small", children: enrichedLabel }))] })),
249
+ },
250
+ {
251
+ key: "actions",
252
+ header: "",
253
+ align: "right",
254
+ render: (_v, row) => ((0, jsx_runtime_1.jsx)(menu_1.Menu, { align: "end", trigger: (0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": `${row.name}`, style: {
255
+ display: "inline-flex",
256
+ border: "none",
257
+ background: "none",
258
+ cursor: "pointer",
259
+ color: "inherit",
260
+ padding: 4,
261
+ }, children: (0, jsx_runtime_1.jsx)(lucide_react_1.MoreHorizontal, { size: 16 }) }), children: rowActions ? (rowActions(row)) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(menu_1.MenuItem, { onSelect: () => onEditAttribute?.(row.id), children: editLabel }), (0, jsx_runtime_1.jsx)(menu_1.MenuItem, { danger: true, onSelect: () => onArchiveAttribute?.(row.id), children: archiveLabel })] })) })),
262
+ },
263
+ ], [
264
+ nameColumnLabel,
265
+ dataTypeColumnLabel,
266
+ constraintsColumnLabel,
267
+ propertiesColumnLabel,
268
+ systemLabel,
269
+ enrichedLabel,
270
+ editLabel,
271
+ archiveLabel,
272
+ onEditAttribute,
273
+ onArchiveAttribute,
274
+ rowActions,
275
+ ]);
276
+ return ((0, jsx_runtime_1.jsx)("div", { className: (0, objectsettings_recipe_1.objectsettingsRecipe)(), children: (0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-page", children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-head", children: [(0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-heading", children: [(0, jsx_runtime_1.jsx)("h2", { className: "crisp-objectsettings-title", style: { fontSize: 16, lineHeight: "20px" }, children: title }), (0, jsx_runtime_1.jsx)("p", { className: "crisp-objectsettings-subtitle", children: subtitle })] }), onCreateAttribute && ((0, jsx_runtime_1.jsx)("button", { type: "button", className: "crisp-objectsettings-filterbtn", onClick: onCreateAttribute, children: createLabel }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-toolbar", children: [(0, jsx_runtime_1.jsxs)("span", { className: "crisp-objectsettings-search", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Search, { size: 15, strokeWidth: 1.75, "aria-hidden": "true" }), (0, jsx_runtime_1.jsx)("input", { type: "search", placeholder: searchPlaceholder, value: query, onChange: (e) => setQuery(e.target.value), "aria-label": searchPlaceholder })] }), (0, jsx_runtime_1.jsxs)("button", { type: "button", className: "crisp-objectsettings-filterbtn", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.SlidersHorizontal, { size: 14 }), filterLabel] })] }), (0, jsx_runtime_1.jsx)(datatable_1.DataTable, { columns: columns, data: filtered, onRowClick: onOpenAttribute ? (row) => onOpenAttribute(row.id) : undefined, onRowsReorder: onReorder, empty: emptyLabel })] }) }));
277
+ }
278
+ AttributesTable.displayName = "AttributesTable";
279
+ /** Attio's own "Type" group, verbatim (crisp#637, P-126) — icons are
280
+ * crisp's Lucide choices, not Attio's proprietary set (golden rule 4). */
281
+ exports.ATTRIBUTE_TYPE_OPTIONS = [
282
+ { value: "checkbox", label: "Checkbox", icon: lucide_react_1.CheckSquare },
283
+ { value: "currency", label: "Currency", icon: lucide_react_1.DollarSign },
284
+ { value: "date", label: "Date", icon: lucide_react_1.Calendar },
285
+ { value: "location", label: "Location", icon: lucide_react_1.MapPin },
286
+ { value: "multiselect", label: "Multi-select", icon: lucide_react_1.ListChecks },
287
+ { value: "number", label: "Number", icon: lucide_react_1.Hash },
288
+ { value: "phone", label: "Phone Number", icon: lucide_react_1.Phone },
289
+ { value: "rating", label: "Rating", icon: lucide_react_1.Star },
290
+ { value: "record", label: "Record", icon: lucide_react_1.FileText },
291
+ { value: "relationship", label: "Relationship", icon: lucide_react_1.Link2, badge: "Pro" },
292
+ { value: "select", label: "Select", icon: lucide_react_1.ListChecks },
293
+ { value: "status", label: "Status", icon: lucide_react_1.Flag },
294
+ { value: "text", label: "Text", icon: lucide_react_1.Type },
295
+ { value: "timestamp", label: "Timestamp", icon: lucide_react_1.Clock },
296
+ { value: "user", label: "User", icon: lucide_react_1.User },
297
+ ];
298
+ /** Attio's own "AI Autofill" group, verbatim (crisp#637, P-126) — opt in via
299
+ * `AttributeTypeSelect`'s `aiAutofillOptions`; omitted by default, since a
300
+ * consumer with no autofill backend of its own (GRC) has nothing to wire
301
+ * these to. */
302
+ exports.ATTRIBUTE_AI_AUTOFILL_OPTIONS = [
303
+ { value: "classify-record", label: "Classify record", icon: lucide_react_1.Sparkles },
304
+ { value: "prompt-completion", label: "Prompt completion", icon: lucide_react_1.Sparkles },
305
+ { value: "summarize-record", label: "Summarize record", icon: lucide_react_1.Sparkles },
306
+ { value: "web-agent", label: "Web agent", icon: lucide_react_1.Sparkles },
307
+ ];
308
+ function toSelectOption(o, group) {
309
+ const Icon = o.icon;
310
+ return {
311
+ value: o.value,
312
+ keywords: o.label,
313
+ group,
314
+ label: ((0, jsx_runtime_1.jsxs)("span", { className: "crisp-objectsettings-typeoption", children: [(0, jsx_runtime_1.jsx)(Icon, { size: 14 }), (0, jsx_runtime_1.jsx)("span", { className: "crisp-objectsettings-typeoption-label", children: o.label }), o.badge != null && ((0, jsx_runtime_1.jsx)("span", { className: "crisp-objectsettings-typeoption-badge", children: o.badge }))] })),
315
+ };
316
+ }
317
+ /** The "Attribute Type" dropdown (crisp#637, P-126, verified live): a
318
+ * searchable, grouped `Select` — reused as-is, not a new picker primitive.
319
+ * Grouping and the per-item trailing "Pro" badge are composed through
320
+ * `Select`'s existing `group` field and rich `label` ReactNode. */
321
+ function AttributeTypeSelect({ value, onValueChange, options = exports.ATTRIBUTE_TYPE_OPTIONS, aiAutofillOptions, aiAutofillGroupLabel: aiAutofillGroupLabelProp, typeGroupLabel: typeGroupLabelProp, searchPlaceholder: searchPlaceholderProp, id, }) {
322
+ const aiAutofillGroupLabel = (0, core_1.useCrispMessage)(aiAutofillGroupLabelProp, "AI Autofill");
323
+ const typeGroupLabel = (0, core_1.useCrispMessage)(typeGroupLabelProp, "Type");
324
+ const searchPlaceholder = (0, core_1.useCrispMessage)(searchPlaceholderProp, "Search attribute types...");
325
+ const selectOptions = React.useMemo(() => {
326
+ const ai = (aiAutofillOptions ?? []).map((o) => toSelectOption(o, aiAutofillGroupLabel));
327
+ const type = options.map((o) => toSelectOption(o, typeGroupLabel));
328
+ return [...ai, ...type];
329
+ }, [aiAutofillOptions, options, aiAutofillGroupLabel, typeGroupLabel]);
330
+ return ((0, jsx_runtime_1.jsx)(select_1.Select, { id: id, options: selectOptions, value: value, onValueChange: onValueChange, searchable: true, searchPlaceholder: searchPlaceholder, width: "full" }));
331
+ }
332
+ AttributeTypeSelect.displayName = "AttributeTypeSelect";
333
+ /**
334
+ * "Create attribute" (crisp#637, P-125, verified live): Attribute Type
335
+ * select (defaults to Text) + Name (autofocused) + Description (optional)
336
+ * + an AI-autofill switch row, footer Cancel/Create. Built on `FormDialog`
337
+ * in its plain (non-`createMode`) shape — Attio's own footer here has a
338
+ * real Cancel button, unlike the record-create dialog `createMode` models
339
+ * (crisp#548), so `createMode` would be the wrong fit even though the
340
+ * button carries the same ⌘↵ hint; that hint is rendered locally instead.
341
+ */
342
+ function CreateAttributeDialog({ type, onTypeChange, typeOptions, aiAutofillTypeOptions, name, onNameChange, description, onDescriptionChange, aiAutofillEnabled = false, onAiAutofillChange, onSubmit, onClose, busy, error, title: titleProp, attributeTypeLabel: attributeTypeLabelProp, nameLabel: nameLabelProp, descriptionLabel: descriptionLabelProp, descriptionPlaceholder: descriptionPlaceholderProp, aiAutofillLabel: aiAutofillLabelProp, setUpAiAutofillLabel: setUpAiAutofillLabelProp, createLabel: createLabelProp, }) {
343
+ const title = (0, core_1.useCrispMessage)(titleProp, "Create attribute");
344
+ const attributeTypeLabel = (0, core_1.useCrispMessage)(attributeTypeLabelProp, "Attribute Type");
345
+ const nameLabel = (0, core_1.useCrispMessage)(nameLabelProp, "Name");
346
+ const descriptionLabel = (0, core_1.useCrispMessage)(descriptionLabelProp, "Description");
347
+ const descriptionPlaceholder = (0, core_1.useCrispMessage)(descriptionPlaceholderProp, "Add a description for this attribute");
348
+ const aiAutofillLabel = (0, core_1.useCrispMessage)(aiAutofillLabelProp, "AI autofill");
349
+ const setUpAiAutofillLabel = (0, core_1.useCrispMessage)(setUpAiAutofillLabelProp, "Set up AI autofill");
350
+ const createLabel = (0, core_1.useCrispMessage)(createLabelProp, "Create attribute");
351
+ const switchId = React.useId();
352
+ const nameId = React.useId();
353
+ return ((0, jsx_runtime_1.jsxs)(formdialog_1.FormDialog, { title: title, submitLabel: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [createLabel, (0, jsx_runtime_1.jsxs)("span", { className: "crisp-objectsettings-kbd", "aria-hidden": "true", children: [(0, jsx_runtime_1.jsx)("kbd", { children: "\u2318" }), (0, jsx_runtime_1.jsx)("kbd", { children: "\u21B5" })] })] }), onSubmit: onSubmit, onClose: onClose, busy: busy, error: error, size: 520, className: (0, objectsettings_recipe_1.objectsettingsRecipe)(), children: [(0, jsx_runtime_1.jsx)(formfield_1.FormField, { label: attributeTypeLabel, children: (0, jsx_runtime_1.jsx)(AttributeTypeSelect, { value: type, onValueChange: onTypeChange, options: typeOptions, aiAutofillOptions: aiAutofillTypeOptions }) }), (0, jsx_runtime_1.jsx)(formfield_1.FormField, { label: nameLabel, htmlFor: nameId, children: (0, jsx_runtime_1.jsx)(input_1.Input, { id: nameId, autoFocus: true, value: name, onChange: (e) => onNameChange(e.target.value) }) }), (0, jsx_runtime_1.jsx)(formfield_1.FormField, { label: descriptionLabel, optional: true, children: (0, jsx_runtime_1.jsx)(textarea_1.Textarea, { placeholder: descriptionPlaceholder, value: description, onChange: (e) => onDescriptionChange(e.target.value) }) }), (0, jsx_runtime_1.jsxs)("div", { className: "crisp-objectsettings-aiautofill", children: [(0, jsx_runtime_1.jsx)("span", { className: "crisp-objectsettings-aiautofill-label", children: aiAutofillLabel }), (0, jsx_runtime_1.jsxs)("label", { className: "crisp-objectsettings-aiautofill-row", htmlFor: switchId, children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Sparkles, { size: 14 }), (0, jsx_runtime_1.jsx)("span", { children: setUpAiAutofillLabel }), (0, jsx_runtime_1.jsx)(switch_1.Switch, { id: switchId, checked: aiAutofillEnabled, onCheckedChange: onAiAutofillChange })] })] })] }));
354
+ }
355
+ CreateAttributeDialog.displayName = "CreateAttributeDialog";
356
+ /**
357
+ * This folder holds several components (`ObjectsList`, `ObjectDetailPage`,
358
+ * `AttributesTable`, `CreateAttributeDialog`, …), not one named after the
359
+ * slug — unlike every other product dir, whose docs `<ManualInstallation>`
360
+ * resolves an import binding from the folder slug ("avatargroup" →
361
+ * `AvatarGroup`) by reading this file's exports. This alias exists only so
362
+ * that resolver finds a match for "objectsettings"; consumers should import
363
+ * the real names above, never this one.
364
+ */
365
+ exports.ObjectSettings = ObjectsList;
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.objectsettingsRecipe = void 0;
4
+ const core_1 = require("../../core");
5
+ const tokens_1 = require("../../tokens");
6
+ // Measured live against app.attio.com/<workspace>/settings/data/objects and
7
+ // .../objects/companies/{general,attributes} (crisp#637, P-122/P-123/P-124/
8
+ // P-125/P-126). Values with no matching scale token are one-off literals,
9
+ // same precedent as authshell's own 20px title (crisp#637 golden rule 3):
10
+ // the 24/600/28/-0.48 page title, the 12px/16px badge dot sizes, and the
11
+ // 30% squircle radius on the object-icon tile.
12
+ const config = (0, core_1.defineRecipe)({
13
+ name: "objectsettings",
14
+ base: `& .crisp-objectsettings-page { display: flex; flex-direction: column; gap: ${tokens_1.contract.space["6"]}; }
15
+ & .crisp-objectsettings-head { display: flex; align-items: flex-start; justify-content: space-between; gap: ${tokens_1.contract.space["4"]}; }
16
+ & .crisp-objectsettings-heading { display: flex; flex-direction: column; gap: ${tokens_1.contract.space["0.5"]}; min-width: 0; }
17
+ & .crisp-objectsettings-title { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; font-size: 24px; line-height: 28px; font-weight: ${tokens_1.contract.weight.semibold}; letter-spacing: -0.48px; color: ${tokens_1.contract.fgPrimary}; margin: 0; }
18
+ & .crisp-objectsettings-subtitle { font-size: ${tokens_1.contract.text.lg}; line-height: 20px; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.fgMuted}; margin: 0; }
19
+
20
+ & .crisp-objectsettings-toolbar { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; }
21
+ & .crisp-objectsettings-search { display: flex; align-items: center; flex: 1; gap: ${tokens_1.contract.space["1.5"]}; height: ${tokens_1.contract.size.menuRow}; padding: 0 ${tokens_1.contract.space["2.5"]}; border-radius: ${tokens_1.contract.radius.md}; background: ${tokens_1.contract.bgSurface}; box-shadow: inset 0 0 0 1px ${tokens_1.contract.borderDefault}; color: ${tokens_1.contract.fgMuted}; transition: box-shadow .15s; }
22
+ & .crisp-objectsettings-search:focus-within { box-shadow: inset 0 0 0 1px ${tokens_1.contract.bgBrandSolid}; }
23
+ & .crisp-objectsettings-search input { flex: 1; min-width: 0; border: none; outline: none; background: none; font-family: inherit; font-size: ${tokens_1.contract.text.lg}; color: ${tokens_1.contract.fgPrimary}; }
24
+ & .crisp-objectsettings-search input::placeholder { color: ${tokens_1.contract.fgMuted}; }
25
+ & .crisp-objectsettings-filterbtn { display: inline-flex; align-items: center; gap: ${tokens_1.contract.space["1.5"]}; height: ${tokens_1.contract.size.menuRow}; padding: 0 ${tokens_1.contract.space["2.5"]} 0 ${tokens_1.contract.space["2"]}; border: none; border-radius: ${tokens_1.contract.radius.lg}; background: ${tokens_1.contract.bgSurface}; box-shadow: inset 0 0 0 1px ${tokens_1.contract.borderDefault}; color: ${tokens_1.contract.fgPrimary}; font-family: inherit; font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.medium}; cursor: pointer; white-space: nowrap; }
26
+ & .crisp-objectsettings-filterbtn:hover { background: ${tokens_1.contract.bgRaised}; }
27
+
28
+ /* Object / attribute icon tile — Attio's own squircle (measured
29
+ border-radius: 30%, not a scale token: it's a proportional radius
30
+ tied to the tile's own size, unlike every other radius crisp uses,
31
+ which is a fixed px). */
32
+ & .crisp-objectsettings-icon { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 16px; height: 16px; border-radius: 30%; color: ${tokens_1.contract.fgOnBrand}; }
33
+ & .crisp-objectsettings-icon--lg { width: 48px; height: 48px; border-radius: 16px; }
34
+
35
+ /* "Standard"/"Custom" type badge and the muted "No records" cell —
36
+ plain text, not a Badge pill (measured: transparent background). */
37
+ & .crisp-objectsettings-typebadge { font-size: ${tokens_1.contract.text.sm}; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.fgNeutralSoft}; }
38
+ & .crisp-objectsettings-muted { color: ${tokens_1.contract.fgMuted}; }
39
+
40
+ /* Attribute-type option row (icon + label + optional trailing badge),
41
+ composed as a Select option's \`label\` ReactNode. */
42
+ & .crisp-objectsettings-typeoption { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; width: 100%; min-width: 0; }
43
+ & .crisp-objectsettings-typeoption-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
44
+ & .crisp-objectsettings-typeoption-badge { flex-shrink: 0; font-size: ${tokens_1.contract.text.sm}; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.fgBrandSoft}; }
45
+
46
+ /* Create-attribute dialog's AI-autofill row and the submit button's
47
+ ⌘↵ hint (measured off the same dialog, same shape as Dialog's own
48
+ .crisp-dialog-submit-key, kept local rather than reaching into
49
+ another component's scoped classes). */
50
+ & .crisp-objectsettings-aiautofill { display: flex; flex-direction: column; gap: ${tokens_1.contract.space["1.5"]}; }
51
+ & .crisp-objectsettings-aiautofill-label { font-size: ${tokens_1.contract.text.md}; font-weight: ${tokens_1.contract.weight.medium}; color: ${tokens_1.contract.fgMuted}; }
52
+ & .crisp-objectsettings-aiautofill-row { display: flex; align-items: center; gap: ${tokens_1.contract.space["2"]}; height: ${tokens_1.contract.size.menuRow}; padding: 0 ${tokens_1.contract.space["2.5"]}; border-radius: ${tokens_1.contract.radius.md}; box-shadow: inset 0 0 0 1px ${tokens_1.contract.borderDefault}; font-size: ${tokens_1.contract.text.lg}; color: ${tokens_1.contract.fgPrimary}; cursor: pointer; }
53
+ & .crisp-objectsettings-aiautofill-row > svg { flex-shrink: 0; color: ${tokens_1.contract.fgMuted}; }
54
+ & .crisp-objectsettings-aiautofill-row > span { flex: 1; }
55
+ & .crisp-objectsettings-kbd { display: inline-flex; align-items: center; gap: ${tokens_1.contract.space["1"]}; margin-inline-start: ${tokens_1.contract.space["1"]}; }
56
+ & .crisp-objectsettings-kbd kbd { box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; width: 16px; height: 16px; padding: 0 3px; border-radius: 4px; background: transparent; color: inherit; font-family: inherit; font-style: normal; font-size: 10px; }
57
+
58
+ & .crisp-objectsettings-fieldrow { display: grid; grid-template-columns: 1fr 1fr; gap: ${tokens_1.contract.space["4"]}; }
59
+ `,
60
+ variants: {},
61
+ defaultVariants: {},
62
+ compoundVariants: [],
63
+ });
64
+ exports.objectsettingsRecipe = (0, core_1.resolveRecipe)(config);
65
+ exports.default = config;
@@ -9,6 +9,14 @@ const tokens_1 = require("../../tokens");
9
9
  // column (transparent, border-right) and a flexible main area whose tab bar
10
10
  // (14/500 labels over a sliding ink underline) switches content — Overview shows
11
11
  // a bordered Highlights stat-grid and an Activity feed.
12
+ //
13
+ // `.crisp-recordpage-panel`'s own `padding-top` (c.space["4"], 16px) was
14
+ // dropped (your-moon/crisp#588): this panel is only ever rendered as Tabs'
15
+ // own children (every branch of `builtinPanel` in recordpage.tsx, all fed to
16
+ // `<Tabs>` below), so it sat inside `.crisp-tabs-panel`, which now carries
17
+ // its own measured 32px top-inset by default — keeping both would have
18
+ // stacked to 48px. Caught by tracing every consumer of `Tabs` before
19
+ // shipping its new default, not by a screenshot.
12
20
  const config = (0, core_1.defineRecipe)({
13
21
  name: "recordpage",
14
22
  base: `
@@ -128,7 +136,8 @@ const config = (0, core_1.defineRecipe)({
128
136
 
129
137
  & .crisp-recordpage-main { flex: 1; min-width: 0; display: flex; flex-direction: column; min-height: 0; overflow-y: auto; padding: ${tokens_1.contract.space["3"]} ${tokens_1.contract.space["5"]}; }
130
138
 
131
- & .crisp-recordpage-panel { padding-top: ${tokens_1.contract.space["4"]}; display: flex; flex-direction: column; gap: ${tokens_1.contract.space["4"]}; }
139
+ /* no padding-top: Tabs' own panel supplies it now (#588) */
140
+ & .crisp-recordpage-panel { display: flex; flex-direction: column; gap: ${tokens_1.contract.space["4"]}; }
132
141
  & .crisp-recordpage-card { border: 1px solid ${tokens_1.contract.borderSubtle}; border-radius: ${tokens_1.contract.radius["2xl"]}; background: ${tokens_1.contract.bgSurface}; overflow: hidden; }
133
142
  & .crisp-recordpage-card-title { font-size: ${tokens_1.contract.text.lg}; font-weight: ${tokens_1.contract.weight.semibold}; letter-spacing: -0.14px; color: ${tokens_1.contract.fgPrimary}; padding: ${tokens_1.contract.space["3"]} ${tokens_1.contract.space["3"]} ${tokens_1.contract.space["2"]}; }
134
143
  & .crisp-recordpage-hl-card { border: 1px solid ${tokens_1.contract.borderSubtle}; border-radius: ${tokens_1.contract.radius["2xl"]}; overflow: hidden; }