Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
262 changes: 166 additions & 96 deletions bestax-mcp/data/bulma-classes.json
Original file line number Diff line number Diff line change
Expand Up @@ -1071,6 +1071,166 @@
"noHelpers": false,
"ownClassOnly": false
},
"field": {
"status": "mapped",
"target": "Field",
"tag": "div",
"as": null,
"sizeDrivesTag": false,
"textColor": "textColor",
"bgColor": "bgColor",
"part": false,
"why": null,
"modifiers": {
"is-horizontal": { "writes": [{ "prop": "horizontal" }] },
"is-grouped": { "writes": [{ "prop": "grouped" }] },
"has-addons": { "writes": [{ "prop": "hasAddons" }] },
"is-narrow": { "writes": [{ "prop": "narrow" }] }
},
"omits": {},
"wrapsChildren": {
"in": "field-body",
"unless": ["Field.Label", "Field.Body"],
"whenEmpty": true,
"when": "is-horizontal"
},
"noHelpers": false,
"ownClassOnly": false
},
"field-label": {
"status": "mapped",
"target": "Field.Label",
"tag": "div",
"as": null,
"sizeDrivesTag": false,
"textColor": null,
"bgColor": null,
"part": false,
"why": null,
"modifiers": {
"is-small": { "writes": [{ "prop": "size", "value": "small" }] },
"is-normal": { "writes": [{ "prop": "size", "value": "normal" }] },
"is-medium": { "writes": [{ "prop": "size", "value": "medium" }] },
"is-large": { "writes": [{ "prop": "size", "value": "large" }] }
},
"omits": {},
"wrapsChildren": null,
"noHelpers": true,
"ownClassOnly": false
},
"field-body": {
"status": "mapped",
"target": "Field.Body",
"tag": "div",
"as": null,
"sizeDrivesTag": false,
"textColor": null,
"bgColor": null,
"part": false,
"why": null,
"modifiers": {},
"omits": {},
"wrapsChildren": null,
"noHelpers": true,
"ownClassOnly": false
},
"control": {
"status": "mapped",
"target": "Control",
"tag": "div",
"as": ["div", "p"],
"sizeDrivesTag": false,
"textColor": null,
"bgColor": null,
"part": false,
"why": null,
"modifiers": {
"has-icons-left": { "writes": [{ "prop": "hasIconsLeft" }] },
"has-icons-right": { "writes": [{ "prop": "hasIconsRight" }] },
"is-loading": { "writes": [{ "prop": "isLoading" }] },
"is-expanded": { "writes": [{ "prop": "isExpanded" }] },
"is-small": { "writes": [{ "prop": "size", "value": "small" }] },
"is-medium": { "writes": [{ "prop": "size", "value": "medium" }] },
"is-large": { "writes": [{ "prop": "size", "value": "large" }] }
},
"omits": {},
"wrapsChildren": null,
"noHelpers": true,
"ownClassOnly": false
},
"input": {
"status": "mapped",
"target": "InputBase",
"tag": "input",
"as": null,
"sizeDrivesTag": false,
"textColor": null,
"bgColor": "backgroundColor",
"part": false,
"why": null,
"modifiers": {
"is-small": { "writes": [{ "prop": "size", "value": "small" }] },
"is-medium": { "writes": [{ "prop": "size", "value": "medium" }] },
"is-large": { "writes": [{ "prop": "size", "value": "large" }] },
"is-rounded": { "writes": [{ "prop": "isRounded" }] },
"is-static": { "writes": [{ "prop": "isStatic" }] },
"is-hovered": { "writes": [{ "prop": "isHovered" }] },
"is-focused": { "writes": [{ "prop": "isFocused" }] },
"is-loading": { "writes": [{ "prop": "isLoading" }] }
},
"omits": {
"is-primary": "`color` renders `has-text-<color>` on the input as well",
"is-link": "`color` renders `has-text-<color>` on the input as well",
"is-info": "`color` renders `has-text-<color>` on the input as well",
"is-success": "`color` renders `has-text-<color>` on the input as well",
"is-warning": "`color` renders `has-text-<color>` on the input as well",
"is-danger": "`color` renders `has-text-<color>` on the input as well",
"is-black": "`color` renders `has-text-<color>` on the input as well",
"is-white": "`color` renders `has-text-<color>` on the input as well",
"is-light": "`color` renders `has-text-<color>` on the input as well",
"is-dark": "`color` renders `has-text-<color>` on the input as well"
},
"wrapsChildren": null,
"noHelpers": false,
"ownClassOnly": false
},
"textarea": {
"status": "mapped",
"target": "TextAreaBase",
"tag": "textarea",
"as": null,
"sizeDrivesTag": false,
"textColor": null,
"bgColor": "backgroundColor",
"part": false,
"why": null,
"modifiers": {
"is-small": { "writes": [{ "prop": "size", "value": "small" }] },
"is-medium": { "writes": [{ "prop": "size", "value": "medium" }] },
"is-large": { "writes": [{ "prop": "size", "value": "large" }] },
"is-rounded": { "writes": [{ "prop": "isRounded" }] },
"is-static": { "writes": [{ "prop": "isStatic" }] },
"is-hovered": { "writes": [{ "prop": "isHovered" }] },
"is-focused": { "writes": [{ "prop": "isFocused" }] },
"is-active": { "writes": [{ "prop": "isActive" }] },
"has-fixed-size": { "writes": [{ "prop": "hasFixedSize" }] }
},
"omits": {
"is-primary": "`color` renders `has-text-<color>` on the textarea as well",
"is-link": "`color` renders `has-text-<color>` on the textarea as well",
"is-info": "`color` renders `has-text-<color>` on the textarea as well",
"is-success": "`color` renders `has-text-<color>` on the textarea as well",
"is-warning": "`color` renders `has-text-<color>` on the textarea as well",
"is-danger": "`color` renders `has-text-<color>` on the textarea as well",
"is-black": "`color` renders `has-text-<color>` on the textarea as well",
"is-white": "`color` renders `has-text-<color>` on the textarea as well",
"is-light": "`color` renders `has-text-<color>` on the textarea as well",
"is-dark": "`color` renders `has-text-<color>` on the textarea as well"
},
"wrapsChildren": null,
"noHelpers": false,
"ownClassOnly": false
},
"footer": {
"status": "mapped",
"target": "Footer",
Expand Down Expand Up @@ -1810,102 +1970,6 @@
"noHelpers": false,
"ownClassOnly": false
},
"field": {
"status": "todo",
"target": "Field",
"tag": null,
"as": null,
"sizeDrivesTag": false,
"textColor": null,
"bgColor": null,
"part": false,
"why": "bestax form controls render their own `.field` and `.control` wrappers; see the unmappables reference",
"modifiers": {},
"omits": {},
"wrapsChildren": null,
"noHelpers": false,
"ownClassOnly": false
},
"field-label": {
"status": "todo",
"target": null,
"tag": null,
"as": null,
"sizeDrivesTag": false,
"textColor": null,
"bgColor": null,
"part": true,
"why": null,
"modifiers": {},
"omits": {},
"wrapsChildren": null,
"noHelpers": false,
"ownClassOnly": false
},
"field-body": {
"status": "todo",
"target": null,
"tag": null,
"as": null,
"sizeDrivesTag": false,
"textColor": null,
"bgColor": null,
"part": true,
"why": null,
"modifiers": {},
"omits": {},
"wrapsChildren": null,
"noHelpers": false,
"ownClassOnly": false
},
"control": {
"status": "todo",
"target": null,
"tag": null,
"as": null,
"sizeDrivesTag": false,
"textColor": null,
"bgColor": null,
"part": true,
"why": null,
"modifiers": {},
"omits": {},
"wrapsChildren": null,
"noHelpers": false,
"ownClassOnly": false
},
"input": {
"status": "todo",
"target": "Input",
"tag": null,
"as": null,
"sizeDrivesTag": false,
"textColor": null,
"bgColor": null,
"part": false,
"why": "bestax `Input` renders its own `.field` and `.control` wrappers",
"modifiers": {},
"omits": {},
"wrapsChildren": null,
"noHelpers": false,
"ownClassOnly": false
},
"textarea": {
"status": "todo",
"target": "TextArea",
"tag": null,
"as": null,
"sizeDrivesTag": false,
"textColor": null,
"bgColor": null,
"part": false,
"why": "bestax `TextArea` renders its own `.field` and `.control` wrappers",
"modifiers": {},
"omits": {},
"wrapsChildren": null,
"noHelpers": false,
"ownClassOnly": false
},
"select": {
"status": "todo",
"target": "Select",
Expand Down Expand Up @@ -2917,6 +2981,12 @@
"navbar-item",
"navbar-dropdown",
"navbar-divider",
"field",
"field-label",
"field-body",
"control",
"input",
"textarea",
"buttons",
"tags",
"button",
Expand Down
6 changes: 3 additions & 3 deletions bestax-mcp/data/skills.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,17 +103,17 @@
{
"id": "bulma-classes-component-map",
"file": "references/bulma-classes/component-map.md",
"bytes": 9829
"bytes": 11299
},
{
"id": "bulma-classes-prop-map",
"file": "references/bulma-classes/prop-map.md",
"bytes": 18331
"bytes": 21038
},
{
"id": "bulma-classes-unmappables",
"file": "references/bulma-classes/unmappables.md",
"bytes": 10958
"bytes": 12302
},
{
"id": "css-migration",
Expand Down
5 changes: 5 additions & 0 deletions bestax-mcp/src/__tests__/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,11 @@ describe('lookup_bulma_classes', () => {
'**Component:** `Card`. It renders its children inside a `.card-content` of its own unless one of them is a `Card.Header`,'
);
expect(await lookup('card-content')).not.toContain('renders its children');
// A field wraps its children only when it's horizontal.
expect(await lookup('field is-horizontal', 'div')).toContain(
'inside a `.field-body` of its own unless one of them is a `Field.Label` or `Field.Body`'
);
expect(await lookup('field', 'div')).not.toContain('renders its children');
});

it('answers helper classes with no component, and says what that depends on', async () => {
Expand Down
9 changes: 8 additions & 1 deletion bestax-mcp/src/bulma-classes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ export interface Wraps {
unless: string[];
/** It renders that element with no children too. */
whenEmpty?: boolean;
/** Only when the element carries this class (`Field` with `is-horizontal`). */
when?: string;
}

export interface HelperRecord {
Expand Down Expand Up @@ -291,7 +293,12 @@ export function lookupClasses(
};
}
const target = entry.target;
const wraps = entry.wrapsChildren ?? undefined;
const wrapsChildren = entry.wrapsChildren;
const wraps =
wrapsChildren &&
(!wrapsChildren.when || tokens.includes(wrapsChildren.when))
? wrapsChildren
: undefined;

// Each class as the root's modifier, else as a helper prop, in order; the
// first class to set a prop keeps it.
Expand Down
3 changes: 2 additions & 1 deletion bestax-migrate/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,8 @@ is the one with no library behind it, converting raw Bulma classes on plain JSX.
`scripts/gen-mcp-index.mjs` for the MCP server's `lookup_bulma_classes`, so no enums or
runtime imports; a change to it wants `pnpm gen:mcp` in the same PR, and bestax-mcp's
tests run this planner beside that tool), `plan.ts` (pure: one element's tag,
classes, attributes and what its children became in, a conversion or a TODO out; an entry
classes, attributes, what its children became and the bestax components already around and
inside it in, a conversion or a TODO out; an entry
with `wrapsChildren` converts only beside one of its parts, so `transform.ts` plans children
before their parents), `rules.ts` (rule ids from a closed
vocabulary, since the input is the app's own class strings and telemetry must never carry
Expand Down
Loading
Loading