diff --git a/apps/probe-viewer/src/App.css b/apps/probe-viewer/src/App.css
index 48a2600..c57495d 100644
--- a/apps/probe-viewer/src/App.css
+++ b/apps/probe-viewer/src/App.css
@@ -291,7 +291,7 @@
color: var(--ink);
}
-/* "{ } JSON" link inline in the subtitle metadata line. It uses the blue link
+/* "JSON" link inline in the subtitle metadata line. It uses the blue link
color and a persistent underline (on the word, not the icon) so it clearly
reads as a clickable link rather than just emphasized text. */
.viewer-json-link {
@@ -314,6 +314,27 @@
color: var(--accent-strong);
}
+.viewer-json-download {
+ display: inline-flex;
+ align-items: center;
+ vertical-align: middle;
+ margin-left: 0.35rem;
+ padding: 0.15rem;
+ border-radius: 0.3rem;
+ color: var(--accent);
+ transition: color 0.2s ease, background 0.2s ease;
+}
+
+.viewer-json-download svg {
+ width: 14px;
+ height: 14px;
+}
+
+.viewer-json-download:hover {
+ color: var(--accent-strong);
+ background: var(--accent-soft);
+}
+
.viewer-subtitle {
margin: 0.25rem 0 0;
color: #475569;
@@ -566,6 +587,107 @@
background: var(--accent-soft);
}
+.viewer-snippet {
+ position: relative;
+ display: inline-flex;
+ vertical-align: middle;
+}
+
+/* Looks like the JSON link, with a chevron to say it opens something. */
+.viewer-snippet-toggle {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.15rem;
+ padding: 0;
+ border: none;
+ background: none;
+ font: inherit;
+ font-weight: 600;
+ color: var(--accent);
+ cursor: pointer;
+}
+
+.viewer-snippet-toggle:hover {
+ color: var(--accent-strong);
+}
+
+.viewer-snippet-popover {
+ position: absolute;
+ top: calc(100% + 0.4rem);
+ left: 0;
+ z-index: 10;
+ display: flex;
+ flex-direction: column;
+ min-width: 22rem;
+ overflow: hidden;
+ background: var(--surface);
+ border: 1px solid var(--line);
+ border-radius: var(--radius);
+ box-shadow: var(--shadow);
+}
+
+/* Language label on the left, copy action on the right, as in docs code blocks. */
+.viewer-snippet-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0.3rem 0.4rem 0.3rem 0.8rem;
+ border-bottom: 1px solid var(--line);
+ background: var(--bg);
+}
+
+.viewer-snippet-language {
+ font-size: 0.72rem;
+ font-weight: 600;
+ letter-spacing: 0.04em;
+ text-transform: uppercase;
+ color: var(--muted);
+}
+
+.viewer-snippet-copy {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.3rem;
+ padding: 0.25rem 0.45rem;
+ border: none;
+ border-radius: 0.3rem;
+ background: none;
+ font: inherit;
+ font-size: 0.78rem;
+ color: var(--ink-2);
+ cursor: pointer;
+}
+
+.viewer-snippet-copy:hover {
+ color: var(--accent-strong);
+ background: var(--accent-soft);
+}
+
+.viewer-snippet-code {
+ display: block;
+ padding: 0.8rem 1rem;
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
+ font-size: 0.82rem;
+ line-height: 1.5;
+ color: var(--ink-2);
+ background: #f8fafc;
+ white-space: pre;
+}
+
+/* Syntax highlighting kept monochrome: the chrome carries no hue (see :root). */
+.viewer-snippet-token--keyword {
+ font-weight: 700;
+ color: var(--accent-strong);
+}
+
+.viewer-snippet-token--function {
+ color: var(--ink);
+}
+
+.viewer-snippet-token--string {
+ color: var(--muted);
+}
+
.viewer-canvas-placeholder {
height: 360px;
border-radius: 1rem;
diff --git a/apps/probe-viewer/src/components/ProbeViewer.tsx b/apps/probe-viewer/src/components/ProbeViewer.tsx
index c43dbe1..e91aec9 100644
--- a/apps/probe-viewer/src/components/ProbeViewer.tsx
+++ b/apps/probe-viewer/src/components/ProbeViewer.tsx
@@ -10,6 +10,7 @@ import { ProbeCanvas } from "./ProbeCanvas";
import { DoubleSidedProbeCanvas } from "./DoubleSidedProbeCanvas";
import { LocalProbePanel } from "./LocalProbePanel";
import { ProbeOverview } from "./ProbeOverview";
+import { PythonSnippet } from "./PythonSnippet";
const CANVAS_PADDING = 40;
// How far past "the smallest contact exactly fills the viewport" the zoom cap
@@ -131,14 +132,6 @@ const DownloadIcon = (
);
-// Curly-braces glyph, the de-facto standard symbol for JSON/code.
-const JsonIcon = (
-
-);
-
// Leading icons for the view-toggle chips: an eye (show/hide contact IDs), an
// I-beam matching the on-canvas scale bar, and a minimap frame for the overview.
const EyeIcon = (
@@ -391,9 +384,20 @@ export function ProbeViewer() {
rel="noreferrer"
title="View this probe's JSON on GitHub"
>
- {JsonIcon}
JSON
+
+ {DownloadIcon}
+
+ {" "}
+ ยท{" "}
+
+ {tokens.map(([kind, text], index) => (
+
+ {text}
+
+ ))}
+
+
+ )}
+
+ );
+}