playground/json-ui-skill/SKILL.md
CRITICAL: Use for json-ui component rendering and development. Triggers on: json-ui, json render, component catalog, report render, HTML report, I18nString, i18n, bilingual, language switch, dual language, PaperHeader, AuthorList, Abstract, MetricsGrid, Section, Highlight, Zod schema, catalog.ts, cli.ts, components/index.tsx, "how to add a component", "how to render JSON", JSON 渲染, 组件目录, 报告渲染, 多语言, 中英文切换
npx skillsauth add actionbook/actionbook json-uiInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Version: 1.0.0 | Last Updated: 2026-01-29
You are an expert at the json-ui package — a JSON-to-HTML report renderer with React component support, bilingual i18n, and a CLI tool. Help users by:
| Task | File | Pattern |
|------|------|---------|
| Define component schema | src/catalog.ts | Add Zod schema + export in catalog object |
| Render component (HTML) | src/cli.ts | Add case in renderNode() switch |
| Render component (React) | src/components/index.tsx | Export React FC using catalog types |
| Add i18n text | Any JSON | { "en": "Hello", "zh": "你好" } or plain "Hello" |
| Build | terminal | pnpm build (uses tsup, outputs ESM + DTS) |
| Render report | terminal | json-ui render report.json [-o out.html] [--no-open] |
Refer to local source files for detailed documentation:
packages/json-ui/src/catalog.ts - All Zod schemas and type definitionspackages/json-ui/src/cli.ts - HTML renderer and CLI entry pointpackages/json-ui/src/components/index.tsx - React component implementationsBefore answering questions, Claude MUST:
Reports are trees of nodes:
{
"type": "Report",
"props": { "title": "My Report", "theme": "auto" },
"children": [
{
"type": "Section",
"props": { "title": "Overview", "icon": "bulb" },
"children": [
{ "type": "Abstract", "props": { "text": "..." } }
]
}
]
}
| Layer | File | Output | Use Case |
|-------|------|--------|----------|
| Zod Schemas | catalog.ts | Type definitions | Validation, type safety |
| HTML Renderer | cli.ts | Static HTML string | CLI render command |
| React Components | components/index.tsx | React elements | Embedded usage |
JSON file → CLI parse → renderNode() recursion → HTML string → file write → browser open
| Component | Key Props | Description |
|-----------|-----------|-------------|
| Report | title?, theme | Root wrapper, 800px max-width |
| Section | title, icon?, collapsible? | Collapsible section with header |
| Grid | cols, gap | CSS grid layout |
| Card | variant, padding, shadow | Card container |
| Component | Key Props | Description |
|-----------|-----------|-------------|
| PaperHeader | title, arxivId, date, categories? | Paper title + metadata |
| AuthorList | authors, layout?, maxVisible? | Author names + affiliations |
| Abstract | text, highlights?, maxLength? | Abstract with keyword highlighting |
| TagList | tags, variant | Tag/category pills |
| Component | Key Props | Description |
|-----------|-----------|-------------|
| ContributionList | items, numbered? | Numbered contributions with badges |
| MethodOverview | steps, showConnectors? | Step-by-step method pipeline |
| Highlight | text, type, source? | Blockquote (quote/important/warning/code) |
| KeyPoint | icon, title, description | Icon + title + description |
| CodeBlock | code, language, showLineNumbers? | Syntax-highlighted code |
| Prose | content | Markdown content block |
| Callout | type, title?, content | Info/tip/warning/important/note box |
| Component | Key Props | Description |
|-----------|-----------|-------------|
| Image | src, alt?, caption?, width? | Single image |
| Figure | images, caption?, label? | Multi-image figure |
| Formula | latex, block?, label? | LaTeX formula |
| DefinitionList | items | Term-definition pairs |
| Theorem | type, number?, title?, content | Theorem/lemma/proposition |
| Algorithm | title, steps, caption? | Algorithm pseudocode |
| ResultsTable | columns, rows, highlights? | Results with best-cell highlighting |
| Component | Key Props | Description |
|-----------|-----------|-------------|
| Metric | label, value, trend?, icon? | Single metric card |
| MetricsGrid | metrics, cols? | Grid of metric cards |
| Table | columns, rows, striped?, caption? | Data table |
| Component | Key Props | Description |
|-----------|-----------|-------------|
| LinkButton | href, label, icon?, external? | Styled link button |
| LinkGroup | links, layout? | Group of link buttons |
| Component | Key Props | Description |
|-----------|-----------|-------------|
| BrandHeader | badge?, poweredBy?, showBadge? | AI-generated badge header |
| BrandFooter | timestamp, attribution?, disclaimer? | Footer with attribution |
The I18nString type accepts both plain strings and bilingual objects:
// catalog.ts
export const I18nString = z.union([
z.string(),
z.object({ en: z.string(), zh: z.string() }),
]);
// Plain string (backward compatible)
{ "title": "Hello World" }
// Bilingual object
{ "title": { "en": "Hello World", "zh": "你好世界" } }
For HTML output, i18n strings render as dual spans:
// renderI18n() outputs:
<span class="i18n-en">Hello</span><span class="i18n-zh">你好</span>
// CSS controls visibility:
html[lang="en"] .i18n-zh { display: none; }
html[lang="zh"] .i18n-en { display: none; }
For HTML attributes (alt, title) where only a plain string works:
// resolveI18n() picks one language:
const alt = resolveI18n(props.alt, 'en'); // returns plain string
// Use <I18nText> component for JSX:
<I18nText value={props.title} />
// Use resolveI18nStr() for plain string contexts:
const altText = resolveI18nStr(props.alt, 'en');
<html lang="en|zh"> attributelocalStorage.getItem('json-ui-lang')catalog.ts:export const MyWidgetSchema = z.object({
label: I18nString, // Use I18nString for user-visible text
count: z.number(), // Use z.string()/z.number() for data
variant: VariantType.default('default'),
});
// Add to catalog object:
export const catalog = {
// ...existing...
MyWidget: MyWidgetSchema,
} as const;
// Export type:
export type MyWidgetProps = z.infer<typeof MyWidgetSchema>;
cli.ts renderNode() switch:case 'MyWidget': {
const { label, count, variant } = props;
return `<div class="my-widget ${variant}">
<span>${renderI18n(label)}</span>
<strong>${escapeHtml(String(count))}</strong>
</div>`;
}
components/index.tsx:export const MyWidget: React.FC<MyWidgetProps> = ({ label, count, variant = 'default' }) => (
<div className={`my-widget ${variant}`}>
<span><I18nText value={label} /></span>
<strong>{count}</strong>
</div>
);
For text that needs processing (e.g., Abstract highlights):
// HTML (cli.ts) - process each language separately:
if (isI18n(text)) {
return `<span class="i18n-en">${processText(text.en)}</span>
<span class="i18n-zh">${processText(text.zh)}</span>`;
} else {
return processText(String(text));
}
// React (components/index.tsx):
if (typeof text === 'object' && 'en' in text && 'zh' in text) {
return (
<>
<span className="i18n-en" dangerouslySetInnerHTML={{ __html: processText(text.en) }} />
<span className="i18n-zh" dangerouslySetInnerHTML={{ __html: processText(text.zh) }} />
</>
);
}
| Error | Cause | Solution |
|-------|-------|---------|
| Type 'I18nStringType' is not assignable to 'ReactNode' | Passing i18n object directly to JSX | Wrap with <I18nText value={...} /> |
| Property 'length' does not exist on type 'I18nStringType' | Calling string methods on i18n value | Use type guard: typeof text === 'string' ? text : text.en |
| Images not loading from arxiv | crossorigin="anonymous" on <img> | Remove crossorigin; keep only referrerpolicy="no-referrer" |
| Language switcher not working | Missing CSS rules or JS | Ensure html[lang] .i18n-* CSS rules and toggle JS are in template |
| Build fails with type errors | Schema changed but components not updated | Update all three files: catalog, cli, components |
Do NOT use crossorigin="anonymous" on <img> tags.
Sites like arxiv.org do not send CORS headers. Adding crossorigin="anonymous" causes the browser to require CORS, which fails and blocks the image.
<!-- WRONG - breaks images from arxiv and similar sites -->
<img src="..." referrerpolicy="no-referrer" crossorigin="anonymous" />
<!-- CORRECT -->
<img src="..." referrerpolicy="no-referrer" />
When writing Chinese translations for ML/AI papers:
| Wrong | Correct | Reason | |-------|---------|--------| | 评论器 | 价值函数(critic) | Standard ML term | | 运行估计 | 滑动估计 | Running estimate = 滑动估计 | | 重加权因子 | 加权系数 | More natural Chinese | | 不断演化的 | 动态更新的 | Clearer meaning | | 简单修复 | 改动小 | Academic tone |
# Build (ESM + DTS via tsup)
cd packages/json-ui && pnpm build
# Render report to HTML
node dist/cli.js render example-report-rich.json
# With options
node dist/cli.js render report.json -o output.html --no-open
I18nString for user-visible text properties in schemas{en, zh} forms in rendererscrossorigin="anonymous" on img tagsreferrerpolicy="no-referrer" on img tags for privacypnpm build after any schema or component changesdevelopment
Browser action engine. Provides up-to-date action manuals for the modern web — operate any website instantly, one tab or dozens, concurrently.
development
Extract structured data from websites and produce an executable Playwright script plus extracted data. Use when the user wants to scrape, extract, pull, collect, or harvest data from any website — product listings, tables, search results, feeds, profiles, or any repeating content.
tools
Deep research and analysis tool. Generates comprehensive HTML reports on any topic, domain, paper, or technology. Enhanced with advanced browser automation — SPA handling, network idle wait, batch operations, stealth browsing, and intelligent page analysis. Use when user asks to research, analyze, investigate, deep-dive, or generate a report on any subject.
development
Learn Rust language features and crate updates. Use when user asks about Rust version changelog, what's new in Rust, crate updates, Cargo.toml dependencies, tokio/serde/axum features, or any Rust ecosystem questions.