@nadicodeai/ui 6.0.2 → 6.2.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.
Files changed (64) hide show
  1. package/dist/components/action-menu.d.ts +8 -0
  2. package/dist/components/action-menu.d.ts.map +1 -0
  3. package/dist/components/action-menu.js +8 -0
  4. package/dist/components/card.d.ts +1 -1
  5. package/dist/components/card.d.ts.map +1 -1
  6. package/dist/components/card.js +5 -3
  7. package/dist/components/chart-breakdown-table.d.ts +3 -2
  8. package/dist/components/chart-breakdown-table.d.ts.map +1 -1
  9. package/dist/components/chart-breakdown-table.js +13 -9
  10. package/dist/components/chart-bullet-bar.d.ts +13 -12
  11. package/dist/components/chart-bullet-bar.d.ts.map +1 -1
  12. package/dist/components/chart-bullet-bar.js +16 -36
  13. package/dist/components/chart-kpi-stat.d.ts +26 -15
  14. package/dist/components/chart-kpi-stat.d.ts.map +1 -1
  15. package/dist/components/chart-kpi-stat.js +28 -4
  16. package/dist/components/chart-time-series-layout.d.ts +6 -0
  17. package/dist/components/chart-time-series-layout.d.ts.map +1 -0
  18. package/dist/components/chart-time-series-layout.js +6 -0
  19. package/dist/components/chart-time-series.d.ts +6 -2
  20. package/dist/components/chart-time-series.d.ts.map +1 -1
  21. package/dist/components/chart-time-series.js +27 -9
  22. package/dist/components/chart.js +1 -1
  23. package/dist/components/collection-filters.d.ts +28 -0
  24. package/dist/components/collection-filters.d.ts.map +1 -0
  25. package/dist/components/collection-filters.js +54 -0
  26. package/dist/components/collection-scope.d.ts +15 -0
  27. package/dist/components/collection-scope.d.ts.map +1 -0
  28. package/dist/components/collection-scope.js +7 -0
  29. package/dist/components/collection-toolbar.d.ts +10 -0
  30. package/dist/components/collection-toolbar.d.ts.map +1 -0
  31. package/dist/components/collection-toolbar.js +4 -0
  32. package/dist/components/forma-appearance.d.ts +13 -0
  33. package/dist/components/forma-appearance.d.ts.map +1 -0
  34. package/dist/components/forma-appearance.js +11 -0
  35. package/dist/components/settings-facts.d.ts +28 -0
  36. package/dist/components/settings-facts.d.ts.map +1 -0
  37. package/dist/components/settings-facts.js +7 -0
  38. package/dist/components/settings-section.d.ts +5 -5
  39. package/dist/components/settings-section.d.ts.map +1 -1
  40. package/dist/components/settings-section.js +6 -11
  41. package/dist/components/sidebar.d.ts +4 -1
  42. package/dist/components/sidebar.d.ts.map +1 -1
  43. package/dist/components/sidebar.js +3 -3
  44. package/dist/components/table-layouts.d.ts +22 -0
  45. package/dist/components/table-layouts.d.ts.map +1 -0
  46. package/dist/components/table-layouts.js +20 -0
  47. package/dist/components/table.d.ts +15 -2
  48. package/dist/components/table.d.ts.map +1 -1
  49. package/dist/components/table.js +8 -4
  50. package/dist/eslint/rules/jsx-class-utility.js +44 -12
  51. package/dist/eslint/rules/no-resting-settings-control.js +15 -13
  52. package/dist/eslint/rules/no-shadcn-appearance-override.js +189 -36
  53. package/dist/eslint/rules/shadcn-composition.js +48 -3
  54. package/dist/index.d.ts +5 -0
  55. package/dist/index.d.ts.map +1 -1
  56. package/dist/index.js +5 -0
  57. package/dist/internal/forma/stored-appearance.d.ts +29 -0
  58. package/dist/internal/forma/stored-appearance.d.ts.map +1 -0
  59. package/dist/internal/forma/stored-appearance.js +53 -0
  60. package/docs/component-classification.md +3 -2
  61. package/docs/contract.md +78 -7
  62. package/docs/forma-avatar.md +18 -0
  63. package/docs/product-compositions.md +14 -11
  64. package/package.json +3 -2
@@ -6,8 +6,8 @@
6
6
  * editor that changes them. The regression this catches is a form control
7
7
  * rendered beside those facts rather than inside the editor they summon.
8
8
  *
9
- * "Inside the editor" means inside an `ActionDialogForm`'s `fields` prop, or
10
- * inside a `Dialog`/`Drawer` subtree for a surface that owns its own. Being a
9
+ * "Inside the editor" means inside a `SettingsSection`, an `ActionDialogForm`'s
10
+ * `fields` prop, or a `Dialog`/`Drawer` subtree for a surface that owns its own. Being a
11
11
  * CHILD of `ActionDialogForm` is the opposite: children are the resting surface
12
12
  * the dialog is opened from, which is exactly where a control must not be.
13
13
  *
@@ -21,6 +21,7 @@
21
21
  * here. Replaces `restingSettingsControlViolations` in the retired portal
22
22
  * page-grammar analyzers (#821).
23
23
  */
24
+ import { jsxComponentName } from "./jsx-class-utility.js";
24
25
 
25
26
  // The controls a person types or picks into. Lowercase `input` is deliberately
26
27
  // absent: a hidden field carries context a summoned editor already decided, and
@@ -37,15 +38,15 @@ const RESTING_CONTROL_TAGS = new Set([
37
38
 
38
39
  const FACTS_SECTION_TAG = "SettingsFactsSection";
39
40
 
40
- function elementName(node) {
41
- return node?.name?.type === "JSXIdentifier" ? node.name.name : undefined;
42
- }
43
-
44
- function summons(node) {
45
- if (node.type === "JSXAttribute") return node.name.name === "fields";
41
+ function summons(node, context) {
42
+ if (node.type === "JSXAttribute") {
43
+ return (
44
+ node.name.name === "fields" && jsxComponentName(node.parent, context) === "ActionDialogForm"
45
+ );
46
+ }
46
47
  if (node.type !== "JSXElement") return false;
47
- const name = elementName(node.openingElement);
48
- return Boolean(name && (name.startsWith("Dialog") || name.startsWith("Drawer")));
48
+ const name = jsxComponentName(node.openingElement, context);
49
+ return ["SettingsSection", "Dialog", "DialogContent", "Drawer", "DrawerContent"].includes(name);
49
50
  }
50
51
 
51
52
  export const noRestingSettingsControl = {
@@ -58,7 +59,7 @@ export const noRestingSettingsControl = {
58
59
  schema: [],
59
60
  messages: {
60
61
  restingControl:
61
- "<{{tag}}> rests beside its facts: put it in the editor the facts summon (an ActionDialogForm `fields` prop, or a Dialog/Drawer subtree).",
62
+ "<{{tag}}> rests beside its facts: use SettingsSection for a form, or put it in the summoned editor (ActionDialogForm `fields`, Dialog, or Drawer).",
62
63
  },
63
64
  },
64
65
  create(context) {
@@ -67,13 +68,14 @@ export const noRestingSettingsControl = {
67
68
 
68
69
  return {
69
70
  JSXOpeningElement(node) {
70
- const name = elementName(node);
71
+ const name = jsxComponentName(node, context);
71
72
  if (name === FACTS_SECTION_TAG) {
72
73
  declaresFacts = true;
73
74
  return;
74
75
  }
75
76
  if (!name || !RESTING_CONTROL_TAGS.has(name)) return;
76
- if (context.sourceCode.getAncestors(node).some(summons)) return;
77
+ if (context.sourceCode.getAncestors(node).some((ancestor) => summons(ancestor, context)))
78
+ return;
77
79
  resting.push({ node, name });
78
80
  },
79
81
  "Program:exit"() {
@@ -12,6 +12,8 @@
12
12
  * of which still compile and still fork the primitive. Those are what survives
13
13
  * here; the raw-palette names are gone from the sets below with the palette.
14
14
  */
15
+ import { posix } from "node:path";
16
+
15
17
  import {
16
18
  classUtilities,
17
19
  openingElementClassUtilities,
@@ -19,6 +21,127 @@ import {
19
21
  skipsPackageOwnedPrimitive,
20
22
  } from "./jsx-class-utility.js";
21
23
 
24
+ // These exact Portal adapters forward props to package primitives. Card
25
+ // chooses its typed surface default; Table owns only structural geometry.
26
+ // Their other exports are direct reexports.
27
+ const portalCardExports = new Set([
28
+ "Card",
29
+ "CardHeader",
30
+ "CardTitle",
31
+ "CardDescription",
32
+ "CardAction",
33
+ "CardContent",
34
+ ]);
35
+ const portalTableExports = new Set([
36
+ "Table",
37
+ "TableHead",
38
+ "TableCell",
39
+ "TableBody",
40
+ "TableCaption",
41
+ "TableFooter",
42
+ "TableHeader",
43
+ "TableRow",
44
+ ]);
45
+
46
+ function primitiveModule(source, filename) {
47
+ if (source.startsWith("@nadicodeai/ui/components/")) {
48
+ return { card: source === "@nadicodeai/ui/components/card", exports: undefined };
49
+ }
50
+ const normalized = filename.replaceAll("\\", "/");
51
+ const portalStart = normalized.lastIndexOf("/apps/portal/src/");
52
+ if (portalStart === -1) return undefined;
53
+ const portalSource = `${normalized.slice(0, portalStart)}/apps/portal/src`;
54
+ const resolved = source.startsWith(".")
55
+ ? posix.normalize(posix.join(posix.dirname(normalized), source)).replace(/\.[jt]sx?$/, "")
56
+ : undefined;
57
+ if (source === "@/components/kit/card" || resolved === `${portalSource}/components/kit/card`) {
58
+ return { card: true, exports: portalCardExports };
59
+ }
60
+ if (source === "@/components/kit/table" || resolved === `${portalSource}/components/kit/table`) {
61
+ return { card: false, exports: portalTableExports };
62
+ }
63
+ return undefined;
64
+ }
65
+
66
+ function primitiveExport(module, name) {
67
+ if (module.exports && !module.exports.has(name)) return undefined;
68
+ return { cardRoot: module.card && name === "Card" };
69
+ }
70
+
71
+ const appearanceStyleProperties = new Set([
72
+ "appearance",
73
+ "WebkitAppearance",
74
+ "MozAppearance",
75
+ "color",
76
+ "background",
77
+ "backgroundColor",
78
+ "backgroundImage",
79
+ "boxShadow",
80
+ "textShadow",
81
+ "opacity",
82
+ "outlineColor",
83
+ "filter",
84
+ "backdropFilter",
85
+ "WebkitBackdropFilter",
86
+ "fill",
87
+ "fillOpacity",
88
+ "stroke",
89
+ "strokeOpacity",
90
+ ...[
91
+ "",
92
+ "Top",
93
+ "Right",
94
+ "Bottom",
95
+ "Left",
96
+ "Block",
97
+ "Inline",
98
+ "BlockStart",
99
+ "BlockEnd",
100
+ "InlineStart",
101
+ "InlineEnd",
102
+ ].map((side) => `border${side}Color`),
103
+ ...[
104
+ "",
105
+ "TopLeft",
106
+ "TopRight",
107
+ "BottomLeft",
108
+ "BottomRight",
109
+ "StartStart",
110
+ "StartEnd",
111
+ "EndStart",
112
+ "EndEnd",
113
+ ].map((corner) => `border${corner}Radius`),
114
+ ]);
115
+
116
+ function staticStyleValue(node) {
117
+ if (node.type === "Literal") return node.value;
118
+ if (node.type === "TemplateLiteral" && node.expressions.length === 0) {
119
+ return node.quasis[0]?.value.cooked;
120
+ }
121
+ return undefined;
122
+ }
123
+
124
+ // Inline object literals only. Unknown values, computed keys, imported style
125
+ // objects and spreads remain review work; layout properties are not paint.
126
+ function inlineAppearanceProperties(attribute) {
127
+ if (attribute.type !== "JSXAttribute" || attribute.name.name !== "style") return [];
128
+ const expression =
129
+ attribute.value?.type === "JSXExpressionContainer" ? attribute.value.expression : undefined;
130
+ if (expression?.type !== "ObjectExpression") return [];
131
+ return expression.properties.filter((property) => {
132
+ if (property.type !== "Property") return false;
133
+ const name =
134
+ property.key.type === "Literal"
135
+ ? property.key.value
136
+ : !property.computed && property.key.type === "Identifier"
137
+ ? property.key.name
138
+ : undefined;
139
+ if (!appearanceStyleProperties.has(name)) return false;
140
+ const value = staticStyleValue(property.value);
141
+ return typeof value === "number" || (typeof value === "string" && value.trim() !== "");
142
+ });
143
+ }
144
+
22
145
  function withoutVariants(utility) {
23
146
  return utility.slice(utility.lastIndexOf(":") + 1);
24
147
  }
@@ -74,7 +197,10 @@ function isAppearanceUtility(utility) {
74
197
 
75
198
  function isProhibitedAppearanceUtility(utility, { cardRoot = false } = {}) {
76
199
  const base = withoutVariants(utility);
77
- return isAppearanceUtility(utility) && (!isBorderRemoval(base) || cardRoot);
200
+ return (
201
+ targetsSharedSlot(utility) ||
202
+ (isAppearanceUtility(utility) && (!isBorderRemoval(base) || cardRoot))
203
+ );
78
204
  }
79
205
 
80
206
  function targetsSharedSlot(utility) {
@@ -92,15 +218,14 @@ export const noShadcnAppearanceOverride = {
92
218
  meta: {
93
219
  type: "problem",
94
220
  docs: {
95
- description:
96
- "Disallow app callers repainting imported shadcn primitives; compose their structural layout only",
221
+ description: "Keep shared component appearance and internal slot layout at their owner",
97
222
  },
98
223
  // Default "exempt": inside @nadicodeai/ui the primitives ARE the appearance,
99
224
  // so every utility here would be a false positive.
100
225
  schema: [packageOwnedPrimitivesSchema],
101
226
  messages: {
102
227
  appearanceOverride:
103
- "Do not repaint an imported @nadicodeai/ui primitive with {{utility}}; use its semantic variant or package primitive.",
228
+ "Do not override shared component appearance or internal slot layout with {{utility}}; use a component prop or fix its shared owner.",
104
229
  },
105
230
  },
106
231
  create(context) {
@@ -108,48 +233,76 @@ export const noShadcnAppearanceOverride = {
108
233
  return {};
109
234
  }
110
235
 
111
- const sharedPrimitiveNames = new Set();
112
- const sharedCardRootNames = new Set();
113
- const isSharedPrimitive = (node) =>
114
- node?.type === "JSXOpeningElement" &&
115
- node.name.type === "JSXIdentifier" &&
116
- sharedPrimitiveNames.has(node.name.name);
117
- const isSharedCardRoot = (node) =>
118
- node?.type === "JSXOpeningElement" &&
119
- node.name.type === "JSXIdentifier" &&
120
- sharedCardRootNames.has(node.name.name);
236
+ const sourceCode = context.sourceCode;
237
+ const sharedBindings = new Map();
238
+
239
+ function importedBinding(identifier) {
240
+ for (let scope = sourceCode.getScope(identifier); scope; scope = scope.upper) {
241
+ const variable = scope.set.get(identifier.name);
242
+ if (variable) return sharedBindings.get(variable);
243
+ }
244
+ return undefined;
245
+ }
246
+
247
+ function sharedPrimitive(node) {
248
+ if (node?.type !== "JSXOpeningElement") return undefined;
249
+ if (node.name.type === "JSXIdentifier") {
250
+ const binding = importedBinding(node.name);
251
+ return binding && binding.name !== "*"
252
+ ? primitiveExport(binding.module, binding.name)
253
+ : undefined;
254
+ }
255
+ if (node.name.type === "JSXMemberExpression" && node.name.object.type === "JSXIdentifier") {
256
+ const binding = importedBinding(node.name.object);
257
+ return binding?.name === "*"
258
+ ? primitiveExport(binding.module, node.name.property.name)
259
+ : undefined;
260
+ }
261
+ return undefined;
262
+ }
263
+
121
264
  return {
122
- ImportDeclaration(node) {
123
- if (
124
- typeof node.source.value !== "string" ||
125
- !node.source.value.startsWith("@nadicodeai/ui/components/")
126
- ) {
127
- return;
128
- }
129
- for (const specifier of node.specifiers) {
130
- sharedPrimitiveNames.add(specifier.local.name);
131
- if (
132
- node.source.value === "@nadicodeai/ui/components/card" &&
133
- specifier.type === "ImportSpecifier" &&
134
- specifier.imported.name === "Card"
135
- ) {
136
- sharedCardRootNames.add(specifier.local.name);
265
+ Program(node) {
266
+ // Imports are hoisted; collect their declared variables before visiting
267
+ // JSX. Binding identity also distinguishes shadowing parameters/locals.
268
+ for (const declaration of node.body) {
269
+ if (declaration.type !== "ImportDeclaration" || declaration.importKind === "type")
270
+ continue;
271
+ const module = primitiveModule(declaration.source.value, context.filename);
272
+ if (!module) continue;
273
+ for (const specifier of declaration.specifiers) {
274
+ if (specifier.importKind === "type") continue;
275
+ const name =
276
+ specifier.type === "ImportNamespaceSpecifier"
277
+ ? "*"
278
+ : specifier.type === "ImportSpecifier"
279
+ ? (specifier.imported.name ?? specifier.imported.value)
280
+ : "default";
281
+ for (const variable of sourceCode.getDeclaredVariables(specifier)) {
282
+ sharedBindings.set(variable, { module, name });
283
+ }
137
284
  }
138
285
  }
139
286
  },
140
287
  JSXOpeningElement(node) {
141
- if (!isSharedPrimitive(node)) return;
142
- for (const utility of openingElementClassUtilities(node, context)) {
143
- if (
144
- utility &&
145
- isProhibitedAppearanceUtility(utility, { cardRoot: isSharedCardRoot(node) })
146
- ) {
288
+ const primitive = sharedPrimitive(node);
289
+ if (!primitive) return;
290
+ for (const utility of openingElementClassUtilities(node, context, true)) {
291
+ if (utility && isProhibitedAppearanceUtility(utility, primitive)) {
147
292
  context.report({ node, messageId: "appearanceOverride", data: { utility } });
148
293
  }
149
294
  }
295
+ for (const property of node.attributes.flatMap(inlineAppearanceProperties)) {
296
+ const name = property.key.name ?? property.key.value;
297
+ context.report({
298
+ node: property,
299
+ messageId: "appearanceOverride",
300
+ data: { utility: `style.${name}` },
301
+ });
302
+ }
150
303
  },
151
304
  JSXAttribute(node) {
152
- if (isSharedPrimitive(node.parent)) return;
305
+ if (sharedPrimitive(node.parent)) return;
153
306
  for (const utility of classUtilities(node, context)) {
154
307
  if (
155
308
  !targetsSharedSlot(utility) ||
@@ -1,5 +1,5 @@
1
1
  /*
2
- * The four composition conventions the shadcn primitives are built to, checked
2
+ * The composition conventions the shared components are built to, checked
3
3
  * where they are composed:
4
4
  *
5
5
  * - an overlay carries its title, so a screen reader announces what opened
@@ -9,7 +9,9 @@
9
9
  * - an `AvatarImage` has an `AvatarFallback` beside it, or the avatar is blank
10
10
  * until the image loads;
11
11
  * - a lucide icon inside a `Button` carries `data-icon`, the hook the button's
12
- * own spacing rules key on.
12
+ * own spacing rules key on;
13
+ * - a settings submit action uses SettingsSection's shared footer, so each
14
+ * caller cannot rebuild its own divider, padding, and action position.
13
15
  *
14
16
  * Standard tool checked first: `jsx-a11y` covers roles and ARIA on
15
17
  * intrinsic elements and says nothing about these composed components;
@@ -18,7 +20,11 @@
18
20
  * of the retired @nadicodeai/ui composition-conventions scanner (#822); stock
19
21
  * upstream shapes carry an inline disable with the reason at the site.
20
22
  */
21
- import { packageOwnedPrimitivesSchema, skipsPackageOwnedPrimitive } from "./jsx-class-utility.js";
23
+ import {
24
+ jsxComponentName,
25
+ packageOwnedPrimitivesSchema,
26
+ skipsPackageOwnedPrimitive,
27
+ } from "./jsx-class-utility.js";
22
28
 
23
29
  const OVERLAY_TITLES = {
24
30
  AlertDialogContent: "AlertDialogTitle",
@@ -75,6 +81,39 @@ function isIconTag(tagName) {
75
81
  return tagName === "Icon" || tagName.endsWith("Icon");
76
82
  }
77
83
 
84
+ function misplacedSettingsSubmit(node, context) {
85
+ if (jsxComponentName(node, context) !== "Button") return false;
86
+ const attribute = node.attributes.find(
87
+ (entry) => entry.type === "JSXAttribute" && entry.name.name === "type",
88
+ );
89
+ const value =
90
+ attribute?.value?.type === "JSXExpressionContainer"
91
+ ? attribute.value.expression
92
+ : attribute?.value;
93
+ const type =
94
+ value?.type === "TemplateLiteral" && value.expressions.length === 0
95
+ ? value.quasis[0]?.value.cooked
96
+ : value?.value;
97
+ if (type !== "submit") return false;
98
+ const ancestors = context.sourceCode.getAncestors(node);
99
+ const section = ancestors.findLast(
100
+ (ancestor) =>
101
+ ancestor.type === "JSXElement" &&
102
+ jsxComponentName(ancestor.openingElement, context) === "SettingsSection",
103
+ );
104
+ if (!section) return false;
105
+ return !ancestors.slice(ancestors.indexOf(section) + 1).some((ancestor) => {
106
+ if (ancestor.type === "JSXAttribute")
107
+ return ancestor.name.name === "footer" && ancestor.parent === section.openingElement;
108
+ return (
109
+ ancestor.type === "JSXElement" &&
110
+ ["DialogContent", "DrawerContent", "AlertDialogContent"].includes(
111
+ jsxComponentName(ancestor.openingElement, context),
112
+ )
113
+ );
114
+ });
115
+ }
116
+
78
117
  export const shadcnComposition = {
79
118
  meta: {
80
119
  type: "problem",
@@ -88,6 +127,8 @@ export const shadcnComposition = {
88
127
  selectGroup: "SelectItem must be rendered inside SelectGroup.",
89
128
  avatarFallback: "AvatarImage requires an AvatarFallback in the same Avatar.",
90
129
  buttonIcon: "An icon inside Button must carry data-icon.",
130
+ settingsFooter:
131
+ "Put a SettingsSection submit action in its footer prop; the shared component owns the divider, spacing, and action position.",
91
132
  },
92
133
  },
93
134
  create(context) {
@@ -101,6 +142,10 @@ export const shadcnComposition = {
101
142
  if (!tag) return;
102
143
  const element = node.parent;
103
144
 
145
+ if (misplacedSettingsSubmit(node, context)) {
146
+ context.report({ node, messageId: "settingsFooter" });
147
+ }
148
+
104
149
  const title = OVERLAY_TITLES[tag];
105
150
  if (title && !hasDescendant(element, title)) {
106
151
  context.report({ node, messageId: "overlayTitle", data: { tag, title } });
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./components/accordion.js";
2
2
  export * from "./components/action-band.js";
3
+ export * from "./components/action-menu.js";
3
4
  export * from "./components/action-status.js";
4
5
  export * from "./components/activity-orb.js";
5
6
  export * from "./components/alert.js";
@@ -20,6 +21,7 @@ export * from "./components/button.js";
20
21
  export * from "./components/button-group.js";
21
22
  export * from "./components/calendar.js";
22
23
  export * from "./components/card.js";
24
+ export * from "./components/collection-scope.js";
23
25
  export * from "./components/card-marketing.js";
24
26
  export * from "./components/carousel.js";
25
27
  export * from "./components/chart.js";
@@ -105,6 +107,9 @@ export * from "./components/security-grid.js";
105
107
  export * from "./components/select.js";
106
108
  export * from "./components/separator.js";
107
109
  export * from "./components/settings-section.js";
110
+ export * from "./components/settings-facts.js";
111
+ export * from "./components/collection-toolbar.js";
112
+ export * from "./components/collection-filters.js";
108
113
  export * from "./components/sheet.js";
109
114
  export * from "./components/shift-band.js";
110
115
  export * from "./components/sidebar.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from "./components/accordion.js";
2
2
  export * from "./components/action-band.js";
3
+ export * from "./components/action-menu.js";
3
4
  export * from "./components/action-status.js";
4
5
  export * from "./components/activity-orb.js";
5
6
  export * from "./components/alert.js";
@@ -20,6 +21,7 @@ export * from "./components/button.js";
20
21
  export * from "./components/button-group.js";
21
22
  export * from "./components/calendar.js";
22
23
  export * from "./components/card.js";
24
+ export * from "./components/collection-scope.js";
23
25
  export * from "./components/card-marketing.js";
24
26
  export * from "./components/carousel.js";
25
27
  export * from "./components/chart.js";
@@ -105,6 +107,9 @@ export * from "./components/security-grid.js";
105
107
  export * from "./components/select.js";
106
108
  export * from "./components/separator.js";
107
109
  export * from "./components/settings-section.js";
110
+ export * from "./components/settings-facts.js";
111
+ export * from "./components/collection-toolbar.js";
112
+ export * from "./components/collection-filters.js";
108
113
  export * from "./components/sheet.js";
109
114
  export * from "./components/shift-band.js";
110
115
  export * from "./components/sidebar.js";
@@ -0,0 +1,29 @@
1
+ import { type FormaChoice, type FormaColor } from "./model.js";
2
+ /** What Hermes stores for an Agent's look. Both may be absent or unknown. */
3
+ export interface StoredAppearance {
4
+ shape?: string | null;
5
+ color?: string | null;
6
+ }
7
+ /** The `shape` value a chosen form is stored as. */
8
+ export declare const shapeFor: (form: FormaChoice) => string;
9
+ /** The stored shape of the form an Agent wears until a customer chooses one. */
10
+ export declare const defaultShape: string;
11
+ /**
12
+ * The hex each palette colour is stored as. Hermes keeps an Agent's colour as
13
+ * a CSS colour for its own surfaces, so the stored spelling is a value rather
14
+ * than a token reference; `stored-appearance.test.ts` holds this table to the
15
+ * shipped design-system tokens, and the ESLint colour-literal exemption in the
16
+ * root config names this file.
17
+ */
18
+ export declare const formaColorValues: Record<FormaColor, string>;
19
+ /**
20
+ * Resolve the stored strings to what `FormaAvatar` draws. An unknown shape is
21
+ * the default form and an unknown colour is undefined, which leaves the form
22
+ * its own pigment. The root Agent is the caller's business: a caller that owns
23
+ * one passes `form="nadia"` instead of asking here.
24
+ */
25
+ export declare function resolveAppearance(stored: StoredAppearance): {
26
+ form: FormaChoice;
27
+ color: FormaColor | undefined;
28
+ };
29
+ //# sourceMappingURL=stored-appearance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stored-appearance.d.ts","sourceRoot":"","sources":["../../../src/internal/forma/stored-appearance.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAA0C,MAAM,SAAS,CAAC;AAEpG,6EAA6E;AAC7E,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,oDAAoD;AACpD,eAAO,MAAM,QAAQ,GAAI,MAAM,WAAW,KAAG,MAAyB,CAAC;AAEvE,gFAAgF;AAChF,eAAO,MAAM,YAAY,EAAE,MAA8B,CAAC;AAc1D;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAOvD,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,gBAAgB,GAAG;IAC3D,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,UAAU,GAAG,SAAS,CAAC;CAC/B,CAEA"}
@@ -0,0 +1,53 @@
1
+ import { defaultForm, formaChoices, formaColors } from "./model.js";
2
+ /** The `shape` value a chosen form is stored as. */
3
+ export const shapeFor = (form) => `forma:${form}`;
4
+ /** The stored shape of the form an Agent wears until a customer chooses one. */
5
+ export const defaultShape = shapeFor(defaultForm);
6
+ /**
7
+ * The five role names the launch Agents were stored under, each kept at the
8
+ * form it wore. Forms are shapes, never roles: nothing new is stored this way.
9
+ */
10
+ const legacyShapes = new Map([
11
+ ["forma:research", "uovo"],
12
+ ["forma:build", "goccia"],
13
+ ["forma:design", "nuvola"],
14
+ ["forma:operate", "quadrato"],
15
+ ["forma:review", "esagono"],
16
+ ]);
17
+ /**
18
+ * The hex each palette colour is stored as. Hermes keeps an Agent's colour as
19
+ * a CSS colour for its own surfaces, so the stored spelling is a value rather
20
+ * than a token reference; `stored-appearance.test.ts` holds this table to the
21
+ * shipped design-system tokens, and the ESLint colour-literal exemption in the
22
+ * root config names this file.
23
+ */
24
+ export const formaColorValues = {
25
+ verde: "#00a85a",
26
+ cobalto: "#1e3cff",
27
+ rosso: "#d3302f",
28
+ arancio: "#ff5a1f",
29
+ giallo: "#ffc220",
30
+ avorio: "#e9e7db",
31
+ };
32
+ /**
33
+ * Resolve the stored strings to what `FormaAvatar` draws. An unknown shape is
34
+ * the default form and an unknown colour is undefined, which leaves the form
35
+ * its own pigment. The root Agent is the caller's business: a caller that owns
36
+ * one passes `form="nadia"` instead of asking here.
37
+ */
38
+ export function resolveAppearance(stored) {
39
+ return { form: formFor(stored.shape), color: colorFor(stored.color) };
40
+ }
41
+ function formFor(shape) {
42
+ if (!shape)
43
+ return defaultForm;
44
+ // A Map, not an object: the shape is a stored string from a machine, and an
45
+ // object lookup would hand back `toString` or `constructor` for those spellings.
46
+ return (formaChoices.find((form) => shape === shapeFor(form)) ?? legacyShapes.get(shape) ?? defaultForm);
47
+ }
48
+ function colorFor(color) {
49
+ if (!color)
50
+ return undefined;
51
+ const stored = color.toLowerCase();
52
+ return formaColors.find((name) => stored === formaColorValues[name] || color === name);
53
+ }
@@ -19,8 +19,9 @@ copy, and behavior.
19
19
 
20
20
  | Component | Classification | Built on | Required states | Package-owned accessibility | Fixture |
21
21
  | --- | --- | --- | --- | --- | --- |
22
- | `SettingsSection` | Net-new NadicodeAI product composition. | shadcn `FieldGroup` plus standard section markup. | header with optional actions; field group content. | section structure; no form behavior. | `../tests/guards/product-compositions.test.ts` |
23
- | `SettingsField` | Net-new NadicodeAI product composition. | shadcn `Field`, `FieldLabel`, `FieldDescription`, and `FieldError`. | valid and invalid field rows; disabled rows. | Label `htmlFor`; render props receive control attributes; element controls require an explicit matching `id` and described-by wiring. | `../tests/guards/product-compositions.test.ts` |
22
+ | `ActionMenu` | Canonical record-action composition. | shadcn `Button` and `DropdownMenu`. | closed, open, keyboard selection, focus return. | Required accessible trigger name; inherited menu mechanics. | `../src/components/action-menu.test.tsx` |
23
+ | `SettingsSection` | NadicodeAI product composition. | shadcn Card slots, Separator, and FieldGroup. | header, field group, and divided action footer. | Section structure; consumer owns the form transaction. | `../src/components/settings-section.test.tsx` |
24
+ | `SettingsField` | NadicodeAI product composition. | shadcn Field, FieldLabel, FieldDescription, and FieldError. | valid, invalid, required, and disabled field rows. | Render callback supplies the control's label and message associations in server and client components. | `../src/components/settings-section.test.tsx` |
24
25
  | `FeedbackState` | Net-new central product state composition adapted from shadcn empty-state patterns. | shadcn `Empty` and lucide status icons. | `empty`, `loading`, `saving`, `error`, `permission`, `offline`, `no-results`, `fatal`. | `role="status"` for loading/saving; `role="alert"` for error, permission, offline, and fatal. | `../tests/guards/product-compositions.test.ts` |
25
26
 
26
27
  ## App-Owned Boundaries