dexterm-cli 0.0.0-stage → 0.1.1
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 +379 -0
- package/dist/packages/agents/src/executor.js +177 -0
- package/dist/packages/agents/src/gate.js +176 -0
- package/dist/packages/agents/src/identity.js +179 -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 +114 -0
- package/dist/packages/agents/src/tools.js +69 -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 +67 -0
- package/dist/packages/cli/assets/claude/plugin/skills/rug-check/SKILL.md +64 -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 +303 -0
- package/dist/packages/cli/src/agent-roster.js +26 -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 +149 -0
- package/dist/packages/cli/src/boot-state.js +62 -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 +361 -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 +509 -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 +5583 -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 +339 -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/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/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/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 +515 -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/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 +208 -0
- package/dist/packages/data/src/intel/helius-history.js +59 -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/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 +170 -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 +247 -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 +849 -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 +19 -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/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 +15 -0
- package/dist/packages/tui/src/keymap.js +355 -0
- package/dist/packages/tui/src/layout.js +11 -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 +395 -0
- package/dist/packages/voice/src/doctor.js +63 -0
- package/dist/packages/voice/src/engines.js +44 -0
- package/dist/packages/voice/src/index.js +16 -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 +102 -0
- package/dist/packages/voice/src/summary.js +44 -0
- package/dist/packages/voice/src/tts/fake.js +30 -0
- package/dist/packages/voice/src/tts/say.js +96 -0
- package/dist/packages/voice/src/types.js +3 -0
- package/package.json +42 -4
- package/README.md +0 -3
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// dexterm popup — shared page harness (task-1290). Inlined BEFORE each page script under a CSP nonce.
|
|
2
|
+
// Provides the window.DT API the page scripts build on. No remote content, no innerHTML: every untrusted
|
|
3
|
+
// string (token name, symbol, address, feed text) is rendered with textContent / fillText only.
|
|
4
|
+
//
|
|
5
|
+
// Channels: a WebSocket to the TUI's loopback server (ticket → one-time token in the page's meta tags) and
|
|
6
|
+
// a native shell bridge (wry ipc) for drag / close. The page may ONLY send the allow-listed events in
|
|
7
|
+
// PopupToTui; the server drops anything else.
|
|
8
|
+
(() => {
|
|
9
|
+
'use strict';
|
|
10
|
+
const meta = (n) => document.querySelector(`meta[name="${n}"]`)?.content || '';
|
|
11
|
+
const WS_TOKEN = meta('dexterm-ws'), POPUP_ID = meta('dexterm-popup'), PORT = meta('dexterm-port');
|
|
12
|
+
|
|
13
|
+
// ---------------------------------------------------------------- color helpers
|
|
14
|
+
function hex(c) { const n = parseInt(String(c).slice(1), 16); return [(n >> 16) & 255, (n >> 8) & 255, n & 255]; }
|
|
15
|
+
function mix(a, b, t) { const A = hex(a), B = hex(b); return `rgb(${A.map((v, i) => Math.round(v + (B[i] - v) * t)).join(',')})`; }
|
|
16
|
+
function rgba(c, a) { const [r, g, b] = hex(c); return `rgba(${r},${g},${b},${a})`; }
|
|
17
|
+
const shortAddr = (s, n = 4) => { s = String(s); return s.length > 2 * n + 1 ? `${s.slice(0, n)}…${s.slice(-n)}` : s; };
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------- theme
|
|
20
|
+
let T = { bg: '#020507', panel: '#03090c', border: '#0c3a24', borderFocus: '#39ff14', text: '#eafff0', muted: '#62c98a', dim: '#3aa866', accent: '#00f0ff', up: '#5dff6e', down: '#ff3b6b', warn: '#f5ff2d', ai: '#d97757', pump: '#ff2bd6', dbc: '#ff8a3d', rain: ['#06301a', '#0b5a2c', '#16a848', '#b8ffc8'], rainPump: ['#3a2a05', '#8a6410', '#f0b429', '#fff1b3'], rainDump: ['#3a0a12', '#7a1226', '#d12a4a', '#ffb3c1'], scan: '#000000' };
|
|
21
|
+
const themeCbs = [];
|
|
22
|
+
function applyTheme(t) {
|
|
23
|
+
T = { ...T, ...t };
|
|
24
|
+
const r = document.documentElement.style;
|
|
25
|
+
for (const k of ['bg', 'panel', 'panelAlt', 'border', 'borderFocus', 'text', 'muted', 'dim', 'accent', 'up', 'down', 'warn', 'ai', 'pump', 'dbc']) if (T[k]) r.setProperty(`--${k}`, T[k]);
|
|
26
|
+
if (rain) rain.invalidate();
|
|
27
|
+
for (const cb of themeCbs) try { cb(T); } catch { /* page error reported separately */ }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ---------------------------------------------------------------- matrix rain (full panel, glyph atlas → drawImage)
|
|
31
|
+
const KATAKANA = 'ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン';
|
|
32
|
+
const HEX = '0123456789ABCDEF∷⋮⋯╳◇◆';
|
|
33
|
+
const CELL = 14, CELL_H = 16;
|
|
34
|
+
class Rain {
|
|
35
|
+
constructor(canvas, cfg) {
|
|
36
|
+
this.canvas = canvas; this.ctx = canvas.getContext('2d', { alpha: true, desynchronized: true });
|
|
37
|
+
this.cfg = cfg || { enabled: true, speedScale: 1, density: 0.55 };
|
|
38
|
+
this.atlas = null; this.drops = []; this.cols = 0; this.rows = 0; this.W = 0; this.H = 0; this.dpr = 1;
|
|
39
|
+
this.mood = 'rain'; this.activity = this.cfg.density; this.sources = [];
|
|
40
|
+
}
|
|
41
|
+
invalidate() { this.atlas = null; }
|
|
42
|
+
setSources(arr) { this.sources = (arr || []).filter(Boolean); this.atlas = null; }
|
|
43
|
+
setActivity(a) { this.activity = Math.max(0.1, Math.min(1, a)); }
|
|
44
|
+
setMood(m) { if (m && m !== this.mood) { this.mood = m; this.atlas = null; } }
|
|
45
|
+
resize(W, H) {
|
|
46
|
+
this.W = W; this.H = H; this.dpr = 1; // rain renders at 1x: phosphor can be soft, the viz above it cannot
|
|
47
|
+
this.canvas.width = Math.max(1, Math.floor(W)); this.canvas.height = Math.max(1, Math.floor(H));
|
|
48
|
+
this.cols = Math.floor(W / CELL); this.rows = Math.floor(H / CELL_H); this.drops = [];
|
|
49
|
+
for (let c = 0; c < this.cols; c++) this.drops.push(this.newDrop(c, true));
|
|
50
|
+
this.atlas = null;
|
|
51
|
+
}
|
|
52
|
+
randGlyph() { return Math.random() < 0.7 ? KATAKANA[Math.floor(Math.random() * KATAKANA.length)] : HEX[Math.floor(Math.random() * HEX.length)]; }
|
|
53
|
+
newDrop(c, init) {
|
|
54
|
+
const src = this.sources.length && Math.random() < 0.5 ? this.sources[Math.floor(Math.random() * this.sources.length)] : null;
|
|
55
|
+
const len = 6 + Math.floor(Math.random() * 14);
|
|
56
|
+
const cells = Array.from({ length: len }, (_, i) => (src && i < src.length ? src[src.length - 1 - i] : this.randGlyph()));
|
|
57
|
+
const speed = (0.18 + 0.5 * this.activity) * (0.6 + Math.random() * 0.9) * (this.cfg.speedScale || 1);
|
|
58
|
+
return { c, y: init ? Math.random() * this.rows * 2 - this.rows : -len - Math.random() * this.rows, speed, len, cells, period: 1 + Math.floor(Math.random() * 3), t: 0 };
|
|
59
|
+
}
|
|
60
|
+
buildAtlas() {
|
|
61
|
+
const glyphs = [...KATAKANA, ...HEX, ...this.sources.join('')].filter((c, i, a) => a.indexOf(c) === i);
|
|
62
|
+
const ramp = T[this.mood] || T.rain;
|
|
63
|
+
const cv = document.createElement('canvas'); cv.width = Math.ceil(glyphs.length * CELL); cv.height = Math.ceil(4 * CELL_H);
|
|
64
|
+
const g = cv.getContext('2d'); g.textBaseline = 'top'; g.textAlign = 'center';
|
|
65
|
+
for (let tier = 0; tier < 4; tier++) { g.font = `${tier === 3 ? 'bold ' : ''}12px ui-monospace, Menlo, monospace`; g.fillStyle = ramp[tier]; for (let i = 0; i < glyphs.length; i++) g.fillText(glyphs[i], i * CELL + CELL / 2, tier * CELL_H + 1); }
|
|
66
|
+
this.atlas = { cv, index: new Map(glyphs.map((c, i) => [c, i])) };
|
|
67
|
+
}
|
|
68
|
+
draw() {
|
|
69
|
+
if (!this.cfg.enabled) return;
|
|
70
|
+
if (!this.atlas) this.buildAtlas();
|
|
71
|
+
const { cv, index } = this.atlas; const ctx = this.ctx;
|
|
72
|
+
ctx.clearRect(0, 0, this.W, this.H);
|
|
73
|
+
const density = 0.4 + 0.55 * this.activity;
|
|
74
|
+
for (const d of this.drops) {
|
|
75
|
+
if (++d.t % d.period === 0) d.y += d.speed * d.period;
|
|
76
|
+
if (d.y - d.len > this.rows) { this.drops[d.c] = this.newDrop(d.c, false); continue; }
|
|
77
|
+
if (Math.random() > density + 0.3) continue;
|
|
78
|
+
for (let i = 0; i < d.len; i++) {
|
|
79
|
+
const row = Math.floor(d.y) - i; if (row < 0 || row >= this.rows) continue;
|
|
80
|
+
if (Math.random() < 0.04) d.cells[i] = this.randGlyph();
|
|
81
|
+
const tier = i === 0 ? 3 : i < 3 ? 2 : i < d.len * 0.6 ? 1 : 0;
|
|
82
|
+
const gi = index.get(d.cells[i]); if (gi == null) continue;
|
|
83
|
+
ctx.drawImage(cv, gi * CELL, tier * CELL_H, CELL, CELL_H, d.c * CELL, row * CELL_H, CELL, CELL_H);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
let rain = null;
|
|
89
|
+
|
|
90
|
+
// ---------------------------------------------------------------- shell bridge (wry ipc; Electron/WK kept for parity)
|
|
91
|
+
function shellPost(obj) {
|
|
92
|
+
const s = JSON.stringify(obj);
|
|
93
|
+
if (window.ipc && window.ipc.postMessage) return window.ipc.postMessage(s);
|
|
94
|
+
if (window.dextermShell && window.dextermShell.post) return window.dextermShell.post(s);
|
|
95
|
+
if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.dexterm) return window.webkit.messageHandlers.dexterm.postMessage(s);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ---------------------------------------------------------------- WebSocket to the TUI's loopback server
|
|
99
|
+
let ws = null, queue = [];
|
|
100
|
+
const handlers = { hello: [], theme: [], data: [], update: [], pause: [], focus: [], close: [] };
|
|
101
|
+
function send(o) { if (ws && ws.readyState === 1) ws.send(JSON.stringify(o)); else queue.push(o); }
|
|
102
|
+
let rainCfg = { enabled: true, speedScale: 1, density: 0.55 };
|
|
103
|
+
function connect() {
|
|
104
|
+
ws = new WebSocket(`ws://127.0.0.1:${PORT}/ws?t=${encodeURIComponent(WS_TOKEN)}`);
|
|
105
|
+
ws.onopen = () => { for (const q of queue) ws.send(JSON.stringify(q)); queue = []; send({ type: 'ready', page: DT.page, t: performance.now() }); };
|
|
106
|
+
ws.onmessage = (e) => {
|
|
107
|
+
let m; try { m = JSON.parse(e.data); } catch { return; }
|
|
108
|
+
switch (m.type) {
|
|
109
|
+
case 'hello': if (m.rain) { rainCfg = m.rain; if (rain) rain.cfg = m.rain; } if (m.theme) applyTheme(m.theme); fire('hello', m); break;
|
|
110
|
+
case 'theme': applyTheme(m.tokens || {}); break;
|
|
111
|
+
case 'data': fire('data', m.payload, m.topic); break; // (task-1358 r3) the topic rides along for the widget wrapper
|
|
112
|
+
case 'update': {
|
|
113
|
+
const p = m.patch || {};
|
|
114
|
+
if (rain) { if (p.activity != null) rain.setActivity(+p.activity); if (p.mood) rain.setMood(p.mood); if (p.rain != null) rain.cfg = { ...rain.cfg, enabled: !!p.rain }; }
|
|
115
|
+
fire('update', p); break;
|
|
116
|
+
}
|
|
117
|
+
case 'pause': DT.paused = !!m.on; fire('pause', DT.paused); break;
|
|
118
|
+
case 'focus': window.focus(); fire('focus', null); break;
|
|
119
|
+
case 'close': fire('close', null); window.close && window.close(); break;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
ws.onclose = () => { const s = document.getElementById('status'); if (s) { s.textContent = 'disconnected'; s.style.color = T.down; } };
|
|
123
|
+
}
|
|
124
|
+
function fire(type, arg, extra) { for (const cb of handlers[type]) try { cb(arg, extra); } catch (err) { send({ type: 'log', msg: `handler ${type}: ${err && err.message}` }); } }
|
|
125
|
+
|
|
126
|
+
// ---------------------------------------------------------------- chrome wiring (drag strip + close button + Escape)
|
|
127
|
+
function wireChrome() {
|
|
128
|
+
const drag = document.getElementById('drag'), close = document.getElementById('close');
|
|
129
|
+
if (drag) drag.addEventListener('mousedown', (e) => { if (e.button === 0 && e.target.id !== 'close') { send({ type: 'event', name: 'popup.drag', payload: {} }); shellPost({ type: 'drag' }); } });
|
|
130
|
+
if (close) close.addEventListener('click', requestClose);
|
|
131
|
+
// task-1325: a page may consume Esc first (e.g. to cancel an armed drawing tool) via DT.onEscape.
|
|
132
|
+
window.addEventListener('keydown', (e) => { if (e.key === 'Escape' && !(DT.onEscape && DT.onEscape())) requestClose(); });
|
|
133
|
+
}
|
|
134
|
+
// The TUI hears the request over the WS (and closes its handle); the native shell closes this window itself
|
|
135
|
+
// on the ipc message (task-1325), so a close never depends on the TUI being responsive.
|
|
136
|
+
function requestClose() { send({ type: 'event', name: 'popup.close-request', payload: {} }); shellPost({ type: 'close-request' }); }
|
|
137
|
+
|
|
138
|
+
// ---------------------------------------------------------------- rAF loop + perf reporter (self-throttled to fpsCap)
|
|
139
|
+
function loop(renderFn) {
|
|
140
|
+
const frameTimes = []; let frames = 0, lastPerfT = performance.now(), lastT = performance.now(), longFrames = 0, lastFrameAt = 0;
|
|
141
|
+
const minMs = 1000 / (DT.fpsCap || 60) - 0.5;
|
|
142
|
+
const fpsEl = document.getElementById('fps');
|
|
143
|
+
function frame(now) {
|
|
144
|
+
requestAnimationFrame(frame);
|
|
145
|
+
if (DT.paused) { lastT = now; return; }
|
|
146
|
+
if (now - lastFrameAt < minMs) return;
|
|
147
|
+
const dt = now - lastT; lastT = now; lastFrameAt = now; frames++; frameTimes.push(dt); if (dt > 20) longFrames++;
|
|
148
|
+
if (rain && (DT.rainEvery <= 1 || frames % DT.rainEvery === 0)) rain.draw();
|
|
149
|
+
try { renderFn(now, dt); } catch (err) { send({ type: 'log', msg: `render: ${err && err.message}` }); }
|
|
150
|
+
if (now - lastPerfT >= 1000) {
|
|
151
|
+
const fps = frames * 1000 / (now - lastPerfT);
|
|
152
|
+
const sorted = frameTimes.slice().sort((a, b) => a - b); const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0;
|
|
153
|
+
const mean = frameTimes.reduce((s, v) => s + v, 0) / (frameTimes.length || 1);
|
|
154
|
+
if (fpsEl) fpsEl.textContent = `${fps.toFixed(0)} fps · ${mean.toFixed(1)} ms`;
|
|
155
|
+
send({ type: 'perf', fps: +fps.toFixed(1), frameMs: +mean.toFixed(2), p95Ms: +p95.toFixed(2), long: longFrames, dpr: Math.min(2, window.devicePixelRatio || 1), w: innerWidth, h: innerHeight, pwned: typeof window.__pwned });
|
|
156
|
+
frames = 0; frameTimes.length = 0; longFrames = 0; lastPerfT = now;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
requestAnimationFrame(frame);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ---------------------------------------------------------------- public API
|
|
163
|
+
const DT = {
|
|
164
|
+
page: 'popup', paused: false, fpsCap: 60,
|
|
165
|
+
/** Draw the rain every Nth frame (task-1325: the chart page halves it; the chart itself renders on demand). */
|
|
166
|
+
rainEvery: 1,
|
|
167
|
+
/** True inside the native wry window (its ipc bridge exists), false in the browser fallback. */
|
|
168
|
+
native: !!(window.ipc && window.ipc.postMessage),
|
|
169
|
+
/** Optional page hook: return true to consume Escape before the default close. */
|
|
170
|
+
onEscape: null,
|
|
171
|
+
popupId: POPUP_ID,
|
|
172
|
+
send, shellPost, requestClose, wireChrome, loop,
|
|
173
|
+
hex, mix, rgba, shortAddr,
|
|
174
|
+
get theme() { return T; },
|
|
175
|
+
onData(fn) { handlers.data.push(fn); }, onUpdate(fn) { handlers.update.push(fn); }, onTheme(fn) { themeCbs.push(fn); },
|
|
176
|
+
onHello(fn) { handlers.hello.push(fn); }, onPause(fn) { handlers.pause.push(fn); }, onClose(fn) { handlers.close.push(fn); }, onFocus(fn) { handlers.focus.push(fn); },
|
|
177
|
+
status(text, kind) { const s = document.getElementById('status'); if (s) { s.textContent = text; s.style.color = kind === 'up' ? T.up : kind === 'down' ? T.down : kind === 'warn' ? T.warn : T.muted; } },
|
|
178
|
+
/** Attach a full-panel rain canvas (by element id). Returns the Rain controller. */
|
|
179
|
+
mountRain(canvasId) { const el = document.getElementById(canvasId); if (el) rain = new Rain(el, rainCfg); return rain; },
|
|
180
|
+
rain() { return rain; },
|
|
181
|
+
/** Start the WS connection; call once after handlers are registered. */
|
|
182
|
+
connect,
|
|
183
|
+
};
|
|
184
|
+
window.addEventListener('error', (e) => send({ type: 'log', msg: `error: ${e.message} @${e.lineno}:${e.colno}` }));
|
|
185
|
+
window.addEventListener('unhandledrejection', (e) => send({ type: 'log', msg: `rejection: ${e.reason && (e.reason.message || e.reason)}` }));
|
|
186
|
+
window.DT = DT;
|
|
187
|
+
})();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="dexterm-ws" content="__WS_TOKEN__">
|
|
6
|
+
<meta name="dexterm-popup" content="__POPUP_ID__">
|
|
7
|
+
<meta name="dexterm-port" content="__PORT__">
|
|
8
|
+
<title>dexterm · feeds</title>
|
|
9
|
+
<style nonce="__NONCE__">/*__CSS__*/</style>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="hud" class="hud">
|
|
13
|
+
<div id="drag" class="drag"><span class="mark">✦</span><span id="title" class="title">dexterm · FEEDS</span><span id="status" class="status">connecting…</span><button id="close" class="close" type="button" aria-label="close">✕</button></div>
|
|
14
|
+
<div class="stage"><canvas id="rain"></canvas><div class="body"><div id="rows" class="rows"></div></div></div>
|
|
15
|
+
<div id="foot" class="foot"><span>↑↓ select</span><span>enter open</span><span>esc close</span><span id="fps" class="fps">— fps</span></div>
|
|
16
|
+
</div>
|
|
17
|
+
<script nonce="__NONCE__">/*__COMMON_JS__*/</script>
|
|
18
|
+
<script nonce="__NONCE__">/*__JS__*/</script>
|
|
19
|
+
</body>
|
|
20
|
+
</html>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// dexterm popup — FEEDS HUD (newest-first list over full-panel rain; task-1290 stub but functional).
|
|
2
|
+
// Inlined after common.js under a CSP nonce. No remote content, no innerHTML: every untrusted string
|
|
3
|
+
// (feed text, source, title) is rendered with textContent only. Data is a FeedVM pushed over the WS.
|
|
4
|
+
(() => {
|
|
5
|
+
'use strict';
|
|
6
|
+
const DT = window.DT; DT.page = 'feeds';
|
|
7
|
+
const $ = (id) => document.getElementById(id);
|
|
8
|
+
const titleEl = $('title'), rowsEl = $('rows');
|
|
9
|
+
const rain = DT.mountRain('rain');
|
|
10
|
+
let items = [], sel = 0;
|
|
11
|
+
|
|
12
|
+
function relTime(t) {
|
|
13
|
+
if (t == null) return '';
|
|
14
|
+
const s = Math.max(0, Math.floor((Date.now() - t) / 1000));
|
|
15
|
+
if (s < 60) return s + 's';
|
|
16
|
+
if (s < 3600) return Math.floor(s / 60) + 'm';
|
|
17
|
+
if (s < 86400) return Math.floor(s / 3600) + 'h';
|
|
18
|
+
return Math.floor(s / 86400) + 'd';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function resize() { if (rain) rain.resize(window.innerWidth, window.innerHeight); }
|
|
22
|
+
window.addEventListener('resize', resize);
|
|
23
|
+
|
|
24
|
+
function clear(el) { while (el.firstChild) el.removeChild(el.firstChild); }
|
|
25
|
+
|
|
26
|
+
// newest-first; selection highlights a .row.sel; every value is textContent only.
|
|
27
|
+
function render() {
|
|
28
|
+
clear(rowsEl);
|
|
29
|
+
if (!items.length) {
|
|
30
|
+
const row = document.createElement('div'); row.className = 'row';
|
|
31
|
+
const k = document.createElement('div'); k.className = 'k muted'; k.textContent = 'feed';
|
|
32
|
+
const v = document.createElement('div'); v.className = 'v muted'; v.textContent = 'waiting for feed…';
|
|
33
|
+
row.appendChild(k); row.appendChild(v); rowsEl.appendChild(row);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const ordered = items.slice().sort((a, b) => (b.t || 0) - (a.t || 0));
|
|
37
|
+
for (let i = 0; i < ordered.length; i++) {
|
|
38
|
+
const it = ordered[i];
|
|
39
|
+
const row = document.createElement('div'); row.className = 'row' + (i === sel ? ' sel' : '');
|
|
40
|
+
const k = document.createElement('div'); k.className = 'k';
|
|
41
|
+
const rt = relTime(it.t);
|
|
42
|
+
k.textContent = `${it.source || '—'}${rt ? ' · ' + rt : ''}`;
|
|
43
|
+
const v = document.createElement('div'); v.className = 'v' + (it.kind ? ' ' + kindCls(it.kind) : '');
|
|
44
|
+
v.textContent = it.text == null ? '' : String(it.text);
|
|
45
|
+
row.appendChild(k); row.appendChild(v); rowsEl.appendChild(row);
|
|
46
|
+
}
|
|
47
|
+
const selRow = rowsEl.children[sel];
|
|
48
|
+
if (selRow && selRow.scrollIntoView) selRow.scrollIntoView({ block: 'nearest' });
|
|
49
|
+
}
|
|
50
|
+
const kindCls = (k) => /up|buy|pump|good/i.test(k) ? 'up' : /down|sell|dump|rug|bad/i.test(k) ? 'down' : /warn|alert/i.test(k) ? 'warn' : 'muted';
|
|
51
|
+
|
|
52
|
+
// arrows move the selection; Enter emits the allow-listed feed.selected event (payload index into newest-first list).
|
|
53
|
+
window.addEventListener('keydown', (e) => {
|
|
54
|
+
if (!items.length) return;
|
|
55
|
+
if (e.key === 'ArrowDown') { sel = Math.min(items.length - 1, sel + 1); e.preventDefault(); render(); }
|
|
56
|
+
else if (e.key === 'ArrowUp') { sel = Math.max(0, sel - 1); e.preventDefault(); render(); }
|
|
57
|
+
else if (e.key === 'Enter') { e.preventDefault(); DT.send({ type: 'event', name: 'feed.selected', payload: { i: sel } }); }
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
function setData(vm) {
|
|
61
|
+
vm = vm || {};
|
|
62
|
+
items = Array.isArray(vm.items) ? vm.items : [];
|
|
63
|
+
if (sel >= items.length) sel = Math.max(0, items.length - 1);
|
|
64
|
+
titleEl.textContent = `dexterm · ${vm.title == null ? 'FEEDS' : String(vm.title)}`;
|
|
65
|
+
if (rain) { rain.setActivity(Math.min(1, 0.3 + items.length * 0.03)); rain.setSources(items.slice(0, 4).map((it) => String(it.source || ''))); }
|
|
66
|
+
DT.status(items.length ? items.length + ' items' : 'no feed', items.length ? 'up' : 'warn');
|
|
67
|
+
render();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
DT.onData(setData);
|
|
71
|
+
DT.onTheme(render);
|
|
72
|
+
DT.wireChrome();
|
|
73
|
+
resize();
|
|
74
|
+
render();
|
|
75
|
+
DT.connect();
|
|
76
|
+
DT.loop(() => {});
|
|
77
|
+
})();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="dexterm-ws" content="__WS_TOKEN__">
|
|
6
|
+
<meta name="dexterm-popup" content="__POPUP_ID__">
|
|
7
|
+
<meta name="dexterm-port" content="__PORT__">
|
|
8
|
+
<title>dexterm · narrative</title>
|
|
9
|
+
<style nonce="__NONCE__">/*__CSS__*/</style>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="hud" class="hud">
|
|
13
|
+
<div id="drag" class="drag"><span class="mark">✦</span><span id="title" class="title">dexterm · NARRATIVE</span><span id="status" class="status">connecting…</span><button id="close" class="close" type="button" aria-label="close">✕</button></div>
|
|
14
|
+
<div class="stage"><canvas id="rain"></canvas><div class="body"><div id="rows" class="rows"></div></div></div>
|
|
15
|
+
<div id="foot" class="foot"><span id="meta">—</span><span id="limits">model output: none</span><span id="fps" class="fps">— fps</span></div>
|
|
16
|
+
</div>
|
|
17
|
+
<script nonce="__NONCE__">/*__COMMON_JS__*/</script>
|
|
18
|
+
<script nonce="__NONCE__">/*__JS__*/</script>
|
|
19
|
+
</body>
|
|
20
|
+
</html>
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// dexterm popup — NARRATIVE BOARD HUD (task-1356, AI-SUITE-DESIGN §7). What is running and what is
|
|
2
|
+
// working, over full-panel rain. Deterministic board (no model): every number is from the TUI's board VM.
|
|
3
|
+
// Inlined after common.js under a CSP nonce. No remote content, no innerHTML: every untrusted string
|
|
4
|
+
// (narrative name, runner symbol) is rendered with textContent only.
|
|
5
|
+
(() => {
|
|
6
|
+
'use strict';
|
|
7
|
+
const DT = window.DT; DT.page = 'narrative';
|
|
8
|
+
const $ = (id) => document.getElementById(id);
|
|
9
|
+
const titleEl = $('title'), rowsEl = $('rows'), metaEl = $('meta'), limitsEl = $('limits');
|
|
10
|
+
const rain = DT.mountRain('rain');
|
|
11
|
+
let vm = null;
|
|
12
|
+
|
|
13
|
+
function resize() { if (rain) rain.resize(window.innerWidth, window.innerHeight); }
|
|
14
|
+
window.addEventListener('resize', resize);
|
|
15
|
+
function clear(el) { while (el.firstChild) el.removeChild(el.firstChild); }
|
|
16
|
+
|
|
17
|
+
const fmtUsd = (v) => { if (v == null) return '—'; const a = Math.abs(v); if (a >= 1e9) return (v / 1e9).toFixed(2) + 'B'; if (a >= 1e6) return (v / 1e6).toFixed(2) + 'M'; if (a >= 1e3) return (v / 1e3).toFixed(1) + 'K'; return String(Math.round(v)); };
|
|
18
|
+
const fmtPct = (v) => (v == null ? '—' : (v > 0 ? '+' : '') + v.toFixed(1) + '%');
|
|
19
|
+
|
|
20
|
+
function render() {
|
|
21
|
+
clear(rowsEl);
|
|
22
|
+
const rows = vm && Array.isArray(vm.rows) ? vm.rows : [];
|
|
23
|
+
if (!rows.length) {
|
|
24
|
+
const row = document.createElement('div'); row.className = 'row';
|
|
25
|
+
const k = document.createElement('div'); k.className = 'k muted'; k.textContent = 'narrative';
|
|
26
|
+
const v = document.createElement('div'); v.className = 'v muted'; v.textContent = 'collecting narratives…';
|
|
27
|
+
row.appendChild(k); row.appendChild(v); rowsEl.appendChild(row);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
for (const n of rows) {
|
|
31
|
+
const row = document.createElement('div'); row.className = 'row';
|
|
32
|
+
const k = document.createElement('div'); k.className = 'k';
|
|
33
|
+
k.textContent = `${n.rank == null ? '·' : '#' + n.rank} ${n.narrative}`;
|
|
34
|
+
const v = document.createElement('div'); v.className = 'v';
|
|
35
|
+
const med = n.med_chg;
|
|
36
|
+
const runner = n.runner ? ` · ${n.runner.symbol} ${fmtPct(n.runner.chg)} ${fmtUsd(n.runner.mcap)}` : '';
|
|
37
|
+
v.textContent = `${n.score == null ? '—' : n.score} · ${n.tokens} tok · ${fmtUsd(n.vol)} · ${n.new_1h}/${n.new_24h} new · med ${fmtPct(med)}${runner}`;
|
|
38
|
+
if (med != null) v.className = 'v ' + (med >= 0 ? 'up' : 'down');
|
|
39
|
+
row.appendChild(k); row.appendChild(v); rowsEl.appendChild(row);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function setData(next) {
|
|
44
|
+
vm = next || {};
|
|
45
|
+
titleEl.textContent = `dexterm · NARRATIVE ${vm.window ? vm.window.toUpperCase() : ''}`;
|
|
46
|
+
metaEl.textContent = `${vm.tokens_total ?? 0} tokens · ${vm.unclassified_pct ?? 0}% unclassified · ${vm.copycats ?? 0} copycats · age ${vm.data_age ?? '—'} · lex v${vm.lexicon_version ?? '—'}`;
|
|
47
|
+
limitsEl.textContent = vm.limits || 'model output: none (deterministic). Name-based: X/TG-only narratives are missed.';
|
|
48
|
+
const n = (vm.rows || []).length;
|
|
49
|
+
if (rain) rain.setActivity(Math.min(1, 0.3 + n * 0.05));
|
|
50
|
+
DT.status(n ? n + ' narratives' : 'collecting', n ? 'up' : 'warn');
|
|
51
|
+
render();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
DT.onData(setData);
|
|
55
|
+
DT.onTheme(render);
|
|
56
|
+
DT.wireChrome();
|
|
57
|
+
resize();
|
|
58
|
+
render();
|
|
59
|
+
DT.connect();
|
|
60
|
+
DT.loop(() => {});
|
|
61
|
+
})();
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/* dexterm popup — order plans & standing mandates page styles (task-1355). Reuses the roster layout classes
|
|
2
|
+
(rows, stats, control buttons) and adds the status-tone pill variants the plans/mandate statuses map to.
|
|
3
|
+
The palette comes from the theme CSS vars common.js sets (--text, --muted, --accent, --up, --down, …). */
|
|
4
|
+
.body { overflow-y: auto; padding: 6px 8px; }
|
|
5
|
+
.roster-summary { font: 12px ui-monospace, Menlo, monospace; color: var(--text); margin-bottom: 6px; }
|
|
6
|
+
.roster-k { color: var(--accent); font-weight: 600; }
|
|
7
|
+
.roster-dim { color: var(--dim); }
|
|
8
|
+
|
|
9
|
+
.roster-agents { display: flex; flex-direction: column; gap: 6px; margin-bottom: 4px; }
|
|
10
|
+
.roster-row { border: 1px solid var(--border); border-radius: 6px; padding: 6px 8px; background: color-mix(in srgb, var(--panel) 85%, transparent); }
|
|
11
|
+
.roster-row.paused { opacity: 0.7; border-color: var(--warn); }
|
|
12
|
+
.roster-row-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; font: 12px ui-monospace, Menlo, monospace; }
|
|
13
|
+
.roster-name { color: var(--text); font-weight: 600; }
|
|
14
|
+
.roster-tier { font-size: 10px; padding: 1px 5px; border-radius: 999px; border: 1px solid var(--border); color: var(--muted); }
|
|
15
|
+
/* status tones (buy/armed = up, cancelled/expired/revoked = dim, paused = warn/down, else muted) */
|
|
16
|
+
.tier-up { color: var(--up); border-color: color-mix(in srgb, var(--up) 55%, var(--border)); }
|
|
17
|
+
.tier-dim { color: var(--dim); }
|
|
18
|
+
.tier-muted { color: var(--muted); }
|
|
19
|
+
.tier-bad { color: var(--down); border-color: color-mix(in srgb, var(--down) 55%, var(--border)); }
|
|
20
|
+
|
|
21
|
+
.roster-stats { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 4px; font: 11px ui-monospace, Menlo, monospace; color: var(--muted); }
|
|
22
|
+
.roster-stat.bad { color: var(--down); }
|
|
23
|
+
|
|
24
|
+
.roster-ctl { display: flex; gap: 6px; margin-top: 6px; }
|
|
25
|
+
.roster-btn { font: 11px ui-monospace, Menlo, monospace; padding: 2px 10px; border-radius: 4px; border: 1px solid var(--border); background: transparent; color: var(--text); cursor: pointer; }
|
|
26
|
+
.roster-btn:hover { border-color: var(--borderFocus); }
|
|
27
|
+
.roster-btn.danger { color: var(--down); border-color: color-mix(in srgb, var(--down) 50%, var(--border)); }
|
|
28
|
+
.roster-btn.danger:hover { border-color: var(--down); }
|
|
29
|
+
|
|
30
|
+
.roster-audit-head { margin: 10px 0 4px; font: 11px ui-monospace, Menlo, monospace; color: var(--dim); text-transform: uppercase; letter-spacing: 0.08em; }
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="dexterm-ws" content="__WS_TOKEN__">
|
|
6
|
+
<meta name="dexterm-popup" content="__POPUP_ID__">
|
|
7
|
+
<meta name="dexterm-port" content="__PORT__">
|
|
8
|
+
<title>dexterm · plans</title>
|
|
9
|
+
<style nonce="__NONCE__">/*__CSS__*/</style>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="hud" class="hud">
|
|
13
|
+
<div id="drag" class="drag"><span class="mark">◈</span><span id="title" class="title">dexterm · ORDER PLANS</span><span id="status" class="status">connecting…</span><button id="close" class="close" type="button" aria-label="close">✕</button></div>
|
|
14
|
+
<div class="stage"><canvas id="rain"></canvas><div class="body">
|
|
15
|
+
<div id="summary" class="roster-summary"></div>
|
|
16
|
+
<div class="roster-audit-head">order plans</div>
|
|
17
|
+
<div id="plans" class="roster-agents"></div>
|
|
18
|
+
<div class="roster-audit-head">standing mandates</div>
|
|
19
|
+
<div id="mandates" class="roster-agents"></div>
|
|
20
|
+
</div></div>
|
|
21
|
+
<div id="foot" class="foot"><span>arming needs a keypress at the terminal</span><span>read-only · cancel only</span><span id="fps" class="fps">— fps</span></div>
|
|
22
|
+
</div>
|
|
23
|
+
<script nonce="__NONCE__">/*__COMMON_JS__*/</script>
|
|
24
|
+
<script nonce="__NONCE__">/*__JS__*/</script>
|
|
25
|
+
</body>
|
|
26
|
+
</html>
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// dexterm popup — order plans & standing mandates page (task-1355). Renders the PlansVM: the conditional
|
|
2
|
+
// order plans and the standing mandates with their status and ledgers, plus the journal-integrity note. This
|
|
3
|
+
// popup is the read-only HUD of the signer trust domain: it receives VIEW MODELS only, never a key, a
|
|
4
|
+
// signature, a mandate secret or trade capability. The ONLY message it can send is `plan.cancel` (cancel a
|
|
5
|
+
// plan by id), which the terminal re-validates against its own live list before acting. There is deliberately
|
|
6
|
+
// NO control here to arm, raise a cap, widen or arm a mandate, resume a paused plan, or sign — arming is a
|
|
7
|
+
// physical keypress at the terminal, and the server drops anything off the event allow-list. Every untrusted
|
|
8
|
+
// string (a coin symbol) is rendered with textContent only — never innerHTML.
|
|
9
|
+
(() => {
|
|
10
|
+
'use strict';
|
|
11
|
+
const DT = window.DT; DT.page = 'plans';
|
|
12
|
+
const $ = (id) => document.getElementById(id);
|
|
13
|
+
const el = (tag, cls, text) => { const e = document.createElement(tag); if (cls) e.className = cls; if (text != null) e.textContent = String(text); return e; };
|
|
14
|
+
const usd = (n) => { const x = Number(n); if (!Number.isFinite(x)) return '—'; const a = Math.abs(x); if (a >= 1e6) return `$${(x / 1e6).toFixed(2)}M`; if (a >= 1e3) return `$${(x / 1e3).toFixed(1)}k`; return `$${x.toFixed(2)}`; };
|
|
15
|
+
const ttl = (expiresAt, now) => { const ms = Number(expiresAt) - Number(now); if (!Number.isFinite(ms) || ms <= 0) return 'expired'; const h = Math.floor(ms / 3600000); const m = Math.floor((ms % 3600000) / 60000); return h > 0 ? `${h}h${m}m` : `${m}m`; };
|
|
16
|
+
// Status tone: armed / filling = up, cancelled/failed/expired/revoked = dim, else muted.
|
|
17
|
+
const tone = (status, paused) => paused ? 'bad' : /^(armed|filled)/.test(status) ? 'up' : /^(cancel|fail|expir|revok|done)/.test(status) ? 'dim' : 'muted';
|
|
18
|
+
|
|
19
|
+
let vm = null;
|
|
20
|
+
|
|
21
|
+
function render() {
|
|
22
|
+
if (!vm) return;
|
|
23
|
+
const sum = $('summary'); sum.textContent = '';
|
|
24
|
+
sum.appendChild(el('span', 'roster-k', vm.mode || 'PAPER'));
|
|
25
|
+
// Gate state is read-only here: turning hands-free on/off is a physical keypress at the terminal (G).
|
|
26
|
+
sum.appendChild(el('span', vm.handsFree ? 'roster-k tier-bad' : 'roster-k', vm.handsFree ? ' · HANDS-FREE' : ' · GATE ON'));
|
|
27
|
+
sum.appendChild(el('span', 'roster-dim', ` · ${vm.plans.length} plan${vm.plans.length === 1 ? '' : 's'} · ${vm.mandates.length} mandate${vm.mandates.length === 1 ? '' : 's'} · ${vm.journal || 'journal —'}`));
|
|
28
|
+
|
|
29
|
+
const ph = $('plans'); ph.textContent = '';
|
|
30
|
+
if (!vm.plans.length) ph.appendChild(el('div', 'roster-dim', 'no order plans. propose one: dexterm plan propose (or by voice — a proposal is inert until you arm it with a keypress)'));
|
|
31
|
+
for (const p of vm.plans) {
|
|
32
|
+
const row = el('div', 'roster-row' + (p.paused ? ' paused' : ''));
|
|
33
|
+
const head = el('div', 'roster-row-head');
|
|
34
|
+
head.appendChild(el('span', 'roster-name', p.symbol)); // textContent — untrusted symbol is safe
|
|
35
|
+
head.appendChild(el('span', 'roster-tier tier-' + tone(p.status, p.paused), p.status + (p.paused ? ' · paused' : '')));
|
|
36
|
+
head.appendChild(el('span', 'roster-dim', ` ${p.side} · ${usd(p.sizeUsd)}${p.tier ? ' · ' + p.tier : ''}${p.mandateId ? ' · ' + p.mandateId : ''}`));
|
|
37
|
+
row.appendChild(head);
|
|
38
|
+
row.appendChild(el('div', 'roster-stats', p.readBack)); // textContent — the signer's read-back line
|
|
39
|
+
if (typeof p.levels === 'string' && p.levels) row.appendChild(el('div', 'roster-dim', p.levels)); // task-1366: mc + price per level
|
|
40
|
+
// Cancel is the only write; proposed/armed plans can be cancelled, terminal ones cannot.
|
|
41
|
+
if (!/^(cancel|fail|expir|done)/.test(p.status)) {
|
|
42
|
+
const ctl = el('div', 'roster-ctl');
|
|
43
|
+
const cancel = el('button', 'roster-btn danger', 'cancel'); cancel.type = 'button';
|
|
44
|
+
cancel.addEventListener('click', () => DT.send({ type: 'event', name: 'plan.cancel', payload: { id: p.id } }));
|
|
45
|
+
ctl.appendChild(cancel);
|
|
46
|
+
row.appendChild(ctl);
|
|
47
|
+
}
|
|
48
|
+
ph.appendChild(row);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const mh = $('mandates'); mh.textContent = '';
|
|
52
|
+
if (!vm.mandates.length) mh.appendChild(el('div', 'roster-dim', 'no standing mandates. draft one: dexterm mandate draft (arming is a keypress; caps are signer-side and can never be raised)'));
|
|
53
|
+
for (const m of vm.mandates) {
|
|
54
|
+
const row = el('div', 'roster-row');
|
|
55
|
+
const head = el('div', 'roster-row-head');
|
|
56
|
+
head.appendChild(el('span', 'roster-name', m.id));
|
|
57
|
+
head.appendChild(el('span', 'roster-tier tier-' + tone(m.status, false), m.status));
|
|
58
|
+
head.appendChild(el('span', 'roster-dim', ` ${m.maxTier} · ${ttl(m.expiresAt, vm.now)}`));
|
|
59
|
+
row.appendChild(head);
|
|
60
|
+
const stats = el('div', 'roster-stats');
|
|
61
|
+
stats.appendChild(el('span', 'roster-stat', `budget ${usd(m.totalUsd)}`));
|
|
62
|
+
stats.appendChild(el('span', 'roster-stat', `reserved ${usd(m.reservedUsd)}`));
|
|
63
|
+
stats.appendChild(el('span', 'roster-stat', `spent ${usd(m.spentUsd)}`));
|
|
64
|
+
stats.appendChild(el('span', 'roster-stat', `pnl ${usd(m.realizedUsd)}`));
|
|
65
|
+
stats.appendChild(el('span', 'roster-stat' + (m.lossUsd ? ' bad' : ''), `loss ${usd(m.lossUsd)}`));
|
|
66
|
+
stats.appendChild(el('span', 'roster-stat', `/trade ${usd(m.perTradeUsd)} · /coin ${usd(m.perCoinUsd)}`));
|
|
67
|
+
row.appendChild(stats);
|
|
68
|
+
mh.appendChild(row);
|
|
69
|
+
}
|
|
70
|
+
const armed = vm.plans.filter((p) => /^armed/.test(p.status)).length;
|
|
71
|
+
DT.status(`${vm.plans.length} plan${vm.plans.length === 1 ? '' : 's'}${armed ? ` · ${armed} armed` : ''}`, armed ? 'up' : 'muted');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
DT.onHello(() => { DT.mountRain('rain'); const r = DT.rain(); if (r) { r.setActivity(0.2); r.setMood('rain'); } });
|
|
75
|
+
DT.onData((payload) => { vm = payload; render(); });
|
|
76
|
+
DT.onUpdate(() => { /* plans are pushed as full `data` frames; nothing incremental */ });
|
|
77
|
+
DT.wireChrome();
|
|
78
|
+
let lastTick = 0;
|
|
79
|
+
DT.rainEvery = 2; DT.fpsCap = 30;
|
|
80
|
+
DT.loop((now) => { if (now - lastTick > 1000) { lastTick = now; if (vm) render(); } });
|
|
81
|
+
DT.connect();
|
|
82
|
+
})();
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/* dexterm popup — agent roster page styles (task-1345). Responsive: compact under ~90 cols, roomy wider.
|
|
2
|
+
The palette comes from the theme CSS vars common.js sets (--text, --muted, --accent, --up, --down, …). */
|
|
3
|
+
.body { overflow-y: auto; padding: 6px 8px; }
|
|
4
|
+
.roster-summary { font: 12px ui-monospace, Menlo, monospace; color: var(--text); margin-bottom: 6px; }
|
|
5
|
+
.roster-k { color: var(--accent); font-weight: 600; }
|
|
6
|
+
.roster-dim { color: var(--dim); }
|
|
7
|
+
|
|
8
|
+
.roster-agents { display: flex; flex-direction: column; gap: 6px; }
|
|
9
|
+
.roster-row { border: 1px solid var(--border); border-radius: 6px; padding: 6px 8px; background: color-mix(in srgb, var(--panel) 85%, transparent); }
|
|
10
|
+
.roster-row.paused { opacity: 0.6; border-color: var(--warn); }
|
|
11
|
+
.roster-row-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; font: 12px ui-monospace, Menlo, monospace; }
|
|
12
|
+
.roster-name { color: var(--text); font-weight: 600; }
|
|
13
|
+
.roster-tier { font-size: 10px; padding: 1px 5px; border-radius: 999px; border: 1px solid var(--border); color: var(--muted); }
|
|
14
|
+
.tier-observe { color: var(--muted); }
|
|
15
|
+
.tier-annotate { color: var(--accent); border-color: var(--accent); }
|
|
16
|
+
.tier-propose { color: var(--pump); border-color: var(--pump); }
|
|
17
|
+
.roster-badge { font-size: 10px; padding: 1px 5px; border-radius: 4px; background: var(--warn); color: #000; }
|
|
18
|
+
|
|
19
|
+
.roster-stats { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 4px; font: 11px ui-monospace, Menlo, monospace; color: var(--muted); }
|
|
20
|
+
.roster-stat.bad { color: var(--down); }
|
|
21
|
+
|
|
22
|
+
.roster-ctl { display: flex; gap: 6px; margin-top: 6px; }
|
|
23
|
+
.roster-btn { font: 11px ui-monospace, Menlo, monospace; padding: 2px 10px; border-radius: 4px; border: 1px solid var(--border); background: transparent; color: var(--text); cursor: pointer; }
|
|
24
|
+
.roster-btn:hover { border-color: var(--borderFocus); }
|
|
25
|
+
.roster-btn.danger { color: var(--down); border-color: color-mix(in srgb, var(--down) 50%, var(--border)); }
|
|
26
|
+
.roster-btn.danger:hover { border-color: var(--down); }
|
|
27
|
+
|
|
28
|
+
.roster-audit-head { margin: 10px 0 4px; font: 11px ui-monospace, Menlo, monospace; color: var(--dim); text-transform: uppercase; letter-spacing: 0.08em; }
|
|
29
|
+
.roster-audit { display: flex; flex-direction: column; gap: 1px; font: 11px ui-monospace, Menlo, monospace; }
|
|
30
|
+
.roster-audit-row { display: grid; grid-template-columns: 34px 90px 72px 1fr; gap: 6px; color: var(--muted); white-space: nowrap; overflow: hidden; }
|
|
31
|
+
.roster-audit-row.bad { color: var(--down); }
|
|
32
|
+
.roster-audit-t { color: var(--dim); }
|
|
33
|
+
.roster-audit-agent { color: var(--text); overflow: hidden; text-overflow: ellipsis; }
|
|
34
|
+
.roster-audit-kind { color: var(--accent); }
|
|
35
|
+
.roster-audit-tool { display: none; }
|
|
36
|
+
.roster-audit-detail { overflow: hidden; text-overflow: ellipsis; }
|
|
37
|
+
|
|
38
|
+
@media (min-width: 720px) {
|
|
39
|
+
.roster-audit-row { grid-template-columns: 40px 120px 90px 140px 1fr; }
|
|
40
|
+
.roster-audit-tool { display: inline; color: var(--muted); overflow: hidden; text-overflow: ellipsis; }
|
|
41
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="dexterm-ws" content="__WS_TOKEN__">
|
|
6
|
+
<meta name="dexterm-popup" content="__POPUP_ID__">
|
|
7
|
+
<meta name="dexterm-port" content="__PORT__">
|
|
8
|
+
<title>dexterm · agents</title>
|
|
9
|
+
<style nonce="__NONCE__">/*__CSS__*/</style>
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="hud" class="hud">
|
|
13
|
+
<div id="drag" class="drag"><span class="mark">✦</span><span id="title" class="title">dexterm · AGENTS</span><span id="status" class="status">connecting…</span><button id="close" class="close" type="button" aria-label="close">✕</button></div>
|
|
14
|
+
<div class="stage"><canvas id="rain"></canvas><div class="body">
|
|
15
|
+
<div id="summary" class="roster-summary"></div>
|
|
16
|
+
<div id="agents" class="roster-agents"></div>
|
|
17
|
+
<div class="roster-audit-head">audit log</div>
|
|
18
|
+
<div id="audit" class="roster-audit"></div>
|
|
19
|
+
</div></div>
|
|
20
|
+
<div id="foot" class="foot"><span>pause / kick per agent</span><span>untrusted by default</span><span id="fps" class="fps">— fps</span></div>
|
|
21
|
+
</div>
|
|
22
|
+
<script nonce="__NONCE__">/*__COMMON_JS__*/</script>
|
|
23
|
+
<script nonce="__NONCE__">/*__JS__*/</script>
|
|
24
|
+
</body>
|
|
25
|
+
</html>
|