@toon-protocol/townhouse 0.1.1 → 0.1.3
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/README.md +95 -438
- package/dist/{chunk-W33MEOPM.js → chunk-QHFUIWEN.js} +14 -8
- package/dist/{chunk-W33MEOPM.js.map → chunk-QHFUIWEN.js.map} +1 -1
- package/dist/cli.js +93 -37
- package/dist/cli.js.map +1 -1
- package/dist/compose/townhouse-hs.yml +8 -8
- package/dist/image-manifest.json +10 -10
- package/dist/index.js +1 -1
- package/dist/{tui-OIFXGBTL.js → tui-QE3ZRZO3.js} +21 -8
- package/dist/tui-QE3ZRZO3.js.map +1 -0
- package/package.json +4 -4
- package/dist/tui-OIFXGBTL.js.map +0 -1
|
@@ -22,10 +22,10 @@
|
|
|
22
22
|
# Story 45.4 boots only connector + townhouse-api at apex install
|
|
23
23
|
#
|
|
24
24
|
# Digest placeholders (substituted at build time from dist/image-manifest.json):
|
|
25
|
-
# @sha256:
|
|
26
|
-
# @sha256:
|
|
27
|
-
# @sha256:
|
|
28
|
-
# @sha256:
|
|
25
|
+
# @sha256:2b4b38bba0538ff1be4ff966ba941461485718bf6c2ba09bdf4b75df3c700e84 → @sha256:<hex>
|
|
26
|
+
# @sha256:36b0563b0bfc9fdda1a5723fb1496427188048cdf9f96c813b71c32161532874 → @sha256:<hex>
|
|
27
|
+
# @sha256:1fd4e8015087314d9f99240ecce81f48bac318311f77aeacb57d881594d133cc → @sha256:<hex>
|
|
28
|
+
# @sha256:ebc5f8e38672954a76c69bda90924a3a0ad7cdac42520f22d2e05960d06986ae → @sha256:<hex>
|
|
29
29
|
# @sha256:3343c19649290043e521c81b467b7c6410b8eaedd76d48804ea9b6fc810cddb0 → @sha256:<hex>
|
|
30
30
|
#
|
|
31
31
|
# Scope guard (Story 45.2 does NOT include):
|
|
@@ -152,7 +152,7 @@ services:
|
|
|
152
152
|
# Port D21-008: Fastify host API on 127.0.0.1:28090.
|
|
153
153
|
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
154
154
|
townhouse-api:
|
|
155
|
-
image: ghcr.io/toon-protocol/townhouse-api@sha256:
|
|
155
|
+
image: ghcr.io/toon-protocol/townhouse-api@sha256:2b4b38bba0538ff1be4ff966ba941461485718bf6c2ba09bdf4b75df3c700e84
|
|
156
156
|
container_name: townhouse-hs-api
|
|
157
157
|
# Run as the operator's host UID so bind-mounted ~/.townhouse files
|
|
158
158
|
# (rw------- 600) are readable. TOWNHOUSE_UID is injected by `townhouse hs up`.
|
|
@@ -252,7 +252,7 @@ services:
|
|
|
252
252
|
# start at first run).
|
|
253
253
|
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
254
254
|
town:
|
|
255
|
-
image: ghcr.io/toon-protocol/town@sha256:
|
|
255
|
+
image: ghcr.io/toon-protocol/town@sha256:36b0563b0bfc9fdda1a5723fb1496427188048cdf9f96c813b71c32161532874
|
|
256
256
|
container_name: townhouse-hs-town
|
|
257
257
|
profiles: [town]
|
|
258
258
|
networks:
|
|
@@ -303,7 +303,7 @@ services:
|
|
|
303
303
|
# Lazy-provisioned via Epic 46: `townhouse node add mill`
|
|
304
304
|
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
305
305
|
mill:
|
|
306
|
-
image: ghcr.io/toon-protocol/mill@sha256:
|
|
306
|
+
image: ghcr.io/toon-protocol/mill@sha256:1fd4e8015087314d9f99240ecce81f48bac318311f77aeacb57d881594d133cc
|
|
307
307
|
container_name: townhouse-hs-mill
|
|
308
308
|
profiles: [mill]
|
|
309
309
|
networks:
|
|
@@ -351,7 +351,7 @@ services:
|
|
|
351
351
|
# Lazy-provisioned via Epic 46: `townhouse node add dvm`
|
|
352
352
|
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
353
353
|
dvm:
|
|
354
|
-
image: ghcr.io/toon-protocol/dvm@sha256:
|
|
354
|
+
image: ghcr.io/toon-protocol/dvm@sha256:ebc5f8e38672954a76c69bda90924a3a0ad7cdac42520f22d2e05960d06986ae
|
|
355
355
|
container_name: townhouse-hs-dvm
|
|
356
356
|
profiles: [dvm]
|
|
357
357
|
networks:
|
package/dist/image-manifest.json
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"townhouseVersion": "0.1.
|
|
4
|
-
"builtAt": "2026-06-
|
|
3
|
+
"townhouseVersion": "0.1.3",
|
|
4
|
+
"builtAt": "2026-06-02T13:43:36.534Z",
|
|
5
5
|
"images": {
|
|
6
6
|
"townhouse-api": {
|
|
7
7
|
"name": "ghcr.io/toon-protocol/townhouse-api",
|
|
8
|
-
"tag": "0.1.
|
|
9
|
-
"digest": "sha256:
|
|
8
|
+
"tag": "0.1.3",
|
|
9
|
+
"digest": "sha256:2b4b38bba0538ff1be4ff966ba941461485718bf6c2ba09bdf4b75df3c700e84"
|
|
10
10
|
},
|
|
11
11
|
"town": {
|
|
12
12
|
"name": "ghcr.io/toon-protocol/town",
|
|
13
|
-
"tag": "0.1.
|
|
14
|
-
"digest": "sha256:
|
|
13
|
+
"tag": "0.1.3",
|
|
14
|
+
"digest": "sha256:36b0563b0bfc9fdda1a5723fb1496427188048cdf9f96c813b71c32161532874"
|
|
15
15
|
},
|
|
16
16
|
"mill": {
|
|
17
17
|
"name": "ghcr.io/toon-protocol/mill",
|
|
18
|
-
"tag": "0.1.
|
|
19
|
-
"digest": "sha256:
|
|
18
|
+
"tag": "0.1.3",
|
|
19
|
+
"digest": "sha256:1fd4e8015087314d9f99240ecce81f48bac318311f77aeacb57d881594d133cc"
|
|
20
20
|
},
|
|
21
21
|
"dvm": {
|
|
22
22
|
"name": "ghcr.io/toon-protocol/dvm",
|
|
23
|
-
"tag": "0.1.
|
|
24
|
-
"digest": "sha256:
|
|
23
|
+
"tag": "0.1.3",
|
|
24
|
+
"digest": "sha256:ebc5f8e38672954a76c69bda90924a3a0ad7cdac42520f22d2e05960d06986ae"
|
|
25
25
|
},
|
|
26
26
|
"connector": {
|
|
27
27
|
"name": "ghcr.io/toon-protocol/connector",
|
package/dist/index.js
CHANGED
|
@@ -20,6 +20,7 @@ import { useEffect, useRef, useState } from "react";
|
|
|
20
20
|
// src/tui/constants.ts
|
|
21
21
|
var DEFAULT_REFRESH_INTERVAL_MS = 2e3;
|
|
22
22
|
var DEFAULT_API_URL = "http://127.0.0.1:28090";
|
|
23
|
+
var STARTING_UP_GRACE_FETCHES = 3;
|
|
23
24
|
|
|
24
25
|
// src/tui/use-earnings.ts
|
|
25
26
|
var EMPTY_EARNINGS = {
|
|
@@ -42,9 +43,15 @@ function useEarnings(opts = {}) {
|
|
|
42
43
|
bannerKey: null
|
|
43
44
|
});
|
|
44
45
|
const prevDataRef = useRef(null);
|
|
46
|
+
const warmupFailuresRef = useRef(0);
|
|
45
47
|
useEffect(() => {
|
|
46
48
|
let cancelled = false;
|
|
47
49
|
let abortController = null;
|
|
50
|
+
function failureBanner(specific) {
|
|
51
|
+
if (prevDataRef.current !== null) return specific;
|
|
52
|
+
warmupFailuresRef.current += 1;
|
|
53
|
+
return warmupFailuresRef.current <= STARTING_UP_GRACE_FETCHES ? "starting_up" : specific;
|
|
54
|
+
}
|
|
48
55
|
async function doFetch() {
|
|
49
56
|
if (cancelled) return;
|
|
50
57
|
const ac = new AbortController();
|
|
@@ -59,7 +66,7 @@ function useEarnings(opts = {}) {
|
|
|
59
66
|
setState({
|
|
60
67
|
phase: "stale",
|
|
61
68
|
data: prev ?? EMPTY_EARNINGS,
|
|
62
|
-
bannerKey: "fetch_failed"
|
|
69
|
+
bannerKey: failureBanner("fetch_failed")
|
|
63
70
|
});
|
|
64
71
|
return;
|
|
65
72
|
}
|
|
@@ -70,20 +77,20 @@ function useEarnings(opts = {}) {
|
|
|
70
77
|
setState({
|
|
71
78
|
phase: "stale",
|
|
72
79
|
data: prev ?? EMPTY_EARNINGS,
|
|
73
|
-
bannerKey: "connector_unavailable"
|
|
80
|
+
bannerKey: failureBanner("connector_unavailable")
|
|
74
81
|
});
|
|
75
82
|
return;
|
|
76
83
|
}
|
|
77
84
|
prevDataRef.current = body;
|
|
85
|
+
warmupFailuresRef.current = 0;
|
|
78
86
|
setState({ phase: "ok", data: body, bannerKey: null });
|
|
79
87
|
} catch (err) {
|
|
80
88
|
if (cancelled) return;
|
|
81
89
|
if (err instanceof Error && err.name === "AbortError") return;
|
|
82
|
-
const prev = prevDataRef.current;
|
|
83
90
|
setState({
|
|
84
91
|
phase: "stale",
|
|
85
|
-
data:
|
|
86
|
-
bannerKey: "fetch_failed"
|
|
92
|
+
data: prevDataRef.current ?? EMPTY_EARNINGS,
|
|
93
|
+
bannerKey: failureBanner("fetch_failed")
|
|
87
94
|
});
|
|
88
95
|
} finally {
|
|
89
96
|
abortController = null;
|
|
@@ -187,14 +194,17 @@ var COPY = {
|
|
|
187
194
|
qualifierEvents: (n) => `${n} events relayed`,
|
|
188
195
|
banners: {
|
|
189
196
|
connectorUnavailable: `Connector not reachable \u2014 showing last known values. Retrying in 2s.`,
|
|
190
|
-
fetchFailed: `Last refresh failed \u2014 retrying
|
|
197
|
+
fetchFailed: `Last refresh failed \u2014 retrying.`,
|
|
198
|
+
// Shown only before the first successful fetch — a fresh node whose API is
|
|
199
|
+
// still warming up should read as "starting", not "failed".
|
|
200
|
+
startingUp: `Starting up \u2014 connecting to your node\u2026`
|
|
191
201
|
},
|
|
192
202
|
apex: {
|
|
193
203
|
routingPrefix: `\u21B3 apex routing: `,
|
|
194
204
|
routingEmpty: `(enable mill to route)`
|
|
195
205
|
},
|
|
196
206
|
peerTable: {
|
|
197
|
-
empty: `no peers yet \u2014
|
|
207
|
+
empty: `no peers yet \u2014 in a new terminal: townhouse node add town`
|
|
198
208
|
},
|
|
199
209
|
activityTicker: {
|
|
200
210
|
prefix: `recent: `,
|
|
@@ -305,6 +315,9 @@ import { Text as Text4 } from "ink";
|
|
|
305
315
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
306
316
|
function Banner({ bannerKey }) {
|
|
307
317
|
if (bannerKey === null) return null;
|
|
318
|
+
if (bannerKey === "starting_up") {
|
|
319
|
+
return /* @__PURE__ */ jsx2(Text4, { color: "cyan", children: COPY.banners.startingUp });
|
|
320
|
+
}
|
|
308
321
|
const isError = bannerKey === "fetch_failed";
|
|
309
322
|
const text = isError ? COPY.banners.fetchFailed : COPY.banners.connectorUnavailable;
|
|
310
323
|
return /* @__PURE__ */ jsx2(Text4, { color: isError ? "red" : "yellow", children: text });
|
|
@@ -622,4 +635,4 @@ function mountTui(opts = {}) {
|
|
|
622
635
|
export {
|
|
623
636
|
mountTui
|
|
624
637
|
};
|
|
625
|
-
//# sourceMappingURL=tui-
|
|
638
|
+
//# sourceMappingURL=tui-QE3ZRZO3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tui/index.ts","../src/tui/App.tsx","../src/tui/use-earnings.ts","../src/tui/constants.ts","../src/tui/use-activity-buffer.ts","../src/tui/components/HeroBand.tsx","../src/tui/components/Sparkline.tsx","../src/tui/components/Qualifier.tsx","../src/tui/copy.ts","../src/tui/components/Banner.tsx","../src/tui/components/ApexStrip.tsx","../src/tui/components/PeerTable.tsx","../src/tui/components/ActivityTicker.tsx","../src/tui/components/Badge.tsx","../src/tui/components/ActivityOverlay.tsx"],"sourcesContent":["import { createElement } from 'react';\nimport { render, type Instance } from 'ink';\nimport App from './App.js';\n\nexport interface MountTuiOptions {\n apiUrl?: string;\n refreshIntervalMs?: number;\n fetchImpl?: typeof fetch;\n}\n\nexport function mountTui(opts: MountTuiOptions = {}): Instance {\n return render(createElement(App, opts), {\n exitOnCtrlC: true,\n patchConsole: false,\n });\n}\n","import React, { useState } from 'react';\nimport { Box, Text, useInput } from 'ink';\nimport { useEarnings } from './use-earnings.js';\nimport { useActivityBuffer, MAX_BUFFER_SIZE } from './use-activity-buffer.js';\nimport { HeroBand } from './components/HeroBand.js';\nimport { Banner } from './components/Banner.js';\nimport { ApexStripSlot } from './components/ApexStripSlot.js';\nimport { PeerTableSlot } from './components/PeerTableSlot.js';\nimport { FooterSlot } from './components/FooterSlot.js';\nimport { Badge } from './components/Badge.js';\nimport { ActivityOverlay } from './components/ActivityOverlay.js';\nimport { COPY } from './copy.js';\n\nexport interface AppProps {\n apiUrl?: string;\n refreshIntervalMs?: number;\n fetchImpl?: typeof fetch;\n}\n\nexport default function App(props: AppProps): React.ReactElement {\n const state = useEarnings(props);\n const recentClaims = state.phase !== 'loading' ? state.data.recentClaims : undefined;\n const buffer = useActivityBuffer(recentClaims);\n const [overlayOpen, setOverlayOpen] = useState(false);\n\n useInput(\n (input, key) => {\n if (key.ctrl || key.meta) return;\n if (input === 'a' || input === 'A') setOverlayOpen(true);\n },\n { isActive: !overlayOpen && state.phase !== 'loading' },\n );\n\n if (state.phase === 'loading') {\n return <Text>{COPY.loading}</Text>;\n }\n\n if (overlayOpen) {\n return <ActivityOverlay claims={buffer} onClose={() => setOverlayOpen(false)} maxBufferSize={MAX_BUFFER_SIZE} />;\n }\n\n const { data } = state;\n const bannerKey = state.phase === 'stale' ? state.bannerKey : null;\n\n return (\n <Box flexDirection=\"column\">\n <HeroBand apex={data.apex} peers={data.peers} eventsRelayed={data.eventsRelayed} />\n <Badge apex={data.apex} peers={data.peers} uptimeSeconds={data.uptimeSeconds} />\n <Banner bannerKey={bannerKey} />\n <ApexStripSlot apex={data.apex} peers={data.peers} />\n <PeerTableSlot peers={data.peers} />\n <FooterSlot recentClaims={data.recentClaims} />\n </Box>\n );\n}\n","import { useEffect, useRef, useState } from 'react';\nimport type { AggregatedEarnings } from './types.js';\nimport {\n DEFAULT_API_URL,\n DEFAULT_REFRESH_INTERVAL_MS,\n STARTING_UP_GRACE_FETCHES,\n} from './constants.js';\n\nexport type EarningsState =\n | { phase: 'loading'; data: null; bannerKey: null }\n | { phase: 'ok'; data: AggregatedEarnings; bannerKey: null }\n | {\n phase: 'stale';\n data: AggregatedEarnings;\n bannerKey: 'connector_unavailable' | 'fetch_failed' | 'starting_up';\n };\n\nexport interface UseEarningsOptions {\n apiUrl?: string;\n refreshIntervalMs?: number;\n fetchImpl?: typeof fetch;\n}\n\nconst EMPTY_EARNINGS: AggregatedEarnings = {\n status: 'connector_unavailable',\n apex: { routingFees: {} },\n peers: [],\n recentClaims: [],\n eventsRelayed: 0,\n uptimeSeconds: 0,\n};\n\nexport function useEarnings(opts: UseEarningsOptions = {}): EarningsState {\n const {\n apiUrl = DEFAULT_API_URL,\n refreshIntervalMs = DEFAULT_REFRESH_INTERVAL_MS,\n fetchImpl = globalThis.fetch,\n } = opts;\n\n const [state, setState] = useState<EarningsState>({\n phase: 'loading',\n data: null,\n bannerKey: null,\n });\n\n const prevDataRef = useRef<AggregatedEarnings | null>(null);\n // Consecutive failures before the first-ever success. Resets on any success.\n const warmupFailuresRef = useRef(0);\n\n useEffect(() => {\n let cancelled = false;\n let abortController: AbortController | null = null;\n\n // Choose the banner for a failed fetch: a calm 'starting_up' while a node\n // that has never responded is within its warm-up grace, escalating to the\n // specific failure banner once we've had data OR the grace is exhausted.\n function failureBanner(\n specific: 'fetch_failed' | 'connector_unavailable'\n ): 'starting_up' | 'fetch_failed' | 'connector_unavailable' {\n if (prevDataRef.current !== null) return specific;\n warmupFailuresRef.current += 1;\n return warmupFailuresRef.current <= STARTING_UP_GRACE_FETCHES\n ? 'starting_up'\n : specific;\n }\n\n async function doFetch(): Promise<void> {\n if (cancelled) return;\n\n const ac = new AbortController();\n abortController = ac;\n\n try {\n const res = await fetchImpl(`${apiUrl}/api/earnings`, {\n signal: ac.signal,\n });\n\n if (cancelled) return;\n\n if (!res.ok) {\n const prev = prevDataRef.current;\n setState({\n phase: 'stale',\n data: prev ?? EMPTY_EARNINGS,\n bannerKey: failureBanner('fetch_failed'),\n });\n return;\n }\n\n const body = (await res.json()) as AggregatedEarnings;\n\n if (cancelled) return;\n\n if (body.status === 'connector_unavailable') {\n const prev = prevDataRef.current;\n setState({\n phase: 'stale',\n data: prev ?? EMPTY_EARNINGS,\n bannerKey: failureBanner('connector_unavailable'),\n });\n return;\n }\n\n prevDataRef.current = body;\n warmupFailuresRef.current = 0;\n setState({ phase: 'ok', data: body, bannerKey: null });\n } catch (err) {\n if (cancelled) return;\n if (err instanceof Error && err.name === 'AbortError') return;\n\n setState({\n phase: 'stale',\n data: prevDataRef.current ?? EMPTY_EARNINGS,\n bannerKey: failureBanner('fetch_failed'),\n });\n } finally {\n abortController = null;\n }\n }\n\n void doFetch();\n\n const intervalId = setInterval(() => {\n void doFetch();\n }, refreshIntervalMs);\n\n return () => {\n cancelled = true;\n clearInterval(intervalId);\n if (abortController !== null) {\n abortController.abort();\n }\n };\n }, [apiUrl, refreshIntervalMs, fetchImpl]);\n\n return state;\n}\n","export const DEFAULT_REFRESH_INTERVAL_MS = 2_000;\nexport const DEFAULT_API_URL = 'http://127.0.0.1:28090';\n\n/**\n * How many consecutive failed fetches to treat as \"still starting up\" before a\n * node has ever responded. During this grace window the dashboard shows a calm\n * \"Starting up…\" banner (a fresh node's API takes a few seconds to come up);\n * after it, persistent failure escalates to the louder \"Last refresh failed\"\n * so a genuinely-broken/crash-looping API doesn't masquerade as \"starting\" forever.\n * At the 2s refresh interval, 3 ≈ a ~6s grace.\n */\nexport const STARTING_UP_GRACE_FETCHES = 3;\n","import { useState, useEffect } from 'react';\nimport type { RecentClaim } from './types.js';\n\nexport const MAX_BUFFER_SIZE = 200;\n\nfunction claimKey(c: RecentClaim): string {\n return `${c.peerId}|${c.at}|${c.amount}|${c.assetCode}|${c.direction}`;\n}\n\nfunction sortKey(c: RecentClaim): number {\n const ms = Date.parse(c.at);\n return Number.isFinite(ms) ? ms : -Infinity;\n}\n\nexport function useActivityBuffer(\n incoming: RecentClaim[] | undefined\n): RecentClaim[] {\n const [buffer, setBuffer] = useState<RecentClaim[]>([]);\n\n useEffect(() => {\n if (!Array.isArray(incoming)) return;\n if (incoming.length === 0 && buffer.length === 0) return;\n\n const seen = new Map<string, RecentClaim>();\n for (const c of buffer) seen.set(claimKey(c), c);\n for (const c of incoming) seen.set(claimKey(c), c);\n\n const merged = Array.from(seen.values());\n merged.sort((a, b) => sortKey(b) - sortKey(a));\n const trimmed = merged.slice(0, MAX_BUFFER_SIZE);\n\n const same =\n trimmed.length === buffer.length &&\n trimmed.every(\n (c, i) =>\n buffer[i] !== undefined &&\n claimKey(c) === claimKey(buffer[i] as RecentClaim)\n );\n if (!same) setBuffer(trimmed);\n }, [incoming]);\n\n return buffer;\n}\n","import { Box, Text, useStdout } from 'ink';\nimport type { ReactElement } from 'react';\nimport type { AggregatedEarnings } from '../types.js';\nimport { formatUsdc } from '../format.js';\nimport { Sparkline } from './Sparkline.js';\nimport { Qualifier } from './Qualifier.js';\n\nconst USDC_SCALE = 6;\nconst ASSET = 'USDC';\nconst DECIMAL_RE = /^-?\\d+$/;\nconst MIN_COL_WIDTH = 8;\n\nfunction addDecimalStrings(a: string, b: string): string {\n // Defensive: malformed peer/apex amounts must not crash the render tree.\n // `formatUsdc` will degrade to '$?.??' on a non-decimal accumulator.\n if (!DECIMAL_RE.test(b)) return a;\n try {\n return (BigInt(a) + BigInt(b)).toString();\n } catch {\n return a;\n }\n}\n\ninterface HeroBandProps {\n apex: AggregatedEarnings['apex'];\n peers: AggregatedEarnings['peers'];\n eventsRelayed: number;\n}\n\ninterface Scalars {\n today: string;\n month: string;\n year: string;\n lifetime: string;\n}\n\nfunction computeScalars(\n apex: AggregatedEarnings['apex'],\n peers: AggregatedEarnings['peers']\n): Scalars {\n let today = '0';\n let month = '0';\n let year = '0';\n let lifetime = '0';\n\n const apexUsdc = apex.routingFees[ASSET];\n if (apexUsdc !== undefined) {\n today = addDecimalStrings(today, apexUsdc.today);\n month = addDecimalStrings(month, apexUsdc.month);\n year = addDecimalStrings(year, apexUsdc.year);\n lifetime = addDecimalStrings(lifetime, apexUsdc.lifetime);\n }\n\n for (const peer of peers) {\n const peerUsdc = peer.byAsset[ASSET];\n if (peerUsdc !== undefined) {\n today = addDecimalStrings(today, peerUsdc.today);\n month = addDecimalStrings(month, peerUsdc.month);\n year = addDecimalStrings(year, peerUsdc.year);\n lifetime = addDecimalStrings(lifetime, peerUsdc.lifetime);\n }\n }\n\n return { today, month, year, lifetime };\n}\n\nfunction isEmptyState(\n apex: AggregatedEarnings['apex'],\n peers: AggregatedEarnings['peers']\n): boolean {\n const apexMonth = apex.routingFees[ASSET]?.month ?? '0';\n if (apexMonth !== '0') return false;\n for (const peer of peers) {\n const peerMonth = peer.byAsset[ASSET]?.month ?? '0';\n if (peerMonth !== '0') return false;\n }\n return true;\n}\n\nexport function HeroBand({ apex, peers, eventsRelayed }: HeroBandProps): ReactElement {\n const { stdout } = useStdout();\n const columns = stdout?.columns ?? 80;\n\n const scalars = computeScalars(apex, peers);\n const showQualifier = isEmptyState(apex, peers);\n\n const todayFmt = formatUsdc(scalars.today, USDC_SCALE);\n const monthFmt = formatUsdc(scalars.month, USDC_SCALE);\n const yearFmt = formatUsdc(scalars.year, USDC_SCALE);\n const lifetimeFmt = formatUsdc(scalars.lifetime, USDC_SCALE);\n\n const shortLabels = columns < 70;\n const labelLifetime = shortLabels ? 'LIFE' : 'LIFETIME';\n\n // Clamp: at very narrow widths (<32ch) Ink would collapse <Box width={0}>\n // and truncate scalar values into garbage. Floor to a usable per-column width.\n const colWidth = Math.max(Math.floor(columns / 4), MIN_COL_WIDTH);\n\n return (\n <Box flexDirection=\"column\">\n <Box>\n <Box width={colWidth}><Text dimColor>TODAY</Text></Box>\n <Box width={colWidth}><Text dimColor>MONTH</Text></Box>\n <Box width={colWidth}><Text dimColor>YEAR</Text></Box>\n <Box width={colWidth}><Text dimColor>{labelLifetime}</Text></Box>\n </Box>\n <Box>\n <Box width={colWidth}>\n <Text color={scalars.today !== '0' ? 'green' : undefined}>{todayFmt}</Text>\n </Box>\n <Box width={colWidth}>\n <Text color={scalars.month !== '0' ? 'green' : undefined}>{monthFmt}</Text>\n </Box>\n <Box width={colWidth}>\n <Text color={scalars.year !== '0' ? 'green' : undefined}>{yearFmt}</Text>\n </Box>\n <Box width={colWidth}>\n <Text color={scalars.lifetime !== '0' ? 'green' : undefined}>{lifetimeFmt}</Text>\n </Box>\n </Box>\n <Sparkline values={[]} width={columns} />\n {showQualifier ? <Qualifier eventsRelayed={eventsRelayed} /> : null}\n </Box>\n );\n}\n","import { Text } from 'ink';\nimport type { ReactElement } from 'react';\n\nconst BLOCKS = '▁▂▃▄▅▆▇█';\nconst PLACEHOLDER = '·······';\n\ninterface SparklineProps {\n values: number[];\n width: number;\n}\n\nexport function Sparkline({ values, width }: SparklineProps): ReactElement | null {\n // Collapse the row entirely at <60ch — return null so the layout doesn't\n // reserve a blank row (wireframe degrade ladder: sparkline drops first).\n if (width < 60) {\n return null;\n }\n\n if (values.length === 0) {\n return <Text>{PLACEHOLDER} 7d</Text>;\n }\n\n // Filter NaN / Infinity / negative values; treat negatives as 0 floor.\n const safe = values\n .filter((v) => Number.isFinite(v))\n .map((v) => (v < 0 ? 0 : v));\n\n if (safe.length === 0) {\n return <Text>{PLACEHOLDER} 7d</Text>;\n }\n\n // Use reduce to avoid Math.max(...arr) stack overflow on large arrays.\n const max = safe.reduce((m, v) => (v > m ? v : m), 0);\n const chars = safe\n .map((v) => {\n if (max === 0) return BLOCKS[0] ?? '▁';\n const idx = Math.floor((v / max) * (BLOCKS.length - 1));\n return BLOCKS[idx] ?? '▁';\n })\n .join('');\n\n return <Text>{chars} 7d</Text>;\n}\n","import { Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport { COPY } from '../copy.js';\n\ninterface QualifierProps {\n eventsRelayed: number;\n}\n\nexport function Qualifier({ eventsRelayed }: QualifierProps): ReactElement {\n return (\n <Text color=\"yellow\">\n {COPY.qualifierPrefix} · {COPY.qualifierEvents(eventsRelayed)} · {COPY.heroEarly}\n </Text>\n );\n}\n","export const COPY = {\n heroEarly: `you're early`,\n heroEarlyRotation: [\n `you're early`,\n `warming up`,\n `first packet en route`,\n ] as const,\n loading: `Fetching earnings…`,\n qualifierPrefix: `MONTH $0.00`,\n qualifierEventsWords: `events relayed`,\n qualifierEvents: (n: number) => `${n} events relayed`,\n banners: {\n connectorUnavailable: `Connector not reachable — showing last known values. Retrying in 2s.`,\n fetchFailed: `Last refresh failed — retrying.`,\n // Shown only before the first successful fetch — a fresh node whose API is\n // still warming up should read as \"starting\", not \"failed\".\n startingUp: `Starting up — connecting to your node…`,\n },\n apex: {\n routingPrefix: `↳ apex routing: `,\n routingEmpty: `(enable mill to route)`,\n },\n peerTable: {\n empty: `no peers yet — in a new terminal: townhouse node add town`,\n },\n activityTicker: {\n prefix: `recent: `,\n empty: `no settlements yet — press [a] when activity arrives`,\n keybind: ` [a] activity`,\n },\n activityOverlay: {\n titlePrefix: `Activity — last `,\n emptyHint: `(no activity yet)`,\n scrollHint: `j/k to scroll · q to close`,\n scrollHintEmpty: `q to close`,\n directionInbound: `in`,\n directionOutbound: `out`,\n directionUnknown: `?`,\n },\n} as const;\n","import { Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport { COPY } from '../copy.js';\n\ninterface BannerProps {\n bannerKey: 'connector_unavailable' | 'fetch_failed' | 'starting_up' | null;\n}\n\nexport function Banner({ bannerKey }: BannerProps): ReactElement | null {\n if (bannerKey === null) return null;\n\n // 'starting_up' is the calm warm-up state (no successful fetch yet); the\n // others mean we had data and then lost it, which warrants a louder colour.\n if (bannerKey === 'starting_up') {\n return <Text color=\"cyan\">{COPY.banners.startingUp}</Text>;\n }\n\n const isError = bannerKey === 'fetch_failed';\n const text = isError\n ? COPY.banners.fetchFailed\n : COPY.banners.connectorUnavailable;\n\n return <Text color={isError ? 'red' : 'yellow'}>{text}</Text>;\n}\n","import { Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport type { AggregatedEarnings } from '../types.js';\nimport { formatUsdc } from '../format.js';\nimport { COPY } from '../copy.js';\n\nconst USDC_SCALE = 6;\nconst ASSET = 'USDC';\nconst DECIMAL_RE = /^-?\\d+$/;\n\nfunction addDecimalStrings(a: string, b: string): string {\n // Defensive: malformed peer amounts must not crash the render tree.\n if (!DECIMAL_RE.test(b)) return a;\n try {\n return (BigInt(a) + BigInt(b)).toString();\n } catch {\n return a;\n }\n}\n\nexport interface ApexStripProps {\n apex: AggregatedEarnings['apex'];\n peers: AggregatedEarnings['peers'];\n}\n\nexport function ApexStrip({ apex, peers }: ApexStripProps): ReactElement {\n const apexMonth = apex.routingFees[ASSET]?.month ?? '0';\n const apexValid = DECIMAL_RE.test(apexMonth);\n const apexMonthBig = apexValid ? BigInt(apexMonth) : 0n;\n\n let totalMonth = apexMonthBig;\n for (const peer of peers) {\n const peerMonth = peer.byAsset[ASSET]?.month ?? '0';\n totalMonth = BigInt(addDecimalStrings(totalMonth.toString(), peerMonth));\n }\n\n const apexFmt = formatUsdc(apexMonth, USDC_SCALE);\n const hasMillPeer = peers.some((p) => p.type === 'mill');\n\n // Malformed apex.month: render the formatUsdc fallback alone — adding the Mill upsell\n // would mix a wire-anomaly signal with a \"you have no Mill\" signal.\n if (!apexValid) {\n return (\n <Text dimColor italic>\n {COPY.apex.routingPrefix}{apexFmt}\n </Text>\n );\n }\n\n if (apexMonthBig === 0n) {\n const upsell = hasMillPeer ? '' : ` ${COPY.apex.routingEmpty}`;\n return (\n <Text dimColor italic>\n {COPY.apex.routingPrefix}{apexFmt}{upsell}\n </Text>\n );\n }\n\n // Defensive: negative apex (refund/chargeback, wire-legal per `^-?\\d+$`) can let peers\n // exactly cancel apex; totalMonth === 0n would throw on BigInt division. Omit instead.\n const pct = totalMonth === 0n ? null : Number((apexMonthBig * 100n) / totalMonth);\n return (\n <Text>\n {COPY.apex.routingPrefix}{apexFmt}{pct !== null ? ` (${pct}%)` : ''}\n </Text>\n );\n}\n","import { Box, Text, useStdout } from 'ink';\nimport type { ReactElement } from 'react';\nimport type { AggregatedEarnings, NodeEarnings, PerAsset } from '../types.js';\nimport { formatUsdc, formatRelativeTime } from '../format.js';\nimport { COPY } from '../copy.js';\n\nconst USDC_SCALE = 6;\nconst MAX_DATA_ROWS = 4;\nconst MIN_COL_WIDTH = 6;\n\ninterface AssetRow {\n peerId: string;\n type: string;\n assetCode: string;\n perAsset: PerAsset;\n lastClaimAt: string | null;\n isFirstRowOfPeer: boolean;\n}\n\nfunction flattenPeers(peers: NodeEarnings[]): AssetRow[] {\n const out: AssetRow[] = [];\n for (const peer of peers) {\n const assetCodes = Object.keys(peer.byAsset).sort();\n if (assetCodes.length === 0) continue;\n let isFirst = true;\n for (const assetCode of assetCodes) {\n const perAsset = peer.byAsset[assetCode];\n if (perAsset === undefined) continue;\n out.push({\n peerId: peer.id,\n type: peer.type,\n assetCode,\n perAsset,\n lastClaimAt: peer.lastClaimAt,\n isFirstRowOfPeer: isFirst,\n });\n isFirst = false;\n }\n }\n return out;\n}\n\nexport interface PeerTableProps {\n peers: AggregatedEarnings['peers'];\n now?: Date;\n /** Override terminal column width. Defaults to useStdout(). Inject in tests to pin width. */\n columns?: number;\n}\n\nexport function PeerTable({ peers, now = new Date(), columns: columnsProp }: PeerTableProps): ReactElement {\n const { stdout } = useStdout();\n // `??` only coalesces null/undefined — a detached/piped tty can ship `columns === 0`,\n // which would clamp every column to MIN_COL_WIDTH and garble the header. Fall back to 80.\n const columns = columnsProp ?? (stdout?.columns || 80);\n\n const rows = flattenPeers(peers).slice(0, MAX_DATA_ROWS);\n\n if (rows.length === 0) {\n return <Text dimColor>{COPY.peerTable.empty}</Text>;\n }\n\n const showLastClaim = columns >= 60;\n const shortType = columns < 70;\n const dropAgoSuffix = columns < 70;\n\n const totalCols = showLastClaim ? 5 : 4;\n const colWidth = Math.max(Math.floor(columns / totalCols), MIN_COL_WIDTH);\n\n const header = (\n <Box>\n <Box width={colWidth}><Text dimColor>PEER</Text></Box>\n <Box width={colWidth}><Text dimColor>TYPE</Text></Box>\n <Box width={colWidth}><Text dimColor>ASSET</Text></Box>\n <Box width={colWidth}><Text dimColor>NET (MONTH)</Text></Box>\n {showLastClaim ? <Box width={colWidth}><Text dimColor>LAST CLAIM</Text></Box> : null}\n </Box>\n );\n\n return (\n <Box flexDirection=\"column\">\n {header}\n {rows.map((row, i) => {\n const peerCell = row.isFirstRowOfPeer ? row.peerId : '';\n const typeRaw = row.isFirstRowOfPeer ? row.type : '';\n const typeCell = shortType && typeRaw.length > 0 ? typeRaw.slice(0, 3) : typeRaw;\n const netFmt = formatUsdc(row.perAsset.month, USDC_SCALE);\n let lastClaim = formatRelativeTime(row.lastClaimAt, now);\n if (dropAgoSuffix && lastClaim.endsWith(' ago')) {\n lastClaim = lastClaim.slice(0, -' ago'.length);\n }\n return (\n <Box key={`${row.peerId}-${row.assetCode}-${i}`}>\n <Box width={colWidth}><Text>{peerCell}</Text></Box>\n <Box width={colWidth}><Text>{typeCell}</Text></Box>\n <Box width={colWidth}><Text>{row.assetCode}</Text></Box>\n <Box width={colWidth}><Text>{netFmt}</Text></Box>\n {showLastClaim ? <Box width={colWidth}><Text>{lastClaim}</Text></Box> : null}\n </Box>\n );\n })}\n </Box>\n );\n}\n","import { Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport type { RecentClaim } from '../types.js';\nimport { formatUsdcMicro, formatRelativeTime } from '../format.js';\nimport { COPY } from '../copy.js';\n\nexport interface ActivityTickerProps {\n recentClaims: RecentClaim[];\n now?: Date;\n}\n\nfunction sortKey(c: RecentClaim): number {\n const ms = Date.parse(c.at);\n return Number.isFinite(ms) ? ms : -Infinity;\n}\n\nfunction arrowFor(direction: RecentClaim['direction']): string {\n return direction === 'inbound' ? '←' : direction === 'outbound' ? '→' : COPY.activityOverlay.directionUnknown;\n}\n\nexport function ActivityTicker({ recentClaims, now = new Date() }: ActivityTickerProps): ReactElement {\n if (recentClaims.length === 0) {\n return <Text dimColor>{COPY.activityTicker.empty}</Text>;\n }\n // Defensive sort DESC by `at` — wire ordering is not contractually guaranteed.\n const sorted = [...recentClaims].sort((a, b) => sortKey(b) - sortKey(a));\n const claim = sorted[0];\n if (!claim) {\n return <Text dimColor>{COPY.activityTicker.empty}</Text>;\n }\n const arrow = arrowFor(claim.direction);\n const amount = formatUsdcMicro(claim.amount, claim.assetScale);\n const rel = formatRelativeTime(claim.at, now);\n return (\n <Text dimColor>\n {COPY.activityTicker.prefix}{claim.peerId} {arrow} {amount} {claim.assetCode} · {rel}{COPY.activityTicker.keybind}\n </Text>\n );\n}\n","import { Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport type { AggregatedEarnings } from '../types.js';\nimport { COPY } from '../copy.js';\n\nconst USDC_ASSET = 'USDC';\nconst DECIMAL_RE = /^-?\\d+$/;\n\nconst LIFETIME_USDC_THRESHOLD = 1_000_000n;\nconst UPTIME_SECONDS_THRESHOLD = 7 * 24 * 60 * 60;\nexport const ROTATION_INTERVAL_MS = 30_000;\n\nfunction parseDecimalOrZero(value: string | undefined): bigint {\n if (value === undefined || !DECIMAL_RE.test(value)) return 0n;\n try {\n return BigInt(value);\n } catch {\n return 0n;\n }\n}\n\nfunction computeLifetimeUsdc(\n apex: AggregatedEarnings['apex'],\n peers: AggregatedEarnings['peers']\n): bigint {\n let total = parseDecimalOrZero(apex.routingFees[USDC_ASSET]?.lifetime);\n for (const peer of peers) {\n total += parseDecimalOrZero(peer.byAsset[USDC_ASSET]?.lifetime);\n }\n return total;\n}\n\nexport interface BadgeProps {\n apex: AggregatedEarnings['apex'];\n peers: AggregatedEarnings['peers'];\n uptimeSeconds: number;\n /** Override the wall clock. Default `new Date()`. Inject in tests to pin rotation. */\n now?: Date;\n}\n\nexport function Badge({\n apex,\n peers,\n uptimeSeconds,\n now = new Date(),\n}: BadgeProps): ReactElement | null {\n const lifetime = computeLifetimeUsdc(apex, peers);\n const lifetimeTriggers = lifetime < LIFETIME_USDC_THRESHOLD;\n const uptimeTriggers = uptimeSeconds < UPTIME_SECONDS_THRESHOLD;\n\n if (!lifetimeTriggers && !uptimeTriggers) return null;\n\n const index =\n Math.floor(now.getTime() / ROTATION_INTERVAL_MS) %\n COPY.heroEarlyRotation.length;\n const text = COPY.heroEarlyRotation[index] ?? COPY.heroEarlyRotation[0];\n\n return (\n <Text color=\"yellow\" bold>\n {text}\n </Text>\n );\n}\n","import { Box, Text, useStdout, useInput } from 'ink';\nimport { useEffect, useState, type ReactElement } from 'react';\nimport type { RecentClaim } from '../types.js';\nimport { formatUsdcMicro } from '../format.js';\nimport { COPY } from '../copy.js';\n\nconst MIN_OVERLAY_WIDTH = 40;\nconst MAX_PEER_ID_WIDTH = 24;\n// Default fallback only. App.tsx provides the authoritative cap (MAX_BUFFER_SIZE in\n// the ring-buffer hook) via the `maxBufferSize` prop so there is one source of truth.\n// Tests that mount the overlay directly fall back to this default.\nconst DEFAULT_MAX_BUFFER_SIZE = 200;\n\nfunction formatTime(iso: string): string {\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return '--:--:--';\n return d.toLocaleTimeString('en-GB', { hour12: false });\n}\n\nfunction truncatePeerId(id: string): string {\n if (id.length <= MAX_PEER_ID_WIDTH) return id;\n return id.slice(0, MAX_PEER_ID_WIDTH - 1) + '…';\n}\n\nfunction arrowFor(direction: RecentClaim['direction']): string {\n return direction === 'inbound' ? '←' : direction === 'outbound' ? '→' : COPY.activityOverlay.directionUnknown;\n}\n\nfunction directionLabel(direction: RecentClaim['direction']): string {\n return direction === 'inbound'\n ? COPY.activityOverlay.directionInbound\n : direction === 'outbound'\n ? COPY.activityOverlay.directionOutbound\n : COPY.activityOverlay.directionUnknown;\n}\n\nfunction formatRow(claim: RecentClaim): string {\n const time = formatTime(claim.at);\n const peer = truncatePeerId(claim.peerId);\n const arrow = arrowFor(claim.direction);\n const amount = formatUsdcMicro(claim.amount, claim.assetScale);\n const dir = directionLabel(claim.direction);\n return `${time} · ${peer} · ${arrow} ${amount} ${claim.assetCode} · ${dir}`;\n}\n\nfunction claimKeyForReact(c: RecentClaim): string {\n return `${c.peerId}|${c.at}|${c.amount}|${c.assetCode}|${c.direction}`;\n}\n\nexport interface ActivityOverlayProps {\n claims: RecentClaim[];\n onClose: () => void;\n columns?: number;\n rows?: number;\n maxBufferSize?: number;\n}\n\nexport function ActivityOverlay({\n claims,\n onClose,\n columns: columnsProp,\n rows: rowsProp,\n maxBufferSize = DEFAULT_MAX_BUFFER_SIZE,\n}: ActivityOverlayProps): ReactElement {\n const { stdout } = useStdout();\n const columns = columnsProp ?? (stdout?.columns || 80);\n const rows = rowsProp ?? (stdout?.rows || 24);\n\n const modalWidth = Math.max(MIN_OVERLAY_WIDTH, Math.floor(columns * 0.7));\n const visibleRows = Math.max(5, rows - 5);\n\n const [scroll, setScroll] = useState(0);\n const maxScroll = Math.max(0, claims.length - visibleRows);\n\n // Reconcile scroll when maxScroll shrinks under it — terminal resize that grows\n // `visibleRows` (or any future shrink of `claims`) would otherwise leave the slice\n // pointing past the data, hiding the newest entries until the operator presses k.\n useEffect(() => {\n if (scroll > maxScroll) setScroll(maxScroll);\n }, [maxScroll, scroll]);\n\n useInput((input, key) => {\n // ESC must be checked BEFORE the ctrl/meta guard — Ink's input parser sets\n // `key.meta` on a bare `\\x1b` byte (Alt-prefix detection), which would\n // otherwise eat the close action.\n if (key.escape) {\n onClose();\n return;\n }\n // Guard against Ctrl-* and Alt-* — they MUST NOT trigger close/scroll.\n if (key.ctrl || key.meta) return;\n if (input === 'q' || input === 'Q') {\n onClose();\n return;\n }\n if (input === 'j' || key.downArrow) {\n setScroll((s) => Math.min(maxScroll, s + 1));\n return;\n }\n if (input === 'k' || key.upArrow) {\n setScroll((s) => Math.max(0, s - 1));\n }\n });\n\n const displayedCount = Math.min(claims.length, maxBufferSize);\n const title = `${COPY.activityOverlay.titlePrefix}${displayedCount} of ${maxBufferSize}`;\n const window = claims.slice(scroll, scroll + visibleRows);\n const hint = claims.length === 0 ? COPY.activityOverlay.scrollHintEmpty : COPY.activityOverlay.scrollHint;\n\n return (\n <Box flexDirection=\"column\" alignItems=\"center\" width={columns}>\n <Box flexDirection=\"column\" borderStyle=\"round\" width={modalWidth} paddingX={1}>\n <Text bold>{title}</Text>\n {claims.length === 0 ? (\n <Text dimColor>{COPY.activityOverlay.emptyHint}</Text>\n ) : (\n window.map((c, i) => (\n <Text key={`${claimKeyForReact(c)}-${scroll + i}`}>{formatRow(c)}</Text>\n ))\n )}\n <Text dimColor>{hint}</Text>\n </Box>\n </Box>\n );\n}\n"],"mappings":"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","names":["useState","Box","Text","useInput","useState","useEffect","Text","Text","jsxs","Text","jsxs","Text","Text","jsx","Text","Text","jsxs","USDC_SCALE","ASSET","DECIMAL_RE","addDecimalStrings","Text","Box","Text","useStdout","jsx","jsxs","USDC_SCALE","MIN_COL_WIDTH","useStdout","Text","Box","Text","jsx","jsxs","sortKey","Text","Text","jsx","DECIMAL_RE","Text","Box","Text","useStdout","useEffect","useState","jsx","jsxs","arrowFor","useStdout","useState","useEffect","Box","Text","jsx","jsxs","useState","useInput","Text","Box"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toon-protocol/townhouse",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "TOON Townhouse — host-native orchestrator + dashboard for Docker-containerized TOON nodes",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -71,11 +71,11 @@
|
|
|
71
71
|
"tsup": "^8.0.0",
|
|
72
72
|
"typescript": "^5.3.0",
|
|
73
73
|
"vitest": "^1.0.0",
|
|
74
|
-
"@toon-protocol/
|
|
74
|
+
"@toon-protocol/core": "^1.4.1",
|
|
75
75
|
"@toon-protocol/mill": "^0.1.0",
|
|
76
|
-
"@toon-protocol/relay": "^1.3.1",
|
|
77
76
|
"@toon-protocol/sdk": "^0.5.0",
|
|
78
|
-
"@toon-protocol/
|
|
77
|
+
"@toon-protocol/relay": "^1.3.1",
|
|
78
|
+
"@toon-protocol/client": "^0.9.1"
|
|
79
79
|
},
|
|
80
80
|
"scripts": {
|
|
81
81
|
"build": "tsup",
|
package/dist/tui-OIFXGBTL.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tui/index.ts","../src/tui/App.tsx","../src/tui/use-earnings.ts","../src/tui/constants.ts","../src/tui/use-activity-buffer.ts","../src/tui/components/HeroBand.tsx","../src/tui/components/Sparkline.tsx","../src/tui/components/Qualifier.tsx","../src/tui/copy.ts","../src/tui/components/Banner.tsx","../src/tui/components/ApexStrip.tsx","../src/tui/components/PeerTable.tsx","../src/tui/components/ActivityTicker.tsx","../src/tui/components/Badge.tsx","../src/tui/components/ActivityOverlay.tsx"],"sourcesContent":["import { createElement } from 'react';\nimport { render, type Instance } from 'ink';\nimport App from './App.js';\n\nexport interface MountTuiOptions {\n apiUrl?: string;\n refreshIntervalMs?: number;\n fetchImpl?: typeof fetch;\n}\n\nexport function mountTui(opts: MountTuiOptions = {}): Instance {\n return render(createElement(App, opts), {\n exitOnCtrlC: true,\n patchConsole: false,\n });\n}\n","import React, { useState } from 'react';\nimport { Box, Text, useInput } from 'ink';\nimport { useEarnings } from './use-earnings.js';\nimport { useActivityBuffer, MAX_BUFFER_SIZE } from './use-activity-buffer.js';\nimport { HeroBand } from './components/HeroBand.js';\nimport { Banner } from './components/Banner.js';\nimport { ApexStripSlot } from './components/ApexStripSlot.js';\nimport { PeerTableSlot } from './components/PeerTableSlot.js';\nimport { FooterSlot } from './components/FooterSlot.js';\nimport { Badge } from './components/Badge.js';\nimport { ActivityOverlay } from './components/ActivityOverlay.js';\nimport { COPY } from './copy.js';\n\nexport interface AppProps {\n apiUrl?: string;\n refreshIntervalMs?: number;\n fetchImpl?: typeof fetch;\n}\n\nexport default function App(props: AppProps): React.ReactElement {\n const state = useEarnings(props);\n const recentClaims = state.phase !== 'loading' ? state.data.recentClaims : undefined;\n const buffer = useActivityBuffer(recentClaims);\n const [overlayOpen, setOverlayOpen] = useState(false);\n\n useInput(\n (input, key) => {\n if (key.ctrl || key.meta) return;\n if (input === 'a' || input === 'A') setOverlayOpen(true);\n },\n { isActive: !overlayOpen && state.phase !== 'loading' },\n );\n\n if (state.phase === 'loading') {\n return <Text>{COPY.loading}</Text>;\n }\n\n if (overlayOpen) {\n return <ActivityOverlay claims={buffer} onClose={() => setOverlayOpen(false)} maxBufferSize={MAX_BUFFER_SIZE} />;\n }\n\n const { data } = state;\n const bannerKey = state.phase === 'stale' ? state.bannerKey : null;\n\n return (\n <Box flexDirection=\"column\">\n <HeroBand apex={data.apex} peers={data.peers} eventsRelayed={data.eventsRelayed} />\n <Badge apex={data.apex} peers={data.peers} uptimeSeconds={data.uptimeSeconds} />\n <Banner bannerKey={bannerKey} />\n <ApexStripSlot apex={data.apex} peers={data.peers} />\n <PeerTableSlot peers={data.peers} />\n <FooterSlot recentClaims={data.recentClaims} />\n </Box>\n );\n}\n","import { useEffect, useRef, useState } from 'react';\nimport type { AggregatedEarnings } from './types.js';\nimport { DEFAULT_API_URL, DEFAULT_REFRESH_INTERVAL_MS } from './constants.js';\n\nexport type EarningsState =\n | { phase: 'loading'; data: null; bannerKey: null }\n | { phase: 'ok'; data: AggregatedEarnings; bannerKey: null }\n | {\n phase: 'stale';\n data: AggregatedEarnings;\n bannerKey: 'connector_unavailable' | 'fetch_failed';\n };\n\nexport interface UseEarningsOptions {\n apiUrl?: string;\n refreshIntervalMs?: number;\n fetchImpl?: typeof fetch;\n}\n\nconst EMPTY_EARNINGS: AggregatedEarnings = {\n status: 'connector_unavailable',\n apex: { routingFees: {} },\n peers: [],\n recentClaims: [],\n eventsRelayed: 0,\n uptimeSeconds: 0,\n};\n\nexport function useEarnings(opts: UseEarningsOptions = {}): EarningsState {\n const {\n apiUrl = DEFAULT_API_URL,\n refreshIntervalMs = DEFAULT_REFRESH_INTERVAL_MS,\n fetchImpl = globalThis.fetch,\n } = opts;\n\n const [state, setState] = useState<EarningsState>({\n phase: 'loading',\n data: null,\n bannerKey: null,\n });\n\n const prevDataRef = useRef<AggregatedEarnings | null>(null);\n\n useEffect(() => {\n let cancelled = false;\n let abortController: AbortController | null = null;\n\n async function doFetch(): Promise<void> {\n if (cancelled) return;\n\n const ac = new AbortController();\n abortController = ac;\n\n try {\n const res = await fetchImpl(`${apiUrl}/api/earnings`, {\n signal: ac.signal,\n });\n\n if (cancelled) return;\n\n if (!res.ok) {\n const prev = prevDataRef.current;\n setState({\n phase: 'stale',\n data: prev ?? EMPTY_EARNINGS,\n bannerKey: 'fetch_failed',\n });\n return;\n }\n\n const body = (await res.json()) as AggregatedEarnings;\n\n if (cancelled) return;\n\n if (body.status === 'connector_unavailable') {\n const prev = prevDataRef.current;\n setState({\n phase: 'stale',\n data: prev ?? EMPTY_EARNINGS,\n bannerKey: 'connector_unavailable',\n });\n return;\n }\n\n prevDataRef.current = body;\n setState({ phase: 'ok', data: body, bannerKey: null });\n } catch (err) {\n if (cancelled) return;\n if (err instanceof Error && err.name === 'AbortError') return;\n\n const prev = prevDataRef.current;\n setState({\n phase: 'stale',\n data: prev ?? EMPTY_EARNINGS,\n bannerKey: 'fetch_failed',\n });\n } finally {\n abortController = null;\n }\n }\n\n void doFetch();\n\n const intervalId = setInterval(() => {\n void doFetch();\n }, refreshIntervalMs);\n\n return () => {\n cancelled = true;\n clearInterval(intervalId);\n if (abortController !== null) {\n abortController.abort();\n }\n };\n }, [apiUrl, refreshIntervalMs, fetchImpl]);\n\n return state;\n}\n","export const DEFAULT_REFRESH_INTERVAL_MS = 2_000;\nexport const DEFAULT_API_URL = 'http://127.0.0.1:28090';\n","import { useState, useEffect } from 'react';\nimport type { RecentClaim } from './types.js';\n\nexport const MAX_BUFFER_SIZE = 200;\n\nfunction claimKey(c: RecentClaim): string {\n return `${c.peerId}|${c.at}|${c.amount}|${c.assetCode}|${c.direction}`;\n}\n\nfunction sortKey(c: RecentClaim): number {\n const ms = Date.parse(c.at);\n return Number.isFinite(ms) ? ms : -Infinity;\n}\n\nexport function useActivityBuffer(\n incoming: RecentClaim[] | undefined\n): RecentClaim[] {\n const [buffer, setBuffer] = useState<RecentClaim[]>([]);\n\n useEffect(() => {\n if (!Array.isArray(incoming)) return;\n if (incoming.length === 0 && buffer.length === 0) return;\n\n const seen = new Map<string, RecentClaim>();\n for (const c of buffer) seen.set(claimKey(c), c);\n for (const c of incoming) seen.set(claimKey(c), c);\n\n const merged = Array.from(seen.values());\n merged.sort((a, b) => sortKey(b) - sortKey(a));\n const trimmed = merged.slice(0, MAX_BUFFER_SIZE);\n\n const same =\n trimmed.length === buffer.length &&\n trimmed.every(\n (c, i) =>\n buffer[i] !== undefined &&\n claimKey(c) === claimKey(buffer[i] as RecentClaim)\n );\n if (!same) setBuffer(trimmed);\n }, [incoming]);\n\n return buffer;\n}\n","import { Box, Text, useStdout } from 'ink';\nimport type { ReactElement } from 'react';\nimport type { AggregatedEarnings } from '../types.js';\nimport { formatUsdc } from '../format.js';\nimport { Sparkline } from './Sparkline.js';\nimport { Qualifier } from './Qualifier.js';\n\nconst USDC_SCALE = 6;\nconst ASSET = 'USDC';\nconst DECIMAL_RE = /^-?\\d+$/;\nconst MIN_COL_WIDTH = 8;\n\nfunction addDecimalStrings(a: string, b: string): string {\n // Defensive: malformed peer/apex amounts must not crash the render tree.\n // `formatUsdc` will degrade to '$?.??' on a non-decimal accumulator.\n if (!DECIMAL_RE.test(b)) return a;\n try {\n return (BigInt(a) + BigInt(b)).toString();\n } catch {\n return a;\n }\n}\n\ninterface HeroBandProps {\n apex: AggregatedEarnings['apex'];\n peers: AggregatedEarnings['peers'];\n eventsRelayed: number;\n}\n\ninterface Scalars {\n today: string;\n month: string;\n year: string;\n lifetime: string;\n}\n\nfunction computeScalars(\n apex: AggregatedEarnings['apex'],\n peers: AggregatedEarnings['peers']\n): Scalars {\n let today = '0';\n let month = '0';\n let year = '0';\n let lifetime = '0';\n\n const apexUsdc = apex.routingFees[ASSET];\n if (apexUsdc !== undefined) {\n today = addDecimalStrings(today, apexUsdc.today);\n month = addDecimalStrings(month, apexUsdc.month);\n year = addDecimalStrings(year, apexUsdc.year);\n lifetime = addDecimalStrings(lifetime, apexUsdc.lifetime);\n }\n\n for (const peer of peers) {\n const peerUsdc = peer.byAsset[ASSET];\n if (peerUsdc !== undefined) {\n today = addDecimalStrings(today, peerUsdc.today);\n month = addDecimalStrings(month, peerUsdc.month);\n year = addDecimalStrings(year, peerUsdc.year);\n lifetime = addDecimalStrings(lifetime, peerUsdc.lifetime);\n }\n }\n\n return { today, month, year, lifetime };\n}\n\nfunction isEmptyState(\n apex: AggregatedEarnings['apex'],\n peers: AggregatedEarnings['peers']\n): boolean {\n const apexMonth = apex.routingFees[ASSET]?.month ?? '0';\n if (apexMonth !== '0') return false;\n for (const peer of peers) {\n const peerMonth = peer.byAsset[ASSET]?.month ?? '0';\n if (peerMonth !== '0') return false;\n }\n return true;\n}\n\nexport function HeroBand({ apex, peers, eventsRelayed }: HeroBandProps): ReactElement {\n const { stdout } = useStdout();\n const columns = stdout?.columns ?? 80;\n\n const scalars = computeScalars(apex, peers);\n const showQualifier = isEmptyState(apex, peers);\n\n const todayFmt = formatUsdc(scalars.today, USDC_SCALE);\n const monthFmt = formatUsdc(scalars.month, USDC_SCALE);\n const yearFmt = formatUsdc(scalars.year, USDC_SCALE);\n const lifetimeFmt = formatUsdc(scalars.lifetime, USDC_SCALE);\n\n const shortLabels = columns < 70;\n const labelLifetime = shortLabels ? 'LIFE' : 'LIFETIME';\n\n // Clamp: at very narrow widths (<32ch) Ink would collapse <Box width={0}>\n // and truncate scalar values into garbage. Floor to a usable per-column width.\n const colWidth = Math.max(Math.floor(columns / 4), MIN_COL_WIDTH);\n\n return (\n <Box flexDirection=\"column\">\n <Box>\n <Box width={colWidth}><Text dimColor>TODAY</Text></Box>\n <Box width={colWidth}><Text dimColor>MONTH</Text></Box>\n <Box width={colWidth}><Text dimColor>YEAR</Text></Box>\n <Box width={colWidth}><Text dimColor>{labelLifetime}</Text></Box>\n </Box>\n <Box>\n <Box width={colWidth}>\n <Text color={scalars.today !== '0' ? 'green' : undefined}>{todayFmt}</Text>\n </Box>\n <Box width={colWidth}>\n <Text color={scalars.month !== '0' ? 'green' : undefined}>{monthFmt}</Text>\n </Box>\n <Box width={colWidth}>\n <Text color={scalars.year !== '0' ? 'green' : undefined}>{yearFmt}</Text>\n </Box>\n <Box width={colWidth}>\n <Text color={scalars.lifetime !== '0' ? 'green' : undefined}>{lifetimeFmt}</Text>\n </Box>\n </Box>\n <Sparkline values={[]} width={columns} />\n {showQualifier ? <Qualifier eventsRelayed={eventsRelayed} /> : null}\n </Box>\n );\n}\n","import { Text } from 'ink';\nimport type { ReactElement } from 'react';\n\nconst BLOCKS = '▁▂▃▄▅▆▇█';\nconst PLACEHOLDER = '·······';\n\ninterface SparklineProps {\n values: number[];\n width: number;\n}\n\nexport function Sparkline({ values, width }: SparklineProps): ReactElement | null {\n // Collapse the row entirely at <60ch — return null so the layout doesn't\n // reserve a blank row (wireframe degrade ladder: sparkline drops first).\n if (width < 60) {\n return null;\n }\n\n if (values.length === 0) {\n return <Text>{PLACEHOLDER} 7d</Text>;\n }\n\n // Filter NaN / Infinity / negative values; treat negatives as 0 floor.\n const safe = values\n .filter((v) => Number.isFinite(v))\n .map((v) => (v < 0 ? 0 : v));\n\n if (safe.length === 0) {\n return <Text>{PLACEHOLDER} 7d</Text>;\n }\n\n // Use reduce to avoid Math.max(...arr) stack overflow on large arrays.\n const max = safe.reduce((m, v) => (v > m ? v : m), 0);\n const chars = safe\n .map((v) => {\n if (max === 0) return BLOCKS[0] ?? '▁';\n const idx = Math.floor((v / max) * (BLOCKS.length - 1));\n return BLOCKS[idx] ?? '▁';\n })\n .join('');\n\n return <Text>{chars} 7d</Text>;\n}\n","import { Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport { COPY } from '../copy.js';\n\ninterface QualifierProps {\n eventsRelayed: number;\n}\n\nexport function Qualifier({ eventsRelayed }: QualifierProps): ReactElement {\n return (\n <Text color=\"yellow\">\n {COPY.qualifierPrefix} · {COPY.qualifierEvents(eventsRelayed)} · {COPY.heroEarly}\n </Text>\n );\n}\n","export const COPY = {\n heroEarly: `you're early`,\n heroEarlyRotation: [\n `you're early`,\n `warming up`,\n `first packet en route`,\n ] as const,\n loading: `Fetching earnings…`,\n qualifierPrefix: `MONTH $0.00`,\n qualifierEventsWords: `events relayed`,\n qualifierEvents: (n: number) => `${n} events relayed`,\n banners: {\n connectorUnavailable: `Connector not reachable — showing last known values. Retrying in 2s.`,\n fetchFailed: `Last refresh failed — retrying.`,\n },\n apex: {\n routingPrefix: `↳ apex routing: `,\n routingEmpty: `(enable mill to route)`,\n },\n peerTable: {\n empty: `no peers yet — run 'townhouse node add town'`,\n },\n activityTicker: {\n prefix: `recent: `,\n empty: `no settlements yet — press [a] when activity arrives`,\n keybind: ` [a] activity`,\n },\n activityOverlay: {\n titlePrefix: `Activity — last `,\n emptyHint: `(no activity yet)`,\n scrollHint: `j/k to scroll · q to close`,\n scrollHintEmpty: `q to close`,\n directionInbound: `in`,\n directionOutbound: `out`,\n directionUnknown: `?`,\n },\n} as const;\n","import { Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport { COPY } from '../copy.js';\n\ninterface BannerProps {\n bannerKey: 'connector_unavailable' | 'fetch_failed' | null;\n}\n\nexport function Banner({ bannerKey }: BannerProps): ReactElement | null {\n if (bannerKey === null) return null;\n\n const isError = bannerKey === 'fetch_failed';\n const text = isError\n ? COPY.banners.fetchFailed\n : COPY.banners.connectorUnavailable;\n\n return <Text color={isError ? 'red' : 'yellow'}>{text}</Text>;\n}\n","import { Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport type { AggregatedEarnings } from '../types.js';\nimport { formatUsdc } from '../format.js';\nimport { COPY } from '../copy.js';\n\nconst USDC_SCALE = 6;\nconst ASSET = 'USDC';\nconst DECIMAL_RE = /^-?\\d+$/;\n\nfunction addDecimalStrings(a: string, b: string): string {\n // Defensive: malformed peer amounts must not crash the render tree.\n if (!DECIMAL_RE.test(b)) return a;\n try {\n return (BigInt(a) + BigInt(b)).toString();\n } catch {\n return a;\n }\n}\n\nexport interface ApexStripProps {\n apex: AggregatedEarnings['apex'];\n peers: AggregatedEarnings['peers'];\n}\n\nexport function ApexStrip({ apex, peers }: ApexStripProps): ReactElement {\n const apexMonth = apex.routingFees[ASSET]?.month ?? '0';\n const apexValid = DECIMAL_RE.test(apexMonth);\n const apexMonthBig = apexValid ? BigInt(apexMonth) : 0n;\n\n let totalMonth = apexMonthBig;\n for (const peer of peers) {\n const peerMonth = peer.byAsset[ASSET]?.month ?? '0';\n totalMonth = BigInt(addDecimalStrings(totalMonth.toString(), peerMonth));\n }\n\n const apexFmt = formatUsdc(apexMonth, USDC_SCALE);\n const hasMillPeer = peers.some((p) => p.type === 'mill');\n\n // Malformed apex.month: render the formatUsdc fallback alone — adding the Mill upsell\n // would mix a wire-anomaly signal with a \"you have no Mill\" signal.\n if (!apexValid) {\n return (\n <Text dimColor italic>\n {COPY.apex.routingPrefix}{apexFmt}\n </Text>\n );\n }\n\n if (apexMonthBig === 0n) {\n const upsell = hasMillPeer ? '' : ` ${COPY.apex.routingEmpty}`;\n return (\n <Text dimColor italic>\n {COPY.apex.routingPrefix}{apexFmt}{upsell}\n </Text>\n );\n }\n\n // Defensive: negative apex (refund/chargeback, wire-legal per `^-?\\d+$`) can let peers\n // exactly cancel apex; totalMonth === 0n would throw on BigInt division. Omit instead.\n const pct = totalMonth === 0n ? null : Number((apexMonthBig * 100n) / totalMonth);\n return (\n <Text>\n {COPY.apex.routingPrefix}{apexFmt}{pct !== null ? ` (${pct}%)` : ''}\n </Text>\n );\n}\n","import { Box, Text, useStdout } from 'ink';\nimport type { ReactElement } from 'react';\nimport type { AggregatedEarnings, NodeEarnings, PerAsset } from '../types.js';\nimport { formatUsdc, formatRelativeTime } from '../format.js';\nimport { COPY } from '../copy.js';\n\nconst USDC_SCALE = 6;\nconst MAX_DATA_ROWS = 4;\nconst MIN_COL_WIDTH = 6;\n\ninterface AssetRow {\n peerId: string;\n type: string;\n assetCode: string;\n perAsset: PerAsset;\n lastClaimAt: string | null;\n isFirstRowOfPeer: boolean;\n}\n\nfunction flattenPeers(peers: NodeEarnings[]): AssetRow[] {\n const out: AssetRow[] = [];\n for (const peer of peers) {\n const assetCodes = Object.keys(peer.byAsset).sort();\n if (assetCodes.length === 0) continue;\n let isFirst = true;\n for (const assetCode of assetCodes) {\n const perAsset = peer.byAsset[assetCode];\n if (perAsset === undefined) continue;\n out.push({\n peerId: peer.id,\n type: peer.type,\n assetCode,\n perAsset,\n lastClaimAt: peer.lastClaimAt,\n isFirstRowOfPeer: isFirst,\n });\n isFirst = false;\n }\n }\n return out;\n}\n\nexport interface PeerTableProps {\n peers: AggregatedEarnings['peers'];\n now?: Date;\n /** Override terminal column width. Defaults to useStdout(). Inject in tests to pin width. */\n columns?: number;\n}\n\nexport function PeerTable({ peers, now = new Date(), columns: columnsProp }: PeerTableProps): ReactElement {\n const { stdout } = useStdout();\n // `??` only coalesces null/undefined — a detached/piped tty can ship `columns === 0`,\n // which would clamp every column to MIN_COL_WIDTH and garble the header. Fall back to 80.\n const columns = columnsProp ?? (stdout?.columns || 80);\n\n const rows = flattenPeers(peers).slice(0, MAX_DATA_ROWS);\n\n if (rows.length === 0) {\n return <Text dimColor>{COPY.peerTable.empty}</Text>;\n }\n\n const showLastClaim = columns >= 60;\n const shortType = columns < 70;\n const dropAgoSuffix = columns < 70;\n\n const totalCols = showLastClaim ? 5 : 4;\n const colWidth = Math.max(Math.floor(columns / totalCols), MIN_COL_WIDTH);\n\n const header = (\n <Box>\n <Box width={colWidth}><Text dimColor>PEER</Text></Box>\n <Box width={colWidth}><Text dimColor>TYPE</Text></Box>\n <Box width={colWidth}><Text dimColor>ASSET</Text></Box>\n <Box width={colWidth}><Text dimColor>NET (MONTH)</Text></Box>\n {showLastClaim ? <Box width={colWidth}><Text dimColor>LAST CLAIM</Text></Box> : null}\n </Box>\n );\n\n return (\n <Box flexDirection=\"column\">\n {header}\n {rows.map((row, i) => {\n const peerCell = row.isFirstRowOfPeer ? row.peerId : '';\n const typeRaw = row.isFirstRowOfPeer ? row.type : '';\n const typeCell = shortType && typeRaw.length > 0 ? typeRaw.slice(0, 3) : typeRaw;\n const netFmt = formatUsdc(row.perAsset.month, USDC_SCALE);\n let lastClaim = formatRelativeTime(row.lastClaimAt, now);\n if (dropAgoSuffix && lastClaim.endsWith(' ago')) {\n lastClaim = lastClaim.slice(0, -' ago'.length);\n }\n return (\n <Box key={`${row.peerId}-${row.assetCode}-${i}`}>\n <Box width={colWidth}><Text>{peerCell}</Text></Box>\n <Box width={colWidth}><Text>{typeCell}</Text></Box>\n <Box width={colWidth}><Text>{row.assetCode}</Text></Box>\n <Box width={colWidth}><Text>{netFmt}</Text></Box>\n {showLastClaim ? <Box width={colWidth}><Text>{lastClaim}</Text></Box> : null}\n </Box>\n );\n })}\n </Box>\n );\n}\n","import { Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport type { RecentClaim } from '../types.js';\nimport { formatUsdcMicro, formatRelativeTime } from '../format.js';\nimport { COPY } from '../copy.js';\n\nexport interface ActivityTickerProps {\n recentClaims: RecentClaim[];\n now?: Date;\n}\n\nfunction sortKey(c: RecentClaim): number {\n const ms = Date.parse(c.at);\n return Number.isFinite(ms) ? ms : -Infinity;\n}\n\nfunction arrowFor(direction: RecentClaim['direction']): string {\n return direction === 'inbound' ? '←' : direction === 'outbound' ? '→' : COPY.activityOverlay.directionUnknown;\n}\n\nexport function ActivityTicker({ recentClaims, now = new Date() }: ActivityTickerProps): ReactElement {\n if (recentClaims.length === 0) {\n return <Text dimColor>{COPY.activityTicker.empty}</Text>;\n }\n // Defensive sort DESC by `at` — wire ordering is not contractually guaranteed.\n const sorted = [...recentClaims].sort((a, b) => sortKey(b) - sortKey(a));\n const claim = sorted[0];\n if (!claim) {\n return <Text dimColor>{COPY.activityTicker.empty}</Text>;\n }\n const arrow = arrowFor(claim.direction);\n const amount = formatUsdcMicro(claim.amount, claim.assetScale);\n const rel = formatRelativeTime(claim.at, now);\n return (\n <Text dimColor>\n {COPY.activityTicker.prefix}{claim.peerId} {arrow} {amount} {claim.assetCode} · {rel}{COPY.activityTicker.keybind}\n </Text>\n );\n}\n","import { Text } from 'ink';\nimport type { ReactElement } from 'react';\nimport type { AggregatedEarnings } from '../types.js';\nimport { COPY } from '../copy.js';\n\nconst USDC_ASSET = 'USDC';\nconst DECIMAL_RE = /^-?\\d+$/;\n\nconst LIFETIME_USDC_THRESHOLD = 1_000_000n;\nconst UPTIME_SECONDS_THRESHOLD = 7 * 24 * 60 * 60;\nexport const ROTATION_INTERVAL_MS = 30_000;\n\nfunction parseDecimalOrZero(value: string | undefined): bigint {\n if (value === undefined || !DECIMAL_RE.test(value)) return 0n;\n try {\n return BigInt(value);\n } catch {\n return 0n;\n }\n}\n\nfunction computeLifetimeUsdc(\n apex: AggregatedEarnings['apex'],\n peers: AggregatedEarnings['peers']\n): bigint {\n let total = parseDecimalOrZero(apex.routingFees[USDC_ASSET]?.lifetime);\n for (const peer of peers) {\n total += parseDecimalOrZero(peer.byAsset[USDC_ASSET]?.lifetime);\n }\n return total;\n}\n\nexport interface BadgeProps {\n apex: AggregatedEarnings['apex'];\n peers: AggregatedEarnings['peers'];\n uptimeSeconds: number;\n /** Override the wall clock. Default `new Date()`. Inject in tests to pin rotation. */\n now?: Date;\n}\n\nexport function Badge({\n apex,\n peers,\n uptimeSeconds,\n now = new Date(),\n}: BadgeProps): ReactElement | null {\n const lifetime = computeLifetimeUsdc(apex, peers);\n const lifetimeTriggers = lifetime < LIFETIME_USDC_THRESHOLD;\n const uptimeTriggers = uptimeSeconds < UPTIME_SECONDS_THRESHOLD;\n\n if (!lifetimeTriggers && !uptimeTriggers) return null;\n\n const index =\n Math.floor(now.getTime() / ROTATION_INTERVAL_MS) %\n COPY.heroEarlyRotation.length;\n const text = COPY.heroEarlyRotation[index] ?? COPY.heroEarlyRotation[0];\n\n return (\n <Text color=\"yellow\" bold>\n {text}\n </Text>\n );\n}\n","import { Box, Text, useStdout, useInput } from 'ink';\nimport { useEffect, useState, type ReactElement } from 'react';\nimport type { RecentClaim } from '../types.js';\nimport { formatUsdcMicro } from '../format.js';\nimport { COPY } from '../copy.js';\n\nconst MIN_OVERLAY_WIDTH = 40;\nconst MAX_PEER_ID_WIDTH = 24;\n// Default fallback only. App.tsx provides the authoritative cap (MAX_BUFFER_SIZE in\n// the ring-buffer hook) via the `maxBufferSize` prop so there is one source of truth.\n// Tests that mount the overlay directly fall back to this default.\nconst DEFAULT_MAX_BUFFER_SIZE = 200;\n\nfunction formatTime(iso: string): string {\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return '--:--:--';\n return d.toLocaleTimeString('en-GB', { hour12: false });\n}\n\nfunction truncatePeerId(id: string): string {\n if (id.length <= MAX_PEER_ID_WIDTH) return id;\n return id.slice(0, MAX_PEER_ID_WIDTH - 1) + '…';\n}\n\nfunction arrowFor(direction: RecentClaim['direction']): string {\n return direction === 'inbound' ? '←' : direction === 'outbound' ? '→' : COPY.activityOverlay.directionUnknown;\n}\n\nfunction directionLabel(direction: RecentClaim['direction']): string {\n return direction === 'inbound'\n ? COPY.activityOverlay.directionInbound\n : direction === 'outbound'\n ? COPY.activityOverlay.directionOutbound\n : COPY.activityOverlay.directionUnknown;\n}\n\nfunction formatRow(claim: RecentClaim): string {\n const time = formatTime(claim.at);\n const peer = truncatePeerId(claim.peerId);\n const arrow = arrowFor(claim.direction);\n const amount = formatUsdcMicro(claim.amount, claim.assetScale);\n const dir = directionLabel(claim.direction);\n return `${time} · ${peer} · ${arrow} ${amount} ${claim.assetCode} · ${dir}`;\n}\n\nfunction claimKeyForReact(c: RecentClaim): string {\n return `${c.peerId}|${c.at}|${c.amount}|${c.assetCode}|${c.direction}`;\n}\n\nexport interface ActivityOverlayProps {\n claims: RecentClaim[];\n onClose: () => void;\n columns?: number;\n rows?: number;\n maxBufferSize?: number;\n}\n\nexport function ActivityOverlay({\n claims,\n onClose,\n columns: columnsProp,\n rows: rowsProp,\n maxBufferSize = DEFAULT_MAX_BUFFER_SIZE,\n}: ActivityOverlayProps): ReactElement {\n const { stdout } = useStdout();\n const columns = columnsProp ?? (stdout?.columns || 80);\n const rows = rowsProp ?? (stdout?.rows || 24);\n\n const modalWidth = Math.max(MIN_OVERLAY_WIDTH, Math.floor(columns * 0.7));\n const visibleRows = Math.max(5, rows - 5);\n\n const [scroll, setScroll] = useState(0);\n const maxScroll = Math.max(0, claims.length - visibleRows);\n\n // Reconcile scroll when maxScroll shrinks under it — terminal resize that grows\n // `visibleRows` (or any future shrink of `claims`) would otherwise leave the slice\n // pointing past the data, hiding the newest entries until the operator presses k.\n useEffect(() => {\n if (scroll > maxScroll) setScroll(maxScroll);\n }, [maxScroll, scroll]);\n\n useInput((input, key) => {\n // ESC must be checked BEFORE the ctrl/meta guard — Ink's input parser sets\n // `key.meta` on a bare `\\x1b` byte (Alt-prefix detection), which would\n // otherwise eat the close action.\n if (key.escape) {\n onClose();\n return;\n }\n // Guard against Ctrl-* and Alt-* — they MUST NOT trigger close/scroll.\n if (key.ctrl || key.meta) return;\n if (input === 'q' || input === 'Q') {\n onClose();\n return;\n }\n if (input === 'j' || key.downArrow) {\n setScroll((s) => Math.min(maxScroll, s + 1));\n return;\n }\n if (input === 'k' || key.upArrow) {\n setScroll((s) => Math.max(0, s - 1));\n }\n });\n\n const displayedCount = Math.min(claims.length, maxBufferSize);\n const title = `${COPY.activityOverlay.titlePrefix}${displayedCount} of ${maxBufferSize}`;\n const window = claims.slice(scroll, scroll + visibleRows);\n const hint = claims.length === 0 ? COPY.activityOverlay.scrollHintEmpty : COPY.activityOverlay.scrollHint;\n\n return (\n <Box flexDirection=\"column\" alignItems=\"center\" width={columns}>\n <Box flexDirection=\"column\" borderStyle=\"round\" width={modalWidth} paddingX={1}>\n <Text bold>{title}</Text>\n {claims.length === 0 ? (\n <Text dimColor>{COPY.activityOverlay.emptyHint}</Text>\n ) : (\n window.map((c, i) => (\n <Text key={`${claimKeyForReact(c)}-${scroll + i}`}>{formatRow(c)}</Text>\n ))\n )}\n <Text dimColor>{hint}</Text>\n </Box>\n </Box>\n );\n}\n"],"mappings":";;;;;;;;;AAAA,SAAS,qBAAqB;AAC9B,SAAS,cAA6B;;;ACDtC,SAAgB,YAAAA,iBAAgB;AAChC,SAAS,OAAAC,MAAK,QAAAC,QAAM,YAAAC,iBAAgB;;;ACDpC,SAAS,WAAW,QAAQ,gBAAgB;;;ACArC,IAAM,8BAA8B;AACpC,IAAM,kBAAkB;;;ADkB/B,IAAM,iBAAqC;AAAA,EACzC,QAAQ;AAAA,EACR,MAAM,EAAE,aAAa,CAAC,EAAE;AAAA,EACxB,OAAO,CAAC;AAAA,EACR,cAAc,CAAC;AAAA,EACf,eAAe;AAAA,EACf,eAAe;AACjB;AAEO,SAAS,YAAY,OAA2B,CAAC,GAAkB;AACxE,QAAM;AAAA,IACJ,SAAS;AAAA,IACT,oBAAoB;AAAA,IACpB,YAAY,WAAW;AAAA,EACzB,IAAI;AAEJ,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB;AAAA,IAChD,OAAO;AAAA,IACP,MAAM;AAAA,IACN,WAAW;AAAA,EACb,CAAC;AAED,QAAM,cAAc,OAAkC,IAAI;AAE1D,YAAU,MAAM;AACd,QAAI,YAAY;AAChB,QAAI,kBAA0C;AAE9C,mBAAe,UAAyB;AACtC,UAAI,UAAW;AAEf,YAAM,KAAK,IAAI,gBAAgB;AAC/B,wBAAkB;AAElB,UAAI;AACF,cAAM,MAAM,MAAM,UAAU,GAAG,MAAM,iBAAiB;AAAA,UACpD,QAAQ,GAAG;AAAA,QACb,CAAC;AAED,YAAI,UAAW;AAEf,YAAI,CAAC,IAAI,IAAI;AACX,gBAAM,OAAO,YAAY;AACzB,mBAAS;AAAA,YACP,OAAO;AAAA,YACP,MAAM,QAAQ;AAAA,YACd,WAAW;AAAA,UACb,CAAC;AACD;AAAA,QACF;AAEA,cAAM,OAAQ,MAAM,IAAI,KAAK;AAE7B,YAAI,UAAW;AAEf,YAAI,KAAK,WAAW,yBAAyB;AAC3C,gBAAM,OAAO,YAAY;AACzB,mBAAS;AAAA,YACP,OAAO;AAAA,YACP,MAAM,QAAQ;AAAA,YACd,WAAW;AAAA,UACb,CAAC;AACD;AAAA,QACF;AAEA,oBAAY,UAAU;AACtB,iBAAS,EAAE,OAAO,MAAM,MAAM,MAAM,WAAW,KAAK,CAAC;AAAA,MACvD,SAAS,KAAK;AACZ,YAAI,UAAW;AACf,YAAI,eAAe,SAAS,IAAI,SAAS,aAAc;AAEvD,cAAM,OAAO,YAAY;AACzB,iBAAS;AAAA,UACP,OAAO;AAAA,UACP,MAAM,QAAQ;AAAA,UACd,WAAW;AAAA,QACb,CAAC;AAAA,MACH,UAAE;AACA,0BAAkB;AAAA,MACpB;AAAA,IACF;AAEA,SAAK,QAAQ;AAEb,UAAM,aAAa,YAAY,MAAM;AACnC,WAAK,QAAQ;AAAA,IACf,GAAG,iBAAiB;AAEpB,WAAO,MAAM;AACX,kBAAY;AACZ,oBAAc,UAAU;AACxB,UAAI,oBAAoB,MAAM;AAC5B,wBAAgB,MAAM;AAAA,MACxB;AAAA,IACF;AAAA,EACF,GAAG,CAAC,QAAQ,mBAAmB,SAAS,CAAC;AAEzC,SAAO;AACT;;;AErHA,SAAS,YAAAC,WAAU,aAAAC,kBAAiB;AAG7B,IAAM,kBAAkB;AAE/B,SAAS,SAAS,GAAwB;AACxC,SAAO,GAAG,EAAE,MAAM,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,IAAI,EAAE,SAAS,IAAI,EAAE,SAAS;AACtE;AAEA,SAAS,QAAQ,GAAwB;AACvC,QAAM,KAAK,KAAK,MAAM,EAAE,EAAE;AAC1B,SAAO,OAAO,SAAS,EAAE,IAAI,KAAK;AACpC;AAEO,SAAS,kBACd,UACe;AACf,QAAM,CAAC,QAAQ,SAAS,IAAID,UAAwB,CAAC,CAAC;AAEtD,EAAAC,WAAU,MAAM;AACd,QAAI,CAAC,MAAM,QAAQ,QAAQ,EAAG;AAC9B,QAAI,SAAS,WAAW,KAAK,OAAO,WAAW,EAAG;AAElD,UAAM,OAAO,oBAAI,IAAyB;AAC1C,eAAW,KAAK,OAAQ,MAAK,IAAI,SAAS,CAAC,GAAG,CAAC;AAC/C,eAAW,KAAK,SAAU,MAAK,IAAI,SAAS,CAAC,GAAG,CAAC;AAEjD,UAAM,SAAS,MAAM,KAAK,KAAK,OAAO,CAAC;AACvC,WAAO,KAAK,CAAC,GAAG,MAAM,QAAQ,CAAC,IAAI,QAAQ,CAAC,CAAC;AAC7C,UAAM,UAAU,OAAO,MAAM,GAAG,eAAe;AAE/C,UAAM,OACJ,QAAQ,WAAW,OAAO,UAC1B,QAAQ;AAAA,MACN,CAAC,GAAG,MACF,OAAO,CAAC,MAAM,UACd,SAAS,CAAC,MAAM,SAAS,OAAO,CAAC,CAAgB;AAAA,IACrD;AACF,QAAI,CAAC,KAAM,WAAU,OAAO;AAAA,EAC9B,GAAG,CAAC,QAAQ,CAAC;AAEb,SAAO;AACT;;;AC1CA,SAAS,KAAK,QAAAC,OAAM,iBAAiB;;;ACArC,SAAS,YAAY;AAmBV;AAhBX,IAAM,SAAS;AACf,IAAM,cAAc;AAOb,SAAS,UAAU,EAAE,QAAQ,MAAM,GAAwC;AAGhF,MAAI,QAAQ,IAAI;AACd,WAAO;AAAA,EACT;AAEA,MAAI,OAAO,WAAW,GAAG;AACvB,WAAO,qBAAC,QAAM;AAAA;AAAA,MAAY;AAAA,OAAI;AAAA,EAChC;AAGA,QAAM,OAAO,OACV,OAAO,CAAC,MAAM,OAAO,SAAS,CAAC,CAAC,EAChC,IAAI,CAAC,MAAO,IAAI,IAAI,IAAI,CAAE;AAE7B,MAAI,KAAK,WAAW,GAAG;AACrB,WAAO,qBAAC,QAAM;AAAA;AAAA,MAAY;AAAA,OAAI;AAAA,EAChC;AAGA,QAAM,MAAM,KAAK,OAAO,CAAC,GAAG,MAAO,IAAI,IAAI,IAAI,GAAI,CAAC;AACpD,QAAM,QAAQ,KACX,IAAI,CAAC,MAAM;AACV,QAAI,QAAQ,EAAG,QAAO,OAAO,CAAC,KAAK;AACnC,UAAM,MAAM,KAAK,MAAO,IAAI,OAAQ,OAAO,SAAS,EAAE;AACtD,WAAO,OAAO,GAAG,KAAK;AAAA,EACxB,CAAC,EACA,KAAK,EAAE;AAEV,SAAO,qBAAC,QAAM;AAAA;AAAA,IAAM;AAAA,KAAI;AAC1B;;;AC1CA,SAAS,QAAAC,aAAY;;;ACAd,IAAM,OAAO;AAAA,EAClB,WAAW;AAAA,EACX,mBAAmB;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,SAAS;AAAA,EACT,iBAAiB;AAAA,EACjB,sBAAsB;AAAA,EACtB,iBAAiB,CAAC,MAAc,GAAG,CAAC;AAAA,EACpC,SAAS;AAAA,IACP,sBAAsB;AAAA,IACtB,aAAa;AAAA,EACf;AAAA,EACA,MAAM;AAAA,IACJ,eAAe;AAAA,IACf,cAAc;AAAA,EAChB;AAAA,EACA,WAAW;AAAA,IACT,OAAO;AAAA,EACT;AAAA,EACA,gBAAgB;AAAA,IACd,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,SAAS;AAAA,EACX;AAAA,EACA,iBAAiB;AAAA,IACf,aAAa;AAAA,IACb,WAAW;AAAA,IACX,YAAY;AAAA,IACZ,iBAAiB;AAAA,IACjB,kBAAkB;AAAA,IAClB,mBAAmB;AAAA,IACnB,kBAAkB;AAAA,EACpB;AACF;;;AD1BI,iBAAAC,aAAA;AAFG,SAAS,UAAU,EAAE,cAAc,GAAiC;AACzE,SACE,gBAAAA,MAACC,OAAA,EAAK,OAAM,UACT;AAAA,SAAK;AAAA,IAAgB;AAAA,IAAI,KAAK,gBAAgB,aAAa;AAAA,IAAE;AAAA,IAAI,KAAK;AAAA,KACzE;AAEJ;;;AFsFM,SACwB,KADxB,QAAAC,aAAA;AA7FN,IAAM,aAAa;AACnB,IAAM,QAAQ;AACd,IAAM,aAAa;AACnB,IAAM,gBAAgB;AAEtB,SAAS,kBAAkB,GAAW,GAAmB;AAGvD,MAAI,CAAC,WAAW,KAAK,CAAC,EAAG,QAAO;AAChC,MAAI;AACF,YAAQ,OAAO,CAAC,IAAI,OAAO,CAAC,GAAG,SAAS;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAeA,SAAS,eACP,MACA,OACS;AACT,MAAI,QAAQ;AACZ,MAAI,QAAQ;AACZ,MAAI,OAAO;AACX,MAAI,WAAW;AAEf,QAAM,WAAW,KAAK,YAAY,KAAK;AACvC,MAAI,aAAa,QAAW;AAC1B,YAAQ,kBAAkB,OAAO,SAAS,KAAK;AAC/C,YAAQ,kBAAkB,OAAO,SAAS,KAAK;AAC/C,WAAO,kBAAkB,MAAM,SAAS,IAAI;AAC5C,eAAW,kBAAkB,UAAU,SAAS,QAAQ;AAAA,EAC1D;AAEA,aAAW,QAAQ,OAAO;AACxB,UAAM,WAAW,KAAK,QAAQ,KAAK;AACnC,QAAI,aAAa,QAAW;AAC1B,cAAQ,kBAAkB,OAAO,SAAS,KAAK;AAC/C,cAAQ,kBAAkB,OAAO,SAAS,KAAK;AAC/C,aAAO,kBAAkB,MAAM,SAAS,IAAI;AAC5C,iBAAW,kBAAkB,UAAU,SAAS,QAAQ;AAAA,IAC1D;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,OAAO,MAAM,SAAS;AACxC;AAEA,SAAS,aACP,MACA,OACS;AACT,QAAM,YAAY,KAAK,YAAY,KAAK,GAAG,SAAS;AACpD,MAAI,cAAc,IAAK,QAAO;AAC9B,aAAW,QAAQ,OAAO;AACxB,UAAM,YAAY,KAAK,QAAQ,KAAK,GAAG,SAAS;AAChD,QAAI,cAAc,IAAK,QAAO;AAAA,EAChC;AACA,SAAO;AACT;AAEO,SAAS,SAAS,EAAE,MAAM,OAAO,cAAc,GAAgC;AACpF,QAAM,EAAE,OAAO,IAAI,UAAU;AAC7B,QAAM,UAAU,QAAQ,WAAW;AAEnC,QAAM,UAAU,eAAe,MAAM,KAAK;AAC1C,QAAM,gBAAgB,aAAa,MAAM,KAAK;AAE9C,QAAM,WAAW,WAAW,QAAQ,OAAO,UAAU;AACrD,QAAM,WAAW,WAAW,QAAQ,OAAO,UAAU;AACrD,QAAM,UAAU,WAAW,QAAQ,MAAM,UAAU;AACnD,QAAM,cAAc,WAAW,QAAQ,UAAU,UAAU;AAE3D,QAAM,cAAc,UAAU;AAC9B,QAAM,gBAAgB,cAAc,SAAS;AAI7C,QAAM,WAAW,KAAK,IAAI,KAAK,MAAM,UAAU,CAAC,GAAG,aAAa;AAEhE,SACE,gBAAAA,MAAC,OAAI,eAAc,UACjB;AAAA,oBAAAA,MAAC,OACC;AAAA,0BAAC,OAAI,OAAO,UAAU,8BAACC,OAAA,EAAK,UAAQ,MAAC,mBAAK,GAAO;AAAA,MACjD,oBAAC,OAAI,OAAO,UAAU,8BAACA,OAAA,EAAK,UAAQ,MAAC,mBAAK,GAAO;AAAA,MACjD,oBAAC,OAAI,OAAO,UAAU,8BAACA,OAAA,EAAK,UAAQ,MAAC,kBAAI,GAAO;AAAA,MAChD,oBAAC,OAAI,OAAO,UAAU,8BAACA,OAAA,EAAK,UAAQ,MAAE,yBAAc,GAAO;AAAA,OAC7D;AAAA,IACA,gBAAAD,MAAC,OACC;AAAA,0BAAC,OAAI,OAAO,UACV,8BAACC,OAAA,EAAK,OAAO,QAAQ,UAAU,MAAM,UAAU,QAAY,oBAAS,GACtE;AAAA,MACA,oBAAC,OAAI,OAAO,UACV,8BAACA,OAAA,EAAK,OAAO,QAAQ,UAAU,MAAM,UAAU,QAAY,oBAAS,GACtE;AAAA,MACA,oBAAC,OAAI,OAAO,UACV,8BAACA,OAAA,EAAK,OAAO,QAAQ,SAAS,MAAM,UAAU,QAAY,mBAAQ,GACpE;AAAA,MACA,oBAAC,OAAI,OAAO,UACV,8BAACA,OAAA,EAAK,OAAO,QAAQ,aAAa,MAAM,UAAU,QAAY,uBAAY,GAC5E;AAAA,OACF;AAAA,IACA,oBAAC,aAAU,QAAQ,CAAC,GAAG,OAAO,SAAS;AAAA,IACtC,gBAAgB,oBAAC,aAAU,eAA8B,IAAK;AAAA,KACjE;AAEJ;;;AI5HA,SAAS,QAAAC,aAAY;AAgBZ,gBAAAC,YAAA;AARF,SAAS,OAAO,EAAE,UAAU,GAAqC;AACtE,MAAI,cAAc,KAAM,QAAO;AAE/B,QAAM,UAAU,cAAc;AAC9B,QAAM,OAAO,UACT,KAAK,QAAQ,cACb,KAAK,QAAQ;AAEjB,SAAO,gBAAAA,KAACC,OAAA,EAAK,OAAO,UAAU,QAAQ,UAAW,gBAAK;AACxD;;;ACjBA,SAAS,QAAAC,aAAY;AA2Cf,iBAAAC,aAAA;AArCN,IAAMC,cAAa;AACnB,IAAMC,SAAQ;AACd,IAAMC,cAAa;AAEnB,SAASC,mBAAkB,GAAW,GAAmB;AAEvD,MAAI,CAACD,YAAW,KAAK,CAAC,EAAG,QAAO;AAChC,MAAI;AACF,YAAQ,OAAO,CAAC,IAAI,OAAO,CAAC,GAAG,SAAS;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOO,SAAS,UAAU,EAAE,MAAM,MAAM,GAAiC;AACvE,QAAM,YAAY,KAAK,YAAYD,MAAK,GAAG,SAAS;AACpD,QAAM,YAAYC,YAAW,KAAK,SAAS;AAC3C,QAAM,eAAe,YAAY,OAAO,SAAS,IAAI;AAErD,MAAI,aAAa;AACjB,aAAW,QAAQ,OAAO;AACxB,UAAM,YAAY,KAAK,QAAQD,MAAK,GAAG,SAAS;AAChD,iBAAa,OAAOE,mBAAkB,WAAW,SAAS,GAAG,SAAS,CAAC;AAAA,EACzE;AAEA,QAAM,UAAU,WAAW,WAAWH,WAAU;AAChD,QAAM,cAAc,MAAM,KAAK,CAAC,MAAM,EAAE,SAAS,MAAM;AAIvD,MAAI,CAAC,WAAW;AACd,WACE,gBAAAD,MAACK,OAAA,EAAK,UAAQ,MAAC,QAAM,MAClB;AAAA,WAAK,KAAK;AAAA,MAAe;AAAA,OAC5B;AAAA,EAEJ;AAEA,MAAI,iBAAiB,IAAI;AACvB,UAAM,SAAS,cAAc,KAAK,IAAI,KAAK,KAAK,YAAY;AAC5D,WACE,gBAAAL,MAACK,OAAA,EAAK,UAAQ,MAAC,QAAM,MAClB;AAAA,WAAK,KAAK;AAAA,MAAe;AAAA,MAAS;AAAA,OACrC;AAAA,EAEJ;AAIA,QAAM,MAAM,eAAe,KAAK,OAAO,OAAQ,eAAe,OAAQ,UAAU;AAChF,SACE,gBAAAL,MAACK,OAAA,EACE;AAAA,SAAK,KAAK;AAAA,IAAe;AAAA,IAAS,QAAQ,OAAO,KAAK,GAAG,OAAO;AAAA,KACnE;AAEJ;;;AClEA,SAAS,OAAAC,MAAK,QAAAC,OAAM,aAAAC,kBAAiB;AA0D1B,gBAAAC,MAWP,QAAAC,aAXO;AApDX,IAAMC,cAAa;AACnB,IAAM,gBAAgB;AACtB,IAAMC,iBAAgB;AAWtB,SAAS,aAAa,OAAmC;AACvD,QAAM,MAAkB,CAAC;AACzB,aAAW,QAAQ,OAAO;AACxB,UAAM,aAAa,OAAO,KAAK,KAAK,OAAO,EAAE,KAAK;AAClD,QAAI,WAAW,WAAW,EAAG;AAC7B,QAAI,UAAU;AACd,eAAW,aAAa,YAAY;AAClC,YAAM,WAAW,KAAK,QAAQ,SAAS;AACvC,UAAI,aAAa,OAAW;AAC5B,UAAI,KAAK;AAAA,QACP,QAAQ,KAAK;AAAA,QACb,MAAM,KAAK;AAAA,QACX;AAAA,QACA;AAAA,QACA,aAAa,KAAK;AAAA,QAClB,kBAAkB;AAAA,MACpB,CAAC;AACD,gBAAU;AAAA,IACZ;AAAA,EACF;AACA,SAAO;AACT;AASO,SAAS,UAAU,EAAE,OAAO,MAAM,oBAAI,KAAK,GAAG,SAAS,YAAY,GAAiC;AACzG,QAAM,EAAE,OAAO,IAAIC,WAAU;AAG7B,QAAM,UAAU,gBAAgB,QAAQ,WAAW;AAEnD,QAAM,OAAO,aAAa,KAAK,EAAE,MAAM,GAAG,aAAa;AAEvD,MAAI,KAAK,WAAW,GAAG;AACrB,WAAO,gBAAAJ,KAACK,OAAA,EAAK,UAAQ,MAAE,eAAK,UAAU,OAAM;AAAA,EAC9C;AAEA,QAAM,gBAAgB,WAAW;AACjC,QAAM,YAAY,UAAU;AAC5B,QAAM,gBAAgB,UAAU;AAEhC,QAAM,YAAY,gBAAgB,IAAI;AACtC,QAAM,WAAW,KAAK,IAAI,KAAK,MAAM,UAAU,SAAS,GAAGF,cAAa;AAExE,QAAM,SACJ,gBAAAF,MAACK,MAAA,EACC;AAAA,oBAAAN,KAACM,MAAA,EAAI,OAAO,UAAU,0BAAAN,KAACK,OAAA,EAAK,UAAQ,MAAC,kBAAI,GAAO;AAAA,IAChD,gBAAAL,KAACM,MAAA,EAAI,OAAO,UAAU,0BAAAN,KAACK,OAAA,EAAK,UAAQ,MAAC,kBAAI,GAAO;AAAA,IAChD,gBAAAL,KAACM,MAAA,EAAI,OAAO,UAAU,0BAAAN,KAACK,OAAA,EAAK,UAAQ,MAAC,mBAAK,GAAO;AAAA,IACjD,gBAAAL,KAACM,MAAA,EAAI,OAAO,UAAU,0BAAAN,KAACK,OAAA,EAAK,UAAQ,MAAC,yBAAW,GAAO;AAAA,IACtD,gBAAgB,gBAAAL,KAACM,MAAA,EAAI,OAAO,UAAU,0BAAAN,KAACK,OAAA,EAAK,UAAQ,MAAC,wBAAU,GAAO,IAAS;AAAA,KAClF;AAGF,SACE,gBAAAJ,MAACK,MAAA,EAAI,eAAc,UAChB;AAAA;AAAA,IACA,KAAK,IAAI,CAAC,KAAK,MAAM;AACpB,YAAM,WAAW,IAAI,mBAAmB,IAAI,SAAS;AACrD,YAAM,UAAU,IAAI,mBAAmB,IAAI,OAAO;AAClD,YAAM,WAAW,aAAa,QAAQ,SAAS,IAAI,QAAQ,MAAM,GAAG,CAAC,IAAI;AACzE,YAAM,SAAS,WAAW,IAAI,SAAS,OAAOJ,WAAU;AACxD,UAAI,YAAY,mBAAmB,IAAI,aAAa,GAAG;AACvD,UAAI,iBAAiB,UAAU,SAAS,MAAM,GAAG;AAC/C,oBAAY,UAAU,MAAM,GAAG,CAAC,OAAO,MAAM;AAAA,MAC/C;AACA,aACE,gBAAAD,MAACK,MAAA,EACC;AAAA,wBAAAN,KAACM,MAAA,EAAI,OAAO,UAAU,0BAAAN,KAACK,OAAA,EAAM,oBAAS,GAAO;AAAA,QAC7C,gBAAAL,KAACM,MAAA,EAAI,OAAO,UAAU,0BAAAN,KAACK,OAAA,EAAM,oBAAS,GAAO;AAAA,QAC7C,gBAAAL,KAACM,MAAA,EAAI,OAAO,UAAU,0BAAAN,KAACK,OAAA,EAAM,cAAI,WAAU,GAAO;AAAA,QAClD,gBAAAL,KAACM,MAAA,EAAI,OAAO,UAAU,0BAAAN,KAACK,OAAA,EAAM,kBAAO,GAAO;AAAA,QAC1C,gBAAgB,gBAAAL,KAACM,MAAA,EAAI,OAAO,UAAU,0BAAAN,KAACK,OAAA,EAAM,qBAAU,GAAO,IAAS;AAAA,WALhE,GAAG,IAAI,MAAM,IAAI,IAAI,SAAS,IAAI,CAAC,EAM7C;AAAA,IAEJ,CAAC;AAAA,KACH;AAEJ;;;ACtGA,SAAS,QAAAE,aAAY;AAsBV,gBAAAC,MAYP,QAAAC,aAZO;AAXX,SAASC,SAAQ,GAAwB;AACvC,QAAM,KAAK,KAAK,MAAM,EAAE,EAAE;AAC1B,SAAO,OAAO,SAAS,EAAE,IAAI,KAAK;AACpC;AAEA,SAAS,SAAS,WAA6C;AAC7D,SAAO,cAAc,YAAY,WAAM,cAAc,aAAa,WAAM,KAAK,gBAAgB;AAC/F;AAEO,SAAS,eAAe,EAAE,cAAc,MAAM,oBAAI,KAAK,EAAE,GAAsC;AACpG,MAAI,aAAa,WAAW,GAAG;AAC7B,WAAO,gBAAAF,KAACG,OAAA,EAAK,UAAQ,MAAE,eAAK,eAAe,OAAM;AAAA,EACnD;AAEA,QAAM,SAAS,CAAC,GAAG,YAAY,EAAE,KAAK,CAAC,GAAG,MAAMD,SAAQ,CAAC,IAAIA,SAAQ,CAAC,CAAC;AACvE,QAAM,QAAQ,OAAO,CAAC;AACtB,MAAI,CAAC,OAAO;AACV,WAAO,gBAAAF,KAACG,OAAA,EAAK,UAAQ,MAAE,eAAK,eAAe,OAAM;AAAA,EACnD;AACA,QAAM,QAAQ,SAAS,MAAM,SAAS;AACtC,QAAM,SAAS,gBAAgB,MAAM,QAAQ,MAAM,UAAU;AAC7D,QAAM,MAAM,mBAAmB,MAAM,IAAI,GAAG;AAC5C,SACE,gBAAAF,MAACE,OAAA,EAAK,UAAQ,MACX;AAAA,SAAK,eAAe;AAAA,IAAQ,MAAM;AAAA,IAAO;AAAA,IAAE;AAAA,IAAM;AAAA,IAAE;AAAA,IAAO;AAAA,IAAE,MAAM;AAAA,IAAU;AAAA,IAAI;AAAA,IAAK,KAAK,eAAe;AAAA,KAC5G;AAEJ;;;ACtCA,SAAS,QAAAC,aAAY;AA0DjB,gBAAAC,YAAA;AArDJ,IAAM,aAAa;AACnB,IAAMC,cAAa;AAEnB,IAAM,0BAA0B;AAChC,IAAM,2BAA2B,IAAI,KAAK,KAAK;AACxC,IAAM,uBAAuB;AAEpC,SAAS,mBAAmB,OAAmC;AAC7D,MAAI,UAAU,UAAa,CAACA,YAAW,KAAK,KAAK,EAAG,QAAO;AAC3D,MAAI;AACF,WAAO,OAAO,KAAK;AAAA,EACrB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,oBACP,MACA,OACQ;AACR,MAAI,QAAQ,mBAAmB,KAAK,YAAY,UAAU,GAAG,QAAQ;AACrE,aAAW,QAAQ,OAAO;AACxB,aAAS,mBAAmB,KAAK,QAAQ,UAAU,GAAG,QAAQ;AAAA,EAChE;AACA,SAAO;AACT;AAUO,SAAS,MAAM;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AAAA,EACA,MAAM,oBAAI,KAAK;AACjB,GAAoC;AAClC,QAAM,WAAW,oBAAoB,MAAM,KAAK;AAChD,QAAM,mBAAmB,WAAW;AACpC,QAAM,iBAAiB,gBAAgB;AAEvC,MAAI,CAAC,oBAAoB,CAAC,eAAgB,QAAO;AAEjD,QAAM,QACJ,KAAK,MAAM,IAAI,QAAQ,IAAI,oBAAoB,IAC/C,KAAK,kBAAkB;AACzB,QAAM,OAAO,KAAK,kBAAkB,KAAK,KAAK,KAAK,kBAAkB,CAAC;AAEtE,SACE,gBAAAD,KAACE,OAAA,EAAK,OAAM,UAAS,MAAI,MACtB,gBACH;AAEJ;;;AC9DA,SAAS,OAAAC,MAAK,QAAAC,OAAM,aAAAC,YAAW,gBAAgB;AAC/C,SAAS,aAAAC,YAAW,YAAAC,iBAAmC;AA8GjD,SACE,OAAAC,MADF,QAAAC,aAAA;AAzGN,IAAM,oBAAoB;AAC1B,IAAM,oBAAoB;AAI1B,IAAM,0BAA0B;AAEhC,SAAS,WAAW,KAAqB;AACvC,QAAM,IAAI,IAAI,KAAK,GAAG;AACtB,MAAI,OAAO,MAAM,EAAE,QAAQ,CAAC,EAAG,QAAO;AACtC,SAAO,EAAE,mBAAmB,SAAS,EAAE,QAAQ,MAAM,CAAC;AACxD;AAEA,SAAS,eAAe,IAAoB;AAC1C,MAAI,GAAG,UAAU,kBAAmB,QAAO;AAC3C,SAAO,GAAG,MAAM,GAAG,oBAAoB,CAAC,IAAI;AAC9C;AAEA,SAASC,UAAS,WAA6C;AAC7D,SAAO,cAAc,YAAY,WAAM,cAAc,aAAa,WAAM,KAAK,gBAAgB;AAC/F;AAEA,SAAS,eAAe,WAA6C;AACnE,SAAO,cAAc,YACjB,KAAK,gBAAgB,mBACrB,cAAc,aACZ,KAAK,gBAAgB,oBACrB,KAAK,gBAAgB;AAC7B;AAEA,SAAS,UAAU,OAA4B;AAC7C,QAAM,OAAO,WAAW,MAAM,EAAE;AAChC,QAAM,OAAO,eAAe,MAAM,MAAM;AACxC,QAAM,QAAQA,UAAS,MAAM,SAAS;AACtC,QAAM,SAAS,gBAAgB,MAAM,QAAQ,MAAM,UAAU;AAC7D,QAAM,MAAM,eAAe,MAAM,SAAS;AAC1C,SAAO,GAAG,IAAI,SAAM,IAAI,SAAM,KAAK,IAAI,MAAM,IAAI,MAAM,SAAS,SAAM,GAAG;AAC3E;AAEA,SAAS,iBAAiB,GAAwB;AAChD,SAAO,GAAG,EAAE,MAAM,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,IAAI,EAAE,SAAS,IAAI,EAAE,SAAS;AACtE;AAUO,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT,MAAM;AAAA,EACN,gBAAgB;AAClB,GAAuC;AACrC,QAAM,EAAE,OAAO,IAAIC,WAAU;AAC7B,QAAM,UAAU,gBAAgB,QAAQ,WAAW;AACnD,QAAM,OAAO,aAAa,QAAQ,QAAQ;AAE1C,QAAM,aAAa,KAAK,IAAI,mBAAmB,KAAK,MAAM,UAAU,GAAG,CAAC;AACxE,QAAM,cAAc,KAAK,IAAI,GAAG,OAAO,CAAC;AAExC,QAAM,CAAC,QAAQ,SAAS,IAAIC,UAAS,CAAC;AACtC,QAAM,YAAY,KAAK,IAAI,GAAG,OAAO,SAAS,WAAW;AAKzD,EAAAC,WAAU,MAAM;AACd,QAAI,SAAS,UAAW,WAAU,SAAS;AAAA,EAC7C,GAAG,CAAC,WAAW,MAAM,CAAC;AAEtB,WAAS,CAAC,OAAO,QAAQ;AAIvB,QAAI,IAAI,QAAQ;AACd,cAAQ;AACR;AAAA,IACF;AAEA,QAAI,IAAI,QAAQ,IAAI,KAAM;AAC1B,QAAI,UAAU,OAAO,UAAU,KAAK;AAClC,cAAQ;AACR;AAAA,IACF;AACA,QAAI,UAAU,OAAO,IAAI,WAAW;AAClC,gBAAU,CAAC,MAAM,KAAK,IAAI,WAAW,IAAI,CAAC,CAAC;AAC3C;AAAA,IACF;AACA,QAAI,UAAU,OAAO,IAAI,SAAS;AAChC,gBAAU,CAAC,MAAM,KAAK,IAAI,GAAG,IAAI,CAAC,CAAC;AAAA,IACrC;AAAA,EACF,CAAC;AAED,QAAM,iBAAiB,KAAK,IAAI,OAAO,QAAQ,aAAa;AAC5D,QAAM,QAAQ,GAAG,KAAK,gBAAgB,WAAW,GAAG,cAAc,OAAO,aAAa;AACtF,QAAM,SAAS,OAAO,MAAM,QAAQ,SAAS,WAAW;AACxD,QAAM,OAAO,OAAO,WAAW,IAAI,KAAK,gBAAgB,kBAAkB,KAAK,gBAAgB;AAE/F,SACE,gBAAAL,KAACM,MAAA,EAAI,eAAc,UAAS,YAAW,UAAS,OAAO,SACrD,0BAAAL,MAACK,MAAA,EAAI,eAAc,UAAS,aAAY,SAAQ,OAAO,YAAY,UAAU,GAC3E;AAAA,oBAAAN,KAACO,OAAA,EAAK,MAAI,MAAE,iBAAM;AAAA,IACjB,OAAO,WAAW,IACjB,gBAAAP,KAACO,OAAA,EAAK,UAAQ,MAAE,eAAK,gBAAgB,WAAU,IAE/C,OAAO,IAAI,CAAC,GAAG,MACb,gBAAAP,KAACO,OAAA,EAAmD,oBAAU,CAAC,KAApD,GAAG,iBAAiB,CAAC,CAAC,IAAI,SAAS,CAAC,EAAkB,CAClE;AAAA,IAEH,gBAAAP,KAACO,OAAA,EAAK,UAAQ,MAAE,gBAAK;AAAA,KACvB,GACF;AAEJ;;;Ab1FW,gBAAAC,MAWP,QAAAC,aAXO;AAfI,SAAR,IAAqB,OAAqC;AAC/D,QAAM,QAAQ,YAAY,KAAK;AAC/B,QAAM,eAAe,MAAM,UAAU,YAAY,MAAM,KAAK,eAAe;AAC3E,QAAM,SAAS,kBAAkB,YAAY;AAC7C,QAAM,CAAC,aAAa,cAAc,IAAIC,UAAS,KAAK;AAEpD,EAAAC;AAAA,IACE,CAAC,OAAO,QAAQ;AACd,UAAI,IAAI,QAAQ,IAAI,KAAM;AAC1B,UAAI,UAAU,OAAO,UAAU,IAAK,gBAAe,IAAI;AAAA,IACzD;AAAA,IACA,EAAE,UAAU,CAAC,eAAe,MAAM,UAAU,UAAU;AAAA,EACxD;AAEA,MAAI,MAAM,UAAU,WAAW;AAC7B,WAAO,gBAAAH,KAACI,QAAA,EAAM,eAAK,SAAQ;AAAA,EAC7B;AAEA,MAAI,aAAa;AACf,WAAO,gBAAAJ,KAAC,mBAAgB,QAAQ,QAAQ,SAAS,MAAM,eAAe,KAAK,GAAG,eAAe,iBAAiB;AAAA,EAChH;AAEA,QAAM,EAAE,KAAK,IAAI;AACjB,QAAM,YAAY,MAAM,UAAU,UAAU,MAAM,YAAY;AAE9D,SACE,gBAAAC,MAACI,MAAA,EAAI,eAAc,UACjB;AAAA,oBAAAL,KAAC,YAAS,MAAM,KAAK,MAAM,OAAO,KAAK,OAAO,eAAe,KAAK,eAAe;AAAA,IACjF,gBAAAA,KAAC,SAAM,MAAM,KAAK,MAAM,OAAO,KAAK,OAAO,eAAe,KAAK,eAAe;AAAA,IAC9E,gBAAAA,KAAC,UAAO,WAAsB;AAAA,IAC9B,gBAAAA,KAAC,aAAc,MAAM,KAAK,MAAM,OAAO,KAAK,OAAO;AAAA,IACnD,gBAAAA,KAAC,aAAc,OAAO,KAAK,OAAO;AAAA,IAClC,gBAAAA,KAAC,kBAAW,cAAc,KAAK,cAAc;AAAA,KAC/C;AAEJ;;;AD5CO,SAAS,SAAS,OAAwB,CAAC,GAAa;AAC7D,SAAO,OAAO,cAAc,KAAK,IAAI,GAAG;AAAA,IACtC,aAAa;AAAA,IACb,cAAc;AAAA,EAChB,CAAC;AACH;","names":["useState","Box","Text","useInput","useState","useEffect","Text","Text","jsxs","Text","jsxs","Text","Text","jsx","Text","Text","jsxs","USDC_SCALE","ASSET","DECIMAL_RE","addDecimalStrings","Text","Box","Text","useStdout","jsx","jsxs","USDC_SCALE","MIN_COL_WIDTH","useStdout","Text","Box","Text","jsx","jsxs","sortKey","Text","Text","jsx","DECIMAL_RE","Text","Box","Text","useStdout","useEffect","useState","jsx","jsxs","arrowFor","useStdout","useState","useEffect","Box","Text","jsx","jsxs","useState","useInput","Text","Box"]}
|