Best for
- Use when editing console UI, panelTypes, useWidgetData, WidgetTable, WidgetBoard, canvas_console_yml, Get/UpdateCanvasConsole, or docs/prd/console-and-widgets.
superplanehq/superplane/.cursor/skills/superplane-dashboard-and-widgets/SKILL.md
Implements and configures SuperPlane canvas consoles (markdown, node, table, board, chart, number, scorecard panels), widget data sources, CEL/templates, table row trigger actions, and console YAML. Use when editing console UI, panelTypes, useWidgetData, WidgetTable, WidgetBoard, canvas_console_yml, Get/UpdateCanvasConsole, or docs/prd/console-and-widgets.md.
Decision brief
Use this skill when working on per-canvas consoles: the console mode overlay, typed panels, widget renderers, YAML import/export, or backend validation.
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/superplanehq/superplane --skill ".cursor/skills/superplane-dashboard-and-widgets"Inspect the Agent Skill "superplane-dashboard-and-widgets" from https://github.com/superplanehq/superplane/blob/5a7aaa87db636301923c4595e31325ebc1df7bd6/.cursor/skills/superplane-dashboard-and-widgets/SKILL.md at commit 5a7aaa87db636301923c4595e31325ebc1df7bd6. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
Review the “Verification” section in the pinned source before continuing.
One console per canvas (not templates). Stored as versioned JSON panels + layout on the canvas version.
Invariant: panelTypes.ts validators (plus satellite modules like boardPanelContent.ts and nodesPanelContent.ts), pkg/yaml/console.go, and widget types.ts must agree. Frontend fast-fails; backend is authoritative on import.
New panels: templateForPanelType in panelTypes.ts. Draft states (e.g. empty memory namespace) should stay valid where possible.
Execution rows get status, nodeName, durationMs. Status vocabulary: passed, failed, running, pending, cancelled, unknown.
Permission review
The documentation asks the agent to run terminal commands or scripts.
make format.jsThe documentation asks the agent to run terminal commands or scripts.
make check.lint.uiEvidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 90/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 5,518 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Use this skill when working on per-canvas consoles: the console mode overlay, typed panels, widget renderers, YAML import/export, or backend validation.
Canonical reference: docs/prd/console-and-widgets.md — read it for full schemas, examples, and maintenance notes. This skill is the operational subset for agents.
panels + layout on the canvas version.react-grid-layout (ConsoleView).canvases:update, not template, canvas not deleted.InvokeNodeTriggerHook; UI uses canRunNodes.kind: trigger only — they fire trigger nodes; they do not call HTTP Request nodes directly.| Layer | Key paths |
|---|---|
| Console page | web_src/src/pages/app/console/ConsoleView.tsx — grid, Add Panel picker, YAML modal wiring |
| Context | console/ConsoleContext.tsx, ConsoleContextProvider.tsx |
| Trigger hook | console/useConsoleRunTrigger.ts, useConsoleTriggerLock.ts |
| Panel router | console/ConsolePanelCards.tsx |
| Schema | console/panelTypes.ts — types, templates, validators, normalizeTablePanelContent, normalizeBoardPanelContent |
| YAML (FE) | console/consoleYaml.ts, ConsoleYamlModal.tsx |
| Widget data | console/widget/useWidgetData.ts |
| Widget UI | console/widget/WidgetTable.tsx, WidgetBoard.tsx, WidgetChart.tsx, WidgetNumber.tsx, WidgetScorecard.tsx |
| Backend | pkg/yaml/console.go — YAML import/export + validators |
| Proto | protos/canvases.proto — console panels live on the canvas version |
Invariant: panelTypes.ts validators (plus satellite modules like boardPanelContent.ts and nodesPanelContent.ts), pkg/yaml/console.go, and widget types.ts must agree. Frontend fast-fails; backend is authoritative on import.
Node references: always accept id or name via resolveConsoleNode in ConsoleContext.tsx.
type | Runtime | Main content |
|---|---|---|
markdown | GFM body with {{ name.field }} interpolation | title?, body?, variables? |
html | Sanitized HTML body with {{ name.field }} interpolation, scoped <style>, Tailwind via safelist | title?, body?, variables? |
nodes | Adaptive card: one entry uses the compact single-node layout; multiple entries render as a row list. Optional per-entry Run button (manual-run triggers only). Optional formMode: "inline" renders the trigger parameter form directly in the widget body (prompt-submission style) for manual-run start triggers that have parameters. Inline entries can suppress the redundant node/field labels and customize submit copy. | title?, nodes[] with node, label?, description?, showRun?, triggerName?, promptConfirmation?, formMode?, showNodeLabel?, showFieldLabels?, submitLabel? |
node (legacy) | Same renderer as nodes — the merged card folds legacy single-node content into a one-entry list. Kept for import compatibility; migrates to nodes on first save. | node, showRun?, triggerName? |
table | WidgetTable | dataSource, render.kind: "table" |
board | WidgetBoard — kanban lanes grouped by a scalar groupBy field; same data sources / filters / row actions as the table panel | dataSource, render.kind: "board" with groupBy, lanes[], card, optional otherLane, where, sort, rowActions |
chart | WidgetChart (SVG) | dataSource, render.kind: "chart" |
number | WidgetNumber | dataSource, render.kind: "number" |
scorecard | WidgetScorecard — single KPI only (no multi-KPI or composite memory); adds change vs the immediately previous value in the series, direction-aware target/progress, and a status-colored sparkline via the shared Sparkline | dataSource, render.kind: "scorecard" with aggregation, optional field, better, target, showProgress, sparklineField, showChange, changeCaption |
New panels: templateForPanelType in panelTypes.ts. Draft states (e.g. empty memory namespace) should stay valid where possible.
useWidgetData){ kind: "memory", namespace: string, fieldPath?: string }
{ kind: "executions", node?: string, limit?: number }
{ kind: "runs", limit?: number }
| Kind | Query | Notes |
|---|---|---|
memory | useCanvasMemoryEntries | Filter by namespace; fieldPath flattens nested lists (memoryRow.ts) |
executions | useInfiniteCanvasEvents | Flatten executions[]; optional node filter; eager pages until limit or cap (~500 events) |
runs | useInfiniteCanvasRuns | totalCount for count KPIs |
Execution rows get status, nodeName, durationMs. Status vocabulary: passed, failed, running, pending, cancelled, unknown.
Non-empty field; optional label, format (text, number, status, relative, link, trend, …), show, href. format: trend also accepts trendBetter (up/down, default up) and trendDisplay (percent/value/none, default percent); the cell compares against the row directly below in the filtered/sorted table (or the first already-loaded row still hidden by the progressive display window).
render.where[] — AND list; ops: eq, neq, contains, not_contains, gt, lt, exists, not_exists.
Required: kind: trigger, node (id or name). Optional: hook (default run), template, payload, confirm, show, variant, icon.
Runtime flow: WidgetTable / WidgetBoard → WidgetRowActionButton → mergeTriggerPayload → onTriggerNode → useConsoleRunTrigger → InvokeNodeTriggerHook → invalidate events/runs/memory queries.
Legacy fields normalized in FE: target → node, triggerName → template.
Manual-run gate: only the built-in start and schedule triggers expose a user-invokable run hook. The UI filters on node.component against the hardcoded allowlist in web_src/src/pages/app/console/manualRunTriggers.ts — TablePanelForm and BoardPanelForm hide non-manual triggers from the dropdown, WidgetTable / WidgetBoard hide their row actions, and NodesPanelCard/NodesPanelForm hide the Run affordance. Backend authorization stays in InvokeNodeTriggerHook; adding a new manual-run trigger requires a matching entry in the frontend allowlist.
{{ CEL }} — @marcbachmann/cel-js via widget/celExpr.ts; row env + now (Unix seconds). The adapter upfront-coerces safe-integer JS numbers (and, on retry, numeric strings) to BigInt for int arithmetic, and normalizes safe-integer BigInt results back to plain number on the way out.show — e.g. status == "running" (showExpression.ts, rowVisibility.ts).where for simple validated filters.Lint: loose equality in legacy expressions is intentional (scalar normalization). Do not add eslint-disable for == in dashboard code; refactor instead.
Editor memory hints: MemoryDiscoveryPanel.tsx, useMemoryCatalog.ts (suggestions only; YAML still validated).
content.variables[] carries named live data refs; body uses {{ name.field }} (or {{ name.$["Node"].data.x }} for runs).{ kind: "memory", namespace, orderBy?, direction?, matches?, mode?, limit? } (default mode: single first-row wins, orderBy: createdAt desc) or { kind: "run", select: latest | latest_passed | latest_failed }.mode: list resolves the memory variable to the full sorted array of matching rows (optionally capped by limit), unlocking CEL list macros (rows.map(r, ...).filter(...)) inside {{ }}; pair with the join(list, sep) builtin in celExpr.ts to flatten into Markdown / HTML.useMarkdownVariables.ts (pickMemoryRows is the exported helper that branches on mode); interpolation in markdownInterpolation.ts (reuses celExpr.compileTemplate/evalTemplate). Validation: markdownVariables.ts (FE, including validateMarkdownContent) + validateMarkdownContent / validateHTMLContent in pkg/models/console_yml.go (BE).status, nodeName, payload, durationMs, and a $ map of node executions (same shape as the table widget).HtmlBody.tsx): interpolate variables → DOMPurify allow-list → scope <style> blocks → dangerouslySetInnerHTML into div[data-console-html-root="<id>"].htmlSanitize.ts) blocks <script> and all on* handlers, removes head-like and resource-fetching elements (link, meta, base, iframe, object, embed, audio, video, form, svg, math, …), allows <img src>/<img srcset> for http(s)/relative URLs (cross-origin image fetches are permitted by policy), strips poster/background/data/xlink:href, restricts href/src/srcset to http(s)/mailto:/tel:/fragments, and rewrites every <style> rule to scope selectors under the widget root while dropping @import, url(...), and unknown at-rules.@source inline(...) safelist in web_src/src/App.css to apply at runtime — extend it conservatively, never bypass it.Chart render.type: bar, stacked-bar, line, area, donut. xField + series[]; omit series[].field to count rows per bucket.
Number aggregations: count, sum, avg, min, max, first, last — non-count requires field.
Scorecard shares the number aggregation vocabulary but is single-KPI only (no multi-KPI / composite memory). Comparison model:
sparklineField when set, or the primary field as a fallback. Only first / last aggregations expose a natural "previous" (adjacent anchor via pickChangeAnchors); combining aggregations (sum / avg / min / max / count) hide the chip. Reuses computeTrend (widgetTrend.ts) for percent/absolute math.{{ CEL }} (evaluated against the newest filtered row + now), used for optional showProgress and fallback status color.better: "up" | "down" controls the polarity for the value change, the sparkline, and the vs-target status.Latest / Earliest because all data sources are newest-first (first → Latest, last → Earliest). Persisted YAML still uses first / last.Helpers live in widget/scorecardMath.ts (extractScorecardSeries, pickChangeAnchors, resolveScorecardTarget, computeScorecardProgress, computeScorecardChange, resolveScorecardStatus, formatScorecardChangeLabel). Rendering is in widget/WidgetScorecard.tsx; the sparkline itself comes from the shared widget/Sparkline.tsx (shared with WidgetNumber) with a className prop for status coloring.
apiVersion: v1
kind: Console
metadata:
canvasId: <uuid> # export only; ignored on import
name: <display>
spec:
panels: [{ id, type, content }]
layout: [{ i, x, y, w, h, minW?, minH? }]
consoleYaml.ts — parse/serialize + validatePanelContentConsoleFromYML / VersionToConsoleYML in pkg/yaml/console.gopanels/layout → empty liststype and dataSource.kind.useWidgetData → widget renderer → (if trigger) useConsoleRunTrigger.pkg/authorization/interceptor.go if RPC-related.web_src/src/pages/app/console/**/*.spec.ts.content fieldswidget/types.ts (if widget-facing)panelTypes.ts — interface, templateForPanelType, validatePanelContent, normalization (satellite modules like boardPanelContent.ts / nodesPanelContent.ts follow the same pattern)pkg/yaml/console.go — mirror validation + testsconsoleYaml.spec.ts / consoleYaml.validation.spec.ts, pkg/yaml/console_test.goPANEL_TYPES, PANEL_TYPE_META, validator, template in panelTypes.tsConsolePanelType* constant + AllowedConsolePanelTypes in pkg/yaml/console.go and a per-type validator*PanelCard.tsx + case in ConsolePanelCards.tsxConsoleView.tsx PANEL_TYPE_ICONSwidget/types.ts + panelTypes.tsDataSourceForm.tsx editoruseWidgetData.tsUse PRD example; namespace must match canvas memory keys. Row actions target trigger nodes only.
# Frontend unit tests (console package)
cd web_src && npm run test:run -- src/pages/app/console
# After UI edits (Docker dev env)
make format.js
make check.lint.ui
make check.build.ui
# After Go validation/API edits
make format.go
make lint
make check.build.app
go test ./pkg/yaml -count=1
go test ./pkg/grpc/actions/canvases -count=1
web_src/src/utils/* — use lib/ or hooks/.make db.migration.create NAME=<dash-name> if persistence changes.| Task | Start here |
|---|---|
| Grid / add panel | ConsoleView.tsx |
| Table CEL / filters / actions | WidgetTable.tsx, WidgetRowActionButton.tsx, celExpr.ts, evalTableWhere.ts, mergeTriggerPayload.ts |
| Table editor | TablePanelForm.tsx, TablePanelFormRows.tsx |
| Board renderer / editor | WidgetBoard.tsx, BoardPanelCard.tsx, BoardPanelForm.tsx, boardPanelContent.ts |
| Trigger from console | useConsoleRunTrigger.ts, consoleTriggerParameters.ts |
| Node status chip / Run button | NodesPanelCard.tsx, NodesPanelInlineRunForm.tsx, useConsoleRunTrigger.ts, useConsoleTriggerLock.ts, deriveNodeStatuses.ts |
| API hooks | web_src/src/hooks/useCanvasData.ts — useCanvasVersion, useUpdateCanvasVersion |
Frequently asked questions
Use this skill when working on per-canvas consoles: the console mode overlay, typed panels, widget renderers, YAML import/export, or backend validation.
The source record exposes this install command: npx skills add https://github.com/superplanehq/superplane --skill ".cursor/skills/superplane-dashboard-and-widgets". Inspect the command and pinned source before running it.
Static rules flagged exec-script in the source; the page lists the matching lines and excerpts.
Alternatives
oaustegard/claude-skills
Generate hierarchical _FEATURES.md files that describe what a codebase DOES from a user/consumer perspective, anchored to source symbols via tree-sitting. Supports large complex codebases through feature-driven decomposition into sub-feature files. Uses a multi-pass synthesis: orientation → detail → overview rewrite. Use when someone says "what does this do", "document features", "feature inventory", "_FEATURES.md", or needs to understand a codebase's purpose before modifying it. Complements tre
NintendaDev/unikit-ai
Generate and maintain the project's TECHNICAL documentation from its codebase — scans the project structure, tech stack, and module boundaries, then writes a lean README landing page plus detailed topic pages (architecture, modules, setup, build, APIs), only the docs that are relevant. Use whenever the user wants to create, update, or validate documentation of the CODE or the project itself, e.g. "generate documentation", "create docs", "write the README", "update the project docs", "document th
eugenelim/agent-ready-repo
Use when implementing or resuming a non-trivial repository change: a feature, behavior-changing fix, refactor, migration, framework or dependency upgrade, schema or API change, performance work, infrastructure or build-system change, reversion, or an existing build spec under `docs/specs/`. Also use for bare continuation commands ('resume', 'continue', 'keep going', 'pick up where I left off', 'let's get going') when conversation or workspace context identifies active build work. Do not use for
K-Dense-AI/scientific-agent-skills
Use when working directly with the `esm` Python SDK, ESM3 or ESMC model IDs, Forge/Biohub inference clients, or ESMFold2 folding workflows.