@ox-content/code-play 3.0.0-alpha.1 → 3.0.0-alpha.11
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 +35 -6
- package/dist/browser.mjs +561 -58
- package/dist/client.mjs +192 -28
- package/dist/client.mjs.map +1 -1
- package/dist/config.d.mts +47 -2
- package/dist/config.d.mts.map +1 -1
- package/dist/hydrate.d.mts +5 -2
- package/dist/hydrate.d.mts.map +1 -1
- package/dist/hydrate.mjs +2 -2
- package/dist/hydrate2.d.mts +2 -2
- package/dist/hydrate2.mjs +369 -33
- package/dist/hydrate2.mjs.map +1 -1
- package/dist/index.d.mts +50 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +4 -4
- package/dist/payload.mjs +10 -2
- package/dist/payload.mjs.map +1 -1
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin2.mjs +712 -162
- package/dist/plugin2.mjs.map +1 -1
- package/package.json +1 -1
package/dist/hydrate2.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as errorResult, i as errorMessage, n as createCodePlay, o as JS_SANDBOX_FLAGS, u as selectStream } from "./client.mjs";
|
|
2
|
-
import { a as escapeHtml,
|
|
2
|
+
import { a as escapeHtml, h as resolveLanguage, i as escapeAttribute, t as decodePayload } from "./payload.mjs";
|
|
3
3
|
//#region src/hydrate-action.ts
|
|
4
4
|
function readPlayPayload(encoded) {
|
|
5
5
|
try {
|
|
@@ -18,12 +18,39 @@ async function runPlayAction(input) {
|
|
|
18
18
|
input.setBusy(false);
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
+
function idleRunActionState() {
|
|
22
|
+
return { phase: "idle" };
|
|
23
|
+
}
|
|
24
|
+
function runningRunActionState(action, startedAtMs = Date.now()) {
|
|
25
|
+
return {
|
|
26
|
+
phase: "running",
|
|
27
|
+
action,
|
|
28
|
+
startedAtMs
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function resultRunActionState(action, result, finishedAtMs = Date.now()) {
|
|
32
|
+
return {
|
|
33
|
+
phase: result.status === "offline" ? "offline" : result.status === "ok" || result.status === "cancelled" ? "result" : "error",
|
|
34
|
+
action,
|
|
35
|
+
result,
|
|
36
|
+
message: result.diagnostics[0]?.message,
|
|
37
|
+
finishedAtMs
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function errorRunActionState(action, error, finishedAtMs = Date.now()) {
|
|
41
|
+
return {
|
|
42
|
+
phase: "error",
|
|
43
|
+
action,
|
|
44
|
+
message: errorMessage(error),
|
|
45
|
+
finishedAtMs
|
|
46
|
+
};
|
|
47
|
+
}
|
|
21
48
|
//#endregion
|
|
22
49
|
//#region src/styles.ts
|
|
23
50
|
const CODE_PLAY_STYLES = `
|
|
24
51
|
.ox-code-play {
|
|
25
52
|
border: 1px solid var(--octc-color-border, color-mix(in srgb, currentColor 16%, transparent));
|
|
26
|
-
border-radius:
|
|
53
|
+
border-radius: 8px;
|
|
27
54
|
background: var(--octc-color-bg-alt, var(--octc-color-bg, Canvas));
|
|
28
55
|
color: var(--octc-color-text, CanvasText);
|
|
29
56
|
overflow: hidden;
|
|
@@ -37,22 +64,147 @@ const CODE_PLAY_STYLES = `
|
|
|
37
64
|
padding: 0.6rem 0.8rem;
|
|
38
65
|
border-bottom: 1px solid var(--octc-color-border, color-mix(in srgb, currentColor 12%, transparent));
|
|
39
66
|
}
|
|
67
|
+
.ox-code-play__summary {
|
|
68
|
+
display: inline-flex;
|
|
69
|
+
flex: 1 1 12rem;
|
|
70
|
+
min-width: 9rem;
|
|
71
|
+
gap: 0.45rem;
|
|
72
|
+
align-items: baseline;
|
|
73
|
+
}
|
|
40
74
|
.ox-code-play__lang {
|
|
41
75
|
font: 600 0.8rem/1.2 ui-sans-serif, system-ui, sans-serif;
|
|
42
|
-
margin-right: auto;
|
|
43
76
|
color: var(--octc-color-text, CanvasText);
|
|
44
77
|
}
|
|
78
|
+
.ox-code-play__title {
|
|
79
|
+
min-width: 0;
|
|
80
|
+
overflow-wrap: anywhere;
|
|
81
|
+
font: 500 0.78rem/1.25 ui-sans-serif, system-ui, sans-serif;
|
|
82
|
+
opacity: 0.72;
|
|
83
|
+
}
|
|
84
|
+
.ox-code-play__status {
|
|
85
|
+
min-width: 5.25rem;
|
|
86
|
+
text-align: center;
|
|
87
|
+
border: 1px solid var(--octc-color-border, color-mix(in srgb, currentColor 16%, transparent));
|
|
88
|
+
border-radius: 6px;
|
|
89
|
+
padding: 0.18rem 0.5rem;
|
|
90
|
+
font: 650 0.68rem/1.25 ui-sans-serif, system-ui, sans-serif;
|
|
91
|
+
color: var(--octc-color-text, CanvasText);
|
|
92
|
+
background: var(--octc-color-bg, Canvas);
|
|
93
|
+
}
|
|
94
|
+
.ox-code-play[data-ox-run-state="running"] .ox-code-play__status {
|
|
95
|
+
color: var(--octc-color-primary, var(--octc-accent, #4f46e5));
|
|
96
|
+
}
|
|
97
|
+
.ox-code-play[data-ox-run-state="error"] .ox-code-play__status,
|
|
98
|
+
.ox-code-play[data-ox-run-state="offline"] .ox-code-play__status {
|
|
99
|
+
color: var(--octc-danger, #b42318);
|
|
100
|
+
}
|
|
45
101
|
.ox-code-play__toolbar button {
|
|
46
102
|
appearance: none;
|
|
47
103
|
border: 1px solid var(--octc-color-border, color-mix(in srgb, currentColor 20%, transparent));
|
|
48
104
|
background: color-mix(in srgb, var(--octc-color-text, CanvasText) 8%, transparent);
|
|
49
105
|
color: var(--octc-color-text, CanvasText);
|
|
50
|
-
border-radius:
|
|
106
|
+
border-radius: 8px;
|
|
51
107
|
padding: 0.25rem 0.75rem;
|
|
52
108
|
font: 600 0.75rem/1.4 ui-sans-serif, system-ui, sans-serif;
|
|
53
109
|
cursor: pointer;
|
|
54
110
|
}
|
|
55
111
|
.ox-code-play__toolbar button:disabled { opacity: 0.55; cursor: progress; }
|
|
112
|
+
.ox-code-play__toolbar button:focus-visible,
|
|
113
|
+
.ox-code-play__tabs button:focus-visible,
|
|
114
|
+
.ox-code-play__field input:focus-visible,
|
|
115
|
+
.ox-code-play__field select:focus-visible,
|
|
116
|
+
.ox-code-play__panel:focus-visible {
|
|
117
|
+
outline: 2px solid var(--octc-color-primary, var(--octc-accent, #4f46e5));
|
|
118
|
+
outline-offset: 2px;
|
|
119
|
+
}
|
|
120
|
+
.ox-code-play__runtime {
|
|
121
|
+
display: flex;
|
|
122
|
+
flex-wrap: wrap;
|
|
123
|
+
gap: 0.45rem;
|
|
124
|
+
align-items: center;
|
|
125
|
+
padding: 0.55rem 0.8rem;
|
|
126
|
+
border-bottom: 1px solid var(--octc-color-border, color-mix(in srgb, currentColor 10%, transparent));
|
|
127
|
+
background: color-mix(in srgb, var(--octc-color-text, CanvasText) 4%, transparent);
|
|
128
|
+
}
|
|
129
|
+
.ox-code-play__runtime-chip {
|
|
130
|
+
display: inline-grid;
|
|
131
|
+
gap: 0.1rem;
|
|
132
|
+
min-width: 7.5rem;
|
|
133
|
+
padding: 0.35rem 0.5rem;
|
|
134
|
+
border: 1px solid var(--octc-color-border, color-mix(in srgb, currentColor 14%, transparent));
|
|
135
|
+
border-radius: 6px;
|
|
136
|
+
background: var(--octc-color-bg, Canvas);
|
|
137
|
+
}
|
|
138
|
+
.ox-code-play__runtime-chip span {
|
|
139
|
+
font: 600 0.62rem/1.1 ui-sans-serif, system-ui, sans-serif;
|
|
140
|
+
text-transform: uppercase;
|
|
141
|
+
letter-spacing: 0.04em;
|
|
142
|
+
opacity: 0.62;
|
|
143
|
+
}
|
|
144
|
+
.ox-code-play__runtime-chip strong {
|
|
145
|
+
font: 650 0.78rem/1.2 ui-sans-serif, system-ui, sans-serif;
|
|
146
|
+
}
|
|
147
|
+
.ox-code-play__runtime-chip--ok strong { color: var(--octc-color-primary, var(--octc-accent, #4f46e5)); }
|
|
148
|
+
.ox-code-play__runtime-chip--warn strong { color: var(--octc-warning, #b54708); }
|
|
149
|
+
.ox-code-play__runtime-chip--muted strong { opacity: 0.72; }
|
|
150
|
+
.ox-code-play__project {
|
|
151
|
+
display: inline-flex;
|
|
152
|
+
flex: 1 1 18rem;
|
|
153
|
+
min-width: min(100%, 16rem);
|
|
154
|
+
gap: 0.45rem;
|
|
155
|
+
align-items: center;
|
|
156
|
+
flex-wrap: wrap;
|
|
157
|
+
margin-left: auto;
|
|
158
|
+
padding: 0.35rem 0.5rem;
|
|
159
|
+
border: 1px solid var(--octc-color-border, color-mix(in srgb, currentColor 14%, transparent));
|
|
160
|
+
border-radius: 6px;
|
|
161
|
+
background: var(--octc-color-bg, Canvas);
|
|
162
|
+
}
|
|
163
|
+
.ox-code-play__project-main,
|
|
164
|
+
.ox-code-play__project-entry {
|
|
165
|
+
display: inline-grid;
|
|
166
|
+
gap: 0.1rem;
|
|
167
|
+
min-width: 0;
|
|
168
|
+
}
|
|
169
|
+
.ox-code-play__project-main { flex: 1 1 8.5rem; }
|
|
170
|
+
.ox-code-play__project-entry { flex: 999 1 9rem; }
|
|
171
|
+
.ox-code-play__project-main span,
|
|
172
|
+
.ox-code-play__project-entry span {
|
|
173
|
+
font: 600 0.62rem/1.1 ui-sans-serif, system-ui, sans-serif;
|
|
174
|
+
text-transform: uppercase;
|
|
175
|
+
letter-spacing: 0.04em;
|
|
176
|
+
opacity: 0.62;
|
|
177
|
+
}
|
|
178
|
+
.ox-code-play__project-main strong,
|
|
179
|
+
.ox-code-play__project-entry strong {
|
|
180
|
+
min-width: 0;
|
|
181
|
+
overflow-wrap: anywhere;
|
|
182
|
+
font: 650 0.78rem/1.2 ui-sans-serif, system-ui, sans-serif;
|
|
183
|
+
}
|
|
184
|
+
.ox-code-play__project-files,
|
|
185
|
+
.ox-code-play__project-warning,
|
|
186
|
+
.ox-code-play__project-link {
|
|
187
|
+
flex: 0 0 auto;
|
|
188
|
+
border-radius: 6px;
|
|
189
|
+
padding: 0.18rem 0.45rem;
|
|
190
|
+
font: 650 0.68rem/1.25 ui-sans-serif, system-ui, sans-serif;
|
|
191
|
+
}
|
|
192
|
+
.ox-code-play__project-files {
|
|
193
|
+
background: color-mix(in srgb, var(--octc-color-text, CanvasText) 8%, transparent);
|
|
194
|
+
}
|
|
195
|
+
.ox-code-play__project-warning {
|
|
196
|
+
color: var(--octc-warning, #b54708);
|
|
197
|
+
background: color-mix(in srgb, var(--octc-warning, #b54708) 12%, transparent);
|
|
198
|
+
}
|
|
199
|
+
.ox-code-play__project-link {
|
|
200
|
+
color: var(--octc-color-primary, var(--octc-accent, #4f46e5));
|
|
201
|
+
background: color-mix(in srgb, var(--octc-color-primary, var(--octc-accent, #4f46e5)) 12%, transparent);
|
|
202
|
+
text-decoration: none;
|
|
203
|
+
}
|
|
204
|
+
.ox-code-play__project-link:focus-visible {
|
|
205
|
+
outline: 2px solid var(--octc-color-primary, var(--octc-accent, #4f46e5));
|
|
206
|
+
outline-offset: 2px;
|
|
207
|
+
}
|
|
56
208
|
.ox-code-play .ox-code { margin: 0; }
|
|
57
209
|
.ox-code-play__source pre { margin: 0; border: 0; border-radius: 0; }
|
|
58
210
|
.ox-code-play__tabs {
|
|
@@ -66,7 +218,7 @@ const CODE_PLAY_STYLES = `
|
|
|
66
218
|
background: transparent;
|
|
67
219
|
color: var(--octc-color-text, CanvasText);
|
|
68
220
|
padding: 0.35rem 0.55rem;
|
|
69
|
-
border-radius:
|
|
221
|
+
border-radius: 6px 6px 0 0;
|
|
70
222
|
font: 600 0.75rem/1.2 ui-sans-serif, system-ui, sans-serif;
|
|
71
223
|
cursor: pointer;
|
|
72
224
|
}
|
|
@@ -93,7 +245,7 @@ const CODE_PLAY_STYLES = `
|
|
|
93
245
|
.ox-code-play__field input, .ox-code-play__field select {
|
|
94
246
|
font: inherit;
|
|
95
247
|
padding: 0.3rem 0.45rem;
|
|
96
|
-
border-radius:
|
|
248
|
+
border-radius: 6px;
|
|
97
249
|
border: 1px solid var(--octc-color-border, color-mix(in srgb, currentColor 18%, transparent));
|
|
98
250
|
background: var(--octc-color-bg, Canvas);
|
|
99
251
|
color: var(--octc-color-text, CanvasText);
|
|
@@ -188,42 +340,141 @@ function renderPlayUi(state) {
|
|
|
188
340
|
if (preset === "headless") return "";
|
|
189
341
|
const panel = state.panel ?? "stdio";
|
|
190
342
|
const canTypecheck = state.payload.capabilities.typecheck;
|
|
343
|
+
const runState = state.runState ?? (state.busy ? {
|
|
344
|
+
phase: "running",
|
|
345
|
+
action: "execute"
|
|
346
|
+
} : idleRunActionState());
|
|
347
|
+
const isBusy = Boolean(state.busy) || runState.phase === "running";
|
|
191
348
|
const tabs = renderTabs(state, panel);
|
|
192
349
|
const viewers = state.payload.viewers;
|
|
193
|
-
|
|
350
|
+
const label = widgetLabel(state.payload, definition);
|
|
351
|
+
return `<div class="ox-code-play ox-code-play--${preset}" data-ox-code-play-ui data-ox-run-state="${runState.phase}" role="region" aria-label="${escapeHtml(label)}" aria-busy="${isBusy ? "true" : "false"}">
|
|
194
352
|
<div class="ox-code-play__toolbar">
|
|
195
|
-
<span class="ox-code-play__lang">${escapeHtml(definition?.name ?? state.payload.language)}</span>
|
|
196
|
-
<
|
|
197
|
-
|
|
198
|
-
|
|
353
|
+
<span class="ox-code-play__summary"><span class="ox-code-play__lang">${escapeHtml(definition?.name ?? state.payload.language)}</span>${state.payload.title ? `<span class="ox-code-play__title">${escapeHtml(state.payload.title)}</span>` : ""}</span>
|
|
354
|
+
<span class="ox-code-play__status" data-ox-status role="status" aria-live="polite">${renderRunStatusText(runState)}</span>
|
|
355
|
+
<button type="button" data-ox-action="run" aria-label="${escapeHtml(`Run ${label}`)}"${actionButtonAttrs("run", Boolean(state.busy))}>Run</button>
|
|
356
|
+
${canTypecheck ? `<button type="button" data-ox-action="typecheck" aria-label="${escapeHtml(`Typecheck ${label}`)}"${actionButtonAttrs("typecheck", Boolean(state.busy))}>Typecheck</button>` : ""}
|
|
357
|
+
<button type="button" data-ox-action="cancel" aria-label="${escapeHtml(`Cancel ${label}`)}"${actionButtonAttrs("cancel", Boolean(state.busy))}>Cancel</button>
|
|
199
358
|
</div>
|
|
359
|
+
${renderRuntimeStrip(state.payload, definition)}
|
|
200
360
|
<div class="ox-code-play__source"></div>
|
|
201
361
|
${tabs}
|
|
202
|
-
${viewers.stdio ?
|
|
203
|
-
${viewers.stderr ?
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
362
|
+
${viewers.stdio ? renderPanel("stdio", "Stdio", panel, preset, `${renderDiagnosticsHtml(state.result)}${renderStdioHtml(state.result?.stdio ?? [])}`) : ""}
|
|
363
|
+
${viewers.stderr ? renderPanel("stderr", "Stderr", panel, preset, renderStderrHtml(state.result)) : ""}
|
|
364
|
+
${viewers.config ? renderPanel("config", "Config", panel, preset, renderConfigHtml(definition?.configSchema ?? [], state.payload.config)) : ""}
|
|
365
|
+
${viewers.provenance ? renderPanel("provenance", "Provenance", panel, preset, renderProvenanceHtml(state.result?.provenance)) : ""}
|
|
366
|
+
${viewers.timing ? renderPanel("timing", "Timing", panel, preset, renderTimingHtml(state.result?.timing)) : ""}
|
|
207
367
|
</div>`;
|
|
208
368
|
}
|
|
369
|
+
function renderRunStatusText(state) {
|
|
370
|
+
if (state.phase === "running") return state.action === "typecheck" ? "Typechecking" : "Running";
|
|
371
|
+
if (state.phase === "offline") return "Offline";
|
|
372
|
+
if (state.phase === "error") {
|
|
373
|
+
if (state.result?.status === "timeout") return "Timed out";
|
|
374
|
+
if (state.result?.status === "unsupported") return "Unsupported";
|
|
375
|
+
return "Error";
|
|
376
|
+
}
|
|
377
|
+
if (state.phase === "result") {
|
|
378
|
+
if (state.result?.status === "cancelled") return "Cancelled";
|
|
379
|
+
return "Done";
|
|
380
|
+
}
|
|
381
|
+
return "Ready";
|
|
382
|
+
}
|
|
383
|
+
function renderRuntimeStrip(payload, definition) {
|
|
384
|
+
const runtime = runtimeLabel(payload, definition);
|
|
385
|
+
const executor = executorLabel(payload, definition);
|
|
386
|
+
const checks = payload.capabilities.typecheck ? "Typecheck ready" : "Run only";
|
|
387
|
+
return `<div class="ox-code-play__runtime" aria-label="Code Play runtime">${[
|
|
388
|
+
runtimeChip("Runtime", runtime.label, runtime.kind),
|
|
389
|
+
runtimeChip("Executor", executor.label, executor.kind),
|
|
390
|
+
runtimeChip("Checks", checks, payload.capabilities.typecheck ? "ok" : "muted")
|
|
391
|
+
].join("")}${renderProjectSandboxHtml(payload.project)}</div>`;
|
|
392
|
+
}
|
|
393
|
+
function runtimeLabel(payload, definition) {
|
|
394
|
+
switch (definition?.backend) {
|
|
395
|
+
case "javascript": return {
|
|
396
|
+
label: "Browser sandbox",
|
|
397
|
+
kind: "ok"
|
|
398
|
+
};
|
|
399
|
+
case "typescript": return {
|
|
400
|
+
label: "TypeScript sandbox",
|
|
401
|
+
kind: "ok"
|
|
402
|
+
};
|
|
403
|
+
case "framework": return {
|
|
404
|
+
label: `${definition.name} iframe preview`,
|
|
405
|
+
kind: "ok"
|
|
406
|
+
};
|
|
407
|
+
case "rust-playground": return {
|
|
408
|
+
label: "Rust Playground",
|
|
409
|
+
kind: payload.endpoints?.rust ? "ok" : "warn"
|
|
410
|
+
};
|
|
411
|
+
case "go-playground": return {
|
|
412
|
+
label: "Go Playground",
|
|
413
|
+
kind: payload.endpoints?.go ? "ok" : "warn"
|
|
414
|
+
};
|
|
415
|
+
case "remote": return payload.endpoint ? {
|
|
416
|
+
label: "Piston-compatible",
|
|
417
|
+
kind: "ok"
|
|
418
|
+
} : {
|
|
419
|
+
label: "Endpoint missing",
|
|
420
|
+
kind: "warn"
|
|
421
|
+
};
|
|
422
|
+
default: return {
|
|
423
|
+
label: definition?.name ?? payload.language,
|
|
424
|
+
kind: "muted"
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
function executorLabel(payload, definition) {
|
|
429
|
+
if (!payload.capabilities.execute) return {
|
|
430
|
+
label: "Disabled",
|
|
431
|
+
kind: "warn"
|
|
432
|
+
};
|
|
433
|
+
if (definition?.backend === "remote" && !payload.endpoint) return {
|
|
434
|
+
label: "Configure endpoint",
|
|
435
|
+
kind: "warn"
|
|
436
|
+
};
|
|
437
|
+
return {
|
|
438
|
+
label: "On demand",
|
|
439
|
+
kind: "ok"
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
function runtimeChip(label, value, kind) {
|
|
443
|
+
return `<span class="ox-code-play__runtime-chip ox-code-play__runtime-chip--${kind}"><span>${escapeHtml(label)}</span><strong>${escapeHtml(value)}</strong></span>`;
|
|
444
|
+
}
|
|
445
|
+
function renderProjectSandboxHtml(project) {
|
|
446
|
+
if (!project) return "";
|
|
447
|
+
const fileCount = `${project.files.length} ${project.files.length === 1 ? "file" : "files"}`;
|
|
448
|
+
const target = project.target === "browser" ? "Browser project" : project.target === "node" ? "Node-like project" : "External project";
|
|
449
|
+
const url = project.openUrl ?? project.fallbackUrl;
|
|
450
|
+
const warnings = project.warnings?.length ? `<span class="ox-code-play__project-warning" title="${escapeAttribute(project.warnings.join("\n"))}">Warnings</span>` : "";
|
|
451
|
+
const link = url ? `<a class="ox-code-play__project-link" href="${escapeAttribute(url)}" target="_blank" rel="noopener noreferrer">Open</a>` : "";
|
|
452
|
+
return `<span class="ox-code-play__project" data-ox-project-provider="${escapeAttribute(project.provider)}">
|
|
453
|
+
<span class="ox-code-play__project-main"><span>${escapeHtml(project.label)}</span><strong>${escapeHtml(target)}</strong></span>
|
|
454
|
+
${project.entry ? `<span class="ox-code-play__project-entry"><span>Entry</span><strong>${escapeHtml(project.entry)}</strong></span>` : ""}
|
|
455
|
+
<span class="ox-code-play__project-files">${escapeHtml(fileCount)}</span>
|
|
456
|
+
${warnings}${link}
|
|
457
|
+
</span>`;
|
|
458
|
+
}
|
|
209
459
|
function renderTabs(state, panel) {
|
|
210
460
|
if (state.payload.ui === "compact") return "";
|
|
211
461
|
const viewers = state.payload.viewers;
|
|
212
462
|
const buttons = [
|
|
213
|
-
viewers.stdio ? tab("stdio", "
|
|
214
|
-
viewers.stderr ? tab("stderr", "
|
|
215
|
-
viewers.config ? tab("config", "
|
|
216
|
-
viewers.provenance ? tab("provenance", "
|
|
217
|
-
viewers.timing ? tab("timing", "
|
|
463
|
+
viewers.stdio ? tab("stdio", "Stdio", panel) : "",
|
|
464
|
+
viewers.stderr ? tab("stderr", "Stderr", panel) : "",
|
|
465
|
+
viewers.config ? tab("config", "Config", panel) : "",
|
|
466
|
+
viewers.provenance ? tab("provenance", "Provenance", panel) : "",
|
|
467
|
+
viewers.timing ? tab("timing", "Timing", panel) : ""
|
|
218
468
|
].filter(Boolean).join("");
|
|
219
469
|
return buttons ? `<div class="ox-code-play__tabs" role="tablist">${buttons}</div>` : "";
|
|
220
470
|
}
|
|
221
471
|
function tab(id, label, selected) {
|
|
222
|
-
|
|
472
|
+
const isSelected = selected === id;
|
|
473
|
+
return `<button type="button" role="tab" data-ox-panel="${id}" aria-selected="${isSelected ? "true" : "false"}" tabindex="${isSelected ? "0" : "-1"}">${label}</button>`;
|
|
223
474
|
}
|
|
224
475
|
function actionButtonAttrs(action, busy) {
|
|
225
476
|
const state = actionBusyState(action, busy);
|
|
226
|
-
return `${state.disabled ? " disabled" : ""}${state.hidden ? " hidden" : ""}`;
|
|
477
|
+
return `${state.disabled ? " disabled aria-disabled=\"true\"" : ""}${state.hidden ? " hidden" : ""}`;
|
|
227
478
|
}
|
|
228
479
|
function actionBusyState(action, busy) {
|
|
229
480
|
if (action === "cancel") return {
|
|
@@ -240,15 +491,30 @@ function applyActionBusy(root, busy) {
|
|
|
240
491
|
const state = actionBusyState(button.dataset.oxAction ?? "", busy);
|
|
241
492
|
button.disabled = state.disabled;
|
|
242
493
|
button.hidden = state.hidden;
|
|
494
|
+
button.setAttribute("aria-disabled", state.disabled ? "true" : "false");
|
|
243
495
|
}
|
|
496
|
+
for (const widget of queryPlayWidgets(root)) widget.setAttribute("aria-busy", busy ? "true" : "false");
|
|
497
|
+
}
|
|
498
|
+
function renderPanel(id, label, current, preset, html) {
|
|
499
|
+
return `<div class="ox-code-play__panel" role="tabpanel" tabindex="0" aria-label="${escapeHtml(label)}" data-panel="${id}"${hidden(current, id, preset)}>${html}</div>`;
|
|
244
500
|
}
|
|
245
501
|
function hidden(current, id, preset) {
|
|
246
502
|
if (preset === "compact" && (id === "stdio" || id === "stderr")) return "";
|
|
247
503
|
return current === id ? "" : " hidden";
|
|
248
504
|
}
|
|
505
|
+
function widgetLabel(payload, definition) {
|
|
506
|
+
const language = definition?.name ?? payload.language;
|
|
507
|
+
return payload.title ? `${payload.title} (${language})` : `${language} Code Play`;
|
|
508
|
+
}
|
|
509
|
+
function queryPlayWidgets(root) {
|
|
510
|
+
const widgets = [...root.querySelectorAll(".ox-code-play")];
|
|
511
|
+
if (typeof HTMLElement !== "undefined" && root instanceof HTMLElement && root.matches(".ox-code-play")) widgets.unshift(root);
|
|
512
|
+
return widgets;
|
|
513
|
+
}
|
|
249
514
|
//#endregion
|
|
250
515
|
//#region src/hydrate.ts
|
|
251
516
|
const STYLE_ID = "ox-code-play-styles";
|
|
517
|
+
let widgetId = 0;
|
|
252
518
|
function hydrateCodePlay(root = defaultRoot(), options = {}) {
|
|
253
519
|
ensureStyles();
|
|
254
520
|
const client = options.client ?? createCodePlayFromPayloads(root);
|
|
@@ -261,7 +527,7 @@ function mountCodePlay(element, options = {}) {
|
|
|
261
527
|
const payload = readPlayPayload(element.getAttribute("data-ox-code-play") ?? "");
|
|
262
528
|
if (!payload || payload.ui === "headless") return;
|
|
263
529
|
const client = options.client ?? createCodePlay({
|
|
264
|
-
languages: { [payload.language]:
|
|
530
|
+
languages: { [payload.language]: languageEnableFromPayload(payload) },
|
|
265
531
|
endpoints: payload.endpoints
|
|
266
532
|
});
|
|
267
533
|
const source = element.innerHTML;
|
|
@@ -273,14 +539,17 @@ function mountCodePlay(element, options = {}) {
|
|
|
273
539
|
if (sourceSlot) sourceSlot.innerHTML = source;
|
|
274
540
|
element.replaceChildren(widget);
|
|
275
541
|
element.dataset.oxCodePlayMounted = "true";
|
|
542
|
+
element.removeAttribute("inert");
|
|
276
543
|
bindWidget(element, payload, client);
|
|
544
|
+
prepareA11y(element);
|
|
277
545
|
}
|
|
278
546
|
function bindWidget(element, payload, client) {
|
|
279
547
|
let current = payload;
|
|
280
548
|
const session = client.createSession({
|
|
281
549
|
language: payload.language,
|
|
282
550
|
code: payload.code,
|
|
283
|
-
config: payload.config
|
|
551
|
+
config: payload.config,
|
|
552
|
+
project: payload.project
|
|
284
553
|
});
|
|
285
554
|
const runButton = element.querySelector("[data-ox-action=\"run\"]");
|
|
286
555
|
const checkButton = element.querySelector("[data-ox-action=\"typecheck\"]");
|
|
@@ -288,6 +557,9 @@ function bindWidget(element, payload, client) {
|
|
|
288
557
|
runButton?.addEventListener("click", () => void run("execute"));
|
|
289
558
|
checkButton?.addEventListener("click", () => void run("typecheck"));
|
|
290
559
|
cancelButton?.addEventListener("click", () => session.cancel());
|
|
560
|
+
element.addEventListener("keydown", (event) => {
|
|
561
|
+
if (event.target instanceof HTMLElement && event.target.matches("[role=\"tab\"]")) handleTabKeydown(element, event);
|
|
562
|
+
});
|
|
291
563
|
element.addEventListener("click", (event) => {
|
|
292
564
|
const target = event.target;
|
|
293
565
|
if (!(target instanceof HTMLElement)) return;
|
|
@@ -306,13 +578,20 @@ function bindWidget(element, payload, client) {
|
|
|
306
578
|
async function run(action) {
|
|
307
579
|
await runPlayAction({
|
|
308
580
|
action: () => action === "typecheck" ? session.typecheck() : session.run(),
|
|
309
|
-
setBusy: (busy) =>
|
|
310
|
-
|
|
311
|
-
|
|
581
|
+
setBusy: (busy) => {
|
|
582
|
+
applyActionBusy(element, busy);
|
|
583
|
+
if (busy) paintRunState(element, runningRunActionState(action));
|
|
584
|
+
},
|
|
585
|
+
onResult: (result) => paintResult(element, current, result, action),
|
|
586
|
+
onError: (error) => {
|
|
587
|
+
paintRunState(element, errorRunActionState(action, error));
|
|
588
|
+
paintResult(element, current, errorResult(errorMessage(error)), action);
|
|
589
|
+
}
|
|
312
590
|
});
|
|
313
591
|
}
|
|
314
592
|
}
|
|
315
|
-
function paintResult(element, _payload, result) {
|
|
593
|
+
function paintResult(element, _payload, result, action) {
|
|
594
|
+
paintRunState(element, resultRunActionState(action, result));
|
|
316
595
|
const stdio = element.querySelector("[data-panel=\"stdio\"]");
|
|
317
596
|
if (stdio) {
|
|
318
597
|
stdio.innerHTML = `${renderDiagnosticsHtml(result)}${renderStdioHtml(result.stdio)}`;
|
|
@@ -333,11 +612,27 @@ function paintResult(element, _payload, result) {
|
|
|
333
612
|
if (timing) timing.innerHTML = renderTimingHtml(result.timing);
|
|
334
613
|
const stderr = element.querySelector("[data-panel=\"stderr\"]");
|
|
335
614
|
if (stderr) stderr.innerHTML = renderStderrHtml(result);
|
|
336
|
-
showPanel(element,
|
|
615
|
+
showPanel(element, resultPanelToShow(result, Boolean(stderr)));
|
|
616
|
+
}
|
|
617
|
+
function paintRunState(element, state) {
|
|
618
|
+
const widget = element.querySelector(".ox-code-play");
|
|
619
|
+
widget?.setAttribute("data-ox-run-state", state.phase);
|
|
620
|
+
widget?.setAttribute("aria-busy", state.phase === "running" ? "true" : "false");
|
|
621
|
+
const status = element.querySelector("[data-ox-status]");
|
|
622
|
+
if (status) status.textContent = renderRunStatusText(state);
|
|
623
|
+
}
|
|
624
|
+
function resultPanelToShow(result, hasStderrPanel) {
|
|
625
|
+
if (!hasStderrPanel) return "stdio";
|
|
626
|
+
if (result.diagnostics.some((diagnostic) => diagnostic.severity === "error") || result.status !== "ok" && Boolean(result.stderr)) return "stderr";
|
|
627
|
+
return "stdio";
|
|
337
628
|
}
|
|
338
629
|
function showPanel(element, panel) {
|
|
339
630
|
const compact = Boolean(element.querySelector(".ox-code-play--compact"));
|
|
340
|
-
for (const tab of element.querySelectorAll("[data-ox-panel]"))
|
|
631
|
+
for (const tab of element.querySelectorAll("[data-ox-panel]")) {
|
|
632
|
+
const selected = tab.getAttribute("data-ox-panel") === panel;
|
|
633
|
+
tab.setAttribute("aria-selected", selected ? "true" : "false");
|
|
634
|
+
tab.tabIndex = selected ? 0 : -1;
|
|
635
|
+
}
|
|
341
636
|
for (const node of element.querySelectorAll(".ox-code-play__panel")) {
|
|
342
637
|
const id = node.dataset.panel;
|
|
343
638
|
if (compact && (id === "stdio" || id === "stderr")) {
|
|
@@ -347,6 +642,38 @@ function showPanel(element, panel) {
|
|
|
347
642
|
node.hidden = id !== panel;
|
|
348
643
|
}
|
|
349
644
|
}
|
|
645
|
+
function handleTabKeydown(element, event) {
|
|
646
|
+
if (![
|
|
647
|
+
"ArrowLeft",
|
|
648
|
+
"ArrowRight",
|
|
649
|
+
"Home",
|
|
650
|
+
"End"
|
|
651
|
+
].includes(event.key)) return;
|
|
652
|
+
const tabs = [...element.querySelectorAll("[data-ox-panel]")];
|
|
653
|
+
const current = tabs.indexOf(event.target);
|
|
654
|
+
if (current === -1) return;
|
|
655
|
+
event.preventDefault();
|
|
656
|
+
const next = tabs[event.key === "Home" ? 0 : event.key === "End" ? tabs.length - 1 : event.key === "ArrowRight" ? (current + 1) % tabs.length : (current - 1 + tabs.length) % tabs.length];
|
|
657
|
+
const panel = next?.dataset.oxPanel;
|
|
658
|
+
if (!next || !panel) return;
|
|
659
|
+
showPanel(element, panel);
|
|
660
|
+
next.focus();
|
|
661
|
+
}
|
|
662
|
+
function prepareA11y(element) {
|
|
663
|
+
const widget = element.querySelector(".ox-code-play");
|
|
664
|
+
if (!widget) return;
|
|
665
|
+
if (!widget.id) widget.id = `ox-code-play-${++widgetId}`;
|
|
666
|
+
for (const tab of element.querySelectorAll("[data-ox-panel]")) {
|
|
667
|
+
const panelName = tab.dataset.oxPanel;
|
|
668
|
+
const panel = panelName ? element.querySelector(`[data-panel="${panelName}"]`) : null;
|
|
669
|
+
if (!panelName || !panel) continue;
|
|
670
|
+
tab.id ||= `${widget.id}-${panelName}-tab`;
|
|
671
|
+
panel.id ||= `${widget.id}-${panelName}-panel`;
|
|
672
|
+
tab.setAttribute("aria-controls", panel.id);
|
|
673
|
+
panel.setAttribute("aria-labelledby", tab.id);
|
|
674
|
+
panel.removeAttribute("aria-label");
|
|
675
|
+
}
|
|
676
|
+
}
|
|
350
677
|
function readForm(form) {
|
|
351
678
|
const data = new FormData(form);
|
|
352
679
|
const config = {};
|
|
@@ -359,7 +686,7 @@ function createCodePlayFromPayloads(root) {
|
|
|
359
686
|
let endpoints;
|
|
360
687
|
for (const element of queryWidgets(root)) try {
|
|
361
688
|
const payload = decodePayload(element.getAttribute("data-ox-code-play") ?? "");
|
|
362
|
-
languages[payload.language] =
|
|
689
|
+
languages[payload.language] = languageEnableFromPayload(payload);
|
|
363
690
|
endpoints = payload.endpoints ?? endpoints;
|
|
364
691
|
} catch {}
|
|
365
692
|
return createCodePlay({
|
|
@@ -367,6 +694,15 @@ function createCodePlayFromPayloads(root) {
|
|
|
367
694
|
endpoints
|
|
368
695
|
});
|
|
369
696
|
}
|
|
697
|
+
function languageEnableFromPayload(payload) {
|
|
698
|
+
const enable = {
|
|
699
|
+
execute: payload.capabilities.execute,
|
|
700
|
+
typecheck: payload.capabilities.typecheck,
|
|
701
|
+
config: payload.config
|
|
702
|
+
};
|
|
703
|
+
if (payload.endpoint) enable.endpoint = payload.endpoint;
|
|
704
|
+
return enable;
|
|
705
|
+
}
|
|
370
706
|
function queryWidgets(root) {
|
|
371
707
|
return [...root.querySelectorAll("[data-ox-code-play]")];
|
|
372
708
|
}
|
|
@@ -382,6 +718,6 @@ function defaultRoot() {
|
|
|
382
718
|
return document;
|
|
383
719
|
}
|
|
384
720
|
//#endregion
|
|
385
|
-
export {
|
|
721
|
+
export { resultRunActionState as _, renderProjectSandboxHtml as a, renderDiagnosticsHtml as c, renderStdioHtml as d, renderTimingHtml as f, readPlayPayload as g, idleRunActionState as h, renderPlayUi as i, renderProvenanceHtml as l, errorRunActionState as m, mountCodePlay as n, renderRunStatusText as o, CODE_PLAY_STYLES as p, resultPanelToShow as r, renderConfigHtml as s, hydrateCodePlay as t, renderStderrHtml as u, runPlayAction as v, runningRunActionState as y };
|
|
386
722
|
|
|
387
723
|
//# sourceMappingURL=hydrate2.mjs.map
|