Skip to main content

custom-blocks

Create, embed, clone, or repair a Custom Block (extension panel). Use for a bespoke visualization native panels cannot draw, or to fix or copy an extension-backed dashboard.

ソース情報

リポジトリ
BuilderIO/agent-native
ソースの最終更新活動
2026年10月7日 13:37
検出された SKILL.md の言語
英語
スター
7,366
フォーク
669

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
custom-blocks
description
Create, embed, clone, or repair a Custom Block (extension panel). Use for a bespoke visualization native panels cannot draw, or to fix or copy an extension-backed dashboard.
# Custom Blocks Analytics has one user-facing artifact type: dashboards. Build with native dashboard panels and Data Programs first. A sandboxed extension embedded in a dashboard is presented to users as a Custom Block, not as a separate Analytics artifact. Use native chart, table, metric, section, funnel, heatmap, callout, filter, and layout capabilities whenever they can represent the request faithfully. Reusable ROI, engagement, cross-sell, and win/loss dashboards compose these native panels around real SQL or Data Program results. Use a Data Program when the durable need is reusable fetching, transformation, or computed data that native panels can render. Dual-axis charts are native (`config.rightYKeys`), never an extension. Do not create a Custom Block merely because a request says custom, asks for a dashboard, or would take more effort with native components. ## When To Create One Create a Custom Block only when the user explicitly asks for a genuinely bespoke or one-off visualization or interaction, the native dashboard model cannot represent it faithfully, and its intended scope is this dashboard. - Create it with `create-extension`, immediately embed it as a `chartType: "extension"` panel with `config.extensionId`, and set `config.customBlock` to `{ authoredBy: "agent", intent: "one-off", scope: "dashboard", nativeGapReason: "custom-visualization" | "custom-interaction" | "custom-layout" | "other" }`. Choose the narrow categorical reason; never put prompt text, customer data, or other free text in this metadata. - Use the host theme CSS variables and match the dashboard typography, card spacing, and density so the sandboxed content reads as an agent-authored patch to Analytics instead of a foreign mini-app. Describe it as a sandboxed, agent-authored dashboard patch. - Never leave it standalone or direct the user to an Extensions page. - A Custom Block is a fast runtime patch, not the durable destination for reusable product behavior. If the request should work across dashboards or users, changes app chrome or business logic, adds a reusable chart type, needs native accessibility/export/governance, or explicitly asks for app code, a PR, or a native feature, call `connect-builder` with the request verbatim instead of creating a Custom Block. If scope is ambiguous, ask whether the user wants a one-off block for this dashboard or a reusable app feature. - When the user chooses Promote to app code, preserve the existing Custom Block and pass its dashboard id, panel id, extension id, and requested native placement through `connect-builder`; do not delete or replace the block until the native implementation is reviewed and deployed. - Legacy analyses and existing extension-backed dashboards remain readable and editable for compatibility. An explicit request to build one authorizes every non-destructive step in the same turn: query or scaffold, seed extension data (`extension-data-set`), save, embed, and `navigate`. Do not leave an empty shell or ask whether to proceed. ## Extension Data Boundary Code inside a Custom Block runs in an iframe and may call only actions that are HTTP-mounted and intended for `appAction`. Use the canonical `bigquery` action for warehouse SQL; never call `query-agent-native-analytics`, `bigquery-table-info`, or another `http: false` agent-only action from extension code. For first-party Analytics data, prefer a native `source: "first-party"` panel, or query it as the agent and seed the extension data store. ## Embedding An Extension As A Panel Use `chartType: "extension"` to add an extension box alongside normal SQL charts. The panel skips `source` and `sql`. For ordinary requests such as "put X in this dashboard," save the author-selected extension id in `config.extensionId`. This makes the selection part of the shared dashboard and keeps the widget present in scheduled report captures: ```jsonc { "id": "pipeline-widget", "title": "Pipeline Widget", "chartType": "extension", "width": 3, "config": { "extensionId": "extension-123" }, } ``` Direct embeds receive the dashboard id, name, description, current filters, and panel context. Embedding does not grant extension access, so share the extension with the dashboard audience (`share-resource --resourceType extension ...`); otherwise viewers see an "extension unavailable" placeholder. Use a stable `config.extensionSlotId` only when the user explicitly wants each viewer to choose or install their own widget: ```text analytics.dashboard.<dashboard-id>.panel.<panel-id> ``` Create or choose the extension, call `add-extension-slot-target` with the extension id and slot id, then call `install-extension` with the same values. The dashboard panel is shared, while the installed extension is per-user. Empty slots show the normal install affordance instead of a broken iframe. ```jsonc { "id": "pipeline-widget", "title": "Pipeline Widget", "chartType": "extension", "width": 3, "config": { "extensionSlotId": "analytics.dashboard.weekly-metrics.panel.pipeline-widget", }, } ``` - Both direct and slot-backed extensions receive dashboard and panel context. - Installs and extension access are per viewer. Slot installs are per-user preferences, so different viewers can see different widgets and scheduled reports running as a service identity may show an empty slot. This is why slots are opt-in rather than the default. - Inspect an existing Custom Block through the `get-sql-dashboard` panel summaries (`extensionId`, `extensionSlotId`). ## Cloning A Direct-Extension Dashboard (e.g. per-customer copies) When the user asks for a copy of an existing extension-backed dashboard for a different customer/org (for example "make an Intuit version of the Roku usage dashboard"), follow this playbook. Extension bodies are frequently tens of thousands of characters. The reliable path is to read+transform+write the body INSIDE `run-code` (where `workspaceRead` returns the full file) and then create from that written file — never by pulling the body into chat context first or re-typing it as a `content` argument. 1. `get-sql-dashboard` on the source dashboard (pass the extension panel's id in `panelIds`) and confirm the target panel is a `chartType: "extension"` panel with `config.extensionId`; grab that extension id. For a slot-backed panel, clone the dashboard panel with a new stable `extensionSlotId`, then target and install the desired extension into that slot instead of using this body-copy playbook. 2. `get-extension` for that id with `forceContent: true` **exactly once**. Reuse that body for the rest of the turn — a second same-run read intentionally omits `content` and returns `contentOmitted` instead. That is not the content disappearing; use the copy you already have. Do NOT try to re-fetch the body with `run-code` (`appAction('get-extension')`) to page past a display truncation — the same-run omit makes it return empty `content`, wasting turns. If you need the full body again, read the workspace resource file (step 5) or set `forceContent: true` on a single native `get-extension`. 3. Change ONLY the small customer-specific static config (e.g. the `ACCOUNT_USAGE_STATIC` block: company name, title, org-discovery filters, messaging). Prefer a focused `update-extension` edit/patch over regenerating the entire HTML. 4. **Call `create-extension` / `update-extension` as native tools.** They are mutating actions and are NOT callable from `run-code` / `appAction` (the sandbox bridge only exposes read-only actions). Do not try to create or update an extension from inside `run-code`. 5. **If the source body already exists as a workspace/shared resource file** (e.g. a pre-built `intuit-analytics-extension.html`), do the read AND the customer swap in ONE `run-code` call, then create from the written file: - Inside `run-code`: `const src = await workspaceRead('<source>.html')` returns the WHOLE file (it auto-pages; there is no 50k cap here), do the small string-replace on the static config block, then `await workspaceWrite('<target>.html', modified)`. - Then call `create-extension` (native) with `contentFromWorkspaceFile: '<target>.html'` and leave `content` empty — the server reads the full file verbatim. Do NOT read the source body with the `resources` read tool (or `get-extension`) first just to transform it: that display is capped and wastes a turn. And do NOT re-emit an 80k+ char body as the `content` argument — it gets cut off mid-stream. `contentFromAttachment` only sees files the user pasted into chat, not workspace resources. `create-extension`/`update-extension` are mutating and cannot run from `run-code`, so only the read+write+transform happens there. 6. Finally save a new dashboard embedding the new extension panel (`chartType: "extension"`, `config.extensionId`) with `update-dashboard`, then `navigate` to it. ## Repairing An Existing Extension-Backed Dashboard When the user asks to fix data loading in an existing or migrated extension-backed dashboard, treat the current extension body as user-authored design. Read the dashboard config and extension once, identify the smallest data-loading seam, and call `update-extension` with exactly `id`, `operation="edit"`, and a `payloadJson` string containing focused `patches`/`edits` that change only that seam. Never send empty placeholder fields. Preserve the existing layout, CSS, copy, and interactions. Do not send a reconstructed full `content` body for a data-only repair. A request that combines a visual rewrite (compacting, removing sections, renaming, changing padding) with a data repair is a broad rewrite: after inspecting the current extension, use `operation="replace"` with the complete replacement in `payloadJson`. If a focused edit fails, inspect the current body and change the target rather than retrying the same arguments. ### Display truncation is cosmetic — do not chase the "missing" tail A tool result ending in `...[truncated — full result was N chars; only first 50,000 shown]` (from the `resources` read tool or `get-extension`) means only the DISPLAYED text was capped. The file is intact. `run-code`'s `workspaceRead` returns the full N chars, and `contentFromWorkspaceFile` hosts the full file. Never read the same file twice or try to "page the rest" to recover the tail — that is the single biggest source of wasted turns on clone requests. Decide to clone, then go straight to the `run-code` read+transform+write path in step 5.
GitHubで見る