| name | dashboard-as-code |
| description | Use when the user wants to create, scaffold, or edit an Embeddable dashboard โ `*.embeddable.yml` files describing layout, widgets, datasets, variables, events, custom canvas templates, and starter canvas. Triggers on phrases like "create a dashboard", "add a widget", "wire a date filter", "make a custom canvas template", or any work on a `.embeddable.yml` file under `src/embeddable.com/embeddables/`. |
Dashboard-as-code
Generate and edit Embeddable dashboards declaratively as *.embeddable.yml files. On embeddable:push, each file becomes a new dashboard version in the workspace; publishing pins that version for live embeds, so subsequent pushes never disturb anything that's already published.
File location
src/embeddable.com/embeddables/<name>.embeddable.yml. New embeddables go in this directory. The filename is cosmetic โ the workspace identifier is embeddables[].name.
Top-level skeleton
embeddables:
- name: <stable-key>
title: <human-readable>
description: <repo-only>
variables: [...]
datasets: [...]
widgets: [...]
customCanvas: {...}
Workflow when generating or editing
- Discover available components from both sources. Components come from (a)
componentLibraries declared in embeddable.config.ts and (b) local *.emb.ts files under src/embeddable.com/components/. Walk both:
- Libraries: each entry is either a string (
<package-name>) or { name, include?, exclude? }. For each enabled library, read node_modules/<package-name>/dist/meta/index.json directly โ do not ls or Glob the dist/meta/ directory first. The index gives you name, label, category, and description? in one read; filenames alone (which is all ls yields) lose every disambiguation signal the library author put in the descriptions, and that's the most common cause of silent wrong component picks. Apply per-library include/exclude filters after reading. If reading dist/meta/index.json fails because it does not exist, tell the user which package needs to be updated and stop โ never guess at component or input names, and don't fall back to listing files.
- Local: glob
src/embeddable.com/components/**/*.emb.ts and read each file's meta const for name, label, category. An empty components/ directory is normal โ not an error.
- Build the union as your candidate set. If a local
meta.name collides with a library component's name, surface the ambiguity to the user instead of picking one.
- Details: references/component-discovery.md.
- Read meta lazily. Only for the components actually being placed: read
node_modules/<package-name>/dist/meta/<componentName>.meta.json for library components, or read the corresponding *.emb.ts (focus on the meta const; ignore the defineComponent call and any sibling React files) for local components. Don't bulk-load. Use the Read tool on the full meta file โ do not grep a narrow projection of fields (e.g. just name/type/label). Flags like "required": true and "array": true are easy to filter out by accident, and missing them produces widgets that fail validation.
- Read the data models. Inspect relevant
src/embeddable.com/models/cubes/*.cube.yml to confirm cube/dimension/measure names exist and are typed correctly. Only join-related cubes can be referenced together inside one widget.
- Generate or edit the YAML under
src/embeddable.com/embeddables/. Use the references for any non-trivial section.
- Check validation feedback if
embeddable:dev is running โ see "Dev events log" below.
Reference index
- references/datasets.md โ dataset shape, filter operators with type rules.
- references/variables.md โ variable types,
defaultValue semantics, array, the noFilter rule.
- references/widgets.md โ widget anatomy, the 12-column grid, inputs, sub-inputs, events,
SET_VARIABLE.
- references/custom-canvas.md โ
customCanvas, templates with key lifecycle, icon catalog, starterCanvas.
- references/component-discovery.md โ enumerating
componentLibraries and reading their meta.
Examples
Read these on demand when scaffolding a new file:
Dev events log
When the user is running embeddable:dev, the build can emit NDJSON events to a log file. The path is not fixed โ read package.json and look at the embeddable:dev (or dev) script for the --events-file=<path> flag, e.g.
"embeddable:dev": "embeddable dev --events-file=.embeddable-dev-logs/dev.events.ndjson"
If the flag is absent, the log is not configured for this project โ tell the user how to enable it (add the flag) rather than guessing a path.
The format is NDJSON; each line is either a marker (build cycle progress) or an issue (validation problem). Key event names: validate_start, validate_end, validation_error, change_detected. Read the log on user request ("check for errors") or right after Claude itself edits a *.embeddable.yml. Surface the latest error(s); on widget overlap errors, propose new coordinates that fit the grid.
Safety rules
embeddables[].name is the workspace identifier. Renaming or deleting it removes the dashboard from the workspace, which breaks any live embeds pointing at it. It also breaks every DRILLDOWN event that targets it โ before renaming, grep embeddable: <old-name> across src/embeddable.com/embeddables/ and update or warn. Never change or delete the name without explicit user confirmation.
customCanvas.templates[].key is the stable identifier that end-user-created widgets and starterCanvas.widgets[].template bind to. Changing or removing a key deletes every dependent widget. Treat key as effectively immutable. name and description are cosmetic and safe to edit. See references/custom-canvas.md.
- Don't run
embeddable:push automatically โ that's the user's call (root CLAUDE.md).
- Don't start
embeddable:dev automatically โ the user starts it themselves (root CLAUDE.md).
Out of scope
- Custom variable types โ only the built-in types listed in
references/variables.md are covered.
- Custom components, theme customization, cube model generation, presets (
*.cc.yml/*.sc.yml) โ separate skills will handle these.