Agent Skillsginlix-ai/LangAlpha › inline-widget

inline-widget

GitHub

用于在聊天中通过ShowWidget渲染内联交互式HTML/SVG小部件,如图表和数据表。强调自包含、无服务器依赖及交互性,遵循特定布局与主题规范。

skills/inline-widget/SKILL.md ginlix-ai/LangAlpha

Trigger Scenarios

用户希望在对话中嵌入可视化图表或数据表 需要展示具有排序、过滤等交互功能的单页HTML组件

Install

npx skills add ginlix-ai/LangAlpha --skill inline-widget -g -y
More Options

Use without installing

npx skills use ginlix-ai/LangAlpha@inline-widget

指定 Agent (Claude Code)

npx skills add ginlix-ai/LangAlpha --skill inline-widget -a claude-code -g -y

安装 repo 全部 skill

npx skills add ginlix-ai/LangAlpha --all -g -y

预览 repo 内 skill

npx skills add ginlix-ai/LangAlpha --list

SKILL.md

Frontmatter
{
    "name": "inline-widget",
    "description": "Inline HTML widgets: charts, dashboards, data tables rendered directly in the chat via ShowWidget"
}

Inline Widget

Render interactive HTML/SVG widgets directly inside the chat conversation using ShowWidget. Widgets appear inline between text — no sandbox, no preview URL, no side panel. They run JavaScript, so lean into making them interactive and explorable where it helps — something the user can sort, filter, toggle, and hover over, not just a static picture.

When to Use

  • User wants a quick visualization embedded in the conversation (chart, metric card, data table)
  • The visualization is self-contained — all data is embedded in the HTML, no server needed
  • User wants interactivity within the chat: buttons, toggles, hover effects, animated charts
  • The output is a single view — not a multi-page app or dashboard that needs routing

Use interactive-dashboard instead if: User needs a multi-page web app, server-side data, live data refresh, or complex interactivity requiring React/FastAPI.

Read .agents/skills/ui-design/SKILL.md for design quality — its color discipline, chart restraint, and anti-slop principles apply here too. Its font pairings and type scale, though, are for full documents; a widget sits on the chat surface, so use the host-font typography rules below instead.

ShowWidget API

ShowWidget(html: str, title: str | None = None, data_files: list[str] | None = None)
  • html: Raw HTML fragment — no <!DOCTYPE>, <html>, <head>, or <body> tags
  • title: Optional metadata (not displayed to user)
  • data_files: Optional list of sandbox file paths to make available as window.__WIDGET_DATA__

The HTML is rendered in a sandboxed iframe with:

  • CDN libraries: cdnjs.cloudflare.com, cdn.jsdelivr.net, unpkg.com, esm.sh
  • CSS theme variables: automatically injected (see Theme section)
  • sendPrompt('text'): global function to trigger follow-up chat messages
  • window.__WIDGET_DATA__: dict of filename→content for files passed via data_files
  • No network to non-CDN origins: fetch() / XMLHttpRequest to arbitrary URLs are blocked by CSP — only CDN domains (cdnjs, jsdelivr, unpkg, esm.sh) are allowed. Use data_files for sandbox files, or embed small data directly in HTML

Layout Rules (CRITICAL)

The widget sits directly on the chat surface inside a transparent iframe. Follow these rules for seamless integration:

Outer Element — Transparent Shell

The outermost HTML element must have:

  • NO background (or background: transparent)
  • NO border
  • NO border-radius
  • NO box-shadow
  • NO padding — add padding on inner sections only
<!-- CORRECT: transparent outer shell -->
<div>
  <div style="background: var(--color-bg-card, #ffffff); border-radius: 8px; padding: 16px; ...">
    ...inner card content...
  </div>
</div>

<!-- WRONG: styled outer wrapper — will be rejected -->
<div style="background: var(--color-bg-page); border: 1px solid ...; border-radius: 8px; padding: 20px;">
  ...content...
</div>

Inner Elements — Use Theme Variables

Inner cards, sections, and components should use CSS variables for styling, always in the fallback form:

/* Card */
background: var(--color-bg-card, #ffffff);
border: 0.5px solid var(--color-border-muted, #e4e1dc);
border-radius: 8px;
padding: 16px;

/* Metric card */
background: var(--color-bg-subtle, #f4f2ee);
border: 0.5px solid var(--color-border-muted, #e4e1dc);
border-radius: 8px;

In chat the variables are always injected, so the fallback never shows there — it is what renders when the widget is saved as a workspace .html file and previewed before theme injection or opened standalone. Use the light literals from the html-report skill's table.

Positioning

  • NO position: fixed — breaks iframe auto-sizing (elements collapse to 0 height)
  • Use position: relative for chart containers
  • No nested scrolling — the iframe auto-sizes to fit all content

Theme Variables

These CSS variables are automatically injected and resolve correctly in both light and dark mode:

Variable Purpose
--color-bg-page Page background
--color-bg-card Card/panel background
--color-bg-elevated Elevated surface
--color-bg-subtle Subtle/muted background
--color-bg-hover Hover state background
--color-text-primary Primary text
--color-text-secondary Secondary/muted text
--color-text-tertiary Hint/label text
--color-border-muted Default border (use with 0.5px)
--color-accent-primary Brand/accent color
--color-profit Positive/gain (green)
--color-loss Negative/loss (red)
--color-warning Warning (amber)
--color-info Info (blue)
--color-success Success (green)

Never hardcode colors like #333 or rgb(...) for text, backgrounds, or borders — they break in dark mode. Author every color as var(--color-x, #lightLiteral): the injected variable always wins in chat, and the fallback keeps the widget legible on its secondary surfaces. The only exception is chart canvas colors — Chart.js canvas cannot read CSS variables; resolve them via getComputedStyle with a literal fallback.

Charts (Chart.js)

Load Chart.js from CDN and follow these rules:

<!-- Wrapper div with explicit height — REQUIRED -->
<div style="position: relative; height: 200px;">
  <canvas id="myChart"></canvas>
</div>

<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
  // Read CSS variables for chart colors (canvas can't use var());
  // the literal fallback covers the widget opened outside the chat surface.
  var cs = getComputedStyle(document.documentElement);
  function pick(name, fallback) { return cs.getPropertyValue(name).trim() || fallback; }
  var accent = pick('--color-accent-primary', '#1f5fb4');
  var border = pick('--color-border-muted', '#e4e1dc');

  new Chart(document.getElementById('myChart'), {
    type: 'line',
    data: {
      labels: [...],
      datasets: [{
        data: [...],
        borderColor: accent,
        backgroundColor: accent + '20',
        tension: 0.4,
        fill: true
      }]
    },
    options: {
      responsive: true,
      maintainAspectRatio: false,
      plugins: { legend: { display: true } },
      scales: {
        y: { grid: { color: border } },
        x: { grid: { display: false } }
      }
    }
  });
</script>

Key rules:

  • Set height on the wrapper div, never on the canvas
  • Always use responsive: true, maintainAspectRatio: false
  • Use UMD build from CDN (sets window.Chart global)
  • Read CSS variables via getComputedStyle with a literal fallback (pick()) for chart colors

Typography

  • Font: inherited from host (-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif)
  • Weight: favor 400 (regular) and 500 (medium); use 600 sparingly for a key figure or heading. Avoid 700 — it reads heavy on the chat surface
  • Heading sizes: h1 = 22px, h2 = 18px, h3 = 16px (all weight 500)
  • Body: 14-16px, weight 400
  • Use sentence case — no Title Case or ALL CAPS (except short metric labels)

Interactivity

Make it explorable

A widget runs JavaScript, so prefer something the user can poke at, not just read. When the data supports it, reach for:

  • Sortable / filterable tables — click a header to sort by P&L or weight; filter to a sector or watchlist.
  • Series & metric toggles — show/hide chart series, switch price ↔ % change, flip timeframe (1M / 6M / 1Y).
  • Hover detail — tooltips on chart points and table rows that surface the underlying numbers.
  • What-if inputs — a slider or field that recomputes a figure live (drag a growth rate, watch the projection update).
  • Tabs / segmented views — split a dense widget (Overview / Holdings / Performance) so the reader drills in.

It all runs client-side over the embedded data — never fetch() a non-CDN origin (CSP blocks it; use data_files for anything large). Keep a meaningful default state so the widget reads correctly before any interaction. Use sendPrompt() only when the next step genuinely belongs back in the chat (a new query, a deeper analysis) — handle exploration the widget can do itself in-place.

sendPrompt()

Call sendPrompt('text') from buttons to trigger a follow-up chat message:

<button onclick="sendPrompt('Show detailed revenue breakdown')"
        style="padding: 8px 16px; background: var(--color-accent-primary); color: white;
               border: none; border-radius: 6px; cursor: pointer;">
  Revenue Details ↗
</button>

Add a ↗ arrow on buttons that call sendPrompt() to signal they trigger a chat action.

Refresh / Animation

setInterval and requestAnimationFrame work normally for animations and live tickers:

setInterval(function() {
  // Update prices, rotate data, animate
  updateDisplay();
}, 3000);

File Data

Use data_files to load data from sandbox files instead of inlining everything in the HTML string. This is especially useful for larger datasets.

Workflow

  1. Generate data files via Python
  2. Pass file paths to ShowWidget via data_files
  3. Access data in the widget via window.__WIDGET_DATA__["filename"]
# Step 1: Generate data
import json
data = {"labels": ["Q1", "Q2", "Q3"], "values": [100, 150, 200]}
with open("work/<task_name>/chart_data.json", "w") as f:
    json.dump(data, f)

# Step 2: Agent calls ShowWidget with data_files
ShowWidget(
    html='<div id="chart">...</div><script>var d = JSON.parse(__WIDGET_DATA__["chart_data.json"]); ...</script>',
    data_files=["work/<task_name>/chart_data.json"]
)

Widget access

// Text files (json, csv, txt, etc.) — returned as strings
var data = JSON.parse(window.__WIDGET_DATA__["chart_data.json"]);
var csvText = window.__WIDGET_DATA__["results.csv"];

// Binary files (png, jpg, etc.) — returned as data URLs
document.getElementById("img").src = window.__WIDGET_DATA__["chart.png"];

Supported file types

  • Text (returned as strings): .json, .csv, .txt, .html, .xml, .svg, .md, .yaml, .yml, .tsv, .geojson, .topojson
  • Binary (returned as data URLs): .png, .jpg, .jpeg, .gif, .webp, .ico

Size limits

Total inline data is capped at 500KB across all files. Keep datasets concise — aggregate or sample large files before passing them.

Blocked Patterns

The following will cause ShowWidget to reject your HTML with an error. Fix and retry:

Pattern Why blocked
new ResizeObserver(...) Host handles iframe sizing — your observer creates infinite resize loops
parent.postMessage(...) Use sendPrompt() instead — direct postMessage bypasses the bridge
window.top.* / window.parent.* Sandboxed iframe — parent access is blocked
position: fixed Breaks iframe auto-sizing
Background/border on outermost element Breaks seamless integration with chat surface

Design Patterns

Metric Cards Row

<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 16px;">
  <div style="background: var(--color-bg-subtle); padding: 14px 16px; border-radius: 8px; border: 0.5px solid var(--color-border-muted);">
    <div style="font-size: 11px; color: var(--color-text-tertiary); text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 6px;">Revenue</div>
    <div style="font-size: 24px; font-weight: 500;">$2.4M</div>
    <div style="font-size: 12px; color: var(--color-profit);">+12.5%</div>
  </div>
  <!-- more cards... -->
</div>

Data Table

<table style="width: 100%; border-collapse: collapse; font-size: 14px;">
  <thead>
    <tr style="border-bottom: 0.5px solid var(--color-border-muted);">
      <th style="text-align: left; padding: 8px; color: var(--color-text-secondary); font-weight: 500; font-size: 12px;">Symbol</th>
      <th style="text-align: right; padding: 8px; color: var(--color-text-secondary); font-weight: 500; font-size: 12px;">Price</th>
    </tr>
  </thead>
  <tbody>
    <tr style="border-bottom: 0.5px solid var(--color-border-muted);">
      <td style="padding: 8px; font-weight: 500;">AAPL</td>
      <td style="text-align: right; padding: 8px;">$213.18</td>
    </tr>
  </tbody>
</table>

Section with Chart

<div style="background: var(--color-bg-card); border-radius: 8px; border: 0.5px solid var(--color-border-muted); padding: 16px; margin-bottom: 16px;">
  <div style="font-size: 16px; font-weight: 500; margin-bottom: 12px;">Performance</div>
  <div style="position: relative; height: 200px;">
    <canvas id="perfChart"></canvas>
  </div>
</div>

Version History

  • ff6c8f0 Current 2026-08-04 21:55

    新增主题自适应令牌、实时调整大小及沙箱加固功能,优化预览与弹出窗口安全策略。

  • b544c1b 2026-07-05 09:23

Same Skill Collection

skills/3-statements/SKILL.md
skills/automation/SKILL.md
skills/catalyst-calendar/SKILL.md
skills/chart-annotation/SKILL.md
skills/check-deck/SKILL.md
skills/check-model/SKILL.md
skills/competitive-analysis/SKILL.md
skills/comps-analysis/SKILL.md
skills/dcf-model/SKILL.md
skills/earnings-analysis/SKILL.md
skills/earnings-preview/SKILL.md
skills/html-report/SKILL.md
skills/idea-generation/SKILL.md
skills/initiating-coverage/SKILL.md
skills/interactive-dashboard/SKILL.md
skills/market-watch/SKILL.md
skills/model-update/SKILL.md
skills/morning-note/SKILL.md
skills/onboarding/SKILL.md
skills/pdf/SKILL.md
skills/run-workflow/SKILL.md
skills/secretary/SKILL.md
skills/sector-overview/SKILL.md
skills/self-improve/SKILL.md
skills/thesis-tracker/SKILL.md
skills/ui-design/SKILL.md
skills/user-profile/SKILL.md
skills/web-scraping/SKILL.md
skills/x-api/SKILL.md
skills/docx/SKILL.md
skills/pptx/SKILL.md
skills/xlsx/SKILL.md

Metadata

Files
0
Version
607ac0f
Hash
f675842f
Indexed
2026-07-05 09:23

Accueil - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-08-06 23:58
浙ICP备14020137号-1 $Carte des visiteurs$