Skip to content

Commit f5d9659

Browse files
wip v26
1 parent 21370bf commit f5d9659

23 files changed

Lines changed: 547 additions & 72 deletions

uapi/app/(root)/components/PublicDocsPageContent.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ const specCoverageCards = [
6363

6464
const quickRouteCards = [
6565
{
66-
href: '/',
66+
href: '/exchange',
6767
eyebrow: 'Exchange',
6868
title: 'Read the public market frame',
6969
summary: BITCODE_PUBLIC_EXPLAINERS.network.summary,

uapi/app/application/ApplicationTransactionDetailActionBar.tsx

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ interface ApplicationTransactionDetailActionBarProps {
2727
isActing: boolean;
2828
shellReady: boolean;
2929
mockMode: boolean;
30+
surface?: 'terminal' | 'exchange';
3031
}
3132

3233
export default function ApplicationTransactionDetailActionBar({
@@ -37,8 +38,10 @@ export default function ApplicationTransactionDetailActionBar({
3738
isActing,
3839
shellReady,
3940
mockMode,
41+
surface = 'terminal',
4042
}: ApplicationTransactionDetailActionBarProps) {
4143
const visibleActions = mockMode ? DETAIL_ACTIONS.filter((action) => action.id !== 'console') : DETAIL_ACTIONS;
44+
const isExchangeSurface = surface === 'exchange';
4245
const runtimeActionDisabled = isActing || !shellReady;
4346
const runtimeActionDisabledTooltip = isActing
4447
? 'Disabled while closure is already running. When enabled, this button writes or refreshes the selected activity detail.'
@@ -70,25 +73,33 @@ export default function ApplicationTransactionDetailActionBar({
7073
<section className="rounded-[1.5rem] border border-white/8 bg-black/20 px-5 py-5">
7174
<div className="flex flex-col gap-4 xl:flex-row xl:items-end xl:justify-between">
7275
<div>
73-
<p className="text-[0.68rem] uppercase tracking-[0.24em] text-neutral-400">Activity interaction</p>
74-
<h3 className="mt-2 text-lg font-semibold text-white">Route-owned detail focus and closure actions</h3>
76+
<p className="text-[0.68rem] uppercase tracking-[0.24em] text-neutral-400">
77+
{isExchangeSurface ? 'Exchange detail interaction' : 'Activity interaction'}
78+
</p>
79+
<h3 className="mt-2 text-lg font-semibold text-white">
80+
{isExchangeSurface ? 'Route-owned Exchange detail focus' : 'Route-owned detail focus and closure actions'}
81+
</h3>
7582
<p className="mt-2 max-w-3xl text-sm leading-6 text-neutral-300">
76-
Switch detail focus, rerun closure, and refresh the selected Bitcode activity from the same place you read it.
83+
{isExchangeSurface
84+
? 'Switch detail focus for the selected Exchange activity while preserving the route-owned master selection.'
85+
: 'Switch detail focus, rerun closure, and refresh the selected Bitcode activity from the same place you read it.'}
7786
</p>
7887
</div>
7988

80-
<div className="flex flex-wrap items-center gap-2">
81-
{runtimeActionDisabled && runtimeActionDisabledTooltip ? (
82-
<DisabledTooltipWrapper tooltip={runtimeActionDisabledTooltip} placement="top">
83-
{runClosureButton}
84-
</DisabledTooltipWrapper>
85-
) : runClosureButton}
86-
{runtimeActionDisabled && runtimeActionDisabledTooltip ? (
87-
<DisabledTooltipWrapper tooltip={runtimeActionDisabledTooltip} placement="top">
88-
{refreshButton}
89-
</DisabledTooltipWrapper>
90-
) : refreshButton}
91-
</div>
89+
{!isExchangeSurface ? (
90+
<div className="flex flex-wrap items-center gap-2">
91+
{runtimeActionDisabled && runtimeActionDisabledTooltip ? (
92+
<DisabledTooltipWrapper tooltip={runtimeActionDisabledTooltip} placement="top">
93+
{runClosureButton}
94+
</DisabledTooltipWrapper>
95+
) : runClosureButton}
96+
{runtimeActionDisabled && runtimeActionDisabledTooltip ? (
97+
<DisabledTooltipWrapper tooltip={runtimeActionDisabledTooltip} placement="top">
98+
{refreshButton}
99+
</DisabledTooltipWrapper>
100+
) : refreshButton}
101+
</div>
102+
) : null}
92103
</div>
93104

94105
<div className="mt-4 flex flex-wrap gap-2">

uapi/app/application/ApplicationTransactionDetailSurface.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ interface ApplicationTransactionDetailSurfaceProps {
6060
detailSection: ApplicationTransactionDetailSection;
6161
onDetailSectionChange: (detailSection: ApplicationTransactionDetailSection) => void;
6262
onRecordActivity?: (draft: ApplicationActivityRecordDraft) => Promise<unknown>;
63+
surface?: 'terminal' | 'exchange';
6364
}
6465

6566
export default function ApplicationTransactionDetailSurface({
@@ -71,6 +72,7 @@ export default function ApplicationTransactionDetailSurface({
7172
detailSection,
7273
onDetailSectionChange,
7374
onRecordActivity,
75+
surface = 'terminal',
7476
}: ApplicationTransactionDetailSurfaceProps) {
7577
const [summaryOpen, setSummaryOpen] = useState(true);
7678
const [isActing, setIsActing] = useState(false);
@@ -292,6 +294,7 @@ export default function ApplicationTransactionDetailSurface({
292294
isActing={isActing}
293295
shellReady={shellReady}
294296
mockMode={usesMockTransactions}
297+
surface={surface}
295298
/>
296299

297300
{showShippables && mergedAssetPackSurface ? (

uapi/app/application/ApplicationTransactionWorkspace.tsx

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ interface ApplicationTransactionWorkspaceProps {
204204
transactionDataMode: TransactionDataMode;
205205
onSelectTransaction: (transactionId: string) => void;
206206
onRecordActivity?: (draft: ApplicationActivityRecordDraft) => Promise<unknown>;
207+
surface?: 'terminal' | 'exchange';
207208
}
208209

209210
export default function ApplicationTransactionWorkspace({
@@ -221,7 +222,17 @@ export default function ApplicationTransactionWorkspace({
221222
transactionDataMode,
222223
onSelectTransaction,
223224
onRecordActivity,
225+
surface = 'terminal',
224226
}: ApplicationTransactionWorkspaceProps) {
227+
const isExchangeSurface = surface === 'exchange';
228+
const surfaceKicker = isExchangeSurface ? 'Bitcode Exchange' : 'Bitcode Terminal';
229+
const surfaceTitle = isExchangeSurface
230+
? 'Activity search, master table, and selected detail'
231+
: 'Master-detail activity, asset packs, proofs, and history';
232+
const surfaceSummary = isExchangeSurface
233+
? 'Search Exchange activity, select a row, and inspect its AssetPack evidence, proof posture, history, and execution detail from the Exchange state layer.'
234+
: 'Use the central ledger as the main read surface: select Bitcode activity, inspect its asset packs, proofs, and history, and keep the full operating chain readable without leaving the page.';
235+
const loadingLabel = isExchangeSurface ? 'Loading Bitcode Exchange…' : 'Loading Bitcode Terminal…';
225236
const mockAssetPackSurface = selectedRun ? MOCK_RUN_ASSET_PACK_SURFACES[selectedRun.id] : null;
226237
const usesMockTransactions = isMockTransactionDataMode(transactionDataMode);
227238
const [runDetail, setRunDetail] = useState<ApplicationRunDetailSnapshot | null>(null);
@@ -296,13 +307,12 @@ export default function ApplicationTransactionWorkspace({
296307
<div className="border-b border-white/8 px-6 py-5">
297308
<div className="flex flex-col gap-5 xl:flex-row xl:items-end xl:justify-between">
298309
<div className="max-w-4xl">
299-
<p className="text-[0.7rem] uppercase tracking-[0.28em] text-neutral-400">Bitcode Terminal</p>
310+
<p className="text-[0.7rem] uppercase tracking-[0.28em] text-neutral-400">{surfaceKicker}</p>
300311
<h2 className="mt-2 text-2xl font-semibold tracking-tight text-white">
301-
Master-detail activity, asset packs, proofs, and history
312+
{surfaceTitle}
302313
</h2>
303314
<p className="mt-2 max-w-3xl text-sm leading-6 text-neutral-300">
304-
Use the central ledger as the main read surface: select Bitcode activity, inspect its asset packs,
305-
proofs, and history, and keep the full operating chain readable without leaving the page.
315+
{surfaceSummary}
306316
</p>
307317
</div>
308318
{selectedRun ? (
@@ -326,7 +336,7 @@ export default function ApplicationTransactionWorkspace({
326336
<div className="px-6 py-6">
327337
{isLoadingRuns ? (
328338
<div className="rounded-[1.5rem] border border-white/6 bg-black/20 px-5 py-10 text-sm text-neutral-400">
329-
Loading Bitcode Terminal…
339+
{loadingLabel}
330340
</div>
331341
) : runsError ? (
332342
<div className="rounded-[1.5rem] border border-red-500/20 bg-red-500/10 px-5 py-5 text-sm text-red-200">
@@ -350,6 +360,7 @@ export default function ApplicationTransactionWorkspace({
350360
isLoadingRuns={isLoadingRuns}
351361
runsError={runsError}
352362
transactionDataMode={transactionDataMode}
363+
surface={surface}
353364
/>
354365

355366
<div className="grid gap-4 2xl:grid-cols-4">
@@ -381,6 +392,7 @@ export default function ApplicationTransactionWorkspace({
381392
detailSection={detailSection}
382393
onDetailSectionChange={onDetailSectionChange}
383394
onRecordActivity={onRecordActivity}
395+
surface={surface}
384396
/>
385397
</div>
386398
)}

uapi/app/application/ApplicationTransactionsTable.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ interface ApplicationTransactionsTableProps {
2828
isLoadingRuns: boolean;
2929
runsError: string | null;
3030
transactionDataMode: TransactionDataMode;
31+
surface?: 'terminal' | 'exchange';
3132
}
3233

3334
export default function ApplicationTransactionsTable({
@@ -42,6 +43,7 @@ export default function ApplicationTransactionsTable({
4243
isLoadingRuns,
4344
runsError,
4445
transactionDataMode,
46+
surface = 'terminal',
4547
}: ApplicationTransactionsTableProps) {
4648
const records = useMemo(() => normalizeApplicationTransactions(runs), [runs]);
4749
const options = useMemo(() => buildApplicationTransactionFilterOptions(records), [records]);
@@ -98,6 +100,7 @@ export default function ApplicationTransactionsTable({
98100
isLoading={isLoadingRuns}
99101
error={runsError}
100102
dataMode={transactionDataMode}
103+
surface={surface}
101104
/>
102105
);
103106
}
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
'use client';
2+
3+
import React from 'react';
4+
import { useCallback, useEffect, useMemo, useState } from 'react';
5+
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
6+
7+
import type { TransactionDataMode } from '@/components/base/bitcode/execution/bitcode-transaction-types';
8+
import ApplicationTransactionWorkspace from '@/app/application/ApplicationTransactionWorkspace';
9+
import {
10+
mapExecutionHistoryRunToWorkspaceRun,
11+
} from '@/app/application/application-activity-history';
12+
import { ApplicationShellBridgeProvider } from '@/app/application/application-shell-bridge';
13+
import {
14+
readApplicationTransactionDetailSection,
15+
readApplicationTransactionFilters,
16+
readApplicationTransactionId,
17+
readApplicationTransactionPagination,
18+
resetApplicationTransactionFilters,
19+
writeApplicationTransactionDetailSection,
20+
writeApplicationTransactionFilters,
21+
writeApplicationTransactionId,
22+
writeApplicationTransactionPagination,
23+
} from '@/app/application/application-transaction-query';
24+
import { resolveApplicationTransactionSource } from '@/app/application/application-transaction-source';
25+
import type { WorkspaceRun } from '@/app/application/application-run-data';
26+
import { isUserOrbitalMockMode } from '@/lib/mock-review-mode';
27+
import { fetchPipelineExecutionHistory } from '@/networking/api-client';
28+
29+
export default function ExchangePageClient() {
30+
const router = useRouter();
31+
const pathname = usePathname();
32+
const searchParams = useSearchParams();
33+
const routeSearchParams = useMemo(() => new URLSearchParams(searchParams.toString()), [searchParams]);
34+
const mockMode = isUserOrbitalMockMode();
35+
const selectedTransactionId = useMemo(
36+
() => readApplicationTransactionId(routeSearchParams),
37+
[routeSearchParams],
38+
);
39+
const selectedTransactionDetailSection = useMemo(
40+
() => readApplicationTransactionDetailSection(routeSearchParams),
41+
[routeSearchParams],
42+
);
43+
const transactionFilters = useMemo(
44+
() => readApplicationTransactionFilters(routeSearchParams),
45+
[routeSearchParams],
46+
);
47+
const transactionPagination = useMemo(
48+
() => readApplicationTransactionPagination(routeSearchParams),
49+
[routeSearchParams],
50+
);
51+
const [liveRuns, setLiveRuns] = useState<WorkspaceRun[]>([]);
52+
const [isLoadingRuns, setIsLoadingRuns] = useState(!mockMode);
53+
const [runsLoadError, setRunsLoadError] = useState<string | null>(null);
54+
55+
const transactionSource = useMemo(
56+
() =>
57+
resolveApplicationTransactionSource({
58+
liveRuns,
59+
mockMode,
60+
selectedTransactionId,
61+
}),
62+
[liveRuns, mockMode, selectedTransactionId],
63+
);
64+
const runs = transactionSource.runs;
65+
const transactionDataMode: TransactionDataMode = transactionSource.dataMode;
66+
const runsError = transactionDataMode === 'review-fallback' ? null : runsLoadError;
67+
68+
const replaceExchangeSearchParams = useCallback(
69+
(nextParams: URLSearchParams) => {
70+
const nextQuery = nextParams.toString();
71+
router.replace(nextQuery ? `${pathname}?${nextQuery}` : pathname, { scroll: false });
72+
},
73+
[pathname, router],
74+
);
75+
76+
const replaceExchangeRoute = useCallback(
77+
(transactionId: string, detailSection = selectedTransactionDetailSection) => {
78+
const nextParams = writeApplicationTransactionDetailSection(
79+
writeApplicationTransactionId(routeSearchParams, transactionId),
80+
detailSection,
81+
);
82+
replaceExchangeSearchParams(nextParams);
83+
},
84+
[replaceExchangeSearchParams, routeSearchParams, selectedTransactionDetailSection],
85+
);
86+
87+
const refreshLiveRuns = useCallback(async () => {
88+
if (mockMode) {
89+
setLiveRuns([]);
90+
setIsLoadingRuns(false);
91+
setRunsLoadError(null);
92+
return [];
93+
}
94+
95+
setIsLoadingRuns(true);
96+
setRunsLoadError(null);
97+
98+
try {
99+
const history = await fetchPipelineExecutionHistory();
100+
const nextRuns = history.map(mapExecutionHistoryRunToWorkspaceRun);
101+
setLiveRuns(nextRuns);
102+
return nextRuns;
103+
} catch (error) {
104+
setLiveRuns([]);
105+
setRunsLoadError(error instanceof Error ? error.message : 'Unable to load Exchange activity.');
106+
return [];
107+
} finally {
108+
setIsLoadingRuns(false);
109+
}
110+
}, [mockMode]);
111+
112+
useEffect(() => {
113+
void refreshLiveRuns();
114+
}, [refreshLiveRuns]);
115+
116+
useEffect(() => {
117+
if (!runs.length) return;
118+
if (selectedTransactionId && runs.some((run) => run.id === selectedTransactionId)) return;
119+
replaceExchangeSearchParams(writeApplicationTransactionId(routeSearchParams, runs[0].id));
120+
}, [replaceExchangeSearchParams, routeSearchParams, runs, selectedTransactionId]);
121+
122+
const selectedRun = useMemo(
123+
() => runs.find((run) => run.id === selectedTransactionId) || runs[0] || null,
124+
[runs, selectedTransactionId],
125+
);
126+
127+
const handleTransactionFiltersChange = (nextFilters: typeof transactionFilters) => {
128+
const nextParams = writeApplicationTransactionPagination(
129+
writeApplicationTransactionFilters(routeSearchParams, nextFilters),
130+
{ page: 1, pageSize: transactionPagination.pageSize },
131+
);
132+
replaceExchangeSearchParams(nextParams);
133+
};
134+
135+
const handleTransactionFiltersReset = () => {
136+
const nextParams = writeApplicationTransactionPagination(
137+
resetApplicationTransactionFilters(routeSearchParams),
138+
{ page: 1, pageSize: transactionPagination.pageSize },
139+
);
140+
replaceExchangeSearchParams(nextParams);
141+
};
142+
143+
const handleTransactionPaginationChange = (nextPagination: typeof transactionPagination) => {
144+
replaceExchangeSearchParams(writeApplicationTransactionPagination(routeSearchParams, nextPagination));
145+
};
146+
147+
const handleTransactionDetailSectionChange = (detailSection: typeof selectedTransactionDetailSection) => {
148+
replaceExchangeSearchParams(writeApplicationTransactionDetailSection(routeSearchParams, detailSection));
149+
};
150+
151+
return (
152+
<ApplicationShellBridgeProvider>
153+
<main className="min-h-screen bg-[radial-gradient(circle_at_top,rgba(73,203,146,0.14),transparent_28%),linear-gradient(180deg,#050915_0%,#02050d_100%)] px-4 pb-24 pt-32 text-neutral-100 tablet:px-6 desktop:px-8">
154+
<div className="mx-auto flex w-full max-w-none flex-col gap-6">
155+
<section className="overflow-hidden rounded-[2rem] border border-emerald-400/15 bg-[linear-gradient(135deg,rgba(7,14,26,0.96),rgba(4,9,18,0.92))] px-6 py-6 shadow-[0_30px_100px_rgba(0,0,0,0.38)]">
156+
<div className="flex flex-col gap-5 xl:flex-row xl:items-end xl:justify-between">
157+
<div className="max-w-4xl">
158+
<p className="text-[0.72rem] uppercase tracking-[0.34em] text-emerald-300/80">Bitcode Exchange</p>
159+
<h1 className="mt-3 text-3xl font-semibold tracking-tight text-white tablet:text-4xl">
160+
Search activity, select a row, and read Exchange state
161+
</h1>
162+
<p className="mt-3 max-w-3xl text-sm leading-7 text-neutral-300 tablet:text-base">
163+
The Exchange is the durable activity layer behind Terminal and connected interfaces. Use this
164+
surface to inspect activity rows, AssetPack evidence, proofs, history, and execution detail without
165+
entering Terminal write controls.
166+
</p>
167+
</div>
168+
<div className="grid gap-3 text-xs uppercase tracking-[0.22em] text-neutral-400 tablet:grid-cols-3">
169+
<div className="rounded-2xl border border-white/8 bg-white/5 px-4 py-3">
170+
<p className="text-emerald-300/85">Primary read</p>
171+
<p className="mt-2 text-neutral-200">activity ledger</p>
172+
</div>
173+
<div className="rounded-2xl border border-white/8 bg-white/5 px-4 py-3">
174+
<p className="text-emerald-300/85">Selected detail</p>
175+
<p className="mt-2 text-neutral-200">AssetPack + proofs</p>
176+
</div>
177+
<div className="rounded-2xl border border-white/8 bg-white/5 px-4 py-3">
178+
<p className="text-emerald-300/85">Terminal relation</p>
179+
<p className="mt-2 text-neutral-200">same state, read-only here</p>
180+
</div>
181+
</div>
182+
</div>
183+
</section>
184+
185+
<ApplicationTransactionWorkspace
186+
runs={runs}
187+
selectedRun={selectedRun}
188+
isLoadingRuns={isLoadingRuns}
189+
runsError={runsError}
190+
transactionDataMode={transactionDataMode}
191+
onSelectTransaction={replaceExchangeRoute}
192+
filters={transactionFilters}
193+
onFiltersChange={handleTransactionFiltersChange}
194+
onResetFilters={handleTransactionFiltersReset}
195+
pagination={transactionPagination}
196+
onPaginationChange={handleTransactionPaginationChange}
197+
detailSection={selectedTransactionDetailSection}
198+
onDetailSectionChange={handleTransactionDetailSectionChange}
199+
surface="exchange"
200+
/>
201+
</div>
202+
</main>
203+
</ApplicationShellBridgeProvider>
204+
);
205+
}

0 commit comments

Comments
 (0)