Skip to content

Commit b643078

Browse files
authored
Merge pull request #195 from paritytech/playground/output-panel-logs-and-editor
Playground: keep logs on timeout, refine copy button, hide editor line numbers
2 parents e2c4903 + 6d29d2e commit b643078

3 files changed

Lines changed: 126 additions & 29 deletions

File tree

playground/src/app/globals.css

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,7 @@ button {
780780
/* Inline console (no outer head, since the tab bar provides it) */
781781

782782
.console--inline {
783+
position: relative;
783784
background: var(--console-bg);
784785
border: 1px solid var(--console-rule);
785786
border-radius: var(--radius);
@@ -1004,6 +1005,43 @@ button {
10041005
max-height: 420px;
10051006
overflow: auto;
10061007
}
1008+
.console__copy {
1009+
position: absolute;
1010+
top: 8px;
1011+
right: 8px;
1012+
z-index: 1;
1013+
display: inline-flex;
1014+
align-items: center;
1015+
justify-content: center;
1016+
width: 30px;
1017+
height: 30px;
1018+
padding: 0;
1019+
color: var(--console-mute);
1020+
background: color-mix(in srgb, var(--console-bg) 72%, transparent);
1021+
border: none;
1022+
border-radius: 8px;
1023+
cursor: pointer;
1024+
opacity: 0.55;
1025+
backdrop-filter: blur(4px);
1026+
transition: opacity 120ms ease, color 120ms ease, background 120ms ease;
1027+
}
1028+
.console__copy svg {
1029+
width: 15px;
1030+
height: 15px;
1031+
}
1032+
.console--inline:hover .console__copy,
1033+
.console__copy:focus-visible {
1034+
opacity: 1;
1035+
}
1036+
.console__copy:hover {
1037+
opacity: 1;
1038+
color: var(--console-ink);
1039+
background: color-mix(in srgb, var(--console-ink) 10%, transparent);
1040+
}
1041+
.console__copy[data-copied="true"] {
1042+
opacity: 1;
1043+
color: var(--good);
1044+
}
10071045
.console__body--error {
10081046
color: #ffb4b4;
10091047
}

playground/src/components/ExampleEditor.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ export function ExampleEditor({
6666
options={{
6767
minimap: { enabled: false },
6868
fontSize: 13,
69+
lineNumbers: "off",
70+
lineDecorationsWidth: 0,
71+
lineNumbersMinChars: 0,
72+
glyphMargin: false,
6973
scrollBeyondLastLine: false,
7074
tabSize: 2,
7175
padding: { top: 12, bottom: 12 },

playground/src/components/MethodView.tsx

Lines changed: 84 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export function MethodView({
6666
const [running, setRunning] = useState(false);
6767
const [error, setError] = useState("");
6868
const [tab, setTab] = useState<"example" | "output">("example");
69+
const [copied, setCopied] = useState(false);
6970
const callAbortRef = useRef<((reason: string) => void) | null>(null);
7071
const cancelRunRef = useRef<(() => void) | null>(null);
7172

@@ -150,6 +151,20 @@ export function MethodView({
150151
callAbortRef.current?.("Call aborted");
151152
};
152153

154+
const handleCopyLogs = async () => {
155+
const text = [...logs.map((entry) => entry.text), ...(error ? [error] : [])]
156+
.join("\n")
157+
.trim();
158+
if (!text) return;
159+
try {
160+
await navigator.clipboard.writeText(text);
161+
setCopied(true);
162+
setTimeout(() => setCopied(false), 1500);
163+
} catch {
164+
/* clipboard unavailable */
165+
}
166+
};
167+
153168
const kind = methodInfo?.type ?? "unary";
154169

155170
const status: Status = error
@@ -305,41 +320,81 @@ export function MethodView({
305320
</>
306321
) : (
307322
<div className="console console--inline" data-status={status}>
308-
{error ? (
309-
<div
310-
className="console__body console__body--error"
311-
data-testid="error-display"
312-
>
313-
{error}
314-
{isHostMissingError(error) && (
315-
<div className="console__cta">
316-
<a
317-
className="open-in-dotli"
318-
href={hostedPlaygroundUrl(service, method)}
319-
target="_blank"
320-
rel="noreferrer"
321-
title="Open this example in the host-backed playground"
323+
{logs.length > 0 || error ? (
324+
<>
325+
<button
326+
type="button"
327+
className="console__copy"
328+
data-copied={copied}
329+
onClick={handleCopyLogs}
330+
aria-label="Copy output to clipboard"
331+
title="Copy output"
332+
>
333+
{copied ? (
334+
<svg
335+
viewBox="0 0 24 24"
336+
fill="none"
337+
stroke="currentColor"
338+
strokeWidth="2"
339+
strokeLinecap="round"
340+
strokeLinejoin="round"
341+
aria-hidden
342+
>
343+
<path d="M20 6 9 17l-5-5" />
344+
</svg>
345+
) : (
346+
<svg
347+
viewBox="0 0 24 24"
348+
fill="none"
349+
stroke="currentColor"
350+
strokeWidth="2"
351+
strokeLinecap="round"
352+
strokeLinejoin="round"
353+
aria-hidden
322354
>
323-
Run in hosted playground ↗
324-
</a>
355+
<rect x="8" y="8" width="14" height="14" rx="2" ry="2" />
356+
<path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" />
357+
</svg>
358+
)}
359+
</button>
360+
{logs.length > 0 && (
361+
<div className="console__body" data-testid="stream-log">
362+
{logs.map((entry, i) => (
363+
<div
364+
key={i}
365+
className={`console__entry console__entry--${entry.level}`}
366+
data-testid="stream-entry"
367+
>
368+
<span className="console__entry-i">
369+
{String(i + 1).padStart(2, "0")}
370+
</span>
371+
<span className="console__entry-body">{entry.text}</span>
372+
</div>
373+
))}
325374
</div>
326375
)}
327-
</div>
328-
) : logs.length > 0 ? (
329-
<div className="console__body" data-testid="stream-log">
330-
{logs.map((entry, i) => (
376+
{error && (
331377
<div
332-
key={i}
333-
className={`console__entry console__entry--${entry.level}`}
334-
data-testid="stream-entry"
378+
className="console__body console__body--error"
379+
data-testid="error-display"
335380
>
336-
<span className="console__entry-i">
337-
{String(i + 1).padStart(2, "0")}
338-
</span>
339-
<span className="console__entry-body">{entry.text}</span>
381+
{error}
382+
{isHostMissingError(error) && (
383+
<div className="console__cta">
384+
<a
385+
className="open-in-dotli"
386+
href={hostedPlaygroundUrl(service, method)}
387+
target="_blank"
388+
rel="noreferrer"
389+
title="Open this example in the host-backed playground"
390+
>
391+
Run in hosted playground ↗
392+
</a>
393+
</div>
394+
)}
340395
</div>
341-
))}
342-
</div>
396+
)}
397+
</>
343398
) : (
344399
<div className="console__body console__body--empty">
345400
{!runnable

0 commit comments

Comments
 (0)