// In an app: import { setupStockCard } from '@aparte/plugin-cards/stock';
const { setupStockCard } = window.apartePlugins.cards.stock;
// A made-up price walk: n values ending at end, spread over span, the same for a seed.
const walk = (seed, n, end, span) => {
let v = 0;
const raw = Array.from({ length: n }, (_, i) => (v += Math.sin(i * 0.7 + seed) * 1.6 + Math.cos(i * 0.23 + seed * 2) + 0.12));
const last = raw[n - 1];
const scale = span / (Math.max(...raw) - Math.min(...raw));
return raw.map((x) => Math.round((end - (last - x) * scale) * 100) / 100);
};
const RANGES = { '1D': [78, 3.6], '5D': [60, 8], '1M': [44, 16], '6M': [60, 30], '1Y': [52, 42] };
const quote = (range) => ({
symbol: 'ACME', name: 'Acme Corp.', exchange: 'NASDAQ', currency: 'USD', marketState: 'open',
price: 184.6, change: 2.35, changePercent: 1.29, previousClose: 182.25,
open: 182.9, high: 185.2, low: 181.6, volume: 48200000, marketCap: 312000000000,
range, ranges: Object.keys(RANGES), series: walk(range.length * 7 + range.charCodeAt(1), RANGES[range][0], 184.6, RANGES[range][1]),
});
// The range buttons are drawn because something answers them: a press redraws the card in
// place. Here the answer is computed; in an app it is a request for the quote over that range.
const answer = async ({ name, value }) => {
if (name !== 'range') return;
await new Promise((done) => setTimeout(done, 250));
return quote(value);
};
setupStockCard({ tool: 'get_quote', onChange: answer });
const viewport = document.querySelector('aparte-chat-viewport');
viewport.appendMessage({ id: 'u1', role: 'user', content: 'How is ACME doing today?', timestamp: Date.now() });
viewport.appendMessage({ id: 'a1', role: 'assistant', timestamp: Date.now(), segments: [{
id: 's1', type: 'tool_call', status: 'resolved',
toolCall: { id: 'c-s1', name: 'get_quote', input: { symbol: 'ACME' } },
structuredResult: quote('1D'),
}] });