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.
Files changed (377) hide show
  1. package/LICENSE +661 -0
  2. package/dist/packages/agents/src/audit.js +122 -0
  3. package/dist/packages/agents/src/bus.js +394 -0
  4. package/dist/packages/agents/src/executor.js +202 -0
  5. package/dist/packages/agents/src/gate.js +179 -0
  6. package/dist/packages/agents/src/identity.js +218 -0
  7. package/dist/packages/agents/src/index.js +15 -0
  8. package/dist/packages/agents/src/proposals.js +153 -0
  9. package/dist/packages/agents/src/schema.js +87 -0
  10. package/dist/packages/agents/src/sdk.js +140 -0
  11. package/dist/packages/agents/src/tiers.js +119 -0
  12. package/dist/packages/agents/src/tools.js +72 -0
  13. package/dist/packages/agents/src/ws.js +158 -0
  14. package/dist/packages/cli/assets/claude/analyst-system.md +76 -0
  15. package/dist/packages/cli/assets/claude/plugin/.claude-plugin/plugin.json +5 -0
  16. package/dist/packages/cli/assets/claude/plugin/skills/REGISTRY.json +16 -0
  17. package/dist/packages/cli/assets/claude/plugin/skills/larp-check/SKILL.md +75 -0
  18. package/dist/packages/cli/assets/claude/plugin/skills/rug-check/SKILL.md +74 -0
  19. package/dist/packages/cli/assets/claude/plugin/skills/token-dossier/SKILL.md +63 -0
  20. package/dist/packages/cli/assets/claude/plugin/skills/trench-scan/SKILL.md +44 -0
  21. package/dist/packages/cli/assets/claude/schemas/larp.schema.json +117 -0
  22. package/dist/packages/cli/assets/claude/schemas/trench-scan.schema.json +71 -0
  23. package/dist/packages/cli/assets/claude/schemas/verdict.schema.json +115 -0
  24. package/dist/packages/cli/bin/dexterm.mjs +11 -0
  25. package/dist/packages/cli/src/agent-cmd.js +336 -0
  26. package/dist/packages/cli/src/agent-roster.js +26 -0
  27. package/dist/packages/cli/src/alerts/adapters/desktop.js +40 -0
  28. package/dist/packages/cli/src/alerts/adapters/discord.js +36 -0
  29. package/dist/packages/cli/src/alerts/adapters/telegram.js +43 -0
  30. package/dist/packages/cli/src/alerts/adapters/toast.js +16 -0
  31. package/dist/packages/cli/src/alerts/creds.js +83 -0
  32. package/dist/packages/cli/src/alerts/format.js +41 -0
  33. package/dist/packages/cli/src/alerts/index.js +10 -0
  34. package/dist/packages/cli/src/alerts/notifier.js +53 -0
  35. package/dist/packages/cli/src/alerts/route.js +19 -0
  36. package/dist/packages/cli/src/alerts/types.js +1 -0
  37. package/dist/packages/cli/src/alerts-cmd.js +212 -0
  38. package/dist/packages/cli/src/analyst/backend.js +526 -0
  39. package/dist/packages/cli/src/analyst/results.js +38 -0
  40. package/dist/packages/cli/src/analyst/session.js +319 -0
  41. package/dist/packages/cli/src/audit.js +51 -0
  42. package/dist/packages/cli/src/backtest-cmd.js +208 -0
  43. package/dist/packages/cli/src/boot-state.js +62 -0
  44. package/dist/packages/cli/src/coin-target.js +2 -0
  45. package/dist/packages/cli/src/custom.js +261 -0
  46. package/dist/packages/cli/src/display.js +191 -0
  47. package/dist/packages/cli/src/doctor.js +375 -0
  48. package/dist/packages/cli/src/export.js +235 -0
  49. package/dist/packages/cli/src/feed.js +634 -0
  50. package/dist/packages/cli/src/fixtures.js +275 -0
  51. package/dist/packages/cli/src/follow-cmd.js +221 -0
  52. package/dist/packages/cli/src/images-config.js +59 -0
  53. package/dist/packages/cli/src/index.js +31 -0
  54. package/dist/packages/cli/src/kb-skill-cmd.js +240 -0
  55. package/dist/packages/cli/src/larp-record.js +21 -0
  56. package/dist/packages/cli/src/live/copy.js +80 -0
  57. package/dist/packages/cli/src/live/flow.js +233 -0
  58. package/dist/packages/cli/src/live/index.js +511 -0
  59. package/dist/packages/cli/src/live/intel.js +227 -0
  60. package/dist/packages/cli/src/live/newpairs.js +334 -0
  61. package/dist/packages/cli/src/live/screens/newpairs-live.js +219 -0
  62. package/dist/packages/cli/src/live/screens/token-live.js +459 -0
  63. package/dist/packages/cli/src/live/screens/watchlist-live.js +149 -0
  64. package/dist/packages/cli/src/live/token.js +585 -0
  65. package/dist/packages/cli/src/live/trade.js +929 -0
  66. package/dist/packages/cli/src/live/watchlist.js +178 -0
  67. package/dist/packages/cli/src/main.js +5785 -0
  68. package/dist/packages/cli/src/mcp-cmd.js +326 -0
  69. package/dist/packages/cli/src/narrative-cmd.js +76 -0
  70. package/dist/packages/cli/src/pack-cmd.js +323 -0
  71. package/dist/packages/cli/src/plan-cmd.js +278 -0
  72. package/dist/packages/cli/src/popup.js +810 -0
  73. package/dist/packages/cli/src/rules-cmd.js +131 -0
  74. package/dist/packages/cli/src/rules.js +358 -0
  75. package/dist/packages/cli/src/scan/command.js +57 -0
  76. package/dist/packages/cli/src/scan/engine.js +419 -0
  77. package/dist/packages/cli/src/scan/observe.js +64 -0
  78. package/dist/packages/cli/src/scan/ui.js +98 -0
  79. package/dist/packages/cli/src/screener/filter.js +241 -0
  80. package/dist/packages/cli/src/screener/presets.js +80 -0
  81. package/dist/packages/cli/src/screener/rows.js +146 -0
  82. package/dist/packages/cli/src/screener/rug.js +50 -0
  83. package/dist/packages/cli/src/screener/sort.js +52 -0
  84. package/dist/packages/cli/src/screener/view.js +183 -0
  85. package/dist/packages/cli/src/screens/analyst-session.js +343 -0
  86. package/dist/packages/cli/src/screens/analyst.js +326 -0
  87. package/dist/packages/cli/src/screens/boot.js +74 -0
  88. package/dist/packages/cli/src/screens/bubbles.js +255 -0
  89. package/dist/packages/cli/src/screens/coin.js +33 -0
  90. package/dist/packages/cli/src/screens/follows.js +201 -0
  91. package/dist/packages/cli/src/screens/kol.js +90 -0
  92. package/dist/packages/cli/src/screens/narrative.js +90 -0
  93. package/dist/packages/cli/src/screens/newpairs.js +150 -0
  94. package/dist/packages/cli/src/screens/overlay.js +257 -0
  95. package/dist/packages/cli/src/screens/plans.js +126 -0
  96. package/dist/packages/cli/src/screens/positions.js +176 -0
  97. package/dist/packages/cli/src/screens/rules.js +170 -0
  98. package/dist/packages/cli/src/screens/screener.js +459 -0
  99. package/dist/packages/cli/src/screens/social-panel.js +107 -0
  100. package/dist/packages/cli/src/screens/ticket.js +342 -0
  101. package/dist/packages/cli/src/screens/token.js +312 -0
  102. package/dist/packages/cli/src/screens/trending.js +181 -0
  103. package/dist/packages/cli/src/screens/watchlist.js +104 -0
  104. package/dist/packages/cli/src/skill-eval-build.js +109 -0
  105. package/dist/packages/cli/src/social/demo.js +96 -0
  106. package/dist/packages/cli/src/social/view.js +250 -0
  107. package/dist/packages/cli/src/state.js +463 -0
  108. package/dist/packages/cli/src/theme-cmd.js +126 -0
  109. package/dist/packages/cli/src/trade.js +319 -0
  110. package/dist/packages/cli/src/update.js +294 -0
  111. package/dist/packages/cli/src/version.js +3 -0
  112. package/dist/packages/cli/src/voice-cmd.js +119 -0
  113. package/dist/packages/cli/src/voice-hud.js +42 -0
  114. package/dist/packages/cli/src/wallet-cmd.js +440 -0
  115. package/dist/packages/cli/src/wallet-command.js +31 -0
  116. package/dist/packages/cli/src/watch-cmd.js +190 -0
  117. package/dist/packages/cli/src/widgets.js +208 -0
  118. package/dist/packages/cli/src/workspace-cmd.js +68 -0
  119. package/dist/packages/cli/src/x-cmd.js +154 -0
  120. package/dist/packages/custom/src/config.js +213 -0
  121. package/dist/packages/custom/src/index.js +10 -0
  122. package/dist/packages/custom/src/keymap.js +107 -0
  123. package/dist/packages/custom/src/layout.js +235 -0
  124. package/dist/packages/custom/src/packs.js +633 -0
  125. package/dist/packages/custom/src/rules.js +704 -0
  126. package/dist/packages/custom/src/schema.js +538 -0
  127. package/dist/packages/custom/src/themes.js +176 -0
  128. package/dist/packages/custom/src/widget-html.js +222 -0
  129. package/dist/packages/custom/src/workspace.js +141 -0
  130. package/dist/packages/data/src/analyst-actions.js +133 -0
  131. package/dist/packages/data/src/budget.js +88 -0
  132. package/dist/packages/data/src/cache.js +142 -0
  133. package/dist/packages/data/src/candles.js +168 -0
  134. package/dist/packages/data/src/clock.js +64 -0
  135. package/dist/packages/data/src/coin-target.js +82 -0
  136. package/dist/packages/data/src/credits.js +137 -0
  137. package/dist/packages/data/src/detail.js +97 -0
  138. package/dist/packages/data/src/fast-lane.js +321 -0
  139. package/dist/packages/data/src/fixtures.js +135 -0
  140. package/dist/packages/data/src/health.js +245 -0
  141. package/dist/packages/data/src/http.js +277 -0
  142. package/dist/packages/data/src/images/cache.js +141 -0
  143. package/dist/packages/data/src/images/decode-worker.js +28 -0
  144. package/dist/packages/data/src/images/decode.js +81 -0
  145. package/dist/packages/data/src/images/fetch.js +162 -0
  146. package/dist/packages/data/src/images/gif.js +214 -0
  147. package/dist/packages/data/src/images/index.js +10 -0
  148. package/dist/packages/data/src/images/jpeg.js +544 -0
  149. package/dist/packages/data/src/images/png.js +310 -0
  150. package/dist/packages/data/src/images/service.js +334 -0
  151. package/dist/packages/data/src/images/types.js +27 -0
  152. package/dist/packages/data/src/index.js +211 -0
  153. package/dist/packages/data/src/intel/helius-history.js +70 -0
  154. package/dist/packages/data/src/intel/holder-class.js +125 -0
  155. package/dist/packages/data/src/intel/rules.js +421 -0
  156. package/dist/packages/data/src/intel/scan.js +170 -0
  157. package/dist/packages/data/src/larp/evidence.js +288 -0
  158. package/dist/packages/data/src/larp/link-baseline.js +196 -0
  159. package/dist/packages/data/src/larp/links.js +137 -0
  160. package/dist/packages/data/src/larp/robots.js +90 -0
  161. package/dist/packages/data/src/larp/safe-fetch.js +389 -0
  162. package/dist/packages/data/src/limiter.js +341 -0
  163. package/dist/packages/data/src/model.js +12 -0
  164. package/dist/packages/data/src/paper.js +331 -0
  165. package/dist/packages/data/src/rpc-config.js +126 -0
  166. package/dist/packages/data/src/rpc-pool.js +166 -0
  167. package/dist/packages/data/src/social/chain.js +83 -0
  168. package/dist/packages/data/src/social/dedupe.js +205 -0
  169. package/dist/packages/data/src/social/feed.js +462 -0
  170. package/dist/packages/data/src/social/firewall.js +110 -0
  171. package/dist/packages/data/src/social/index.js +11 -0
  172. package/dist/packages/data/src/social/kol.js +269 -0
  173. package/dist/packages/data/src/social/model.js +11 -0
  174. package/dist/packages/data/src/social/rank.js +184 -0
  175. package/dist/packages/data/src/social/rss.js +113 -0
  176. package/dist/packages/data/src/social/summary.js +104 -0
  177. package/dist/packages/data/src/social/venues.js +186 -0
  178. package/dist/packages/data/src/sources/dexscreener.js +184 -0
  179. package/dist/packages/data/src/sources/geckoterminal.js +133 -0
  180. package/dist/packages/data/src/sources/goplus.js +161 -0
  181. package/dist/packages/data/src/sources/jupiter-swap.js +236 -0
  182. package/dist/packages/data/src/sources/jupiter.js +198 -0
  183. package/dist/packages/data/src/sources/pda.js +113 -0
  184. package/dist/packages/data/src/sources/pump-curve.js +58 -0
  185. package/dist/packages/data/src/sources/pumpportal.js +252 -0
  186. package/dist/packages/data/src/sources/rugcheck.js +71 -0
  187. package/dist/packages/data/src/sources/solana-rpc.js +299 -0
  188. package/dist/packages/data/src/sources/solana-ws.js +287 -0
  189. package/dist/packages/data/src/store.js +289 -0
  190. package/dist/packages/data/src/trade/bridge.js +464 -0
  191. package/dist/packages/data/src/trade/caps.js +323 -0
  192. package/dist/packages/data/src/trade/copy/auto.js +385 -0
  193. package/dist/packages/data/src/trade/copy/follows.js +103 -0
  194. package/dist/packages/data/src/trade/copy/index.js +9 -0
  195. package/dist/packages/data/src/trade/copy/mirror.js +91 -0
  196. package/dist/packages/data/src/trade/copy/stats.js +120 -0
  197. package/dist/packages/data/src/trade/copy/tracker.js +158 -0
  198. package/dist/packages/data/src/trade/copy/trades.js +87 -0
  199. package/dist/packages/data/src/trade/copy/types.js +6 -0
  200. package/dist/packages/data/src/trade/executor.js +933 -0
  201. package/dist/packages/data/src/trade/kill-switch.js +47 -0
  202. package/dist/packages/data/src/trade/link-store.js +52 -0
  203. package/dist/packages/data/src/trade/plans/confirm.js +157 -0
  204. package/dist/packages/data/src/trade/plans/engine.js +784 -0
  205. package/dist/packages/data/src/trade/plans/executor-live.js +220 -0
  206. package/dist/packages/data/src/trade/plans/executor-paper.js +44 -0
  207. package/dist/packages/data/src/trade/plans/executor.js +11 -0
  208. package/dist/packages/data/src/trade/plans/index.js +12 -0
  209. package/dist/packages/data/src/trade/plans/journal.js +77 -0
  210. package/dist/packages/data/src/trade/plans/mandate.js +129 -0
  211. package/dist/packages/data/src/trade/plans/plan.js +179 -0
  212. package/dist/packages/data/src/trade/plans/service.js +168 -0
  213. package/dist/packages/data/src/trade/plans/types.js +6 -0
  214. package/dist/packages/data/src/trade/rpc-exec.js +229 -0
  215. package/dist/packages/data/src/trade/signer/client.js +197 -0
  216. package/dist/packages/data/src/trade/signer/fast-wallet.js +154 -0
  217. package/dist/packages/data/src/trade/signer/host.js +97 -0
  218. package/dist/packages/data/src/trade/signer/main.js +121 -0
  219. package/dist/packages/data/src/trade/signer/probe-builder.js +85 -0
  220. package/dist/packages/data/src/trade/signer/protocol.js +92 -0
  221. package/dist/packages/data/src/trade/signer/service.js +400 -0
  222. package/dist/packages/data/src/trade/signer/sol-usd-feed.js +98 -0
  223. package/dist/packages/data/src/trade/sol-usd.js +86 -0
  224. package/dist/packages/data/src/trade/swap-build.js +108 -0
  225. package/dist/packages/data/src/trade/token-delta.js +201 -0
  226. package/dist/packages/data/src/trade/tx-decode.js +632 -0
  227. package/dist/packages/data/src/util.js +24 -0
  228. package/dist/packages/data/src/watchlist.js +176 -0
  229. package/dist/packages/data/src/x/api.js +256 -0
  230. package/dist/packages/data/src/x/budget.js +66 -0
  231. package/dist/packages/data/src/x/config.js +49 -0
  232. package/dist/packages/data/src/x/index.js +10 -0
  233. package/dist/packages/data/src/x/mock-server.js +217 -0
  234. package/dist/packages/data/src/x/oauth.js +242 -0
  235. package/dist/packages/data/src/x/pkce.js +21 -0
  236. package/dist/packages/data/src/x/source.js +285 -0
  237. package/dist/packages/data/src/x/token-store.js +134 -0
  238. package/dist/packages/data/src/x/transport.js +71 -0
  239. package/dist/packages/data/vendor/solana-web3/LICENSE +20 -0
  240. package/dist/packages/data/vendor/solana-web3/NOTICE.md +13 -0
  241. package/dist/packages/data/vendor/solana-web3/index.iife.min.js +20 -0
  242. package/dist/packages/mcp/assets/cases.json +1194 -0
  243. package/dist/packages/mcp/assets/kb/entries/cabal-and-coordinated-launches.md +28 -0
  244. package/dist/packages/mcp/assets/kb/entries/common-rug-patterns.md +40 -0
  245. package/dist/packages/mcp/assets/kb/entries/dbc-mechanics.md +30 -0
  246. package/dist/packages/mcp/assets/kb/entries/dev-wallet-behaviour.md +33 -0
  247. package/dist/packages/mcp/assets/kb/entries/glossary.md +35 -0
  248. package/dist/packages/mcp/assets/kb/entries/holder-concentration.md +30 -0
  249. package/dist/packages/mcp/assets/kb/entries/jito-bundles.md +29 -0
  250. package/dist/packages/mcp/assets/kb/entries/launchlab-curve.md +31 -0
  251. package/dist/packages/mcp/assets/kb/entries/lp-burn-vs-lock.md +31 -0
  252. package/dist/packages/mcp/assets/kb/entries/metadata-and-copycats.md +29 -0
  253. package/dist/packages/mcp/assets/kb/entries/mint-freeze-authority.md +31 -0
  254. package/dist/packages/mcp/assets/kb/entries/narrative-lifecycle.md +32 -0
  255. package/dist/packages/mcp/assets/kb/entries/pump-bonding-curve.md +33 -0
  256. package/dist/packages/mcp/assets/kb/entries/pumpswap-migration.md +29 -0
  257. package/dist/packages/mcp/assets/kb/entries/sniping-and-snipers.md +29 -0
  258. package/dist/packages/mcp/assets/kb/entries/token2022-extensions-risk.md +32 -0
  259. package/dist/packages/mcp/assets/kb/entries/wash-trading-fake-volume.md +33 -0
  260. package/dist/packages/mcp/assets/skills/rug-check.md +52 -0
  261. package/dist/packages/mcp/assets/skills/token-dossier.md +57 -0
  262. package/dist/packages/mcp/assets/skills/trench-scan.md +40 -0
  263. package/dist/packages/mcp/bin/dexterm-mcp.mjs +16 -0
  264. package/dist/packages/mcp/src/dossier.js +78 -0
  265. package/dist/packages/mcp/src/firewall.js +21 -0
  266. package/dist/packages/mcp/src/index.js +22 -0
  267. package/dist/packages/mcp/src/server.js +936 -0
  268. package/dist/packages/mcp/src/user-content.js +434 -0
  269. package/dist/packages/packs/src/backtest.js +183 -0
  270. package/dist/packages/packs/src/digest.js +95 -0
  271. package/dist/packages/packs/src/firewall.js +57 -0
  272. package/dist/packages/packs/src/index.js +20 -0
  273. package/dist/packages/packs/src/larp-eval.js +100 -0
  274. package/dist/packages/packs/src/narrative.js +309 -0
  275. package/dist/packages/packs/src/reason.js +135 -0
  276. package/dist/packages/packs/src/rug-model.js +137 -0
  277. package/dist/packages/packs/src/schema.js +54 -0
  278. package/dist/packages/packs/src/skill-backtest.js +300 -0
  279. package/dist/packages/packs/src/token-dossier.js +174 -0
  280. package/dist/packages/packs/src/verdicts.js +17 -0
  281. package/dist/packages/packs/src/wallet-dossier.js +105 -0
  282. package/dist/packages/popup/src/config.js +71 -0
  283. package/dist/packages/popup/src/dock.js +271 -0
  284. package/dist/packages/popup/src/host.js +210 -0
  285. package/dist/packages/popup/src/index.js +12 -0
  286. package/dist/packages/popup/src/loopback.js +53 -0
  287. package/dist/packages/popup/src/open.js +183 -0
  288. package/dist/packages/popup/src/pages/bubbles-lib.js +160 -0
  289. package/dist/packages/popup/src/pages/bubbles.css +23 -0
  290. package/dist/packages/popup/src/pages/bubbles.html +36 -0
  291. package/dist/packages/popup/src/pages/bubbles.js +214 -0
  292. package/dist/packages/popup/src/pages/chart-lib.js +262 -0
  293. package/dist/packages/popup/src/pages/chart.css +50 -0
  294. package/dist/packages/popup/src/pages/chart.html +56 -0
  295. package/dist/packages/popup/src/pages/chart.js +554 -0
  296. package/dist/packages/popup/src/pages/common.css +34 -0
  297. package/dist/packages/popup/src/pages/common.js +187 -0
  298. package/dist/packages/popup/src/pages/feeds.html +20 -0
  299. package/dist/packages/popup/src/pages/feeds.js +77 -0
  300. package/dist/packages/popup/src/pages/narrative.html +20 -0
  301. package/dist/packages/popup/src/pages/narrative.js +61 -0
  302. package/dist/packages/popup/src/pages/plans.css +30 -0
  303. package/dist/packages/popup/src/pages/plans.html +26 -0
  304. package/dist/packages/popup/src/pages/plans.js +82 -0
  305. package/dist/packages/popup/src/pages/roster.css +41 -0
  306. package/dist/packages/popup/src/pages/roster.html +25 -0
  307. package/dist/packages/popup/src/pages/roster.js +75 -0
  308. package/dist/packages/popup/src/pages/token.html +20 -0
  309. package/dist/packages/popup/src/pages/token.js +112 -0
  310. package/dist/packages/popup/src/pages/widget-sdk.js +224 -0
  311. package/dist/packages/popup/src/pages/widget.css +5 -0
  312. package/dist/packages/popup/src/pages/widget.html +21 -0
  313. package/dist/packages/popup/src/pages/widget.js +85 -0
  314. package/dist/packages/popup/src/protocol.js +31 -0
  315. package/dist/packages/popup/src/selftest.js +156 -0
  316. package/dist/packages/popup/src/server.js +582 -0
  317. package/dist/packages/popup/src/sinkproxy.js +167 -0
  318. package/dist/packages/popup/src/widget-html.js +231 -0
  319. package/dist/packages/popup/vendor/lightweight-charts/LICENSE +201 -0
  320. package/dist/packages/popup/vendor/lightweight-charts/NOTICE +2 -0
  321. package/dist/packages/popup/vendor/lightweight-charts/README.md +30 -0
  322. package/dist/packages/popup/vendor/lightweight-charts/VERSION +1 -0
  323. package/dist/packages/popup/vendor/lightweight-charts/lightweight-charts.standalone.production.js +7 -0
  324. package/dist/packages/recorder/bin/dexterm-recorder.mjs +11 -0
  325. package/dist/packages/recorder/src/cli.js +113 -0
  326. package/dist/packages/recorder/src/export.js +71 -0
  327. package/dist/packages/recorder/src/index.js +20 -0
  328. package/dist/packages/recorder/src/labels.js +151 -0
  329. package/dist/packages/recorder/src/open.js +29 -0
  330. package/dist/packages/recorder/src/recorder.js +553 -0
  331. package/dist/packages/recorder/src/schedule.js +43 -0
  332. package/dist/packages/recorder/src/schema.js +135 -0
  333. package/dist/packages/recorder/src/snapshot.js +191 -0
  334. package/dist/packages/recorder/src/sweep.js +52 -0
  335. package/dist/packages/recorder/src/verdicts.js +52 -0
  336. package/dist/packages/tui/src/bubbles.js +415 -0
  337. package/dist/packages/tui/src/canvas.js +370 -0
  338. package/dist/packages/tui/src/chrome.js +85 -0
  339. package/dist/packages/tui/src/color.js +133 -0
  340. package/dist/packages/tui/src/flush.js +62 -0
  341. package/dist/packages/tui/src/fmt.js +188 -0
  342. package/dist/packages/tui/src/fx-trade.js +278 -0
  343. package/dist/packages/tui/src/fx.js +527 -0
  344. package/dist/packages/tui/src/images/blocks.js +141 -0
  345. package/dist/packages/tui/src/images/coin.js +22 -0
  346. package/dist/packages/tui/src/images/detect.js +140 -0
  347. package/dist/packages/tui/src/images/index.js +7 -0
  348. package/dist/packages/tui/src/images/layer.js +146 -0
  349. package/dist/packages/tui/src/images/pixels.js +157 -0
  350. package/dist/packages/tui/src/images/protocols.js +87 -0
  351. package/dist/packages/tui/src/index.js +18 -0
  352. package/dist/packages/tui/src/keymap.js +357 -0
  353. package/dist/packages/tui/src/layout.js +11 -0
  354. package/dist/packages/tui/src/raster-font.js +195 -0
  355. package/dist/packages/tui/src/raster.js +213 -0
  356. package/dist/packages/tui/src/sound.js +177 -0
  357. package/dist/packages/tui/src/theme.js +182 -0
  358. package/dist/packages/tui/src/widgets.js +598 -0
  359. package/dist/packages/voice/src/controller.js +407 -0
  360. package/dist/packages/voice/src/doctor.js +131 -0
  361. package/dist/packages/voice/src/download.js +184 -0
  362. package/dist/packages/voice/src/engines.js +84 -0
  363. package/dist/packages/voice/src/index.js +20 -0
  364. package/dist/packages/voice/src/router.js +502 -0
  365. package/dist/packages/voice/src/stt/apple-helper.swift.js +103 -0
  366. package/dist/packages/voice/src/stt/apple.js +179 -0
  367. package/dist/packages/voice/src/stt/fake.js +34 -0
  368. package/dist/packages/voice/src/stt/whisper.js +112 -0
  369. package/dist/packages/voice/src/summary.js +44 -0
  370. package/dist/packages/voice/src/tts/espeak.js +131 -0
  371. package/dist/packages/voice/src/tts/fake.js +30 -0
  372. package/dist/packages/voice/src/tts/piper.js +206 -0
  373. package/dist/packages/voice/src/tts/say.js +132 -0
  374. package/dist/packages/voice/src/tts/voices.js +206 -0
  375. package/dist/packages/voice/src/types.js +3 -0
  376. package/package.json +38 -4
  377. package/README.md +0 -3
@@ -0,0 +1,527 @@
1
+ // @dexterm/tui — the cyberspace effects engine (VISUAL-V2 §3), ported from prototype-v2/src/fx.mjs.
2
+ //
3
+ // Everything here is a pure function of (canvas, vfx state, frame index). The PRNG is a hash of
4
+ // (seed, x, y, frame) so a given frame index always produces the same cells: snapshots stay testable
5
+ // and the demo video is reproducible. Effects write through Canvas.putFx, which refuses hard cells
6
+ // (data glyphs) and, for rain, cells outside registered rain zones. Nothing here can overdraw a
7
+ // number, a ticker or a key hint.
8
+ //
9
+ // Passes (applied in this order by applyFx):
10
+ // rain matrix rain of katakana / hex / the user's own tickers / tx-hash fragments, in zones only
11
+ // trails bloom: last frames' rain cells linger dimmer (CRT phosphor persistence)
12
+ // aberrate chromatic aberration ghosts on headings (magenta left, cyan right) + per-glyph colour split
13
+ // mosh datamosh blocks: soft cells (borders, rain, backgrounds) shift sideways; bg tint on rows
14
+ // shake whole-frame 1-cell offset for big events (moves data, never covers it)
15
+ // scan CRT scanlines: odd rows' backgrounds pulled 7 % toward the scan colour
16
+ //
17
+ // Separate helpers: blinkFrame (screen transition), decrypt (streaming text), spinner, bootLogo,
18
+ // and the market → rain mapping (marketActivity / candleActivity).
19
+ import { Canvas } from "./canvas.js";
20
+ import { mix } from "./color.js";
21
+ import { tradeFx, stepMoment } from "./fx-trade.js";
22
+ export const KATAKANA = 'アイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン';
23
+ export const HEX = '0123456789ABCDEF';
24
+ /** Code glyphs for floods / decrypt: katakana + hex + a few symbols. */
25
+ export const CODE = KATAKANA + HEX + '∷⋮⋯╳◇◆';
26
+ /** The same set without katakana, for C/POSIX locales or terminals that draw half-width kana two cells wide. */
27
+ export const CODE_ASCII = HEX + '∷⋮⋯╳◇◆';
28
+ /** Claude Code spinner family. */
29
+ export const SPINNER = ['·', '✢', '✳', '✶', '✻', '✽'];
30
+ /** 32-bit integer hash of up to three ints; stable across platforms. */
31
+ export function hash3(a, b = 0, c = 0) {
32
+ let h = (a | 0) * 0x9e3779b1 ^ (b | 0) * 0x85ebca77 ^ (c | 0) * 0xc2b2ae3d;
33
+ h = Math.imul(h ^ (h >>> 15), 0x2c1b3c6d);
34
+ h = Math.imul(h ^ (h >>> 12), 0x297a2d39);
35
+ return (h ^ (h >>> 15)) >>> 0;
36
+ }
37
+ const unit = (a, b, c) => hash3(a, b, c) / 4294967296;
38
+ export function makeVfx(opts = {}) {
39
+ const intensity = opts.calm ? 'off' : (opts.intensity ?? 'fever');
40
+ return {
41
+ seed: opts.seed ?? 7,
42
+ frame: 0,
43
+ intensity,
44
+ activity: 0.5,
45
+ mood: 'neutral',
46
+ glitch: 0,
47
+ shake: 0,
48
+ stutter: 0,
49
+ trail: new Map(),
50
+ sources: opts.sources ? sourcesWithCode(opts.sources, opts.ascii ? CODE_ASCII : CODE) : [],
51
+ baseline: intensity === 'fever' ? 0.12 : 0,
52
+ code: opts.ascii ? CODE_ASCII : CODE,
53
+ toast: null,
54
+ };
55
+ }
56
+ export function isCalm(v) { return v.intensity === 'off'; }
57
+ /** Rain columns are the user's own data (tickers, mint prefixes, tx tails) plus katakana and hex runs. */
58
+ export function setSources(v, strings) {
59
+ v.sources = sourcesWithCode(strings, v.code);
60
+ }
61
+ function sourcesWithCode(strings, code) {
62
+ const s = strings.filter((x) => x && x.length >= 2).slice(0, 80);
63
+ const kana = code === CODE ? KATAKANA : HEX;
64
+ for (let i = 0; i < 12; i++)
65
+ s.push(Array.from({ length: 8 + i % 5 }, (_, k) => kana[(i * 7 + k * 13) % kana.length]).join(''));
66
+ for (let i = 0; i < 6; i++)
67
+ s.push(Array.from({ length: 10 }, (_, k) => HEX[(i * 5 + k * 3) % 16]).join(''));
68
+ return s;
69
+ }
70
+ /** Event hooks. Glitch decays per frame; mood sticks until the market data says otherwise. */
71
+ export function pulse(v, kind) {
72
+ if (isCalm(v))
73
+ return;
74
+ const k = v.intensity === 'fever' ? 1 : 0.6;
75
+ if (kind === 'newpair') {
76
+ v.glitch = Math.max(v.glitch, 0.8 * k);
77
+ v.shake = v.intensity === 'fever' ? 2 : 1;
78
+ v.stutter = v.intensity === 'fever' ? 2 : 1;
79
+ }
80
+ else if (kind === 'rug') {
81
+ v.glitch = 1 * k;
82
+ v.shake = v.intensity === 'fever' ? 3 : 2;
83
+ v.stutter = v.intensity === 'fever' ? 3 : 1;
84
+ v.mood = 'dump';
85
+ }
86
+ else if (kind === 'pump') {
87
+ v.mood = 'pump';
88
+ v.activity = 1;
89
+ v.shake = 1;
90
+ }
91
+ else if (kind === 'dump') {
92
+ v.mood = 'dump';
93
+ v.activity = 1;
94
+ v.shake = 1;
95
+ }
96
+ }
97
+ export function stepVfx(v) {
98
+ v.frame += 1;
99
+ v.glitch = v.glitch > 0.02 ? v.glitch * 0.82 : 0;
100
+ if (v.shake > 0)
101
+ v.shake -= 1;
102
+ if (v.stutter > 0)
103
+ v.stutter -= 1;
104
+ stepMoment(v);
105
+ }
106
+ /**
107
+ * True when a per-event animation is live and needs full-rate redraws (task-1303): a glitch/mosh is
108
+ * decaying, or the frame is shaking or held (datamosh). Ambient rain and its phosphor trails are NOT
109
+ * counted — they animate forever, so between events the loop throttles them down to the idle rate to
110
+ * keep CPU low while keeping the look. (High market `activity` keeps the loop at full rate via the
111
+ * caller, so a fast-moving market still rains smoothly.)
112
+ */
113
+ export function fxBusy(v) {
114
+ return v.glitch > 0.02 || v.shake > 0 || v.stutter > 0 || !!v.moment;
115
+ }
116
+ /**
117
+ * Activity from a market cross-section: 5-minute price deltas (percent) and, when known, volume
118
+ * velocities (5 m volume scaled to 24 h over the 24 h volume; 1 = steady). Mean |Δ5m| / 6 maps 0..1
119
+ * (fixture noise ≈ 2 % → 0.33); velocity above 1 raises it. Net bias beyond ±3 % sets the mood.
120
+ */
121
+ export function marketActivity(deltas, velocities = []) {
122
+ let sum = 0, net = 0, n = 0;
123
+ for (const d of deltas) {
124
+ if (d == null || !Number.isFinite(d))
125
+ continue;
126
+ sum += Math.abs(d);
127
+ net += d;
128
+ n += 1;
129
+ }
130
+ const mean = n ? sum / n : 0;
131
+ let act = mean / 6;
132
+ let vsum = 0, vn = 0;
133
+ for (const v of velocities) {
134
+ if (v == null || !Number.isFinite(v))
135
+ continue;
136
+ vsum += Math.max(0, Math.min(3, v));
137
+ vn += 1;
138
+ }
139
+ if (vn)
140
+ act = 0.5 * act + 0.5 * ((vsum / vn - 0.5) / 2.5);
141
+ const activity = n || vn ? Math.max(0.15, Math.min(1, act)) : 0.5;
142
+ const bias = n ? net / n : 0;
143
+ return { activity, mood: bias > 3 ? 'pump' : bias < -3 ? 'dump' : 'neutral' };
144
+ }
145
+ /**
146
+ * Activity for a token screen: the selected token's own candles. The last three candles' net move
147
+ * beyond ±6 % (or a 5 m move beyond ±5 %) sets the mood; their absolute moves and the last candle's
148
+ * volume against the previous dozen set the activity.
149
+ */
150
+ export function candleActivity(candles, m5 = null) {
151
+ const n = candles.length;
152
+ if (n < 2)
153
+ return { activity: 0.5, mood: 'neutral' };
154
+ const k = Math.min(3, n - 1);
155
+ const base = candles[n - 1 - k].c || candles[n - 1 - k].o || 1;
156
+ const net = (candles[n - 1].c / base - 1) * 100;
157
+ let moves = 0;
158
+ for (let i = n - k; i < n; i++) {
159
+ const o = candles[i].o || 1;
160
+ moves += Math.abs(candles[i].c / o - 1) * 100;
161
+ }
162
+ const prev = candles.slice(Math.max(0, n - 13), n - 1);
163
+ const vmean = prev.length ? prev.reduce((s, c) => s + c.v, 0) / prev.length : candles[n - 1].v;
164
+ const vratio = vmean > 0 ? candles[n - 1].v / vmean : 1;
165
+ const act = Math.max(0.15, Math.min(1, 0.6 * (moves / 12) + 0.4 * Math.min(1, vratio / 2.5)));
166
+ const m = m5 ?? 0;
167
+ const mood = net > 6 || m > 5 ? 'pump' : net < -6 || m < -5 ? 'dump' : 'neutral';
168
+ return { activity: act, mood };
169
+ }
170
+ /** EMA step for activity (0.7 old + 0.3 new per data tick). */
171
+ export function blendActivity(v, act, setMood = true) {
172
+ v.activity = v.activity * 0.7 + act.activity * 0.3;
173
+ if (setMood)
174
+ v.mood = act.mood;
175
+ }
176
+ // ---------------------------------------------------------------- rain
177
+ function rainPalette(c, v) {
178
+ const t = c.theme;
179
+ return v.mood === 'pump' ? t.rainPump : v.mood === 'dump' ? t.rainDump : t.rain;
180
+ }
181
+ export function rain(c, v) {
182
+ const t = c.theme;
183
+ const pal = rainPalette(c, v);
184
+ const f = v.frame;
185
+ const W = c.w, H = c.h;
186
+ const scale = v.intensity === 'fever' ? 1 : 0.6;
187
+ const density = (0.4 + 0.55 * v.activity) * scale; // share of columns carrying a drop
188
+ const speedBase = 0.18 + 0.5 * v.activity; // rows per frame (30 fps → 5..20 rows/s)
189
+ const dimPanel = (col) => mix(t.panel, col, 0.8);
190
+ const code = v.code;
191
+ for (let x = 0; x < W; x++) {
192
+ // Each column has an independent drop; its parameters re-roll every cycle so columns do not sync.
193
+ const colSeed = hash3(v.seed, x, 1);
194
+ const speed = speedBase * (0.6 + 0.8 * unit(colSeed, 2, 0));
195
+ const len = 5 + Math.floor(unit(colSeed, 3, 0) * 18);
196
+ const period = H + len + 6 + Math.floor(unit(colSeed, 4, 0) * 20);
197
+ const pos = (f * speed + unit(colSeed, 5, 0) * period);
198
+ const cycle = Math.floor(pos / period);
199
+ if (unit(colSeed, 6 + cycle, 0) > density)
200
+ continue; // this cycle the column is quiet
201
+ const head = Math.floor(pos % period) - 2;
202
+ const src = v.sources.length ? v.sources[hash3(colSeed, cycle, 9) % v.sources.length] : KATAKANA;
203
+ const phase = hash3(colSeed, cycle, 10) % src.length;
204
+ for (let k = 0; k < len; k++) {
205
+ const y = head - k;
206
+ if (y < 0 || y >= H)
207
+ continue;
208
+ const i = c.idx(x, y);
209
+ if (!c.zone[i] || c.hard[i] || c.ch[i] !== ' ')
210
+ continue;
211
+ // glyph: source text reads downward; a few cells mutate each frame
212
+ let ch = src[(y + phase) % src.length];
213
+ if (unit(x, y, f) < 0.04)
214
+ ch = code[hash3(x, y, f) % code.length];
215
+ const tier = k === 0 ? 3 : k < 3 ? 2 : k < len * 0.6 ? 1 : 0;
216
+ let fg = pal[tier];
217
+ if (c.zone[i] === 2)
218
+ fg = tier === 3 ? mix(pal[3], pal[2], 0.5) : dimPanel(pal[tier]); // panel interiors stay quieter than bands
219
+ c.putFx(x, y, ch, { fg, b: tier === 3 });
220
+ v.trail.set(i, { ch, fg, age: 0 });
221
+ }
222
+ }
223
+ }
224
+ /** Phosphor persistence: cells drawn in the last few frames linger, dimmer each frame. */
225
+ export function trails(c, v) {
226
+ for (const [i, tr] of v.trail) {
227
+ tr.age += 1;
228
+ if (tr.age > 3) {
229
+ v.trail.delete(i);
230
+ continue;
231
+ }
232
+ if (i >= c.ch.length) {
233
+ v.trail.delete(i);
234
+ continue;
235
+ }
236
+ if (c.ch[i] !== ' ' || c.hard[i] || !c.zone[i])
237
+ continue;
238
+ const bg = c.bg[i];
239
+ const fg = mix(bg, tr.fg, tr.age === 1 ? 0.5 : tr.age === 2 ? 0.28 : 0.14);
240
+ c.putFx(i % c.w, Math.floor(i / c.w), tr.ch, { fg });
241
+ }
242
+ }
243
+ // ---------------------------------------------------------------- glitch
244
+ export function aberrate(c, v) {
245
+ const t = c.theme;
246
+ const g = Math.max(v.baseline, v.glitch);
247
+ if (g <= 0)
248
+ return;
249
+ const f = v.frame;
250
+ for (const h of c.headings) {
251
+ const i0 = c.idx(h.x, h.y);
252
+ const str = [];
253
+ for (let k = 0; k < h.w; k++)
254
+ str.push(c.ch[i0 + k]);
255
+ const first = str.findIndex((ch) => ch !== ' ');
256
+ const last = str.length - 1 - [...str].reverse().findIndex((ch) => ch !== ' ');
257
+ // ghost echoes only while an event glitch is live (idle headings just get the occasional colour split)
258
+ if (first >= 0 && v.glitch > 0.25 && unit(f, h.x, h.y) < g) {
259
+ const bgL = c.bg[c.idx(h.x + first - 1, h.y)] || t.bg;
260
+ c.putFx(h.x + first - 1, h.y, str[first], { fg: mix(bgL, t.magenta, 0.75 * Math.min(1, 0.6 + g)) });
261
+ const bgR = c.bg[c.idx(h.x + last + 1, h.y)] || t.bg;
262
+ c.putFx(h.x + last + 1, h.y, str[last], { fg: mix(bgR, t.cyan, 0.75 * Math.min(1, 0.6 + g)) });
263
+ }
264
+ // per-glyph colour split: a few glyphs take the magenta / cyan fringe (glyph itself untouched)
265
+ for (let k = 0; k < h.w; k++) {
266
+ const i = i0 + k;
267
+ if (c.ch[i] === ' ' || c.shield[i])
268
+ continue;
269
+ const r = unit(f >> 1, h.x + k, h.y + 7);
270
+ if (r < g * 0.45)
271
+ c.fg[i] = (k & 1) ? t.magenta : t.cyan;
272
+ }
273
+ }
274
+ }
275
+ /** Datamosh: rectangular blocks whose soft cells (borders, rain, blanks) slide sideways; bg of alternate rows tinted. */
276
+ export function mosh(c, v) {
277
+ if (v.glitch <= 0.05)
278
+ return;
279
+ const t = c.theme;
280
+ const f = v.frame;
281
+ const n = Math.round(v.glitch * 7);
282
+ for (let b = 0; b < n; b++) {
283
+ const bw = 8 + Math.floor(unit(f, b, 1) * 40);
284
+ const bh = 1 + Math.floor(unit(f, b, 2) * 4);
285
+ const bx = Math.floor(unit(f, b, 3) * Math.max(1, c.w - bw));
286
+ const by = Math.floor(unit(f, b, 4) * Math.max(1, c.h - bh));
287
+ const k = (unit(f, b, 5) < 0.5 ? -1 : 1) * (1 + Math.floor(unit(f, b, 6) * 3));
288
+ const tint = (b & 1) ? t.magenta : t.cyan;
289
+ for (let y = by; y < Math.min(c.h, by + bh); y++) {
290
+ const row = [];
291
+ for (let x = bx; x < bx + bw; x++) {
292
+ const i = c.idx(x, y);
293
+ row.push([c.ch[i], c.fg[i], c.bg[i], c.hard[i]]);
294
+ }
295
+ for (let x = bx; x < bx + bw; x++) {
296
+ if (x >= c.w)
297
+ break;
298
+ const i = c.idx(x, y);
299
+ if (c.hard[i] || c.shield[i])
300
+ continue;
301
+ const sx = x - k;
302
+ if (sx < bx || sx >= bx + bw || sx >= c.w)
303
+ continue;
304
+ const [sch, sfg, sbg, shard] = row[sx - bx];
305
+ if (shard || c.shield[c.idx(sx, y)])
306
+ continue;
307
+ c.ch[i] = sch;
308
+ c.fg[i] = sfg;
309
+ c.bg[i] = sbg;
310
+ }
311
+ if ((y - by) % 2 === 0)
312
+ for (let x = bx; x < Math.min(c.w, bx + bw); x++) {
313
+ const i = c.idx(x, y);
314
+ if (!c.shield[i])
315
+ c.bg[i] = mix(c.bg[i], tint, 0.12 * v.glitch);
316
+ }
317
+ }
318
+ }
319
+ }
320
+ /** Whole-frame offset: data moves with the frame, nothing covers it. Vacated cells take the theme bg. */
321
+ export function shake(c, v) {
322
+ if (v.shake <= 0)
323
+ return;
324
+ if (c.hasShield)
325
+ return; // task-1340: a shielded ticket never moves, so nothing moves
326
+ const t = c.theme;
327
+ const dx = ((v.frame & 1) ? 1 : -1) * (v.shake >= 3 ? 2 : 1);
328
+ const dy = v.shake >= 2 && (v.frame % 3 === 0) ? 1 : 0;
329
+ const ch = c.ch.slice(), fg = c.fg.slice(), bg = c.bg.slice(), at = c.at.slice(), hard = c.hard.slice();
330
+ for (let y = 0; y < c.h; y++) {
331
+ for (let x = 0; x < c.w; x++) {
332
+ const sx = x - dx, sy = y - dy;
333
+ const i = c.idx(x, y);
334
+ if (sx < 0 || sx >= c.w || sy < 0 || sy >= c.h) {
335
+ c.ch[i] = ' ';
336
+ c.fg[i] = t.text;
337
+ c.bg[i] = t.bg;
338
+ c.at[i] = 0;
339
+ c.hard[i] = 0;
340
+ continue;
341
+ }
342
+ const j = c.idx(sx, sy);
343
+ c.ch[i] = ch[j];
344
+ c.fg[i] = fg[j];
345
+ c.bg[i] = bg[j];
346
+ c.at[i] = at[j];
347
+ c.hard[i] = hard[j];
348
+ }
349
+ }
350
+ }
351
+ /** CRT scanlines: odd rows pulled slightly toward the scan colour. Uniform, so contrast ratios barely move. */
352
+ export function scanlines(c, amount = 0.07) {
353
+ const t = c.theme;
354
+ for (let y = 1; y < c.h; y += 2) {
355
+ const base = c.idx(0, y);
356
+ for (let x = 0; x < c.w; x++)
357
+ if (!c.shield[base + x])
358
+ c.bg[base + x] = mix(c.bg[base + x], t.scan, amount);
359
+ }
360
+ }
361
+ /** Full post-pass. Order matters (see header comment). A no-op when the intensity is off. */
362
+ export function applyFx(c, v, opts = {}) {
363
+ if (isCalm(v))
364
+ return c;
365
+ if (opts.rain !== false) {
366
+ rain(c, v);
367
+ trails(c, v);
368
+ }
369
+ aberrate(c, v);
370
+ mosh(c, v);
371
+ shake(c, v);
372
+ tradeFx(c, v); // task-1340: trade-moment post-pass (same putFx/tintFx rules; a no-op without a live moment)
373
+ if (opts.scan !== false)
374
+ scanlines(c);
375
+ return c;
376
+ }
377
+ // ---------------------------------------------------------------- blink transition
378
+ /**
379
+ * t in [0,1]. Phases: 0–0.28 eyelids close · 0.28–0.40 black beat · 0.40–0.62 code flood · 0.62–1 decrypt.
380
+ * Returns a fresh Canvas. `from` may be null (boot). Any keypress in the interactive loop skips to t = 1.
381
+ */
382
+ export function blinkFrame(from, to, t, v) {
383
+ const th = to.theme;
384
+ const out = new Canvas(to.w, to.h, th);
385
+ const W = to.w, H = to.h;
386
+ const f = v.frame;
387
+ const pal = th.rain;
388
+ const code = v.code;
389
+ const lidStyle = { fg: th.borderFocus, bg: '#000000', soft: true };
390
+ const black = { fg: th.text, bg: '#000000', soft: true };
391
+ if (t < 0.28) {
392
+ if (from && from.w === W && from.h === H)
393
+ out.copyFrom(from);
394
+ else
395
+ out.fill(0, 0, W, H, ' ', black);
396
+ const p = t / 0.28;
397
+ const half = Math.ceil(H / 2);
398
+ const cover = Math.floor(p * half);
399
+ for (let y = 0; y < cover; y++)
400
+ out.fill(0, y, W, 1, ' ', black);
401
+ for (let y = H - cover; y < H; y++)
402
+ out.fill(0, y, W, 1, ' ', black);
403
+ if (cover > 0 && cover < half) {
404
+ out.fill(0, cover, W, 1, '▀', lidStyle);
405
+ out.fill(0, H - cover - 1, W, 1, '▄', lidStyle);
406
+ }
407
+ return out;
408
+ }
409
+ if (t < 0.40) {
410
+ out.fill(0, 0, W, H, ' ', black);
411
+ return out;
412
+ }
413
+ if (t < 0.62) {
414
+ out.fill(0, 0, W, H, ' ', black);
415
+ const p = (t - 0.40) / 0.22;
416
+ for (let y = 0; y < H; y++)
417
+ for (let x = 0; x < W; x++) {
418
+ const r = unit(v.seed, x * 131 + y, 77);
419
+ if (r > 0.25 + 0.65 * p)
420
+ continue;
421
+ const ch = code[hash3(x, y, f) % code.length];
422
+ const tier = unit(x, y, f + 3) < 0.06 ? 3 : unit(x, y, f + 5) < 0.3 ? 2 : 1;
423
+ out.put(x, y, ch, { fg: pal[tier], bg: '#000000', soft: true });
424
+ }
425
+ return out;
426
+ }
427
+ // decrypt: every cell resolves at its own time; unresolved cells show code glyphs
428
+ out.copyFrom(to);
429
+ const p = (t - 0.62) / 0.38;
430
+ for (let y = 0; y < H; y++)
431
+ for (let x = 0; x < W; x++) {
432
+ const r = unit(v.seed, x * 131 + y, 99);
433
+ if (r <= p)
434
+ continue;
435
+ const i = out.idx(x, y);
436
+ const near = r - p < 0.12;
437
+ out.ch[i] = code[hash3(x, y, f) % code.length];
438
+ out.fg[i] = near ? pal[3] : pal[1 + (hash3(x, y, 5) % 2)];
439
+ out.bg[i] = mix(out.bg[i], '#000000', 0.5 * (1 - p));
440
+ out.at[i] = near ? 1 : 0;
441
+ out.hard[i] = 0;
442
+ }
443
+ return out;
444
+ }
445
+ /** Number of blink frames at a given fps so the whole transition stays ≤ 350 ms (never fewer than 4). */
446
+ export function blinkFrames(fps = 30) { return Math.max(4, Math.min(Math.floor(0.35 * fps), 11)); }
447
+ /**
448
+ * Streams `str` in: chars before `revealed` are plain, the next `window` non-space chars scramble
449
+ * through code glyphs, the rest are blank.
450
+ */
451
+ export function decrypt(str, revealed, frame, window = 6, code = CODE) {
452
+ const out = [];
453
+ for (let k = 0; k < str.length; k++) {
454
+ if (k < revealed)
455
+ out.push({ ch: str[k], kind: 'text' });
456
+ else if (k < revealed + window && str[k] !== ' ')
457
+ out.push({ ch: code[hash3(k, frame, 11) % code.length], kind: 'code' });
458
+ else if (k < revealed + window)
459
+ out.push({ ch: ' ', kind: 'text' });
460
+ else
461
+ out.push({ ch: ' ', kind: 'blank' });
462
+ }
463
+ return out;
464
+ }
465
+ export function spinnerGlyph(frame) { return SPINNER[Math.floor(frame / 3) % SPINNER.length]; }
466
+ /** Draw a decrypting line of text; returns x after the last revealed/coded glyph. */
467
+ export function decryptText(c, x, y, str, revealed, frame, st, maxW = Infinity, code = CODE) {
468
+ const t = c.theme;
469
+ const cells = decrypt(str, revealed, frame, 6, code);
470
+ const lim = Math.min(cells.length, maxW);
471
+ for (let k = 0; k < lim; k++) {
472
+ const cell = cells[k];
473
+ if (cell.kind === 'blank')
474
+ break;
475
+ if (cell.kind === 'code')
476
+ c.put(x + k, y, cell.ch, { fg: t.rain[2], bg: st.bg, soft: true });
477
+ else
478
+ c.put(x + k, y, cell.ch, st);
479
+ }
480
+ return x + lim;
481
+ }
482
+ // ---------------------------------------------------------------- boot logo
483
+ /** 5-row block font for the letters used by the logo. */
484
+ const FONT = {
485
+ D: ['████ ', '█ █', '█ █', '█ █', '████ '],
486
+ E: ['█████', '█ ', '████ ', '█ ', '█████'],
487
+ X: ['█ █', ' █ █ ', ' █ ', ' █ █ ', '█ █'],
488
+ T: ['█████', ' █ ', ' █ ', ' █ ', ' █ '],
489
+ R: ['████ ', '█ █', '████ ', '█ █ ', '█ █'],
490
+ M: ['█ █', '██ ██', '█ █ █', '█ █', '█ █'],
491
+ ' ': [' ', ' ', ' ', ' ', ' '],
492
+ };
493
+ export function logoRows(word = 'DEXTERM') {
494
+ const rows = ['', '', '', '', ''];
495
+ for (const ch of word) {
496
+ const g = FONT[ch] || FONT[' '];
497
+ for (let r = 0; r < 5; r++)
498
+ rows[r] += g[r] + ' ';
499
+ }
500
+ return rows.map((r) => r.replace(/\s+$/, ''));
501
+ }
502
+ /** Big glitching logo. `intensity` 0..1 controls row jitter + colour split; returns the rect it occupies. */
503
+ export function bootLogo(c, cx, cy, v, intensity = 0.3, word = 'DEXTERM') {
504
+ const t = c.theme;
505
+ const rows = logoRows(word);
506
+ const w = Math.max(...rows.map((r) => r.length));
507
+ const x0 = Math.floor(cx - w / 2);
508
+ const f = v.frame;
509
+ rows.forEach((row, r) => {
510
+ const jitter = unit(f, r, 21) < intensity ? (unit(f, r, 22) < 0.5 ? -1 : 1) * (1 + Math.floor(unit(f, r, 23) * 2)) : 0;
511
+ const color = t.logo[r % t.logo.length];
512
+ for (let k = 0; k < row.length; k++) {
513
+ if (row[k] === ' ')
514
+ continue;
515
+ const x = x0 + k + jitter, y = cy + r;
516
+ const split = unit(f >> 1, k, r) < intensity * 0.5;
517
+ const fg = split ? ((k & 1) ? t.magenta : t.cyan) : color;
518
+ // ghost fringe one cell either side when intensity is high
519
+ if (intensity > 0.5) {
520
+ c.putFx(x - 1, y, '▌', { fg: mix(t.bg, t.magenta, 0.6) });
521
+ c.putFx(x + 1, y, '▐', { fg: mix(t.bg, t.cyan, 0.6) });
522
+ }
523
+ c.put(x, y, '█', { fg, bg: t.bg, soft: true });
524
+ }
525
+ });
526
+ return { x: x0, y: cy, w, h: 5 };
527
+ }
@@ -0,0 +1,141 @@
1
+ import { rgbToHex, hexToRgb } from "../color.js";
2
+ import { sampleGrid, ditherTo256, gridHex, luma, nearest256 } from "./pixels.js";
3
+ // Sampled (and dithered) grids per image × box × surface × depth: the sampler reads every thumbnail pixel
4
+ // (96² for a 2-cell icon), which is the whole cost of block art; a screenful of rows re-asks for the same
5
+ // grids 30 times a second. Bounded LRU; a theme change or a new size is just a different key.
6
+ const GRIDS = new Map();
7
+ const GRID_CAP = 1024;
8
+ function gridFor(img, gw, gh, opts) {
9
+ const key = opts.cacheKey ? `${opts.cacheKey}|${gw}x${gh}|${opts.bg}|${opts.depth}|${opts.dither === false ? 'n' : 'd'}` : null;
10
+ if (key) {
11
+ const hit = GRIDS.get(key);
12
+ if (hit) {
13
+ GRIDS.delete(key);
14
+ GRIDS.set(key, hit);
15
+ return hit;
16
+ }
17
+ }
18
+ const grid = sampleGrid(img, gw, gh, opts.bg);
19
+ if (opts.depth === '256' && opts.dither !== false)
20
+ ditherTo256(grid, gw, gh);
21
+ else if (opts.depth === '256')
22
+ for (let i = 0; i < gw * gh; i++) {
23
+ const [r, g, b] = nearest256(grid[i * 3], grid[i * 3 + 1], grid[i * 3 + 2]);
24
+ grid[i * 3] = r;
25
+ grid[i * 3 + 1] = g;
26
+ grid[i * 3 + 2] = b;
27
+ }
28
+ if (key) {
29
+ GRIDS.set(key, grid);
30
+ if (GRIDS.size > GRID_CAP)
31
+ GRIDS.delete(GRIDS.keys().next().value);
32
+ }
33
+ return grid;
34
+ }
35
+ // bit 8 = top-left, 4 = top-right, 2 = bottom-left, 1 = bottom-right set to the FOREGROUND colour
36
+ const QUAD = [' ', '▗', '▖', '▄', '▝', '▐', '▞', '▟', '▘', '▚', '▌', '▙', '▀', '▜', '▛', '█'];
37
+ /** Draw `img` into the `w × h` cell box at (x, y). Pure cell writes; never throws on an odd box. */
38
+ export function drawBlockArt(c, x, y, w, h, img, opts) {
39
+ if (w <= 0 || h <= 0 || img.w <= 0 || img.h <= 0)
40
+ return;
41
+ const mode = opts.mode ?? 'half';
42
+ const gw = mode === 'half' ? w : w * 2, gh = h * 2;
43
+ const grid = gridFor(img, gw, gh, opts);
44
+ c.noRain(x, y, w, h);
45
+ if (mode === 'half') {
46
+ for (let cy = 0; cy < h; cy++) {
47
+ for (let cx = 0; cx < w; cx++) {
48
+ const top = gridHex(grid, (cy * 2) * gw + cx), bot = gridHex(grid, (cy * 2 + 1) * gw + cx);
49
+ c.put(x + cx, y + cy, '▀', { fg: top, bg: bot, hard: true });
50
+ }
51
+ }
52
+ return;
53
+ }
54
+ // quad: cluster the four samples of a cell into two colours by luma, pick the matching glyph
55
+ const px = (gx, gy) => { const i = (gy * gw + gx) * 3; return [grid[i], grid[i + 1], grid[i + 2]]; };
56
+ for (let cy = 0; cy < h; cy++) {
57
+ for (let cx = 0; cx < w; cx++) {
58
+ const s = [px(cx * 2, cy * 2), px(cx * 2 + 1, cy * 2), px(cx * 2, cy * 2 + 1), px(cx * 2 + 1, cy * 2 + 1)];
59
+ const l = s.map((p) => luma(p[0], p[1], p[2]));
60
+ const lo = Math.min(...l), hi = Math.max(...l);
61
+ if (hi - lo < 6) {
62
+ // flat cell: one colour, full block (keeps the exact average instead of a 2-colour split)
63
+ const avg = s.reduce((a, p) => [a[0] + p[0] / 4, a[1] + p[1] / 4, a[2] + p[2] / 4], [0, 0, 0]);
64
+ const col = quantHex(avg[0], avg[1], avg[2], opts.depth);
65
+ c.put(x + cx, y + cy, '█', { fg: col, bg: col, hard: true });
66
+ continue;
67
+ }
68
+ const mid = (lo + hi) / 2;
69
+ let bits = 0;
70
+ const fgS = [], bgS = [];
71
+ for (let k = 0; k < 4; k++) {
72
+ if (l[k] >= mid) {
73
+ bits |= 8 >> k;
74
+ fgS.push(s[k]);
75
+ }
76
+ else
77
+ bgS.push(s[k]);
78
+ }
79
+ const mean = (xs) => [xs.reduce((a, p) => a + p[0], 0) / xs.length, xs.reduce((a, p) => a + p[1], 0) / xs.length, xs.reduce((a, p) => a + p[2], 0) / xs.length];
80
+ const fgC = mean(fgS), bgC = mean(bgS);
81
+ c.put(x + cx, y + cy, QUAD[bits], { fg: quantHex(fgC[0], fgC[1], fgC[2], opts.depth), bg: quantHex(bgC[0], bgC[1], bgC[2], opts.depth), hard: true });
82
+ }
83
+ }
84
+ }
85
+ function quantHex(r, g, b, depth) {
86
+ if (depth === '256') {
87
+ const [qr, qg, qb] = nearest256(r, g, b);
88
+ return rgbToHex(qr, qg, qb);
89
+ }
90
+ return rgbToHex(Math.round(r), Math.round(g), Math.round(b));
91
+ }
92
+ // ---------------------------------------------------------------------------------------------- placeholder
93
+ /** FNV-1a over the symbol → a stable hue; saturated but not neon so numbers beside it stay readable. */
94
+ export function placeholderColor(symbol, depth = 'truecolor') {
95
+ let h = 0x811c9dc5;
96
+ for (const ch of symbol.toUpperCase()) {
97
+ h ^= ch.codePointAt(0);
98
+ h = Math.imul(h, 0x01000193) >>> 0;
99
+ }
100
+ const hue = (h % 360) / 360;
101
+ const [r, g, b] = hslToRgb(hue, 0.62, 0.42);
102
+ if (depth === '256') {
103
+ const [qr, qg, qb] = nearest256(r, g, b);
104
+ return rgbToHex(qr, qg, qb);
105
+ }
106
+ return rgbToHex(r, g, b);
107
+ }
108
+ function hslToRgb(h, s, l) {
109
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s, p = 2 * l - q;
110
+ const f = (t) => { t = (t + 1) % 1; if (t < 1 / 6)
111
+ return p + (q - p) * 6 * t; if (t < 1 / 2)
112
+ return q; if (t < 2 / 3)
113
+ return p + (q - p) * (2 / 3 - t) * 6; return p; };
114
+ return [Math.round(f(h + 1 / 3) * 255), Math.round(f(h) * 255), Math.round(f(h - 1 / 3) * 255)];
115
+ }
116
+ /** The letter a placeholder shows: the first letter/digit of the ticker, else `?`. */
117
+ export function placeholderInitial(symbol) {
118
+ const m = /[A-Za-z0-9]/.exec(symbol);
119
+ return m ? m[0].toUpperCase() : '?';
120
+ }
121
+ /** Ticker initial on a hashed colour disc, sized to the box. Never an error glyph. */
122
+ export function drawPlaceholder(c, x, y, w, h, symbol, opts) {
123
+ if (w <= 0 || h <= 0)
124
+ return;
125
+ const col = placeholderColor(symbol, opts.depth);
126
+ const [r, g, b] = hexToRgb(col);
127
+ const ink = luma(r, g, b) > 140 ? '#000000' : '#ffffff';
128
+ c.noRain(x, y, w, h);
129
+ // a rounded look: at 2+ rows, soften the four corner cells with half blocks toward the surface
130
+ for (let cy = 0; cy < h; cy++)
131
+ for (let cx = 0; cx < w; cx++)
132
+ c.put(x + cx, y + cy, ' ', { fg: ink, bg: col, hard: true });
133
+ if (h >= 2 && w >= 4) {
134
+ c.put(x, y, '▗', { fg: col, bg: opts.bg, hard: true });
135
+ c.put(x + w - 1, y, '▖', { fg: col, bg: opts.bg, hard: true });
136
+ c.put(x, y + h - 1, '▝', { fg: col, bg: opts.bg, hard: true });
137
+ c.put(x + w - 1, y + h - 1, '▘', { fg: col, bg: opts.bg, hard: true });
138
+ }
139
+ const letter = placeholderInitial(symbol);
140
+ c.put(x + Math.floor((w - 1) / 2), y + Math.floor((h - 1) / 2), letter, { fg: ink, bg: col, b: true, d: !!opts.pending, hard: true });
141
+ }