Skip to main content

vistral

Use when building React streaming data visualizations with @timeplus/vistral — creating charts, writing VistralSpec, using VistralChart, configuring streaming data sources, or wiring up live data via ChartHandle.

설치로 이동

소스 정보

저장소
timeplus-io/vistral
최근 소스 활동
2026년 4월 1일 17:19
감지된 SKILL.md 언어
영어
스타
64
포크
2

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
vistral
description
Use when building React streaming data visualizations with @timeplus/vistral — creating charts, writing VistralSpec, using VistralChart, configuring streaming data sources, or wiring up live data via ChartHandle.
# Vistral — Streaming Data Visualization ## Overview `@timeplus/vistral` is a React library for real-time streaming charts built on AntV G2. Two APIs exist — pick the right one before writing any code. ## Which API to Use ``` Need custom marks / full G2 control? → Grammar API: VistralChart + VistralSpec Want a quick chart with minimal config? → Config API: StreamChart + TimeSeriesConfig / BarColumnConfig / etc. ``` **AI agents should default to the Grammar API** — it's more expressive and maps cleanly to spec-driven generation. --- ## Grammar API (VistralChart + VistralSpec) ### Component ```tsx import { VistralChart, type ChartHandle } from '@timeplus/vistral'; import type { VistralSpec } from '@timeplus/vistral'; const ref = useRef<ChartHandle>(null); <VistralChart ref={ref} spec={spec} source={dataSource} // optional: declarative initial data height={400} theme="dark" // string | VistralTheme — 'dark', 'light', registered name, or VistralTheme object onReady={(handle) => { handle.append(rows); // add rows, re-render handle.replace(rows); // replace all data, re-render handle.clear(); // empty buffer, re-render handle.g2; // raw G2 instance }} /> ``` ### VistralSpec Fields | Field | Type | Description | |-------|------|-------------| | `marks` | `MarkSpec[]` | **Required.** Visual elements: `line`, `area`, `interval`, `point`, `rect`, `text`, etc. | | `scales` | `Record<string, ScaleSpec>` | Scale config per channel (`x`, `y`, `color`). Per-mark scales override. | | `transforms` | `TransformSpec[]` | Visual transforms: `stackY`, `dodgeX`, `bin`, `group` | | `temporal` | `TemporalSpec` | Streaming time control (see below) | | `streaming` | `StreamingSpec` | `maxItems`, `mode`, `throttle` | | `axes` | `AxesSpec` | `{ x?, y? }` — title, grid, label format/rotate | | `legend` | `LegendSpec \| false` | `{ position?, interactive? }` or `false` to hide | | `tooltip` | `TooltipSpec \| false` | `{ title?, items[] }` — `items[].format` maps to G2 `valueFormatter` | | `coordinate` | `CoordinateSpec` | `polar`, `theta`, `transpose`, etc. | | `annotations` | `AnnotationSpec[]` | Reference lines, ranges, text overlays | | `interactions` | `InteractionSpec[]` | `{ type: 'tooltip' \| 'brush' \| ... }` | | `theme` | `string \| VistralTheme` | Spec-level theme — overridden by component `theme` prop. Prefer setting theme on the component. | | `animate` | `boolean` | Disable for streaming: `animate: false` | | `g2Overrides` | `Record<string, unknown>` | Raw G2 options deep-merged last — override anything | ### Common Mark Pattern ```tsx const spec: VistralSpec = { marks: [{ type: 'line', encode: { x: 'time', y: 'value', color: 'series' }, scales: { x: { type: 'time' } }, style: { shape: 'smooth' }, }], temporal: { mode: 'axis', field: 'time', range: 5 }, axes: { y: { labels: { format: (v) => `${Number(v).toFixed(1)}` } } }, tooltip: { items: [{ field: 'value', name: 'Value', format: (v) => `${v}` }] }, animate: false, theme: 'dark', }; ``` ### Temporal Modes | Mode | When to use | Key fields | |------|-------------|------------| | `axis` | Sliding time window on X | `field: 'time'`, `range: 5` (minutes) | | `frame` | Latest timestamp only | `field: 'time'` | | `key` | Latest value per entity | `field: 'id'` or `field: ['region','server']` | ### g2Overrides — Escape Hatch Use for any G2 option not modeled by VistralSpec: ```tsx g2Overrides: { axis: { y: { tickCount: 5 } }, // tick count paddingLeft: 60, // view padding tooltip: { // raw G2 tooltip items: [{ channel: 'y', valueFormatter: (v) => formatBps(v) }] } } ``` --- ## Custom Themes ```tsx import { registerTheme, type VistralTheme } from '@timeplus/vistral'; // Register once at app startup (module level) registerTheme('corporate', { extends: 'light', // inherit from 'dark' (default) or 'light' palette: ['#0066CC', '#FF6600', '#00AA44'], font: { family: 'Roboto, sans-serif', size: 12 }, axis: { grid: { color: '#E0E0E0', dash: [4, 4] }, label: { color: '#333' } }, tooltip: { background: '#FFF', text: { color: '#111' }, border: { color: '#E0E0E0' } }, legend: { label: { color: '#333' } }, } satisfies VistralTheme); // Use by name <VistralChart spec={spec} theme="corporate" /> <StreamChart config={config} data={data} theme="corporate" /> // Or pass a VistralTheme object directly (no registration needed) <VistralChart spec={spec} theme={{ palette: ['#FF73B6', '#8890FF'], axis: { grid: { color: '#1A1A2E' } } }} /> ``` Built-in themes: `'dark'` (default) and `'light'`. Custom themes deep-merge onto the base. --- ## StreamDataSource Format ```tsx import type { StreamDataSource } from '@timeplus/vistral'; const source: StreamDataSource = { columns: [ { name: 'time', type: 'datetime64' }, { name: 'value', type: 'float64' }, { name: 'series', type: 'string' }, ], data: [ { time: '2024-01-01T00:00:00Z', value: 42.5, series: 'A' }, // object rows // OR array rows (must match column order): // ['2024-01-01T00:00:00Z', 42.5, 'A'], ], }; ``` **Column types:** `string`, `number`, `datetime`, `datetime64`, `float32`, `float64`, `int32`, `int64`, `boolean` --- ## Streaming Pattern (Grammar API) ```tsx function LiveChart() { const handleRef = useRef<ChartHandle>(null); const loadedRef = useRef(false); // ← REQUIRED: React 18 Strict Mode guard useEffect(() => { if (!loadedRef.current && handleRef.current) { loadedRef.current = true; handleRef.current.append(historicalRows); // seed history once } }, []); useEffect(() => { const id = setInterval(() => { handleRef.current?.append(newRows); // push live rows }, 1000); return () => clearInterval(id); }, []); return <VistralChart ref={handleRef} spec={spec} height={400} theme="dark" />; } // Note: use `ref` for ongoing appends, `onReady` for one-time setup. // Do NOT duplicate seeding in both — use loadedRef to guard whichever you choose. ``` --- ## Config API (StreamChart) — Quick Reference ```tsx import { StreamChart, useStreamingData } from '@timeplus/vistral'; import type { TimeSeriesConfig, StreamDataSource } from '@timeplus/vistral'; function QuickChart() { // useStreamingData<ElementType> — generic is the ROW type, not array const { data, append } = useStreamingData<Record<string, unknown>>([], 1000); const config: TimeSeriesConfig = { chartType: 'line', xAxis: 'time', yAxis: 'value', color: 'series', lineStyle: 'curve', legend: true, temporal: { mode: 'axis', field: 'time', range: 5 }, }; // StreamChart has NO ref/ChartHandle (use VistralChart for imperative control) return <StreamChart config={config} data={{ columns, data }} theme="dark" />; } ``` --- ## Critical Gotchas | Mistake | Fix | |---------|-----| | `useStreamingData<Row[]>` (wrong generic) | Use `useStreamingData<Row>` — element type, not array | | `<StreamChart ref={...}>` | `StreamChart` has NO ref/ChartHandle. Use `VistralChart` for imperative append/replace/clear. | | `append([singleRow])` when row is already an array | Wrap: `append([[singleRow]])` — `append` treats top-level array as multiple items | | No `loadedRef` guard when seeding history | React 18 Strict Mode runs effects twice → doubled rows | | `tooltip.items[].format` not working | `format` IS supported — translated to G2's `valueFormatter` internally (v0.1.6+). Use `g2Overrides.tooltip` only for raw G2 tooltip features beyond what `TooltipSpec` models. | | `legend.interactive` silently ignored | Use `legend: { interactive: true }` — now translated correctly | --- ## Axis Label Formatting ```tsx axes: { y: { title: 'Throughput', labels: { format: (v) => { const n = Number(v); if (n >= 1e9) return `${(n/1e9).toFixed(1)} Gbps`; if (n >= 1e6) return `${(n/1e6).toFixed(1)} Mbps`; if (n >= 1e3) return `${(n/1e3).toFixed(1)} Kbps`; return `${n.toFixed(0)} bps`; } } } } ```
GitHub에서 보기