@zhive/cli 0.6.3 → 0.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CLAUDE.md +7 -0
- package/dist/backtest/CLAUDE.md +7 -0
- package/dist/cli.js +20 -0
- package/dist/commands/agent/commands/profile.js +3 -2
- package/dist/commands/agent/commands/profile.test.js +10 -12
- package/dist/commands/doctor/commands/index.js +93 -0
- package/dist/commands/megathread/commands/create-comment.js +4 -9
- package/dist/commands/megathread/commands/create-comment.test.js +15 -173
- package/dist/commands/megathread/commands/create-comments.js +83 -0
- package/dist/commands/megathread/commands/index.js +2 -0
- package/dist/commands/megathread/commands/list.js +5 -5
- package/dist/commands/megathread/commands/list.test.js +14 -14
- package/dist/commands/start/commands/prediction.js +3 -4
- package/dist/commands/start/hooks/useChat.js +40 -41
- package/dist/commands/start/services/command-registry.js +1 -1
- package/dist/index.js +2 -0
- package/dist/{agent → services/agent}/analysis.js +5 -5
- package/dist/{load-agent-env.js → services/agent/env.js} +1 -1
- package/dist/{agent → services/agent/helpers}/model.js +2 -2
- package/dist/{agent → services/agent/prompts}/memory-prompt.js +20 -22
- package/dist/{agent → services/agent/prompts}/prompt.js +80 -54
- package/dist/{agent → services/agent}/tools/market/client.js +1 -1
- package/dist/{agent → services/agent}/tools/mindshare/client.js +1 -1
- package/dist/{agents.js → services/config/agent.js} +2 -2
- package/dist/{config.js → services/config/config.js} +1 -7
- package/dist/services/config/constant.js +8 -0
- package/dist/shared/agent/config.js +75 -0
- package/dist/shared/agent/env.js +30 -0
- package/dist/shared/agent/helpers/model.js +92 -0
- package/dist/shared/ai-providers.js +66 -0
- package/dist/shared/config/agent.js +0 -11
- package/dist/shared/config/agent.test.js +4 -35
- package/package.json +2 -2
- package/dist/agent/app.js +0 -122
- package/dist/agent/commands/registry.js +0 -12
- package/dist/agent/components/AsciiTicker.js +0 -81
- package/dist/agent/components/CommandInput.js +0 -65
- package/dist/agent/components/HoneycombBoot.js +0 -291
- package/dist/agent/components/Spinner.js +0 -37
- package/dist/agent/hooks/useAgent.js +0 -480
- package/dist/agent/objects.js +0 -1
- package/dist/agent/process-lifecycle.js +0 -18
- package/dist/agent/run-headless.js +0 -189
- package/dist/agent/theme.js +0 -41
- package/dist/avatar.js +0 -34
- package/dist/backtest/default-backtest-data.js +0 -200
- package/dist/backtest/fetch.js +0 -41
- package/dist/backtest/import.js +0 -106
- package/dist/backtest/index.js +0 -10
- package/dist/backtest/results.js +0 -113
- package/dist/backtest/runner.js +0 -134
- package/dist/backtest/storage.js +0 -11
- package/dist/backtest/types.js +0 -1
- package/dist/commands/install.js +0 -50
- package/dist/commands/start/ui/PollText.js +0 -23
- package/dist/commands/start/ui/PredictionsPanel.js +0 -88
- package/dist/commands/start/ui/SpinnerContext.js +0 -20
- package/dist/components/InputGuard.js +0 -6
- package/dist/components/stdout-spinner.js +0 -48
- package/dist/create/CreateApp.js +0 -153
- package/dist/create/ai-generate.js +0 -147
- package/dist/create/generate.js +0 -73
- package/dist/create/steps/ApiKeyStep.js +0 -97
- package/dist/create/steps/AvatarStep.js +0 -16
- package/dist/create/steps/BioStep.js +0 -14
- package/dist/create/steps/DoneStep.js +0 -14
- package/dist/create/steps/IdentityStep.js +0 -163
- package/dist/create/steps/NameStep.js +0 -71
- package/dist/create/steps/ScaffoldStep.js +0 -58
- package/dist/create/steps/SoulStep.js +0 -58
- package/dist/create/steps/StrategyStep.js +0 -58
- package/dist/create/validate-api-key.js +0 -47
- package/dist/create/welcome.js +0 -304
- package/dist/list/ListApp.js +0 -79
- package/dist/migrate-templates/MigrateApp.js +0 -131
- package/dist/migrate-templates/migrate.js +0 -86
- package/dist/presets.js +0 -613
- package/dist/start/AgentProcessManager.js +0 -98
- package/dist/start/Dashboard.js +0 -92
- package/dist/start/SelectAgentApp.js +0 -81
- package/dist/start/StartApp.js +0 -189
- package/dist/start/patch-headless.js +0 -101
- package/dist/start/patch-managed-mode.js +0 -142
- package/dist/start/start-command.js +0 -24
- package/dist/theme.js +0 -54
- /package/dist/{agent → services/agent}/config.js +0 -0
- /package/dist/{agent → services/agent}/helpers.js +0 -0
- /package/dist/{agent → services/agent/prompts}/chat-prompt.js +0 -0
- /package/dist/{agent → services/agent}/skills/index.js +0 -0
- /package/dist/{agent → services/agent}/skills/skill-parser.js +0 -0
- /package/dist/{agent → services/agent}/skills/types.js +0 -0
- /package/dist/{agent → services/agent/tools}/edit-section.js +0 -0
- /package/dist/{agent → services/agent/tools}/fetch-rules.js +0 -0
- /package/dist/{agent → services/agent}/tools/index.js +0 -0
- /package/dist/{agent → services/agent}/tools/market/index.js +0 -0
- /package/dist/{agent → services/agent}/tools/market/tools.js +0 -0
- /package/dist/{agent → services/agent}/tools/mindshare/index.js +0 -0
- /package/dist/{agent → services/agent}/tools/mindshare/tools.js +0 -0
- /package/dist/{agent → services/agent}/tools/read-skill-tool.js +0 -0
- /package/dist/{agent → services/agent}/tools/ta/index.js +0 -0
- /package/dist/{agent → services/agent}/tools/ta/indicators.js +0 -0
- /package/dist/{agent → services/agent}/types.js +0 -0
- /package/dist/{ai-providers.js → services/ai-providers.js} +0 -0
package/dist/agent/app.js
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useRef } from 'react';
|
|
3
|
-
import { Box, Text, useStdout } from 'ink';
|
|
4
|
-
import chalk from 'chalk';
|
|
5
|
-
import { useAgent } from './hooks/useAgent.js';
|
|
6
|
-
import { colors, symbols, border } from './theme.js';
|
|
7
|
-
import { formatTime, convictionColor } from './helpers.js';
|
|
8
|
-
import { Spinner, PollText } from './components/Spinner.js';
|
|
9
|
-
import { AsciiTicker } from './components/AsciiTicker.js';
|
|
10
|
-
import { CommandInput } from './components/CommandInput.js';
|
|
11
|
-
// ─── Format a settled poll item as a chalk string for stdout ───
|
|
12
|
-
function formatSettledItem(item) {
|
|
13
|
-
const time = chalk.gray.dim(`${formatTime(item.timestamp)} `);
|
|
14
|
-
const lines = [];
|
|
15
|
-
if (item.type === 'online') {
|
|
16
|
-
lines.push(` ${time}${chalk.hex(colors.honey)(symbols.hive)} ${chalk.white(item.text)}`);
|
|
17
|
-
}
|
|
18
|
-
if (item.type === 'signal' || item.type === 'megathread') {
|
|
19
|
-
const isMega = item.type === 'megathread';
|
|
20
|
-
const accent = isMega ? colors.controversial : colors.cyan;
|
|
21
|
-
const hiveColor = isMega ? colors.controversial : colors.honey;
|
|
22
|
-
let mainLine = ` ${time}${chalk.hex(hiveColor)(symbols.hive)} ${chalk.hex(accent)(item.text)}`;
|
|
23
|
-
if (item.status === 'skipped') {
|
|
24
|
-
mainLine += chalk.hex(colors.honey)(` ${symbols.diamondOpen} skipped`);
|
|
25
|
-
if (item.tokenUsage) {
|
|
26
|
-
let tokenInfo = ` ${symbols.circle} ${item.tokenUsage.inputTokens.toLocaleString()} in`;
|
|
27
|
-
if (item.tokenUsage.cacheReadTokens > 0) {
|
|
28
|
-
tokenInfo += ` (${item.tokenUsage.cacheReadTokens.toLocaleString()} cached)`;
|
|
29
|
-
}
|
|
30
|
-
else if (item.tokenUsage.cacheWriteTokens > 0) {
|
|
31
|
-
tokenInfo += ` (${item.tokenUsage.cacheWriteTokens.toLocaleString()} cache write)`;
|
|
32
|
-
}
|
|
33
|
-
tokenInfo += ` \u00b7 ${item.tokenUsage.outputTokens.toLocaleString()} out`;
|
|
34
|
-
if (item.tokenUsage.toolCalls > 0) {
|
|
35
|
-
tokenInfo += ` \u00b7 tools: ${item.tokenUsage.toolNames.join(', ')}`;
|
|
36
|
-
}
|
|
37
|
-
mainLine += chalk.gray.dim(tokenInfo);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
lines.push(mainLine);
|
|
41
|
-
if (item.status === 'posted' && item.result) {
|
|
42
|
-
const pad = ' '.repeat(13);
|
|
43
|
-
const cColor = isMega ? colors.controversial : convictionColor(item.conviction ?? 0);
|
|
44
|
-
lines.push(`${pad}${chalk.hex(cColor)(symbols.diamond)} ${chalk.white(item.result)}`);
|
|
45
|
-
if (item.url) {
|
|
46
|
-
lines.push(`${' '.repeat(15)}${chalk.gray.dim(`url: ${item.url}`)}`);
|
|
47
|
-
}
|
|
48
|
-
if (item.tokenUsage) {
|
|
49
|
-
let tokenInfo = `tokens: ${item.tokenUsage.inputTokens.toLocaleString()} in`;
|
|
50
|
-
if (item.tokenUsage.cacheReadTokens > 0) {
|
|
51
|
-
tokenInfo += ` (${item.tokenUsage.cacheReadTokens.toLocaleString()} cached)`;
|
|
52
|
-
}
|
|
53
|
-
else if (item.tokenUsage.cacheWriteTokens > 0) {
|
|
54
|
-
tokenInfo += ` (${item.tokenUsage.cacheWriteTokens.toLocaleString()} cache write)`;
|
|
55
|
-
}
|
|
56
|
-
tokenInfo += ` \u00b7 ${item.tokenUsage.outputTokens.toLocaleString()} out`;
|
|
57
|
-
lines.push(`${' '.repeat(15)}${chalk.gray.dim(tokenInfo)}`);
|
|
58
|
-
if (item.tokenUsage.toolCalls > 0) {
|
|
59
|
-
const toolInfo = `tools: ${item.tokenUsage.toolCalls} tools (${item.tokenUsage.toolNames.join(', ')})`;
|
|
60
|
-
lines.push(`${' '.repeat(15)}${chalk.gray.dim(toolInfo)}`);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
if (item.status === 'error' && item.result) {
|
|
65
|
-
const pad = ' '.repeat(13);
|
|
66
|
-
lines.push(`${pad}${chalk.hex(colors.red)(`${symbols.cross} ${item.result}`)}`);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
if (item.type === 'idle') {
|
|
70
|
-
lines.push(` ${time}${chalk.gray(`${symbols.circle} ${item.text}`)}`);
|
|
71
|
-
}
|
|
72
|
-
if (item.type === 'error') {
|
|
73
|
-
lines.push(` ${time}${chalk.hex(colors.red)(`${symbols.cross} ${item.text}`)}`);
|
|
74
|
-
}
|
|
75
|
-
return lines.join('\n');
|
|
76
|
-
}
|
|
77
|
-
// ─── Main TUI App ────────────────────────────────────
|
|
78
|
-
export function App() {
|
|
79
|
-
const { connected, agentName, modelInfo, pollActivity, chatActivity, input, chatStreaming, chatBuffer, predictionCount, termWidth, setInput, handleChatSubmit, } = useAgent();
|
|
80
|
-
const { write } = useStdout();
|
|
81
|
-
// When stdin is not a TTY (piped by hive-cli start), skip interactive input
|
|
82
|
-
const isInteractive = process.stdin.isTTY === true;
|
|
83
|
-
const boxWidth = termWidth;
|
|
84
|
-
// Split poll items: settled items are written to stdout via useStdout (scrollback),
|
|
85
|
-
// active items render in the dynamic Ink section (need spinner animation).
|
|
86
|
-
const settledStatuses = new Set(['posted', 'skipped', 'error', undefined]);
|
|
87
|
-
const settledTypes = new Set(['idle', 'online', 'error']);
|
|
88
|
-
const isSettled = (item) => {
|
|
89
|
-
if (settledTypes.has(item.type))
|
|
90
|
-
return true;
|
|
91
|
-
return settledStatuses.has(item.status) && item.status !== undefined;
|
|
92
|
-
};
|
|
93
|
-
const settledPollItems = pollActivity.filter(isSettled);
|
|
94
|
-
const activePollItems = pollActivity.filter((item) => !isSettled(item));
|
|
95
|
-
const visibleChatActivity = chatActivity.slice(-5);
|
|
96
|
-
// Write settled items to stdout permanently (scrollback history).
|
|
97
|
-
// Track how many we've already written so each item is written once.
|
|
98
|
-
const writtenCountRef = useRef(0);
|
|
99
|
-
useEffect(() => {
|
|
100
|
-
const newCount = settledPollItems.length;
|
|
101
|
-
if (newCount > writtenCountRef.current) {
|
|
102
|
-
const newItems = settledPollItems.slice(writtenCountRef.current);
|
|
103
|
-
for (const item of newItems) {
|
|
104
|
-
const formatted = formatSettledItem(item);
|
|
105
|
-
write(formatted + '\n');
|
|
106
|
-
}
|
|
107
|
-
writtenCountRef.current = newCount;
|
|
108
|
-
}
|
|
109
|
-
}, [settledPollItems.length, write]);
|
|
110
|
-
const statsText = predictionCount > 0 ? ` ${border.horizontal.repeat(3)} ${predictionCount} predicted` : '';
|
|
111
|
-
const connectedDisplay = connected ? 'Connected to the Hive' : 'connecting...';
|
|
112
|
-
const nameDisplay = `${agentName} agent`;
|
|
113
|
-
const headerFill = Math.max(0, boxWidth - nameDisplay.length - connectedDisplay.length - 12 - statsText.length);
|
|
114
|
-
return (_jsxs(Box, { flexDirection: "column", width: boxWidth, children: [_jsx(AsciiTicker, { rows: 2, step: predictionCount }), _jsxs(Box, { children: [_jsx(Text, { color: colors.honey, children: `${border.topLeft}${border.horizontal} ${symbols.hive} ` }), _jsxs(Text, { color: colors.white, bold: true, children: [agentName, " agent"] }), _jsxs(Text, { color: colors.gray, children: [" ", `${border.horizontal.repeat(3)} `] }), _jsx(Text, { color: connected ? colors.green : colors.honey, children: connected ? 'Connected to the Hive' : 'connecting...' }), statsText && _jsxs(Text, { color: colors.gray, children: [" ", `${border.horizontal.repeat(3)} `] }), statsText && _jsxs(Text, { color: colors.honey, children: [predictionCount, " predicted"] }), _jsxs(Text, { color: colors.gray, children: [' ', border.horizontal.repeat(Math.max(0, headerFill)), border.topRight] })] }), modelInfo && (_jsxs(Box, { paddingLeft: 1, children: [_jsxs(Text, { color: colors.gray, children: [symbols.hive, " "] }), _jsx(Text, { color: colors.cyan, children: modelInfo.modelId }), _jsxs(Text, { color: colors.gray, children: [" ", '\u00d7', " "] }), _jsx(Text, { color: colors.white, children: "zData" })] })), _jsxs(Box, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, minHeight: 2, children: [!connected && _jsx(Spinner, { label: "Initiating neural link..." }), activePollItems.map((item, i) => {
|
|
115
|
-
const isMega = item.type === 'megathread';
|
|
116
|
-
const accentColor = isMega ? colors.controversial : colors.cyan;
|
|
117
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsxs(Text, { color: colors.gray, dimColor: true, children: [formatTime(item.timestamp), ' '] }), _jsxs(Text, { color: isMega ? colors.controversial : colors.honey, children: [symbols.hive, " "] }), _jsx(PollText, { color: accentColor, text: item.text, animate: false }), _jsx(Text, { children: " " }), _jsx(Spinner, { label: "analyzing..." })] }), item.detail && (_jsx(Box, { marginLeft: 13, children: _jsx(PollText, { color: colors.gray, text: `"${item.detail}"`, animate: false }) }))] }, `active-${item.id ?? i}`));
|
|
118
|
-
})] }), (chatActivity.length > 0 || chatStreaming) && (_jsxs(_Fragment, { children: [_jsx(Box, { children: _jsxs(Text, { color: colors.gray, children: [border.teeLeft, `${border.horizontal.repeat(2)} chat with ${agentName} agent `, border.horizontal.repeat(Math.max(0, boxWidth - agentName.length - 22)), border.teeRight] }) }), _jsxs(Box, { flexDirection: "column", paddingLeft: 1, paddingRight: 1, minHeight: 2, maxHeight: 8, children: [visibleChatActivity.map((item, i) => (_jsxs(Box, { children: [item.type === 'chat-user' && (_jsxs(Box, { children: [_jsxs(Text, { color: colors.white, bold: true, children: ["you:", ' '] }), _jsx(Text, { color: colors.white, children: item.text })] })), item.type === 'chat-agent' && (_jsxs(Box, { children: [_jsxs(Text, { color: colors.honey, bold: true, children: [agentName, " agent:", ' '] }), _jsx(Text, { color: colors.white, wrap: "wrap", children: item.text })] })), item.type === 'chat-error' && (_jsx(Box, { children: _jsxs(Text, { color: colors.red, children: [symbols.cross, " ", item.text] }) }))] }, i))), chatStreaming && chatBuffer && (_jsxs(Box, { children: [_jsxs(Text, { color: colors.honey, bold: true, children: [agentName, " agent:", ' '] }), _jsx(Text, { color: colors.white, wrap: "wrap", children: chatBuffer })] }))] })] })), _jsx(Box, { children: _jsxs(Text, { color: colors.gray, children: [isInteractive ? border.teeLeft : border.bottomLeft, border.horizontal.repeat(boxWidth - 2), isInteractive ? border.teeRight : border.bottomRight] }) }), isInteractive && (_jsxs(_Fragment, { children: [_jsx(Box, { paddingLeft: 1, children: _jsx(CommandInput, { value: input, onChange: setInput, onSubmit: (val) => {
|
|
119
|
-
setInput('');
|
|
120
|
-
void handleChatSubmit(val);
|
|
121
|
-
}, placeholder: chatStreaming ? 'thinking...' : `chat with ${agentName} agent...` }) }), _jsx(Box, { children: _jsxs(Text, { color: colors.gray, children: [border.bottomLeft, border.horizontal.repeat(boxWidth - 2), border.bottomRight] }) })] }))] }));
|
|
122
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export const SLASH_COMMANDS = [
|
|
2
|
-
{ name: '/skills', description: 'List available skills' },
|
|
3
|
-
{ name: '/help', description: 'Show available commands' },
|
|
4
|
-
{ name: '/clear', description: 'Clear chat history' },
|
|
5
|
-
{ name: '/memory', description: 'Show current memory state' },
|
|
6
|
-
{ name: '/backtest', description: 'Run agent against test set (/backtest <num> fetches from API)' },
|
|
7
|
-
];
|
|
8
|
-
export function filterCommands(prefix) {
|
|
9
|
-
const lowerPrefix = prefix.toLowerCase();
|
|
10
|
-
const filtered = SLASH_COMMANDS.filter((cmd) => cmd.name.toLowerCase().startsWith(lowerPrefix));
|
|
11
|
-
return filtered;
|
|
12
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useEffect } from 'react';
|
|
3
|
-
import { Box, Text } from 'ink';
|
|
4
|
-
import { colors, animation } from '../theme.js';
|
|
5
|
-
function buildTickerChars(step) {
|
|
6
|
-
const stepStr = String(step).padStart(2, '0');
|
|
7
|
-
const digits = stepStr.split('');
|
|
8
|
-
return animation.HEX_CHARS + digits.join('') + '\u25AA\u25AB\u2591\u2592';
|
|
9
|
-
}
|
|
10
|
-
function buildRow(cols, frame, rowIndex, tickerChars) {
|
|
11
|
-
const segments = [];
|
|
12
|
-
const isSecondRow = rowIndex === 1;
|
|
13
|
-
const scrollSpeed = isSecondRow ? 3 : 2;
|
|
14
|
-
const direction = isSecondRow ? -1 : 1;
|
|
15
|
-
const sinFreq = isSecondRow ? 0.4 : 0.3;
|
|
16
|
-
const sinPhase = isSecondRow ? -0.4 : 0.6;
|
|
17
|
-
const wrapLen = cols * 2;
|
|
18
|
-
for (let c = 0; c < cols; c++) {
|
|
19
|
-
const scrolledC = ((direction === 1)
|
|
20
|
-
? (c + frame * scrollSpeed) % wrapLen
|
|
21
|
-
: (cols - c + frame * scrollSpeed) % wrapLen);
|
|
22
|
-
const charIdx = scrolledC % tickerChars.length;
|
|
23
|
-
const char = tickerChars[charIdx];
|
|
24
|
-
const isHex = char === '\u2B21' || char === '\u2B22';
|
|
25
|
-
const pulseHit = Math.sin((c + frame * sinPhase) * sinFreq) > 0.5;
|
|
26
|
-
// Edge fade: dim the outermost 4 columns
|
|
27
|
-
const edgeDist = Math.min(c, cols - 1 - c);
|
|
28
|
-
if (edgeDist < 2) {
|
|
29
|
-
segments.push({ char: '\u00B7', color: colors.grayDim });
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
if (edgeDist < 4) {
|
|
33
|
-
segments.push({ char, color: colors.grayDim });
|
|
34
|
-
continue;
|
|
35
|
-
}
|
|
36
|
-
if (pulseHit && isHex) {
|
|
37
|
-
segments.push({ char, color: colors.honey });
|
|
38
|
-
}
|
|
39
|
-
else if (pulseHit) {
|
|
40
|
-
segments.push({ char, color: colors.green });
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
segments.push({ char, color: colors.grayDim });
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return segments;
|
|
47
|
-
}
|
|
48
|
-
function renderSegments(segments) {
|
|
49
|
-
const elements = [];
|
|
50
|
-
let runColor = segments[0]?.color ?? colors.grayDim;
|
|
51
|
-
let runChars = '';
|
|
52
|
-
for (let i = 0; i < segments.length; i++) {
|
|
53
|
-
const seg = segments[i];
|
|
54
|
-
if (seg.color === runColor) {
|
|
55
|
-
runChars += seg.char;
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
58
|
-
elements.push(_jsx(Text, { color: runColor, children: runChars }, `${elements.length}`));
|
|
59
|
-
runColor = seg.color;
|
|
60
|
-
runChars = seg.char;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
if (runChars.length > 0) {
|
|
64
|
-
elements.push(_jsx(Text, { color: runColor, children: runChars }, `${elements.length}`));
|
|
65
|
-
}
|
|
66
|
-
return elements;
|
|
67
|
-
}
|
|
68
|
-
export function AsciiTicker({ rows = 1, step = 1 }) {
|
|
69
|
-
const [frame, setFrame] = useState(0);
|
|
70
|
-
const cols = process.stdout.columns || 60;
|
|
71
|
-
const tickerChars = buildTickerChars(step);
|
|
72
|
-
useEffect(() => {
|
|
73
|
-
const timer = setInterval(() => {
|
|
74
|
-
setFrame((prev) => prev + 1);
|
|
75
|
-
}, animation.TICK_MS);
|
|
76
|
-
return () => {
|
|
77
|
-
clearInterval(timer);
|
|
78
|
-
};
|
|
79
|
-
}, []);
|
|
80
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: renderSegments(buildRow(cols, frame, 0, tickerChars)) }), rows === 2 && (_jsx(Text, { children: renderSegments(buildRow(cols, frame, 1, tickerChars)) }))] }));
|
|
81
|
-
}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useCallback, useMemo } from 'react';
|
|
3
|
-
import { Box, Text, useInput } from 'ink';
|
|
4
|
-
import TextInput from 'ink-text-input';
|
|
5
|
-
import { colors, symbols } from '../theme.js';
|
|
6
|
-
import { filterCommands } from '../commands/registry.js';
|
|
7
|
-
export function CommandInput({ value, onChange, onSubmit, placeholder, }) {
|
|
8
|
-
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
9
|
-
// Determine if autocomplete should be active
|
|
10
|
-
const isAutocompleteActive = value.startsWith('/') && !value.includes(' ');
|
|
11
|
-
// Get filtered commands
|
|
12
|
-
const filteredCommands = useMemo(() => {
|
|
13
|
-
if (!isAutocompleteActive) {
|
|
14
|
-
return [];
|
|
15
|
-
}
|
|
16
|
-
const commands = filterCommands(value);
|
|
17
|
-
return commands;
|
|
18
|
-
}, [value, isAutocompleteActive]);
|
|
19
|
-
// Reset selection when filtered commands change
|
|
20
|
-
const commandCount = filteredCommands.length;
|
|
21
|
-
const safeSelectedIndex = commandCount > 0 ? Math.min(selectedIndex, commandCount - 1) : 0;
|
|
22
|
-
const handleChange = useCallback((newValue) => {
|
|
23
|
-
onChange(newValue);
|
|
24
|
-
setSelectedIndex(0);
|
|
25
|
-
}, [onChange]);
|
|
26
|
-
const handleSubmit = useCallback((submittedValue) => {
|
|
27
|
-
// If autocomplete is active and there are matches, complete first
|
|
28
|
-
if (isAutocompleteActive && filteredCommands.length > 0) {
|
|
29
|
-
const selected = filteredCommands[safeSelectedIndex];
|
|
30
|
-
if (selected && submittedValue !== selected.name) {
|
|
31
|
-
onChange(selected.name);
|
|
32
|
-
onSubmit(selected.name);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
onSubmit(submittedValue);
|
|
37
|
-
}, [isAutocompleteActive, filteredCommands, safeSelectedIndex, onChange, onSubmit]);
|
|
38
|
-
useInput((input, key) => {
|
|
39
|
-
if (!isAutocompleteActive || filteredCommands.length === 0) {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
if (key.upArrow) {
|
|
43
|
-
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : commandCount - 1));
|
|
44
|
-
}
|
|
45
|
-
else if (key.downArrow) {
|
|
46
|
-
setSelectedIndex((prev) => (prev < commandCount - 1 ? prev + 1 : 0));
|
|
47
|
-
}
|
|
48
|
-
else if (key.tab) {
|
|
49
|
-
// Tab to complete without submitting
|
|
50
|
-
const selected = filteredCommands[safeSelectedIndex];
|
|
51
|
-
if (selected) {
|
|
52
|
-
onChange(selected.name);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
else if (key.escape) {
|
|
56
|
-
// Escape to clear input
|
|
57
|
-
onChange('');
|
|
58
|
-
setSelectedIndex(0);
|
|
59
|
-
}
|
|
60
|
-
}, { isActive: isAutocompleteActive && filteredCommands.length > 0 });
|
|
61
|
-
return (_jsxs(Box, { flexDirection: "column", children: [isAutocompleteActive && filteredCommands.length > 0 && (_jsx(Box, { flexDirection: "column", marginBottom: 0, marginLeft: 2, children: filteredCommands.map((cmd, index) => {
|
|
62
|
-
const isSelected = index === safeSelectedIndex;
|
|
63
|
-
return (_jsxs(Box, { children: [_jsxs(Text, { color: isSelected ? colors.honey : colors.gray, children: [isSelected ? symbols.diamond : ' ', ' '] }), _jsx(Text, { color: isSelected ? colors.honey : colors.white, children: cmd.name }), _jsxs(Text, { color: colors.gray, children: [" - ", cmd.description] })] }, cmd.name));
|
|
64
|
-
}) })), _jsxs(Box, { children: [_jsxs(Text, { color: colors.honey, children: [symbols.arrow, " "] }), _jsx(TextInput, { value: value, onChange: handleChange, onSubmit: handleSubmit, placeholder: placeholder })] })] }));
|
|
65
|
-
}
|
|
@@ -1,291 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { colors, animation } from '../theme.js';
|
|
3
|
-
const BOOT_TOTAL_FRAMES = 58;
|
|
4
|
-
const BOOT_FRAME_MS = 80;
|
|
5
|
-
const DURATION_MS = BOOT_TOTAL_FRAMES * BOOT_FRAME_MS;
|
|
6
|
-
const NUM_BEES = 4;
|
|
7
|
-
const NUM_STREAMS = 5;
|
|
8
|
-
const SCRAMBLE_CHARS = '\u2B21\u2B22\u25C6\u25C7\u2591\u2592!@#$%01';
|
|
9
|
-
const BOOT_MESSAGES = [
|
|
10
|
-
{ prefix: '\u2B21', text: 'Initializing {name} agent...', frame: 30 },
|
|
11
|
-
{ prefix: '\u25C6', text: 'Loading personality matrix...', frame: 36 },
|
|
12
|
-
{ prefix: '\u25C7', text: 'Connecting to the hive...', frame: 42 },
|
|
13
|
-
{ prefix: '\u2713', text: 'Neural link established', frame: 48 },
|
|
14
|
-
];
|
|
15
|
-
// ─── Private helpers ─────────────────────────────────
|
|
16
|
-
function isHexEdge(r, c) {
|
|
17
|
-
const rowInHex = ((r % animation.HEX_H) + animation.HEX_H) % animation.HEX_H;
|
|
18
|
-
const isOddHex = Math.floor(r / animation.HEX_H) % 2 === 1;
|
|
19
|
-
const colOffset = isOddHex ? animation.HEX_W / 2 : 0;
|
|
20
|
-
const colInHex = (((c - colOffset) % animation.HEX_W) + animation.HEX_W) % animation.HEX_W;
|
|
21
|
-
if (rowInHex === 0 || rowInHex === animation.HEX_H - 1) {
|
|
22
|
-
return colInHex >= 2 && colInHex <= 5;
|
|
23
|
-
}
|
|
24
|
-
if (rowInHex === 1 || rowInHex === 2) {
|
|
25
|
-
return colInHex === 1 || colInHex === 6;
|
|
26
|
-
}
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
29
|
-
// ─── Raw ANSI boot animation ────────────────────────
|
|
30
|
-
export function showHoneycombBoot(agentName) {
|
|
31
|
-
return new Promise((resolve) => {
|
|
32
|
-
const cols = process.stdout.columns || 60;
|
|
33
|
-
const gridRows = process.stdout.rows || 24;
|
|
34
|
-
let frame = 0;
|
|
35
|
-
// Init bees
|
|
36
|
-
const bees = [];
|
|
37
|
-
for (let i = 0; i < NUM_BEES; i++) {
|
|
38
|
-
bees.push({
|
|
39
|
-
r: Math.floor(Math.random() * gridRows),
|
|
40
|
-
c: Math.floor(Math.random() * cols),
|
|
41
|
-
vr: Math.random() > 0.5 ? 1 : -1,
|
|
42
|
-
vc: Math.random() > 0.5 ? 1 : -1,
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
// Init stream columns
|
|
46
|
-
const streamCols = [];
|
|
47
|
-
const spacing = Math.floor(cols / (NUM_STREAMS + 1));
|
|
48
|
-
for (let i = 1; i <= NUM_STREAMS; i++) {
|
|
49
|
-
streamCols.push(spacing * i);
|
|
50
|
-
}
|
|
51
|
-
let pulses = [];
|
|
52
|
-
// Text positioning
|
|
53
|
-
const centerR = Math.floor(gridRows / 2) - 2;
|
|
54
|
-
const centerC = Math.floor(cols / 2);
|
|
55
|
-
const nameText = `\u2B21 ${agentName} agent \u2B21`;
|
|
56
|
-
const nameStart = Math.max(0, centerC - Math.floor(nameText.length / 2));
|
|
57
|
-
const msgStartRow = centerR + 4;
|
|
58
|
-
// Quiet zone around text: no animation renders here
|
|
59
|
-
const PADDING_H = 3;
|
|
60
|
-
const PADDING_V = 1;
|
|
61
|
-
const longestMsg = BOOT_MESSAGES.reduce((max, m) => Math.max(max, m.prefix.length + 1 + m.text.replace('{name}', agentName).length), 0);
|
|
62
|
-
const msgLeftEdge = Math.floor((cols - longestMsg) / 2);
|
|
63
|
-
const msgRightEdge = msgLeftEdge + longestMsg;
|
|
64
|
-
const quietLeft = Math.min(nameStart, msgLeftEdge) - PADDING_H;
|
|
65
|
-
const quietRight = Math.max(nameStart + nameText.length, msgRightEdge) + PADDING_H;
|
|
66
|
-
const quietTop = (centerR - 1) - PADDING_V;
|
|
67
|
-
const quietBottom = msgStartRow + BOOT_MESSAGES.length + PADDING_V;
|
|
68
|
-
// Hide cursor
|
|
69
|
-
process.stdout.write('\x1b[?25l');
|
|
70
|
-
// Clear screen
|
|
71
|
-
process.stdout.write('\x1b[2J');
|
|
72
|
-
function renderFrame() {
|
|
73
|
-
// Move cursor to top-left
|
|
74
|
-
process.stdout.write('\x1b[H');
|
|
75
|
-
// Advance bees every other frame
|
|
76
|
-
if (frame > 0 && frame % 2 === 0) {
|
|
77
|
-
for (const bee of bees) {
|
|
78
|
-
bee.r += bee.vr;
|
|
79
|
-
bee.c += bee.vc;
|
|
80
|
-
if (bee.r <= 0 || bee.r >= gridRows - 1) {
|
|
81
|
-
bee.vr *= -1;
|
|
82
|
-
bee.r = Math.max(0, Math.min(gridRows - 1, bee.r));
|
|
83
|
-
}
|
|
84
|
-
if (bee.c <= 0 || bee.c >= cols - 1) {
|
|
85
|
-
bee.vc *= -1;
|
|
86
|
-
bee.c = Math.max(0, Math.min(cols - 1, bee.c));
|
|
87
|
-
}
|
|
88
|
-
if (Math.random() > 0.3) {
|
|
89
|
-
bee.vc = Math.random() > 0.5 ? 1 : -1;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
// Spawn pulses
|
|
94
|
-
if (frame % 4 === 0) {
|
|
95
|
-
for (let i = 0; i < 3; i++) {
|
|
96
|
-
const pr = Math.floor(Math.random() * gridRows);
|
|
97
|
-
const pc = Math.floor(Math.random() * cols);
|
|
98
|
-
if (isHexEdge(pr, pc)) {
|
|
99
|
-
const pulseColors = [colors.green, colors.red, colors.honey];
|
|
100
|
-
const color = pulseColors[Math.floor(Math.random() * pulseColors.length)];
|
|
101
|
-
pulses.push({ r: pr, c: pc, ttl: 8, color });
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
pulses = pulses.filter((p) => p.ttl > 0).map((p) => ({ ...p, ttl: p.ttl - 1 }));
|
|
105
|
-
}
|
|
106
|
-
// Build grid: char + color pairs
|
|
107
|
-
const charGrid = [];
|
|
108
|
-
const colorGrid = [];
|
|
109
|
-
for (let r = 0; r < gridRows; r++) {
|
|
110
|
-
const chars = [];
|
|
111
|
-
const clrs = [];
|
|
112
|
-
for (let c = 0; c < cols; c++) {
|
|
113
|
-
// Skip animation in quiet zone around text
|
|
114
|
-
const inQuietZone = r >= quietTop && r <= quietBottom && c >= quietLeft && c < quietRight;
|
|
115
|
-
if (inQuietZone) {
|
|
116
|
-
chars.push(' ');
|
|
117
|
-
clrs.push(colors.grayDim);
|
|
118
|
-
continue;
|
|
119
|
-
}
|
|
120
|
-
const hexEdge = isHexEdge(r, c);
|
|
121
|
-
// Scanning wave
|
|
122
|
-
const scanRow = frame % (gridRows + 6);
|
|
123
|
-
const dist = Math.abs(r - scanRow);
|
|
124
|
-
if (hexEdge && dist === 0) {
|
|
125
|
-
chars.push('\u2B22');
|
|
126
|
-
clrs.push(colors.honey);
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
if (hexEdge && dist <= 1) {
|
|
130
|
-
chars.push('\u2B21');
|
|
131
|
-
clrs.push(colors.honey);
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
// Data streams
|
|
135
|
-
let isStream = false;
|
|
136
|
-
if (frame >= 8) {
|
|
137
|
-
for (const sc of streamCols) {
|
|
138
|
-
if (c === sc) {
|
|
139
|
-
const streamOffset = ((frame - 8) * 2 + sc) % (gridRows * 3);
|
|
140
|
-
const streamDist = (((r - streamOffset) % gridRows) + gridRows) % gridRows;
|
|
141
|
-
if (streamDist < 6) {
|
|
142
|
-
const charIdx = (frame + r) % animation.DATA_CHARS.length;
|
|
143
|
-
const streamChar = animation.DATA_CHARS[charIdx];
|
|
144
|
-
chars.push(streamChar);
|
|
145
|
-
if (streamDist === 0) {
|
|
146
|
-
clrs.push(colors.white);
|
|
147
|
-
}
|
|
148
|
-
else if (streamDist < 3) {
|
|
149
|
-
clrs.push(colors.green);
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
clrs.push(colors.grayDim);
|
|
153
|
-
}
|
|
154
|
-
isStream = true;
|
|
155
|
-
break;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
if (isStream)
|
|
161
|
-
continue;
|
|
162
|
-
// Default
|
|
163
|
-
if (hexEdge) {
|
|
164
|
-
chars.push('\u00B7');
|
|
165
|
-
clrs.push(colors.grayDim);
|
|
166
|
-
}
|
|
167
|
-
else {
|
|
168
|
-
chars.push(' ');
|
|
169
|
-
clrs.push(colors.grayDim);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
charGrid.push(chars);
|
|
173
|
-
colorGrid.push(clrs);
|
|
174
|
-
}
|
|
175
|
-
// Overlay pulses (skip quiet zone)
|
|
176
|
-
for (const pulse of pulses) {
|
|
177
|
-
if (pulse.r >= 0 && pulse.r < gridRows && pulse.c >= 0 && pulse.c < cols) {
|
|
178
|
-
const inQuietZone = pulse.r >= quietTop &&
|
|
179
|
-
pulse.r <= quietBottom &&
|
|
180
|
-
pulse.c >= quietLeft &&
|
|
181
|
-
pulse.c < quietRight;
|
|
182
|
-
if (inQuietZone)
|
|
183
|
-
continue;
|
|
184
|
-
const brightness = pulse.ttl / 8;
|
|
185
|
-
const cell = charGrid[pulse.r][pulse.c];
|
|
186
|
-
if (cell === '\u00B7' || cell === ' ') {
|
|
187
|
-
charGrid[pulse.r][pulse.c] = brightness > 0.5 ? '\u2B21' : '\u00B7';
|
|
188
|
-
colorGrid[pulse.r][pulse.c] = pulse.color;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
// Overlay bees (skip quiet zone)
|
|
193
|
-
for (const bee of bees) {
|
|
194
|
-
const br = Math.max(0, Math.min(gridRows - 1, Math.round(bee.r)));
|
|
195
|
-
const bc = Math.max(0, Math.min(cols - 1, Math.round(bee.c)));
|
|
196
|
-
const inQuietZone = br >= quietTop && br <= quietBottom && bc >= quietLeft && bc < quietRight;
|
|
197
|
-
if (!inQuietZone) {
|
|
198
|
-
charGrid[br][bc] = '\u25C6';
|
|
199
|
-
colorGrid[br][bc] = colors.honey;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
// Overlay agent name with scramble→reveal effect
|
|
203
|
-
if (frame >= 22) {
|
|
204
|
-
const scrambleProgress = Math.min(1, (frame - 22) / 8);
|
|
205
|
-
// Top/bottom border lines around name
|
|
206
|
-
for (let c = nameStart; c < nameStart + nameText.length && c < cols; c++) {
|
|
207
|
-
if (centerR - 1 >= 0) {
|
|
208
|
-
charGrid[centerR - 1][c] = '\u2500';
|
|
209
|
-
colorGrid[centerR - 1][c] = colors.honey;
|
|
210
|
-
}
|
|
211
|
-
if (centerR + 1 < gridRows) {
|
|
212
|
-
charGrid[centerR + 1][c] = '\u2500';
|
|
213
|
-
colorGrid[centerR + 1][c] = colors.honey;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
// Name text with scramble effect
|
|
217
|
-
for (let i = 0; i < nameText.length; i++) {
|
|
218
|
-
const c = nameStart + i;
|
|
219
|
-
if (c >= cols)
|
|
220
|
-
break;
|
|
221
|
-
const charThreshold = i / nameText.length;
|
|
222
|
-
if (charThreshold <= scrambleProgress) {
|
|
223
|
-
charGrid[centerR][c] = nameText[i];
|
|
224
|
-
colorGrid[centerR][c] = colors.honey;
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
227
|
-
const scrambleIdx = Math.floor(Math.random() * SCRAMBLE_CHARS.length);
|
|
228
|
-
charGrid[centerR][c] = SCRAMBLE_CHARS[scrambleIdx];
|
|
229
|
-
colorGrid[centerR][c] = colors.gray;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
// Overlay typewriter boot messages
|
|
234
|
-
for (let idx = 0; idx < BOOT_MESSAGES.length; idx++) {
|
|
235
|
-
const msg = BOOT_MESSAGES[idx];
|
|
236
|
-
if (frame < msg.frame)
|
|
237
|
-
continue;
|
|
238
|
-
const r = msgStartRow + idx;
|
|
239
|
-
if (r < 0 || r >= gridRows)
|
|
240
|
-
continue;
|
|
241
|
-
const fullText = `${msg.prefix} ${msg.text.replace('{name}', agentName)}`;
|
|
242
|
-
const msgCol = Math.floor((cols - fullText.length) / 2);
|
|
243
|
-
const visibleChars = Math.min(fullText.length, (frame - msg.frame) * 3);
|
|
244
|
-
const isCheckmark = msg.prefix === '\u2713';
|
|
245
|
-
const msgColor = isCheckmark ? colors.green : colors.honey;
|
|
246
|
-
for (let i = 0; i < visibleChars; i++) {
|
|
247
|
-
const c = msgCol + i;
|
|
248
|
-
if (c < 0 || c >= cols)
|
|
249
|
-
continue;
|
|
250
|
-
charGrid[r][c] = fullText[i];
|
|
251
|
-
colorGrid[r][c] = msgColor;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
// Render to stdout
|
|
255
|
-
let output = '';
|
|
256
|
-
for (let r = 0; r < gridRows; r++) {
|
|
257
|
-
let line = '';
|
|
258
|
-
let runColor = colorGrid[r][0];
|
|
259
|
-
let runChars = '';
|
|
260
|
-
for (let c = 0; c < cols; c++) {
|
|
261
|
-
const curColor = colorGrid[r][c];
|
|
262
|
-
const curChar = charGrid[r][c];
|
|
263
|
-
if (curColor === runColor) {
|
|
264
|
-
runChars += curChar;
|
|
265
|
-
}
|
|
266
|
-
else {
|
|
267
|
-
line += chalk.hex(runColor)(runChars);
|
|
268
|
-
runColor = curColor;
|
|
269
|
-
runChars = curChar;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
if (runChars.length > 0) {
|
|
273
|
-
line += chalk.hex(runColor)(runChars);
|
|
274
|
-
}
|
|
275
|
-
output += line;
|
|
276
|
-
if (r < gridRows - 1) {
|
|
277
|
-
output += '\n';
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
process.stdout.write(output);
|
|
281
|
-
frame++;
|
|
282
|
-
}
|
|
283
|
-
const timer = setInterval(renderFrame, BOOT_FRAME_MS);
|
|
284
|
-
setTimeout(() => {
|
|
285
|
-
clearInterval(timer);
|
|
286
|
-
// Clear screen, show cursor, move to top
|
|
287
|
-
process.stdout.write('\x1b[2J\x1b[H\x1b[?25h');
|
|
288
|
-
resolve();
|
|
289
|
-
}, DURATION_MS);
|
|
290
|
-
});
|
|
291
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useEffect } from 'react';
|
|
3
|
-
import { Text } from 'ink';
|
|
4
|
-
import { colors } from '../theme.js';
|
|
5
|
-
const SPINNER_FRAMES = ['\u25D0', '\u25D3', '\u25D1', '\u25D2'];
|
|
6
|
-
export function Spinner({ label }) {
|
|
7
|
-
const [frame, setFrame] = useState(0);
|
|
8
|
-
useEffect(() => {
|
|
9
|
-
const timer = setInterval(() => {
|
|
10
|
-
setFrame((prev) => (prev + 1) % SPINNER_FRAMES.length);
|
|
11
|
-
}, 120);
|
|
12
|
-
return () => {
|
|
13
|
-
clearInterval(timer);
|
|
14
|
-
};
|
|
15
|
-
}, []);
|
|
16
|
-
return (_jsxs(Text, { children: [_jsx(Text, { color: colors.honey, children: SPINNER_FRAMES[frame] }), _jsxs(Text, { color: colors.gray, children: [" ", label] })] }));
|
|
17
|
-
}
|
|
18
|
-
export function TypewriterText({ text, color, speed = 25, }) {
|
|
19
|
-
const [visible, setVisible] = useState(0);
|
|
20
|
-
useEffect(() => {
|
|
21
|
-
if (visible >= text.length)
|
|
22
|
-
return;
|
|
23
|
-
const timer = setTimeout(() => {
|
|
24
|
-
setVisible((prev) => Math.min(prev + 2, text.length));
|
|
25
|
-
}, speed);
|
|
26
|
-
return () => {
|
|
27
|
-
clearTimeout(timer);
|
|
28
|
-
};
|
|
29
|
-
}, [visible, text, speed]);
|
|
30
|
-
return _jsx(Text, { color: color, children: text.slice(0, visible) });
|
|
31
|
-
}
|
|
32
|
-
export function PollText({ text, color, animate, }) {
|
|
33
|
-
if (animate) {
|
|
34
|
-
return _jsx(TypewriterText, { text: text, color: color });
|
|
35
|
-
}
|
|
36
|
-
return _jsx(Text, { color: color, children: text });
|
|
37
|
-
}
|