dexterm-cli 0.0.0-stage → 0.1.2
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/LICENSE +661 -0
- package/dist/packages/agents/src/audit.js +122 -0
- package/dist/packages/agents/src/bus.js +394 -0
- package/dist/packages/agents/src/executor.js +202 -0
- package/dist/packages/agents/src/gate.js +179 -0
- package/dist/packages/agents/src/identity.js +218 -0
- package/dist/packages/agents/src/index.js +15 -0
- package/dist/packages/agents/src/proposals.js +153 -0
- package/dist/packages/agents/src/schema.js +87 -0
- package/dist/packages/agents/src/sdk.js +140 -0
- package/dist/packages/agents/src/tiers.js +119 -0
- package/dist/packages/agents/src/tools.js +72 -0
- package/dist/packages/agents/src/ws.js +158 -0
- package/dist/packages/cli/assets/claude/analyst-system.md +76 -0
- package/dist/packages/cli/assets/claude/plugin/.claude-plugin/plugin.json +5 -0
- package/dist/packages/cli/assets/claude/plugin/skills/REGISTRY.json +16 -0
- package/dist/packages/cli/assets/claude/plugin/skills/larp-check/SKILL.md +75 -0
- package/dist/packages/cli/assets/claude/plugin/skills/rug-check/SKILL.md +74 -0
- package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +63 -0
- package/dist/packages/cli/assets/claude/plugin/skills/trench-scan/SKILL.md +44 -0
- package/dist/packages/cli/assets/claude/schemas/larp.schema.json +117 -0
- package/dist/packages/cli/assets/claude/schemas/trench-scan.schema.json +71 -0
- package/dist/packages/cli/assets/claude/schemas/verdict.schema.json +115 -0
- package/dist/packages/cli/bin/dexterm.mjs +11 -0
- package/dist/packages/cli/src/agent-cmd.js +336 -0
- package/dist/packages/cli/src/agent-roster.js +26 -0
- package/dist/packages/cli/src/alerts/adapters/desktop.js +40 -0
- package/dist/packages/cli/src/alerts/adapters/discord.js +36 -0
- package/dist/packages/cli/src/alerts/adapters/telegram.js +43 -0
- package/dist/packages/cli/src/alerts/adapters/toast.js +16 -0
- package/dist/packages/cli/src/alerts/creds.js +83 -0
- package/dist/packages/cli/src/alerts/format.js +41 -0
- package/dist/packages/cli/src/alerts/index.js +10 -0
- package/dist/packages/cli/src/alerts/notifier.js +53 -0
- package/dist/packages/cli/src/alerts/route.js +19 -0
- package/dist/packages/cli/src/alerts/types.js +1 -0
- package/dist/packages/cli/src/alerts-cmd.js +212 -0
- package/dist/packages/cli/src/analyst/backend.js +526 -0
- package/dist/packages/cli/src/analyst/results.js +38 -0
- package/dist/packages/cli/src/analyst/session.js +319 -0
- package/dist/packages/cli/src/audit.js +51 -0
- package/dist/packages/cli/src/backtest-cmd.js +208 -0
- package/dist/packages/cli/src/boot-state.js +62 -0
- package/dist/packages/cli/src/coin-target.js +2 -0
- package/dist/packages/cli/src/custom.js +261 -0
- package/dist/packages/cli/src/display.js +191 -0
- package/dist/packages/cli/src/doctor.js +375 -0
- package/dist/packages/cli/src/export.js +235 -0
- package/dist/packages/cli/src/feed.js +634 -0
- package/dist/packages/cli/src/fixtures.js +275 -0
- package/dist/packages/cli/src/follow-cmd.js +221 -0
- package/dist/packages/cli/src/images-config.js +59 -0
- package/dist/packages/cli/src/index.js +31 -0
- package/dist/packages/cli/src/kb-skill-cmd.js +240 -0
- package/dist/packages/cli/src/larp-record.js +21 -0
- package/dist/packages/cli/src/live/copy.js +80 -0
- package/dist/packages/cli/src/live/flow.js +233 -0
- package/dist/packages/cli/src/live/index.js +511 -0
- package/dist/packages/cli/src/live/intel.js +227 -0
- package/dist/packages/cli/src/live/newpairs.js +334 -0
- package/dist/packages/cli/src/live/screens/newpairs-live.js +219 -0
- package/dist/packages/cli/src/live/screens/token-live.js +459 -0
- package/dist/packages/cli/src/live/screens/watchlist-live.js +149 -0
- package/dist/packages/cli/src/live/token.js +585 -0
- package/dist/packages/cli/src/live/trade.js +929 -0
- package/dist/packages/cli/src/live/watchlist.js +178 -0
- package/dist/packages/cli/src/main.js +5785 -0
- package/dist/packages/cli/src/mcp-cmd.js +326 -0
- package/dist/packages/cli/src/narrative-cmd.js +76 -0
- package/dist/packages/cli/src/pack-cmd.js +323 -0
- package/dist/packages/cli/src/plan-cmd.js +278 -0
- package/dist/packages/cli/src/popup.js +810 -0
- package/dist/packages/cli/src/rules-cmd.js +131 -0
- package/dist/packages/cli/src/rules.js +358 -0
- package/dist/packages/cli/src/scan/command.js +57 -0
- package/dist/packages/cli/src/scan/engine.js +419 -0
- package/dist/packages/cli/src/scan/observe.js +64 -0
- package/dist/packages/cli/src/scan/ui.js +98 -0
- package/dist/packages/cli/src/screener/filter.js +241 -0
- package/dist/packages/cli/src/screener/presets.js +80 -0
- package/dist/packages/cli/src/screener/rows.js +146 -0
- package/dist/packages/cli/src/screener/rug.js +50 -0
- package/dist/packages/cli/src/screener/sort.js +52 -0
- package/dist/packages/cli/src/screener/view.js +183 -0
- package/dist/packages/cli/src/screens/analyst-session.js +343 -0
- package/dist/packages/cli/src/screens/analyst.js +326 -0
- package/dist/packages/cli/src/screens/boot.js +74 -0
- package/dist/packages/cli/src/screens/bubbles.js +255 -0
- package/dist/packages/cli/src/screens/coin.js +33 -0
- package/dist/packages/cli/src/screens/follows.js +201 -0
- package/dist/packages/cli/src/screens/kol.js +90 -0
- package/dist/packages/cli/src/screens/narrative.js +90 -0
- package/dist/packages/cli/src/screens/newpairs.js +150 -0
- package/dist/packages/cli/src/screens/overlay.js +257 -0
- package/dist/packages/cli/src/screens/plans.js +126 -0
- package/dist/packages/cli/src/screens/positions.js +176 -0
- package/dist/packages/cli/src/screens/rules.js +170 -0
- package/dist/packages/cli/src/screens/screener.js +459 -0
- package/dist/packages/cli/src/screens/social-panel.js +107 -0
- package/dist/packages/cli/src/screens/ticket.js +342 -0
- package/dist/packages/cli/src/screens/token.js +312 -0
- package/dist/packages/cli/src/screens/trending.js +181 -0
- package/dist/packages/cli/src/screens/watchlist.js +104 -0
- package/dist/packages/cli/src/skill-eval-build.js +109 -0
- package/dist/packages/cli/src/social/demo.js +96 -0
- package/dist/packages/cli/src/social/view.js +250 -0
- package/dist/packages/cli/src/state.js +463 -0
- package/dist/packages/cli/src/theme-cmd.js +126 -0
- package/dist/packages/cli/src/trade.js +319 -0
- package/dist/packages/cli/src/update.js +294 -0
- package/dist/packages/cli/src/version.js +3 -0
- package/dist/packages/cli/src/voice-cmd.js +119 -0
- package/dist/packages/cli/src/voice-hud.js +42 -0
- package/dist/packages/cli/src/wallet-cmd.js +440 -0
- package/dist/packages/cli/src/wallet-command.js +31 -0
- package/dist/packages/cli/src/watch-cmd.js +190 -0
- package/dist/packages/cli/src/widgets.js +208 -0
- package/dist/packages/cli/src/workspace-cmd.js +68 -0
- package/dist/packages/cli/src/x-cmd.js +154 -0
- package/dist/packages/custom/src/config.js +213 -0
- package/dist/packages/custom/src/index.js +10 -0
- package/dist/packages/custom/src/keymap.js +107 -0
- package/dist/packages/custom/src/layout.js +235 -0
- package/dist/packages/custom/src/packs.js +633 -0
- package/dist/packages/custom/src/rules.js +704 -0
- package/dist/packages/custom/src/schema.js +538 -0
- package/dist/packages/custom/src/themes.js +176 -0
- package/dist/packages/custom/src/widget-html.js +222 -0
- package/dist/packages/custom/src/workspace.js +141 -0
- package/dist/packages/data/src/analyst-actions.js +133 -0
- package/dist/packages/data/src/budget.js +88 -0
- package/dist/packages/data/src/cache.js +142 -0
- package/dist/packages/data/src/candles.js +168 -0
- package/dist/packages/data/src/clock.js +64 -0
- package/dist/packages/data/src/coin-target.js +82 -0
- package/dist/packages/data/src/credits.js +137 -0
- package/dist/packages/data/src/detail.js +97 -0
- package/dist/packages/data/src/fast-lane.js +321 -0
- package/dist/packages/data/src/fixtures.js +135 -0
- package/dist/packages/data/src/health.js +245 -0
- package/dist/packages/data/src/http.js +277 -0
- package/dist/packages/data/src/images/cache.js +141 -0
- package/dist/packages/data/src/images/decode-worker.js +28 -0
- package/dist/packages/data/src/images/decode.js +81 -0
- package/dist/packages/data/src/images/fetch.js +162 -0
- package/dist/packages/data/src/images/gif.js +214 -0
- package/dist/packages/data/src/images/index.js +10 -0
- package/dist/packages/data/src/images/jpeg.js +544 -0
- package/dist/packages/data/src/images/png.js +310 -0
- package/dist/packages/data/src/images/service.js +334 -0
- package/dist/packages/data/src/images/types.js +27 -0
- package/dist/packages/data/src/index.js +211 -0
- package/dist/packages/data/src/intel/helius-history.js +70 -0
- package/dist/packages/data/src/intel/holder-class.js +125 -0
- package/dist/packages/data/src/intel/rules.js +421 -0
- package/dist/packages/data/src/intel/scan.js +170 -0
- package/dist/packages/data/src/larp/evidence.js +288 -0
- package/dist/packages/data/src/larp/link-baseline.js +196 -0
- package/dist/packages/data/src/larp/links.js +137 -0
- package/dist/packages/data/src/larp/robots.js +90 -0
- package/dist/packages/data/src/larp/safe-fetch.js +389 -0
- package/dist/packages/data/src/limiter.js +341 -0
- package/dist/packages/data/src/model.js +12 -0
- package/dist/packages/data/src/paper.js +331 -0
- package/dist/packages/data/src/rpc-config.js +126 -0
- package/dist/packages/data/src/rpc-pool.js +166 -0
- package/dist/packages/data/src/social/chain.js +83 -0
- package/dist/packages/data/src/social/dedupe.js +205 -0
- package/dist/packages/data/src/social/feed.js +462 -0
- package/dist/packages/data/src/social/firewall.js +110 -0
- package/dist/packages/data/src/social/index.js +11 -0
- package/dist/packages/data/src/social/kol.js +269 -0
- package/dist/packages/data/src/social/model.js +11 -0
- package/dist/packages/data/src/social/rank.js +184 -0
- package/dist/packages/data/src/social/rss.js +113 -0
- package/dist/packages/data/src/social/summary.js +104 -0
- package/dist/packages/data/src/social/venues.js +186 -0
- package/dist/packages/data/src/sources/dexscreener.js +184 -0
- package/dist/packages/data/src/sources/geckoterminal.js +133 -0
- package/dist/packages/data/src/sources/goplus.js +161 -0
- package/dist/packages/data/src/sources/jupiter-swap.js +236 -0
- package/dist/packages/data/src/sources/jupiter.js +198 -0
- package/dist/packages/data/src/sources/pda.js +113 -0
- package/dist/packages/data/src/sources/pump-curve.js +58 -0
- package/dist/packages/data/src/sources/pumpportal.js +252 -0
- package/dist/packages/data/src/sources/rugcheck.js +71 -0
- package/dist/packages/data/src/sources/solana-rpc.js +299 -0
- package/dist/packages/data/src/sources/solana-ws.js +287 -0
- package/dist/packages/data/src/store.js +289 -0
- package/dist/packages/data/src/trade/bridge.js +464 -0
- package/dist/packages/data/src/trade/caps.js +323 -0
- package/dist/packages/data/src/trade/copy/auto.js +385 -0
- package/dist/packages/data/src/trade/copy/follows.js +103 -0
- package/dist/packages/data/src/trade/copy/index.js +9 -0
- package/dist/packages/data/src/trade/copy/mirror.js +91 -0
- package/dist/packages/data/src/trade/copy/stats.js +120 -0
- package/dist/packages/data/src/trade/copy/tracker.js +158 -0
- package/dist/packages/data/src/trade/copy/trades.js +87 -0
- package/dist/packages/data/src/trade/copy/types.js +6 -0
- package/dist/packages/data/src/trade/executor.js +933 -0
- package/dist/packages/data/src/trade/kill-switch.js +47 -0
- package/dist/packages/data/src/trade/link-store.js +52 -0
- package/dist/packages/data/src/trade/plans/confirm.js +157 -0
- package/dist/packages/data/src/trade/plans/engine.js +784 -0
- package/dist/packages/data/src/trade/plans/executor-live.js +220 -0
- package/dist/packages/data/src/trade/plans/executor-paper.js +44 -0
- package/dist/packages/data/src/trade/plans/executor.js +11 -0
- package/dist/packages/data/src/trade/plans/index.js +12 -0
- package/dist/packages/data/src/trade/plans/journal.js +77 -0
- package/dist/packages/data/src/trade/plans/mandate.js +129 -0
- package/dist/packages/data/src/trade/plans/plan.js +179 -0
- package/dist/packages/data/src/trade/plans/service.js +168 -0
- package/dist/packages/data/src/trade/plans/types.js +6 -0
- package/dist/packages/data/src/trade/rpc-exec.js +229 -0
- package/dist/packages/data/src/trade/signer/client.js +197 -0
- package/dist/packages/data/src/trade/signer/fast-wallet.js +154 -0
- package/dist/packages/data/src/trade/signer/host.js +97 -0
- package/dist/packages/data/src/trade/signer/main.js +121 -0
- package/dist/packages/data/src/trade/signer/probe-builder.js +85 -0
- package/dist/packages/data/src/trade/signer/protocol.js +92 -0
- package/dist/packages/data/src/trade/signer/service.js +400 -0
- package/dist/packages/data/src/trade/signer/sol-usd-feed.js +98 -0
- package/dist/packages/data/src/trade/sol-usd.js +86 -0
- package/dist/packages/data/src/trade/swap-build.js +108 -0
- package/dist/packages/data/src/trade/token-delta.js +201 -0
- package/dist/packages/data/src/trade/tx-decode.js +632 -0
- package/dist/packages/data/src/util.js +24 -0
- package/dist/packages/data/src/watchlist.js +176 -0
- package/dist/packages/data/src/x/api.js +256 -0
- package/dist/packages/data/src/x/budget.js +66 -0
- package/dist/packages/data/src/x/config.js +49 -0
- package/dist/packages/data/src/x/index.js +10 -0
- package/dist/packages/data/src/x/mock-server.js +217 -0
- package/dist/packages/data/src/x/oauth.js +242 -0
- package/dist/packages/data/src/x/pkce.js +21 -0
- package/dist/packages/data/src/x/source.js +285 -0
- package/dist/packages/data/src/x/token-store.js +134 -0
- package/dist/packages/data/src/x/transport.js +71 -0
- package/dist/packages/data/vendor/solana-web3/LICENSE +20 -0
- package/dist/packages/data/vendor/solana-web3/NOTICE.md +13 -0
- package/dist/packages/data/vendor/solana-web3/index.iife.min.js +20 -0
- package/dist/packages/mcp/assets/cases.json +1194 -0
- package/dist/packages/mcp/assets/kb/entries/cabal-and-coordinated-launches.md +28 -0
- package/dist/packages/mcp/assets/kb/entries/common-rug-patterns.md +40 -0
- package/dist/packages/mcp/assets/kb/entries/dbc-mechanics.md +30 -0
- package/dist/packages/mcp/assets/kb/entries/dev-wallet-behaviour.md +33 -0
- package/dist/packages/mcp/assets/kb/entries/glossary.md +35 -0
- package/dist/packages/mcp/assets/kb/entries/holder-concentration.md +30 -0
- package/dist/packages/mcp/assets/kb/entries/jito-bundles.md +29 -0
- package/dist/packages/mcp/assets/kb/entries/launchlab-curve.md +31 -0
- package/dist/packages/mcp/assets/kb/entries/lp-burn-vs-lock.md +31 -0
- package/dist/packages/mcp/assets/kb/entries/metadata-and-copycats.md +29 -0
- package/dist/packages/mcp/assets/kb/entries/mint-freeze-authority.md +31 -0
- package/dist/packages/mcp/assets/kb/entries/narrative-lifecycle.md +32 -0
- package/dist/packages/mcp/assets/kb/entries/pump-bonding-curve.md +33 -0
- package/dist/packages/mcp/assets/kb/entries/pumpswap-migration.md +29 -0
- package/dist/packages/mcp/assets/kb/entries/sniping-and-snipers.md +29 -0
- package/dist/packages/mcp/assets/kb/entries/token2022-extensions-risk.md +32 -0
- package/dist/packages/mcp/assets/kb/entries/wash-trading-fake-volume.md +33 -0
- package/dist/packages/mcp/assets/skills/rug-check.md +52 -0
- package/dist/packages/mcp/assets/skills/token-dossier.md +57 -0
- package/dist/packages/mcp/assets/skills/trench-scan.md +40 -0
- package/dist/packages/mcp/bin/dexterm-mcp.mjs +16 -0
- package/dist/packages/mcp/src/dossier.js +78 -0
- package/dist/packages/mcp/src/firewall.js +21 -0
- package/dist/packages/mcp/src/index.js +22 -0
- package/dist/packages/mcp/src/server.js +936 -0
- package/dist/packages/mcp/src/user-content.js +434 -0
- package/dist/packages/packs/src/backtest.js +183 -0
- package/dist/packages/packs/src/digest.js +95 -0
- package/dist/packages/packs/src/firewall.js +57 -0
- package/dist/packages/packs/src/index.js +20 -0
- package/dist/packages/packs/src/larp-eval.js +100 -0
- package/dist/packages/packs/src/narrative.js +309 -0
- package/dist/packages/packs/src/reason.js +135 -0
- package/dist/packages/packs/src/rug-model.js +137 -0
- package/dist/packages/packs/src/schema.js +54 -0
- package/dist/packages/packs/src/skill-backtest.js +300 -0
- package/dist/packages/packs/src/token-dossier.js +174 -0
- package/dist/packages/packs/src/verdicts.js +17 -0
- package/dist/packages/packs/src/wallet-dossier.js +105 -0
- package/dist/packages/popup/src/config.js +71 -0
- package/dist/packages/popup/src/dock.js +271 -0
- package/dist/packages/popup/src/host.js +210 -0
- package/dist/packages/popup/src/index.js +12 -0
- package/dist/packages/popup/src/loopback.js +53 -0
- package/dist/packages/popup/src/open.js +183 -0
- package/dist/packages/popup/src/pages/bubbles-lib.js +160 -0
- package/dist/packages/popup/src/pages/bubbles.css +23 -0
- package/dist/packages/popup/src/pages/bubbles.html +36 -0
- package/dist/packages/popup/src/pages/bubbles.js +214 -0
- package/dist/packages/popup/src/pages/chart-lib.js +262 -0
- package/dist/packages/popup/src/pages/chart.css +50 -0
- package/dist/packages/popup/src/pages/chart.html +56 -0
- package/dist/packages/popup/src/pages/chart.js +554 -0
- package/dist/packages/popup/src/pages/common.css +34 -0
- package/dist/packages/popup/src/pages/common.js +187 -0
- package/dist/packages/popup/src/pages/feeds.html +20 -0
- package/dist/packages/popup/src/pages/feeds.js +77 -0
- package/dist/packages/popup/src/pages/narrative.html +20 -0
- package/dist/packages/popup/src/pages/narrative.js +61 -0
- package/dist/packages/popup/src/pages/plans.css +30 -0
- package/dist/packages/popup/src/pages/plans.html +26 -0
- package/dist/packages/popup/src/pages/plans.js +82 -0
- package/dist/packages/popup/src/pages/roster.css +41 -0
- package/dist/packages/popup/src/pages/roster.html +25 -0
- package/dist/packages/popup/src/pages/roster.js +75 -0
- package/dist/packages/popup/src/pages/token.html +20 -0
- package/dist/packages/popup/src/pages/token.js +112 -0
- package/dist/packages/popup/src/pages/widget-sdk.js +224 -0
- package/dist/packages/popup/src/pages/widget.css +5 -0
- package/dist/packages/popup/src/pages/widget.html +21 -0
- package/dist/packages/popup/src/pages/widget.js +85 -0
- package/dist/packages/popup/src/protocol.js +31 -0
- package/dist/packages/popup/src/selftest.js +156 -0
- package/dist/packages/popup/src/server.js +582 -0
- package/dist/packages/popup/src/sinkproxy.js +167 -0
- package/dist/packages/popup/src/widget-html.js +231 -0
- package/dist/packages/popup/vendor/lightweight-charts/LICENSE +201 -0
- package/dist/packages/popup/vendor/lightweight-charts/NOTICE +2 -0
- package/dist/packages/popup/vendor/lightweight-charts/README.md +30 -0
- package/dist/packages/popup/vendor/lightweight-charts/VERSION +1 -0
- package/dist/packages/popup/vendor/lightweight-charts/lightweight-charts.standalone.production.js +7 -0
- package/dist/packages/recorder/bin/dexterm-recorder.mjs +11 -0
- package/dist/packages/recorder/src/cli.js +113 -0
- package/dist/packages/recorder/src/export.js +71 -0
- package/dist/packages/recorder/src/index.js +20 -0
- package/dist/packages/recorder/src/labels.js +151 -0
- package/dist/packages/recorder/src/open.js +29 -0
- package/dist/packages/recorder/src/recorder.js +553 -0
- package/dist/packages/recorder/src/schedule.js +43 -0
- package/dist/packages/recorder/src/schema.js +135 -0
- package/dist/packages/recorder/src/snapshot.js +191 -0
- package/dist/packages/recorder/src/sweep.js +52 -0
- package/dist/packages/recorder/src/verdicts.js +52 -0
- package/dist/packages/tui/src/bubbles.js +415 -0
- package/dist/packages/tui/src/canvas.js +370 -0
- package/dist/packages/tui/src/chrome.js +85 -0
- package/dist/packages/tui/src/color.js +133 -0
- package/dist/packages/tui/src/flush.js +62 -0
- package/dist/packages/tui/src/fmt.js +188 -0
- package/dist/packages/tui/src/fx-trade.js +278 -0
- package/dist/packages/tui/src/fx.js +527 -0
- package/dist/packages/tui/src/images/blocks.js +141 -0
- package/dist/packages/tui/src/images/coin.js +22 -0
- package/dist/packages/tui/src/images/detect.js +140 -0
- package/dist/packages/tui/src/images/index.js +7 -0
- package/dist/packages/tui/src/images/layer.js +146 -0
- package/dist/packages/tui/src/images/pixels.js +157 -0
- package/dist/packages/tui/src/images/protocols.js +87 -0
- package/dist/packages/tui/src/index.js +18 -0
- package/dist/packages/tui/src/keymap.js +357 -0
- package/dist/packages/tui/src/layout.js +11 -0
- package/dist/packages/tui/src/raster-font.js +195 -0
- package/dist/packages/tui/src/raster.js +213 -0
- package/dist/packages/tui/src/sound.js +177 -0
- package/dist/packages/tui/src/theme.js +182 -0
- package/dist/packages/tui/src/widgets.js +598 -0
- package/dist/packages/voice/src/controller.js +407 -0
- package/dist/packages/voice/src/doctor.js +131 -0
- package/dist/packages/voice/src/download.js +184 -0
- package/dist/packages/voice/src/engines.js +84 -0
- package/dist/packages/voice/src/index.js +20 -0
- package/dist/packages/voice/src/router.js +502 -0
- package/dist/packages/voice/src/stt/apple-helper.swift.js +103 -0
- package/dist/packages/voice/src/stt/apple.js +179 -0
- package/dist/packages/voice/src/stt/fake.js +34 -0
- package/dist/packages/voice/src/stt/whisper.js +112 -0
- package/dist/packages/voice/src/summary.js +44 -0
- package/dist/packages/voice/src/tts/espeak.js +131 -0
- package/dist/packages/voice/src/tts/fake.js +30 -0
- package/dist/packages/voice/src/tts/piper.js +206 -0
- package/dist/packages/voice/src/tts/say.js +132 -0
- package/dist/packages/voice/src/tts/voices.js +206 -0
- package/dist/packages/voice/src/types.js +3 -0
- package/package.json +38 -4
- package/README.md +0 -3
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
// @dexterm/tui — widgets drawn onto a Canvas (SPEC §7.3, VISUAL-V2 §3 "viz"): neon gradient
|
|
2
|
+
// sparkline, area sparkline with halo, glowing candles, gradient bars, badges, heat cells with ▲/▼,
|
|
3
|
+
// priority-dropping table, stat grid, key hints, trade tape rain.
|
|
4
|
+
// Every glyph is single-column. Numbers are always drawn with the theme's `text` colour on the local
|
|
5
|
+
// background (hard cells); glow is only ever applied to neighbouring blank cells or to bg tints.
|
|
6
|
+
import { Canvas } from "./canvas.js";
|
|
7
|
+
import { mix, luminance } from "./color.js";
|
|
8
|
+
import { fmtUsd, fmtPct, fmtNum, fmtPrice, fmtAgeSec, shortAddr, clamp } from "./fmt.js";
|
|
9
|
+
import { launchpadColor } from "./theme.js";
|
|
10
|
+
export const BLOCKS = '▁▂▃▄▅▆▇█';
|
|
11
|
+
// braille dot bits: column 0 rows 0..3, column 1 rows 0..3 (row 0 = top)
|
|
12
|
+
const BR_L = [0x01, 0x02, 0x04, 0x40];
|
|
13
|
+
const BR_R = [0x08, 0x10, 0x20, 0x80];
|
|
14
|
+
export function resample(values, w) {
|
|
15
|
+
if (values.length === w)
|
|
16
|
+
return values.slice();
|
|
17
|
+
const out = [];
|
|
18
|
+
for (let i = 0; i < w; i++) {
|
|
19
|
+
const p = (i / Math.max(1, w - 1)) * (values.length - 1);
|
|
20
|
+
const a = Math.floor(p), b = Math.min(values.length - 1, a + 1);
|
|
21
|
+
out.push(values[a] + (values[b] - values[a]) * (p - a));
|
|
22
|
+
}
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
/** Neon gradient: dim at the left, saturated in the middle, 45 % toward white at the recent end. */
|
|
26
|
+
export function neon(color, u, bg) {
|
|
27
|
+
if (u < 0.6)
|
|
28
|
+
return mix(mix(bg, color, 0.65), color, u / 0.6);
|
|
29
|
+
return mix(color, '#ffffff', (u - 0.6) / 0.4 * 0.45);
|
|
30
|
+
}
|
|
31
|
+
/** One-row sparkline. Block levels, neon gradient along time, hot last point. */
|
|
32
|
+
export function sparkline(c, x, y, w, values, opts = {}) {
|
|
33
|
+
const t = c.theme;
|
|
34
|
+
if (!values || values.length < 2 || w < 2)
|
|
35
|
+
return;
|
|
36
|
+
const v = resample(values, w);
|
|
37
|
+
const lo = Math.min(...v), hi = Math.max(...v);
|
|
38
|
+
const range = hi - lo || 1;
|
|
39
|
+
const up = v[v.length - 1] >= v[0];
|
|
40
|
+
const color = opts.color || (up ? t.sparkUp : t.sparkDown);
|
|
41
|
+
const bg = opts.bg || t.panel;
|
|
42
|
+
for (let i = 0; i < w; i++) {
|
|
43
|
+
const lvl = Math.round(((v[i] - lo) / range) * 7);
|
|
44
|
+
const u = i / (w - 1);
|
|
45
|
+
c.put(x + i, y, BLOCKS[clamp(lvl, 0, 7)], { fg: neon(color, u, bg), bg, soft: true });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Multi-row area sparkline: block glyphs, eighth-row precision, hot edge on top, gradient fill below,
|
|
50
|
+
* and a braille glow halo in the row above the edge. Reads as a solid neon mountain on a dark scope.
|
|
51
|
+
*/
|
|
52
|
+
export function sparkArea(c, x, y, w, h, values, opts = {}) {
|
|
53
|
+
const t = c.theme;
|
|
54
|
+
if (!values || values.length < 2 || w < 2 || h < 1)
|
|
55
|
+
return;
|
|
56
|
+
const v = resample(values, w);
|
|
57
|
+
const lo = Math.min(...v), hi = Math.max(...v);
|
|
58
|
+
const range = hi - lo || 1;
|
|
59
|
+
const up = v[v.length - 1] >= v[0];
|
|
60
|
+
const color = opts.color || (up ? t.sparkUp : t.sparkDown);
|
|
61
|
+
const bg = opts.bg || t.panel;
|
|
62
|
+
for (let i = 0; i < w; i++) {
|
|
63
|
+
const u = i / (w - 1);
|
|
64
|
+
const units = ((v[i] - lo) / range) * (h * 8 - 1) + 1; // eighths, min 1
|
|
65
|
+
const edgeRow = h - 1 - Math.floor((units - 1) / 8);
|
|
66
|
+
for (let r = 0; r < h; r++) {
|
|
67
|
+
const rowFromBottom = h - 1 - r;
|
|
68
|
+
const rem = units - rowFromBottom * 8;
|
|
69
|
+
if (rem <= 0) {
|
|
70
|
+
if (r === edgeRow - 1)
|
|
71
|
+
c.put(x + i, y + r, '⣀', { fg: mix(bg, color, 0.3), bg, soft: true }); // halo
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
const lvl = clamp(Math.round(rem), 1, 8);
|
|
75
|
+
const isEdge = r === edgeRow;
|
|
76
|
+
const depth = (r - edgeRow) / Math.max(1, h - edgeRow);
|
|
77
|
+
const fg = isEdge ? neon(color, u, bg) : mix(bg, color, 0.55 - depth * 0.3);
|
|
78
|
+
c.put(x + i, y + r, BLOCKS[lvl - 1], { fg, bg, soft: true });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/** Braille line chart (2 samples per column, 4 levels per row): a thin neon trace. */
|
|
83
|
+
export function brailleLine(c, x, y, w, h, values, opts = {}) {
|
|
84
|
+
const t = c.theme;
|
|
85
|
+
if (!values || values.length < 2 || w < 2 || h < 1)
|
|
86
|
+
return;
|
|
87
|
+
const v = resample(values, w * 2);
|
|
88
|
+
const lo = Math.min(...v), hi = Math.max(...v);
|
|
89
|
+
const range = hi - lo || 1;
|
|
90
|
+
const up = v[v.length - 1] >= v[0];
|
|
91
|
+
const color = opts.color || (up ? t.sparkUp : t.sparkDown);
|
|
92
|
+
const bg = opts.bg || t.panel;
|
|
93
|
+
const rows = h * 4;
|
|
94
|
+
const cells = new Array(w * h).fill(0);
|
|
95
|
+
let prev = null;
|
|
96
|
+
for (let s = 0; s < w * 2; s++) {
|
|
97
|
+
const r = Math.round((1 - (v[s] - lo) / range) * (rows - 1));
|
|
98
|
+
const a = Math.min(prev ?? r, r), b = Math.max(prev ?? r, r);
|
|
99
|
+
for (let rr = a; rr <= b; rr++)
|
|
100
|
+
cells[Math.floor(rr / 4) * w + Math.floor(s / 2)] |= (s % 2 === 0 ? BR_L : BR_R)[rr % 4];
|
|
101
|
+
prev = r;
|
|
102
|
+
}
|
|
103
|
+
for (let col = 0; col < w; col++)
|
|
104
|
+
for (let r = 0; r < h; r++) {
|
|
105
|
+
const bits = cells[r * w + col];
|
|
106
|
+
if (bits)
|
|
107
|
+
c.put(x + col, y + r, String.fromCharCode(0x2800 + bits), { fg: neon(color, col / (w - 1), bg), bg, soft: true });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function fmtPriceAxis(p) {
|
|
111
|
+
if (p >= 1000)
|
|
112
|
+
return p.toFixed(0);
|
|
113
|
+
if (p >= 1)
|
|
114
|
+
return p.toFixed(3);
|
|
115
|
+
if (p >= 0.001)
|
|
116
|
+
return p.toFixed(5);
|
|
117
|
+
return p.toExponential(2);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Candlestick chart with right-hand price axis, last-price tag, volume strip and time axis.
|
|
121
|
+
* 3-column bodies with a centered wick, half-block tops/bottoms, ━ for dojis. Newest at right.
|
|
122
|
+
* Bodies brighten with recency, glow ▐ into the gap column, and tint the row above/below 12 %.
|
|
123
|
+
*/
|
|
124
|
+
export function candleChart(c, x, y, w, h, candles, opts = {}) {
|
|
125
|
+
const t = c.theme;
|
|
126
|
+
const bg = opts.bg || t.panel;
|
|
127
|
+
const axisW = 10;
|
|
128
|
+
const volH = opts.volume === false ? 0 : 3;
|
|
129
|
+
const timeH = 1;
|
|
130
|
+
const plotW = w - axisW;
|
|
131
|
+
const plotH = h - volH - timeH;
|
|
132
|
+
if (plotW < 10 || plotH < 4 || candles.length === 0)
|
|
133
|
+
return;
|
|
134
|
+
const CW = 4; // 3-col body with centered wick + 1-col gap
|
|
135
|
+
const n = Math.min(candles.length, Math.floor(plotW / CW));
|
|
136
|
+
const data = candles.slice(-n);
|
|
137
|
+
// task-1366: the bars are laid out in price units either way (identical glyphs); only the labels are scaled
|
|
138
|
+
const s = opts.scale && Number.isFinite(opts.scale) && opts.scale > 0 ? opts.scale : 1;
|
|
139
|
+
const baseFmt = opts.axisFmt ?? fmtPriceAxis;
|
|
140
|
+
const fmtAxis = s === 1 ? baseFmt : (p) => baseFmt(p * s);
|
|
141
|
+
let lo = Infinity, hi = -Infinity, vmax = 0;
|
|
142
|
+
for (const k of data) {
|
|
143
|
+
lo = Math.min(lo, k.l);
|
|
144
|
+
hi = Math.max(hi, k.h);
|
|
145
|
+
vmax = Math.max(vmax, k.v);
|
|
146
|
+
}
|
|
147
|
+
const pad = (hi - lo) * 0.06 || hi * 0.01;
|
|
148
|
+
lo -= pad;
|
|
149
|
+
hi += pad;
|
|
150
|
+
const range = hi - lo || 1;
|
|
151
|
+
const rowsOf = (p) => (hi - p) / range * plotH; // float row position
|
|
152
|
+
const gridStyle = { fg: mix(bg, t.border, 0.9), bg, soft: true };
|
|
153
|
+
const labelStyle = { fg: t.muted, bg };
|
|
154
|
+
const ticks = Math.max(2, Math.min(6, Math.floor(plotH / 3)));
|
|
155
|
+
for (let i = 0; i <= ticks; i++) {
|
|
156
|
+
const row = Math.round((i / ticks) * (plotH - 1));
|
|
157
|
+
const price = hi - (row / (plotH - 1)) * range;
|
|
158
|
+
for (let k = 0; k < plotW; k++)
|
|
159
|
+
c.put(x + k, y + row, '·', gridStyle);
|
|
160
|
+
c.textR(x + plotW, y + row, axisW, ' ' + fmtAxis(price), labelStyle);
|
|
161
|
+
}
|
|
162
|
+
// last price line + tag
|
|
163
|
+
const last = data[data.length - 1];
|
|
164
|
+
const lastRow = Math.round(rowsOf(last.c));
|
|
165
|
+
const upLast = last.c >= last.o;
|
|
166
|
+
const lastColor = upLast ? t.up : t.down;
|
|
167
|
+
if (lastRow >= 0 && lastRow < plotH) {
|
|
168
|
+
for (let k = 0; k < plotW; k++)
|
|
169
|
+
c.put(x + k, y + lastRow, '╌', { fg: mix(bg, lastColor, 0.5), bg, soft: true });
|
|
170
|
+
c.textR(x + plotW, y + lastRow, axisW, ' ' + fmtAxis(last.c), { fg: '#000000', bg: lastColor, b: true });
|
|
171
|
+
}
|
|
172
|
+
const glowOf = (color) => mix(bg, color, 0.14);
|
|
173
|
+
for (let i = 0; i < n; i++) {
|
|
174
|
+
const k = data[i];
|
|
175
|
+
const col = x + i * CW;
|
|
176
|
+
const up = k.c >= k.o;
|
|
177
|
+
const color = up ? t.up : t.down;
|
|
178
|
+
const top = Math.max(k.o, k.c), bot = Math.min(k.o, k.c);
|
|
179
|
+
const rTop = rowsOf(top), rBot = rowsOf(bot);
|
|
180
|
+
const rHi = rowsOf(k.h), rLo = rowsOf(k.l);
|
|
181
|
+
// recent candles burn hotter
|
|
182
|
+
const heat = i / Math.max(1, n - 1);
|
|
183
|
+
const bodyFg = heat > 0.85 ? mix(color, '#ffffff', 0.25) : mix(mix(bg, color, 0.7), color, heat);
|
|
184
|
+
for (let r = Math.floor(rHi); r <= Math.floor(rLo); r++) {
|
|
185
|
+
if (r < 0 || r >= plotH)
|
|
186
|
+
continue;
|
|
187
|
+
c.put(col + 1, y + r, '│', { fg: mix(bg, color, 0.75), bg, soft: true });
|
|
188
|
+
}
|
|
189
|
+
const bTop = Math.floor(rTop), bBot = Math.floor(rBot);
|
|
190
|
+
const putBody = (r, ch) => {
|
|
191
|
+
for (let q = 0; q < 3; q++)
|
|
192
|
+
c.put(col + q, y + r, ch, { fg: bodyFg, bg, soft: true });
|
|
193
|
+
// glow trail into the gap column on the left
|
|
194
|
+
if (col - 1 >= x) {
|
|
195
|
+
const gi = c.idx(col - 1, y + r);
|
|
196
|
+
if (c.ch[gi] === ' ' || c.ch[gi] === '·')
|
|
197
|
+
c.put(col - 1, y + r, '▐', { fg: glowOf(color), bg, soft: true });
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
if (bTop === bBot) {
|
|
201
|
+
const frac = rTop - bTop;
|
|
202
|
+
const ch = (rBot - rTop) < 0.25 ? '━' : frac < 0.5 ? '▀' : '▄';
|
|
203
|
+
if (bTop >= 0 && bTop < plotH)
|
|
204
|
+
putBody(bTop, ch);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
for (let r = bTop; r <= bBot; r++) {
|
|
208
|
+
if (r < 0 || r >= plotH)
|
|
209
|
+
continue;
|
|
210
|
+
let ch = '█';
|
|
211
|
+
if (r === bTop && (rTop - bTop) >= 0.5)
|
|
212
|
+
ch = '▄';
|
|
213
|
+
if (r === bBot && (rBot - bBot) < 0.5)
|
|
214
|
+
ch = '▀';
|
|
215
|
+
putBody(r, ch);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
// bloom: bg tint one row above and below the body
|
|
219
|
+
for (const r of [bTop - 1, bBot + 1]) {
|
|
220
|
+
if (r < 0 || r >= plotH)
|
|
221
|
+
continue;
|
|
222
|
+
for (let q = 0; q < 3; q++) {
|
|
223
|
+
const gi = c.idx(col + q, y + r);
|
|
224
|
+
if (!c.hard[gi])
|
|
225
|
+
c.bg[gi] = mix(bg, color, 0.12);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (volH) {
|
|
230
|
+
const vy = y + plotH;
|
|
231
|
+
for (let i = 0; i < n; i++) {
|
|
232
|
+
const k = data[i];
|
|
233
|
+
const col = x + i * CW;
|
|
234
|
+
const up = k.c >= k.o;
|
|
235
|
+
const color = mix(bg, up ? t.up : t.down, 0.45 + 0.4 * (i / n));
|
|
236
|
+
const units = (k.v / (vmax || 1)) * volH * 8; // eighths
|
|
237
|
+
for (let r = 0; r < volH; r++) {
|
|
238
|
+
const rowFromBottom = volH - 1 - r;
|
|
239
|
+
const rem = units - rowFromBottom * 8;
|
|
240
|
+
if (rem <= 0)
|
|
241
|
+
continue;
|
|
242
|
+
const lvl = clamp(Math.round(rem), 1, 8);
|
|
243
|
+
for (let q = 0; q < 3; q++)
|
|
244
|
+
c.put(col + q, vy + r, BLOCKS[lvl - 1], { fg: color, bg, soft: true });
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
c.text(x + plotW + 1, vy, 'vol', labelStyle);
|
|
248
|
+
c.text(x + plotW + 1, vy + 1, fmtUsd(last.v, { digits: 1 }), { fg: t.text, bg });
|
|
249
|
+
}
|
|
250
|
+
const ty = y + h - 1;
|
|
251
|
+
const labels = Math.max(2, Math.floor(plotW / 18));
|
|
252
|
+
for (let i = 0; i < labels; i++) {
|
|
253
|
+
const idx = Math.round((i / (labels - 1)) * (n - 1));
|
|
254
|
+
const d = new Date(data[idx].t);
|
|
255
|
+
const lab = `${String(d.getUTCDate()).padStart(2, '0')}/${String(d.getUTCMonth() + 1).padStart(2, '0')} ${String(d.getUTCHours()).padStart(2, '0')}:00`;
|
|
256
|
+
const cx = clamp(x + idx * CW - 4, x, x + plotW - lab.length);
|
|
257
|
+
c.text(cx, ty, lab, labelStyle);
|
|
258
|
+
}
|
|
259
|
+
c.textR(x + plotW, ty, axisW, (opts.interval || '1h') + ' UTC', { fg: t.dim, bg });
|
|
260
|
+
}
|
|
261
|
+
/** Horizontal progress bar: gradient fill, hot head cell, one-cell ▓ glow past the head, ░ empty. pct 0..100 */
|
|
262
|
+
export function bar(c, x, y, w, pct, opts = {}) {
|
|
263
|
+
const t = c.theme;
|
|
264
|
+
const bg = opts.bg || t.panel;
|
|
265
|
+
const color = opts.color || t.accent;
|
|
266
|
+
const filled = Math.round((clamp(pct, 0, 100) / 100) * w);
|
|
267
|
+
for (let i = 0; i < w; i++) {
|
|
268
|
+
const on = i < filled;
|
|
269
|
+
if (on) {
|
|
270
|
+
const u = filled > 1 ? i / (filled - 1) : 1;
|
|
271
|
+
const fg = i === filled - 1 ? mix(color, '#ffffff', 0.35) : mix(mix(bg, color, 0.5), color, u);
|
|
272
|
+
c.put(x + i, y, '█', { fg, bg, soft: true });
|
|
273
|
+
}
|
|
274
|
+
else if (i === filled && filled > 0) {
|
|
275
|
+
c.put(x + i, y, '▓', { fg: mix(bg, color, 0.35), bg, soft: true });
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
c.put(x + i, y, '░', { fg: mix(bg, t.border, 0.9), bg, soft: true });
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/** Two-tone buy/sell ratio bar drawn with ━; both sides brighten toward the meeting point. */
|
|
283
|
+
export function ratioBar(c, x, y, w, buys, sells, opts = {}) {
|
|
284
|
+
const t = c.theme;
|
|
285
|
+
const bg = opts.bg || t.panel;
|
|
286
|
+
const tot = buys + sells || 1;
|
|
287
|
+
const b = Math.round((buys / tot) * w);
|
|
288
|
+
for (let i = 0; i < w; i++) {
|
|
289
|
+
const isBuy = i < b;
|
|
290
|
+
const color = isBuy ? t.up : t.down;
|
|
291
|
+
const u = isBuy ? (b > 1 ? i / (b - 1) : 1) : (w - b > 1 ? 1 - (i - b) / (w - b - 1) : 1);
|
|
292
|
+
c.put(x + i, y, '━', { fg: mix(mix(bg, color, 0.45), color, u), bg, soft: true });
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
/** Text with an 18 % tinted background of its semantic color. Returns the x after the badge. */
|
|
296
|
+
export function badge(c, x, y, label, fg, opts = {}) {
|
|
297
|
+
const t = c.theme;
|
|
298
|
+
const bg = opts.bg || mix(opts.on || t.panel, fg, 0.18);
|
|
299
|
+
const s = ` ${label} `;
|
|
300
|
+
c.text(x, y, s, { fg, bg, b: !!opts.bold });
|
|
301
|
+
return x + s.length;
|
|
302
|
+
}
|
|
303
|
+
const LAUNCHPAD_LABEL = { 'pump.fun': 'PUMP', dbc: 'DBC', raydium: 'RAY', meteora: 'MET', bonk: 'BONK' };
|
|
304
|
+
export function launchpadBadge(c, x, y, lp, opts = {}) {
|
|
305
|
+
const t = c.theme;
|
|
306
|
+
const label = LAUNCHPAD_LABEL[lp ?? ''] || (lp || '—').toUpperCase().slice(0, 4);
|
|
307
|
+
return badge(c, x, y, label, launchpadColor(t, lp), opts);
|
|
308
|
+
}
|
|
309
|
+
/** Up/down/neutral foreground for a signed number. */
|
|
310
|
+
export function pctStyle(c, n, bg) {
|
|
311
|
+
const t = c.theme;
|
|
312
|
+
if (n == null)
|
|
313
|
+
return { fg: t.dim, bg };
|
|
314
|
+
if (n > 0)
|
|
315
|
+
return { fg: t.up, bg };
|
|
316
|
+
if (n < 0)
|
|
317
|
+
return { fg: t.down, bg };
|
|
318
|
+
return { fg: t.muted, bg };
|
|
319
|
+
}
|
|
320
|
+
/** Direction glyph: the second encoding next to colour (colour-blind safe, VISUAL-V2 §6.5). */
|
|
321
|
+
export function dirGlyph(n) { return n > 0 ? '▲' : n < 0 ? '▼' : '◆'; }
|
|
322
|
+
/** `▲12.3%` / `▼4.0%` / `◆0.0%`; `—` for unknown. */
|
|
323
|
+
export function fmtPctDir(n, opts = {}) {
|
|
324
|
+
if (n == null || Number.isNaN(n))
|
|
325
|
+
return '—';
|
|
326
|
+
return dirGlyph(n) + fmtPct(Math.abs(n), { ...opts, sign: false });
|
|
327
|
+
}
|
|
328
|
+
/** Signed count with the direction glyph: `▲312`, `▼41`. */
|
|
329
|
+
export function fmtDeltaDir(n) { return dirGlyph(n) + Math.abs(n); }
|
|
330
|
+
/** Heat-shaded percentage cell: background intensity follows magnitude (cap 40 %); the glyph carries direction; bold above 24 %. */
|
|
331
|
+
export function heatPct(c, x, y, w, n, bg) {
|
|
332
|
+
const t = c.theme;
|
|
333
|
+
if (n == null) {
|
|
334
|
+
c.textR(x, y, w, '—', { fg: t.dim, bg });
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
const mag = clamp(Math.abs(n) / 40, 0, 1);
|
|
338
|
+
const base = n >= 0 ? t.upSoft : t.downSoft;
|
|
339
|
+
const cellBg = mix(bg, base, 0.25 + mag * 0.75);
|
|
340
|
+
const fg = n >= 0 ? t.up : t.down;
|
|
341
|
+
c.fill(x, y, w, 1, ' ', { bg: cellBg });
|
|
342
|
+
c.textR(x, y, w, fmtPctDir(n), { fg: mag > 0.6 ? mix(fg, '#ffffff', 0.3) : fg, bg: cellBg, b: mag > 0.6 });
|
|
343
|
+
}
|
|
344
|
+
/** Table with priority-based column dropping and one flex column. Header, rule, then rows. The area under the last row becomes a quiet rain zone. */
|
|
345
|
+
export function table(c, x, y, w, h, spec) {
|
|
346
|
+
const t = c.theme;
|
|
347
|
+
const bg = spec.bg || t.panel;
|
|
348
|
+
let cols = spec.columns.slice().sort((a, b) => a.prio - b.prio);
|
|
349
|
+
const gap = 1;
|
|
350
|
+
const fits = (list) => list.reduce((s, col) => s + col.w + gap, 0) - gap <= w;
|
|
351
|
+
while (!fits(cols) && cols.length > 1)
|
|
352
|
+
cols.pop();
|
|
353
|
+
const kept = new Set(cols);
|
|
354
|
+
cols = spec.columns.filter((col) => kept.has(col));
|
|
355
|
+
const used = cols.reduce((s, col) => s + col.w + gap, 0) - gap;
|
|
356
|
+
const flex = cols.find((col) => col.flex);
|
|
357
|
+
const widths = new Map(cols.map((col) => [col, col.w]));
|
|
358
|
+
if (flex && w - used > 0)
|
|
359
|
+
widths.set(flex, flex.w + (w - used));
|
|
360
|
+
let cx = x;
|
|
361
|
+
const headSt = { fg: t.muted, bg, b: false };
|
|
362
|
+
for (const col of cols) {
|
|
363
|
+
const cw = widths.get(col);
|
|
364
|
+
let label = col.label;
|
|
365
|
+
if (spec.sortKey === col.key)
|
|
366
|
+
label = (spec.sortDir === 'desc' ? '▼ ' : '▲ ') + label;
|
|
367
|
+
const st = spec.sortKey === col.key ? { fg: t.accent, bg, b: true } : headSt;
|
|
368
|
+
if (col.align === 'right')
|
|
369
|
+
c.textR(cx, y, cw, label, st);
|
|
370
|
+
else
|
|
371
|
+
c.text(cx, y, label, st, cw);
|
|
372
|
+
cx += cw + gap;
|
|
373
|
+
}
|
|
374
|
+
c.hline(x, y + 1, w, { fg: mix(bg, t.border, 1), bg, soft: true });
|
|
375
|
+
const bodyY = y + 2;
|
|
376
|
+
const rowsVisible = h - 2;
|
|
377
|
+
const start = spec.scroll || 0;
|
|
378
|
+
let drawn = 0;
|
|
379
|
+
for (let r = 0; r < rowsVisible; r++) {
|
|
380
|
+
const ri = start + r;
|
|
381
|
+
const row = spec.rows[ri];
|
|
382
|
+
const ry = bodyY + r;
|
|
383
|
+
if (row === undefined)
|
|
384
|
+
break;
|
|
385
|
+
drawn += 1;
|
|
386
|
+
const selected = ri === spec.selected;
|
|
387
|
+
const rowBg = selected ? t.selBg : (r % 2 === 1 && spec.zebra !== false ? mix(bg, t.panelAlt, 1) : bg);
|
|
388
|
+
c.fill(x, ry, w, 1, ' ', { bg: rowBg });
|
|
389
|
+
if (selected)
|
|
390
|
+
c.put(x - 1 >= 0 ? x - 1 : x, ry, '▎', { fg: t.accent, bg: rowBg, soft: true });
|
|
391
|
+
cx = x;
|
|
392
|
+
for (const col of cols) {
|
|
393
|
+
const cw = widths.get(col);
|
|
394
|
+
col.render(c, cx, ry, cw, row, rowBg, { selected, index: ri });
|
|
395
|
+
cx += cw + gap;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (drawn < rowsVisible)
|
|
399
|
+
c.rainZone(x, bodyY + drawn + 1, w, rowsVisible - drawn - 1, 2);
|
|
400
|
+
return { cols: cols.map((col) => col.key), rowsVisible };
|
|
401
|
+
}
|
|
402
|
+
/** Key/value stat grid in n columns, two rows per item. Returns the height used. */
|
|
403
|
+
export function statGrid(c, x, y, w, items, opts = {}) {
|
|
404
|
+
const t = c.theme;
|
|
405
|
+
const bg = opts.bg || t.panel;
|
|
406
|
+
const cols = opts.cols || Math.max(1, Math.floor(w / 22));
|
|
407
|
+
const cw = Math.floor(w / cols);
|
|
408
|
+
items.forEach((it, i) => {
|
|
409
|
+
const cx = x + (i % cols) * cw;
|
|
410
|
+
const cy = y + Math.floor(i / cols) * 2;
|
|
411
|
+
c.text(cx, cy, it.label.toUpperCase(), { fg: t.dim, bg }, cw - 1);
|
|
412
|
+
const st = it.style || { fg: t.text, bg, b: true };
|
|
413
|
+
c.text(cx, cy + 1, it.value, { ...st, bg }, cw - 1);
|
|
414
|
+
if (it.sub)
|
|
415
|
+
c.text(cx + String(it.value).length + 1, cy + 1, it.sub, { fg: t.muted, bg }, cw - 1 - String(it.value).length - 1);
|
|
416
|
+
});
|
|
417
|
+
return Math.ceil(items.length / cols) * 2;
|
|
418
|
+
}
|
|
419
|
+
/** Key-cap text colour: black on bright accents, white on dark ones (paper). */
|
|
420
|
+
export function onAccent(accent) { return luminance(accent) > 0.25 ? '#000000' : '#ffffff'; }
|
|
421
|
+
/** Footer key hints: accent key cap followed by a label; stops when the next hint would not fit. Returns the x after the last hint. */
|
|
422
|
+
export function keyHints(c, x, y, w, hints, bg) {
|
|
423
|
+
const t = c.theme;
|
|
424
|
+
let cx = x;
|
|
425
|
+
for (const [k, label] of hints) {
|
|
426
|
+
if (cx + k.length + label.length + 3 > x + w)
|
|
427
|
+
break;
|
|
428
|
+
cx = c.text(cx, y, ` ${k} `, { fg: onAccent(t.accent), bg: t.accent, b: true, hard: true });
|
|
429
|
+
cx = c.text(cx, y, ` ${label} `, { fg: t.muted, bg, hard: true });
|
|
430
|
+
}
|
|
431
|
+
return cx;
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* Live trade tape as falling rain lines: each trade owns a column; buys fall green, sells fall red.
|
|
435
|
+
* Newer trades sit nearer the left; the head of each line is the hot ▲/▼ cell; length ∝ log10(usd).
|
|
436
|
+
*/
|
|
437
|
+
export function tapeRain(c, x, y, w, h, trades, frame, opts = {}) {
|
|
438
|
+
const t = c.theme;
|
|
439
|
+
const bg = opts.bg || t.panel;
|
|
440
|
+
if (w < 3 || h < 2)
|
|
441
|
+
return;
|
|
442
|
+
const n = Math.min(trades.length, w);
|
|
443
|
+
for (let k = 0; k < n; k++) {
|
|
444
|
+
const tr = trades[k];
|
|
445
|
+
const col = x + k;
|
|
446
|
+
const color = tr.side === 'buy' ? t.up : t.down;
|
|
447
|
+
const speed = 0.25 + (tr.usd > 2000 ? 0.35 : 0.1);
|
|
448
|
+
const len = Math.min(h, 2 + Math.floor(Math.log10(tr.usd + 1)));
|
|
449
|
+
const period = h + len + 2;
|
|
450
|
+
const head = Math.floor((frame * speed + k * 3.7) % period) - 1;
|
|
451
|
+
for (let j = 0; j < len; j++) {
|
|
452
|
+
const yy = head - j;
|
|
453
|
+
if (yy < 0 || yy >= h)
|
|
454
|
+
continue;
|
|
455
|
+
const u = 1 - j / len;
|
|
456
|
+
const fg = j === 0 ? mix(color, '#ffffff', 0.4) : mix(mix(bg, color, 0.25), color, u);
|
|
457
|
+
c.put(col, y + yy, j === 0 ? (tr.side === 'buy' ? '▲' : '▼') : '│', { fg, bg, soft: true });
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Trade tape as a list: age · BUY/SELL · USD · price · wallet, newest first, one trade per line.
|
|
463
|
+
* Draws at most `h` lines; returns the number drawn.
|
|
464
|
+
*/
|
|
465
|
+
export function tradeTape(c, x, y, w, h, trades, now, opts = {}) {
|
|
466
|
+
const t = c.theme;
|
|
467
|
+
const bg = opts.bg || t.panel;
|
|
468
|
+
let n = 0;
|
|
469
|
+
for (const tr of trades) {
|
|
470
|
+
if (n >= h)
|
|
471
|
+
break;
|
|
472
|
+
const yy = y + n;
|
|
473
|
+
const color = tr.side === 'buy' ? t.up : t.down;
|
|
474
|
+
let cx = c.text(x, yy, fmtAgeSec(Math.max(0, (now - tr.t) / 1000)).padStart(4), { fg: t.dim, bg }, w);
|
|
475
|
+
cx = c.text(cx + 1, yy, tr.side === 'buy' ? 'BUY ' : 'SELL', { fg: color, bg, b: true }, Math.max(0, x + w - cx - 1));
|
|
476
|
+
if (cx + 9 > x + w) {
|
|
477
|
+
n += 1;
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
cx = c.textR(cx + 1, yy, 8, fmtUsd(tr.usd), { fg: t.text, bg });
|
|
481
|
+
cx = c.text(cx + 2, yy, tr.price > 0 ? fmtPrice(tr.price) : '—', { fg: t.muted, bg }, Math.max(0, x + w - cx - 2));
|
|
482
|
+
c.text(cx + 2, yy, shortAddr(tr.wallet, 3), { fg: t.dim, bg }, Math.max(0, x + w - cx - 2));
|
|
483
|
+
n += 1;
|
|
484
|
+
}
|
|
485
|
+
return n;
|
|
486
|
+
}
|
|
487
|
+
/** buys ÷ sells: Infinity when only buys, null when nothing traded (or unknown). */
|
|
488
|
+
export function flowRatio(buys, sells) {
|
|
489
|
+
if (buys === null || sells === null || buys + sells <= 0)
|
|
490
|
+
return null;
|
|
491
|
+
if (sells === 0)
|
|
492
|
+
return Infinity;
|
|
493
|
+
return buys / sells;
|
|
494
|
+
}
|
|
495
|
+
/** `1.71×` · `∞` (only buys) · `0.00×` (only sells) · `—` (nothing traded). */
|
|
496
|
+
export function fmtFlowRatio(r) {
|
|
497
|
+
if (r === null || Number.isNaN(r))
|
|
498
|
+
return '—';
|
|
499
|
+
if (r === Infinity)
|
|
500
|
+
return '∞';
|
|
501
|
+
return r >= 100 ? `${Math.round(r)}×` : `${r.toFixed(2)}×`;
|
|
502
|
+
}
|
|
503
|
+
/** Cells of a `w`-wide ratio bar painted as BUY (the rest are SELL); 0 when nothing traded. */
|
|
504
|
+
export function buyCells(w, buys, sells) {
|
|
505
|
+
if (buys === null || sells === null || buys + sells <= 0 || w <= 0)
|
|
506
|
+
return 0;
|
|
507
|
+
return Math.round((buys / (buys + sells)) * w);
|
|
508
|
+
}
|
|
509
|
+
/** `▲ +6pt` / `▼ -3pt` / `◆ flat` / `wait 3m` / the n/a reason. */
|
|
510
|
+
export function fmtFlowTrend(cell) {
|
|
511
|
+
if ('wait' in cell)
|
|
512
|
+
return `wait ${cell.wait}`;
|
|
513
|
+
if ('na' in cell)
|
|
514
|
+
return cell.na;
|
|
515
|
+
if (cell.dir === 0)
|
|
516
|
+
return '◆ flat';
|
|
517
|
+
return `${cell.dir > 0 ? '▲' : '▼'} ${cell.deltaPts > 0 ? '+' : cell.deltaPts < 0 ? '-' : ''}${Math.abs(cell.deltaPts)}pt`;
|
|
518
|
+
}
|
|
519
|
+
function fmtCount(n, partial) {
|
|
520
|
+
if (n === null)
|
|
521
|
+
return '—';
|
|
522
|
+
return (n >= 10_000 ? fmtNum(n) : String(Math.round(n))) + (partial ? '+' : '');
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* The BUY/SELL FLOW table: a header row, then one row per timeframe — buy and sell COUNT, the buy share
|
|
526
|
+
* as a two-tone bar, buys÷sells as a number, USD volume (split buy/sell when the source splits it and
|
|
527
|
+
* there is room), and the trend vs the previous same-length window. The bar is the flex column; the
|
|
528
|
+
* volume column drops first when the panel is narrow. Returns the number of lines drawn.
|
|
529
|
+
*/
|
|
530
|
+
export function flowPanel(c, x, y, w, h, rows, opts = {}) {
|
|
531
|
+
const t = c.theme;
|
|
532
|
+
const bg = opts.bg || t.panel;
|
|
533
|
+
if (h < 1 || w < 24)
|
|
534
|
+
return 0;
|
|
535
|
+
const labW = 3, cntW = 5, ratW = 5, trW = 9;
|
|
536
|
+
const split = w >= 64 && rows.some((r) => r.buyUsd !== null);
|
|
537
|
+
let volW = split ? 15 : 7;
|
|
538
|
+
let barW = w - (labW + 1 + cntW + 1 + cntW + 1 + 1 + ratW + 1 + volW + 1 + trW);
|
|
539
|
+
if (barW < 4) {
|
|
540
|
+
barW += volW + 1;
|
|
541
|
+
volW = 0;
|
|
542
|
+
}
|
|
543
|
+
barW = Math.max(1, barW);
|
|
544
|
+
const xB = x + labW + 1, xS = xB + cntW + 1, xBar = xS + cntW + 1, xR = xBar + barW + 1, xV = xR + ratW + 1, xT = xV + (volW ? volW + 1 : 0);
|
|
545
|
+
const head = { fg: t.dim, bg };
|
|
546
|
+
c.text(x, y, 'WIN', head);
|
|
547
|
+
c.textR(xB, y, cntW, 'BUYS', head);
|
|
548
|
+
c.textR(xS, y, cntW, 'SELLS', head);
|
|
549
|
+
c.text(xBar, y, barW >= 10 ? 'buy ━ sell' : 'b━s', head, barW);
|
|
550
|
+
c.textR(xR, y, ratW, 'B:S', head);
|
|
551
|
+
if (volW)
|
|
552
|
+
c.textR(xV, y, volW, split ? 'USD VOL B/S' : 'USD VOL', head);
|
|
553
|
+
c.text(xT, y, 'TREND', head, x + w - xT);
|
|
554
|
+
let n = 1;
|
|
555
|
+
for (const r of rows) {
|
|
556
|
+
if (n >= h)
|
|
557
|
+
break;
|
|
558
|
+
const yy = y + n;
|
|
559
|
+
c.text(x, yy, r.label, { fg: t.muted, bg, b: true }, labW);
|
|
560
|
+
if (r.note) {
|
|
561
|
+
c.text(xB, yy, r.note, { fg: t.dim, bg, i: true }, x + w - xB);
|
|
562
|
+
n += 1;
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
565
|
+
const partial = !!r.partial;
|
|
566
|
+
c.textR(xB, yy, cntW, fmtCount(r.buys, partial), { fg: r.buys ? t.up : t.dim, bg });
|
|
567
|
+
c.textR(xS, yy, cntW, fmtCount(r.sells, partial), { fg: r.sells ? t.down : t.dim, bg });
|
|
568
|
+
if (buyCells(barW, r.buys, r.sells) === 0 && flowRatio(r.buys, r.sells) === null) {
|
|
569
|
+
for (let i = 0; i < barW; i++)
|
|
570
|
+
c.put(xBar + i, yy, '─', { fg: mix(bg, t.border, 0.9), bg, soft: true });
|
|
571
|
+
}
|
|
572
|
+
else {
|
|
573
|
+
ratioBar(c, xBar, yy, barW, r.buys ?? 0, r.sells ?? 0, { bg });
|
|
574
|
+
}
|
|
575
|
+
const ratio = flowRatio(r.buys, r.sells);
|
|
576
|
+
c.textR(xR, yy, ratW, fmtFlowRatio(ratio), { fg: ratio === null ? t.dim : ratio > 1 ? t.up : ratio < 1 ? t.down : t.muted, bg, b: ratio !== null });
|
|
577
|
+
if (volW) {
|
|
578
|
+
if (split && r.buyUsd !== null && r.sellUsd !== null) {
|
|
579
|
+
const bs = fmtUsd(r.buyUsd, { digits: 2 }), ss = fmtUsd(r.sellUsd, { digits: 2 });
|
|
580
|
+
const sx = xV + volW - ss.length;
|
|
581
|
+
c.text(sx, yy, ss, { fg: t.down, bg });
|
|
582
|
+
c.text(sx - 1, yy, '/', { fg: t.dim, bg });
|
|
583
|
+
c.textR(xV, yy, Math.max(0, sx - 1 - xV), bs, { fg: t.up, bg });
|
|
584
|
+
}
|
|
585
|
+
else {
|
|
586
|
+
c.textR(xV, yy, volW, r.volUsd === null ? '—' : fmtUsd(r.volUsd, { digits: 2 }), { fg: r.volUsd === null ? t.dim : t.text, bg });
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
const tc = r.trend;
|
|
590
|
+
const tst = 'dir' in tc
|
|
591
|
+
? { fg: tc.dir > 0 ? t.up : tc.dir < 0 ? t.down : t.muted, bg, b: tc.dir !== 0 }
|
|
592
|
+
: { fg: t.dim, bg, i: true };
|
|
593
|
+
c.text(xT, yy, fmtFlowTrend(tc), tst, x + w - xT);
|
|
594
|
+
n += 1;
|
|
595
|
+
}
|
|
596
|
+
return n;
|
|
597
|
+
}
|
|
598
|
+
export { fmtNum };
|