feat: syntax-highlight decision trace JSON on order detail page
ci/woodpecker/push/test Pipeline was successful
ci/woodpecker/push/build-1 Pipeline was successful
ci/woodpecker/push/build-2 Pipeline was successful
ci/woodpecker/push/build-3 Pipeline was successful
ci/woodpecker/push/finalize Pipeline was successful
Build and Push / lint-and-test (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.adapters.broker_adapter name:broker-adapter]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.aggregation.worker name:aggregation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.extractor.worker name:extractor]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.ingestion.worker name:ingestion]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.lake_publisher.worker name:lake-publisher]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.parser.worker name:parser]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.recommendation.worker name:recommendation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.scheduler.app name:scheduler]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.api.app:app --host 0.0.0.0 --port 8000 name:query-api]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.risk.app:app --host 0.0.0.0 --port 8000 name:risk]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.symbol_registry.app:app --host 0.0.0.0 --port 8000 name:symbol-registry]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.trading.app:app --host 0.0.0.0 --port 8000 name:trading-engine]) (push) Has been cancelled
Build and Push / build-dashboard (push) Has been cancelled
Build and Push / build-superset (push) Has been cancelled
Build and Push / integration-test (push) Has been cancelled
Build and Push / beta-gate (push) Has been cancelled
ci/woodpecker/push/test Pipeline was successful
ci/woodpecker/push/build-1 Pipeline was successful
ci/woodpecker/push/build-2 Pipeline was successful
ci/woodpecker/push/build-3 Pipeline was successful
ci/woodpecker/push/finalize Pipeline was successful
Build and Push / lint-and-test (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.adapters.broker_adapter name:broker-adapter]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.aggregation.worker name:aggregation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.extractor.worker name:extractor]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.ingestion.worker name:ingestion]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.lake_publisher.worker name:lake-publisher]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.parser.worker name:parser]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.recommendation.worker name:recommendation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.scheduler.app name:scheduler]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.api.app:app --host 0.0.0.0 --port 8000 name:query-api]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.risk.app:app --host 0.0.0.0 --port 8000 name:risk]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.symbol_registry.app:app --host 0.0.0.0 --port 8000 name:symbol-registry]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.trading.app:app --host 0.0.0.0 --port 8000 name:trading-engine]) (push) Has been cancelled
Build and Push / build-dashboard (push) Has been cancelled
Build and Push / build-superset (push) Has been cancelled
Build and Push / integration-test (push) Has been cancelled
Build and Push / beta-gate (push) Has been cancelled
Add lightweight JSON highlighter for the decision trace panel: - Keys: cyan - Strings: green - Numbers: yellow - Booleans: purple - Null: red - Structural chars: gray SQL explorer already uses Monaco with SQL highlighting — no changes needed there.
This commit is contained in:
@@ -2,6 +2,55 @@ import { useParams } from '@tanstack/react-router';
|
|||||||
import { useOrder } from '../api/hooks';
|
import { useOrder } from '../api/hooks';
|
||||||
import { StatusBadge, LoadingSpinner, Card } from '../components/ui';
|
import { StatusBadge, LoadingSpinner, Card } from '../components/ui';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lightweight JSON syntax highlighter for read-only display.
|
||||||
|
* Returns React elements with colored spans for keys, strings, numbers, booleans, and null.
|
||||||
|
*/
|
||||||
|
function highlightJson(json: string): React.ReactNode {
|
||||||
|
const parts: React.ReactNode[] = [];
|
||||||
|
// Regex matches JSON tokens: strings, numbers, booleans, null, and structural chars
|
||||||
|
const tokenRe = /("(?:\\.|[^"\\])*")\s*:|("(?:\\.|[^"\\])*")|(-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)|(\btrue\b|\bfalse\b)|(\bnull\b)|([{}[\],])/g;
|
||||||
|
let lastIndex = 0;
|
||||||
|
let match: RegExpExecArray | null;
|
||||||
|
|
||||||
|
while ((match = tokenRe.exec(json)) !== null) {
|
||||||
|
// Add any whitespace/text between tokens
|
||||||
|
if (match.index > lastIndex) {
|
||||||
|
parts.push(json.slice(lastIndex, match.index));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match[1]) {
|
||||||
|
// Key (string followed by colon)
|
||||||
|
parts.push(<span key={match.index} className="text-cyan-400">{match[1]}</span>);
|
||||||
|
parts.push(':');
|
||||||
|
} else if (match[2]) {
|
||||||
|
// String value
|
||||||
|
parts.push(<span key={match.index} className="text-green-400">{match[2]}</span>);
|
||||||
|
} else if (match[3]) {
|
||||||
|
// Number
|
||||||
|
parts.push(<span key={match.index} className="text-yellow-300">{match[3]}</span>);
|
||||||
|
} else if (match[4]) {
|
||||||
|
// Boolean
|
||||||
|
parts.push(<span key={match.index} className="text-purple-400">{match[4]}</span>);
|
||||||
|
} else if (match[5]) {
|
||||||
|
// Null
|
||||||
|
parts.push(<span key={match.index} className="text-red-400">{match[5]}</span>);
|
||||||
|
} else if (match[6]) {
|
||||||
|
// Structural characters
|
||||||
|
parts.push(<span key={match.index} className="text-gray-500">{match[6]}</span>);
|
||||||
|
}
|
||||||
|
|
||||||
|
lastIndex = match.index + match[0].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remaining text
|
||||||
|
if (lastIndex < json.length) {
|
||||||
|
parts.push(json.slice(lastIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
return <>{parts}</>;
|
||||||
|
}
|
||||||
|
|
||||||
export function OrderDetailPage() {
|
export function OrderDetailPage() {
|
||||||
const { id } = useParams({ from: '/orders/$id' });
|
const { id } = useParams({ from: '/orders/$id' });
|
||||||
const { data: order, isLoading } = useOrder(id);
|
const { data: order, isLoading } = useOrder(id);
|
||||||
@@ -33,8 +82,8 @@ export function OrderDetailPage() {
|
|||||||
{order.decision_trace && Object.keys(order.decision_trace).length > 0 && (
|
{order.decision_trace && Object.keys(order.decision_trace).length > 0 && (
|
||||||
<Card>
|
<Card>
|
||||||
<h2 className="mb-2 text-sm font-medium text-gray-400">Decision Trace</h2>
|
<h2 className="mb-2 text-sm font-medium text-gray-400">Decision Trace</h2>
|
||||||
<pre className="overflow-x-auto rounded bg-surface-950 p-3 text-xs text-gray-300">
|
<pre className="overflow-x-auto rounded bg-surface-950 p-3 text-xs leading-relaxed">
|
||||||
{JSON.stringify(order.decision_trace, null, 2)}
|
{highlightJson(JSON.stringify(order.decision_trace, null, 2))}
|
||||||
</pre>
|
</pre>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user