@ox-content/code-play 3.0.0-alpha.1 → 3.0.0-alpha.10

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/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, p as resolveLanguage, t as decodePayload } from "./payload.mjs";
2
+ import { a as escapeHtml, h as resolveLanguage, 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: 12px;
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,89 @@ 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: 999px;
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; }
56
150
  .ox-code-play .ox-code { margin: 0; }
57
151
  .ox-code-play__source pre { margin: 0; border: 0; border-radius: 0; }
58
152
  .ox-code-play__tabs {
@@ -66,7 +160,7 @@ const CODE_PLAY_STYLES = `
66
160
  background: transparent;
67
161
  color: var(--octc-color-text, CanvasText);
68
162
  padding: 0.35rem 0.55rem;
69
- border-radius: 8px 8px 0 0;
163
+ border-radius: 6px 6px 0 0;
70
164
  font: 600 0.75rem/1.2 ui-sans-serif, system-ui, sans-serif;
71
165
  cursor: pointer;
72
166
  }
@@ -93,7 +187,7 @@ const CODE_PLAY_STYLES = `
93
187
  .ox-code-play__field input, .ox-code-play__field select {
94
188
  font: inherit;
95
189
  padding: 0.3rem 0.45rem;
96
- border-radius: 8px;
190
+ border-radius: 6px;
97
191
  border: 1px solid var(--octc-color-border, color-mix(in srgb, currentColor 18%, transparent));
98
192
  background: var(--octc-color-bg, Canvas);
99
193
  color: var(--octc-color-text, CanvasText);
@@ -188,42 +282,127 @@ function renderPlayUi(state) {
188
282
  if (preset === "headless") return "";
189
283
  const panel = state.panel ?? "stdio";
190
284
  const canTypecheck = state.payload.capabilities.typecheck;
285
+ const runState = state.runState ?? (state.busy ? {
286
+ phase: "running",
287
+ action: "execute"
288
+ } : idleRunActionState());
289
+ const isBusy = Boolean(state.busy) || runState.phase === "running";
191
290
  const tabs = renderTabs(state, panel);
192
291
  const viewers = state.payload.viewers;
193
- return `<div class="ox-code-play ox-code-play--${preset}" data-ox-code-play-ui>
292
+ const label = widgetLabel(state.payload, definition);
293
+ 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
294
  <div class="ox-code-play__toolbar">
195
- <span class="ox-code-play__lang">${escapeHtml(definition?.name ?? state.payload.language)}</span>
196
- <button type="button" data-ox-action="run"${actionButtonAttrs("run", Boolean(state.busy))}>Run</button>
197
- ${canTypecheck ? `<button type="button" data-ox-action="typecheck"${actionButtonAttrs("typecheck", Boolean(state.busy))}>Typecheck</button>` : ""}
198
- <button type="button" data-ox-action="cancel"${actionButtonAttrs("cancel", Boolean(state.busy))}>Cancel</button>
295
+ <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>
296
+ <span class="ox-code-play__status" data-ox-status role="status" aria-live="polite">${renderRunStatusText(runState)}</span>
297
+ <button type="button" data-ox-action="run" aria-label="${escapeHtml(`Run ${label}`)}"${actionButtonAttrs("run", Boolean(state.busy))}>Run</button>
298
+ ${canTypecheck ? `<button type="button" data-ox-action="typecheck" aria-label="${escapeHtml(`Typecheck ${label}`)}"${actionButtonAttrs("typecheck", Boolean(state.busy))}>Typecheck</button>` : ""}
299
+ <button type="button" data-ox-action="cancel" aria-label="${escapeHtml(`Cancel ${label}`)}"${actionButtonAttrs("cancel", Boolean(state.busy))}>Cancel</button>
199
300
  </div>
301
+ ${renderRuntimeStrip(state.payload, definition)}
200
302
  <div class="ox-code-play__source"></div>
201
303
  ${tabs}
202
- ${viewers.stdio ? `<div class="ox-code-play__panel" data-panel="stdio">${renderDiagnosticsHtml(state.result)}${renderStdioHtml(state.result?.stdio ?? [])}</div>` : ""}
203
- ${viewers.stderr ? `<div class="ox-code-play__panel" data-panel="stderr"${hidden(panel, "stderr", preset)}>${renderStderrHtml(state.result)}</div>` : ""}
204
- <div class="ox-code-play__panel" data-panel="config"${hidden(panel, "config", preset)}>${renderConfigHtml(definition?.configSchema ?? [], state.payload.config)}</div>
205
- <div class="ox-code-play__panel" data-panel="provenance"${hidden(panel, "provenance", preset)}>${renderProvenanceHtml(state.result?.provenance)}</div>
206
- <div class="ox-code-play__panel" data-panel="timing"${hidden(panel, "timing", preset)}>${renderTimingHtml(state.result?.timing)}</div>
304
+ ${viewers.stdio ? renderPanel("stdio", "Stdio", panel, preset, `${renderDiagnosticsHtml(state.result)}${renderStdioHtml(state.result?.stdio ?? [])}`) : ""}
305
+ ${viewers.stderr ? renderPanel("stderr", "Stderr", panel, preset, renderStderrHtml(state.result)) : ""}
306
+ ${viewers.config ? renderPanel("config", "Config", panel, preset, renderConfigHtml(definition?.configSchema ?? [], state.payload.config)) : ""}
307
+ ${viewers.provenance ? renderPanel("provenance", "Provenance", panel, preset, renderProvenanceHtml(state.result?.provenance)) : ""}
308
+ ${viewers.timing ? renderPanel("timing", "Timing", panel, preset, renderTimingHtml(state.result?.timing)) : ""}
207
309
  </div>`;
208
310
  }
311
+ function renderRunStatusText(state) {
312
+ if (state.phase === "running") return state.action === "typecheck" ? "Typechecking" : "Running";
313
+ if (state.phase === "offline") return "Offline";
314
+ if (state.phase === "error") {
315
+ if (state.result?.status === "timeout") return "Timed out";
316
+ if (state.result?.status === "unsupported") return "Unsupported";
317
+ return "Error";
318
+ }
319
+ if (state.phase === "result") {
320
+ if (state.result?.status === "cancelled") return "Cancelled";
321
+ return "Done";
322
+ }
323
+ return "Ready";
324
+ }
325
+ function renderRuntimeStrip(payload, definition) {
326
+ const runtime = runtimeLabel(payload, definition);
327
+ const executor = executorLabel(payload, definition);
328
+ const checks = payload.capabilities.typecheck ? "Typecheck ready" : "Run only";
329
+ return `<div class="ox-code-play__runtime" aria-label="Code Play runtime">${[
330
+ runtimeChip("Runtime", runtime.label, runtime.kind),
331
+ runtimeChip("Executor", executor.label, executor.kind),
332
+ runtimeChip("Checks", checks, payload.capabilities.typecheck ? "ok" : "muted")
333
+ ].join("")}</div>`;
334
+ }
335
+ function runtimeLabel(payload, definition) {
336
+ switch (definition?.backend) {
337
+ case "javascript": return {
338
+ label: "Browser sandbox",
339
+ kind: "ok"
340
+ };
341
+ case "typescript": return {
342
+ label: "TypeScript sandbox",
343
+ kind: "ok"
344
+ };
345
+ case "framework": return {
346
+ label: `${definition.name} iframe preview`,
347
+ kind: "ok"
348
+ };
349
+ case "rust-playground": return {
350
+ label: "Rust Playground",
351
+ kind: payload.endpoints?.rust ? "ok" : "warn"
352
+ };
353
+ case "go-playground": return {
354
+ label: "Go Playground",
355
+ kind: payload.endpoints?.go ? "ok" : "warn"
356
+ };
357
+ case "remote": return payload.endpoint ? {
358
+ label: "Piston-compatible",
359
+ kind: "ok"
360
+ } : {
361
+ label: "Endpoint missing",
362
+ kind: "warn"
363
+ };
364
+ default: return {
365
+ label: definition?.name ?? payload.language,
366
+ kind: "muted"
367
+ };
368
+ }
369
+ }
370
+ function executorLabel(payload, definition) {
371
+ if (!payload.capabilities.execute) return {
372
+ label: "Disabled",
373
+ kind: "warn"
374
+ };
375
+ if (definition?.backend === "remote" && !payload.endpoint) return {
376
+ label: "Configure endpoint",
377
+ kind: "warn"
378
+ };
379
+ return {
380
+ label: "On demand",
381
+ kind: "ok"
382
+ };
383
+ }
384
+ function runtimeChip(label, value, kind) {
385
+ return `<span class="ox-code-play__runtime-chip ox-code-play__runtime-chip--${kind}"><span>${escapeHtml(label)}</span><strong>${escapeHtml(value)}</strong></span>`;
386
+ }
209
387
  function renderTabs(state, panel) {
210
388
  if (state.payload.ui === "compact") return "";
211
389
  const viewers = state.payload.viewers;
212
390
  const buttons = [
213
- viewers.stdio ? tab("stdio", "stdio", panel) : "",
214
- viewers.stderr ? tab("stderr", "stderr", panel) : "",
215
- viewers.config ? tab("config", "config", panel) : "",
216
- viewers.provenance ? tab("provenance", "provenance", panel) : "",
217
- viewers.timing ? tab("timing", "timing", panel) : ""
391
+ viewers.stdio ? tab("stdio", "Stdio", panel) : "",
392
+ viewers.stderr ? tab("stderr", "Stderr", panel) : "",
393
+ viewers.config ? tab("config", "Config", panel) : "",
394
+ viewers.provenance ? tab("provenance", "Provenance", panel) : "",
395
+ viewers.timing ? tab("timing", "Timing", panel) : ""
218
396
  ].filter(Boolean).join("");
219
397
  return buttons ? `<div class="ox-code-play__tabs" role="tablist">${buttons}</div>` : "";
220
398
  }
221
399
  function tab(id, label, selected) {
222
- return `<button type="button" role="tab" data-ox-panel="${id}" aria-selected="${selected === id ? "true" : "false"}">${label}</button>`;
400
+ const isSelected = selected === id;
401
+ return `<button type="button" role="tab" data-ox-panel="${id}" aria-selected="${isSelected ? "true" : "false"}" tabindex="${isSelected ? "0" : "-1"}">${label}</button>`;
223
402
  }
224
403
  function actionButtonAttrs(action, busy) {
225
404
  const state = actionBusyState(action, busy);
226
- return `${state.disabled ? " disabled" : ""}${state.hidden ? " hidden" : ""}`;
405
+ return `${state.disabled ? " disabled aria-disabled=\"true\"" : ""}${state.hidden ? " hidden" : ""}`;
227
406
  }
228
407
  function actionBusyState(action, busy) {
229
408
  if (action === "cancel") return {
@@ -240,15 +419,30 @@ function applyActionBusy(root, busy) {
240
419
  const state = actionBusyState(button.dataset.oxAction ?? "", busy);
241
420
  button.disabled = state.disabled;
242
421
  button.hidden = state.hidden;
422
+ button.setAttribute("aria-disabled", state.disabled ? "true" : "false");
243
423
  }
424
+ for (const widget of queryPlayWidgets(root)) widget.setAttribute("aria-busy", busy ? "true" : "false");
425
+ }
426
+ function renderPanel(id, label, current, preset, html) {
427
+ 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
428
  }
245
429
  function hidden(current, id, preset) {
246
430
  if (preset === "compact" && (id === "stdio" || id === "stderr")) return "";
247
431
  return current === id ? "" : " hidden";
248
432
  }
433
+ function widgetLabel(payload, definition) {
434
+ const language = definition?.name ?? payload.language;
435
+ return payload.title ? `${payload.title} (${language})` : `${language} Code Play`;
436
+ }
437
+ function queryPlayWidgets(root) {
438
+ const widgets = [...root.querySelectorAll(".ox-code-play")];
439
+ if (typeof HTMLElement !== "undefined" && root instanceof HTMLElement && root.matches(".ox-code-play")) widgets.unshift(root);
440
+ return widgets;
441
+ }
249
442
  //#endregion
250
443
  //#region src/hydrate.ts
251
444
  const STYLE_ID = "ox-code-play-styles";
445
+ let widgetId = 0;
252
446
  function hydrateCodePlay(root = defaultRoot(), options = {}) {
253
447
  ensureStyles();
254
448
  const client = options.client ?? createCodePlayFromPayloads(root);
@@ -261,7 +455,7 @@ function mountCodePlay(element, options = {}) {
261
455
  const payload = readPlayPayload(element.getAttribute("data-ox-code-play") ?? "");
262
456
  if (!payload || payload.ui === "headless") return;
263
457
  const client = options.client ?? createCodePlay({
264
- languages: { [payload.language]: true },
458
+ languages: { [payload.language]: languageEnableFromPayload(payload) },
265
459
  endpoints: payload.endpoints
266
460
  });
267
461
  const source = element.innerHTML;
@@ -273,7 +467,9 @@ function mountCodePlay(element, options = {}) {
273
467
  if (sourceSlot) sourceSlot.innerHTML = source;
274
468
  element.replaceChildren(widget);
275
469
  element.dataset.oxCodePlayMounted = "true";
470
+ element.removeAttribute("inert");
276
471
  bindWidget(element, payload, client);
472
+ prepareA11y(element);
277
473
  }
278
474
  function bindWidget(element, payload, client) {
279
475
  let current = payload;
@@ -288,6 +484,9 @@ function bindWidget(element, payload, client) {
288
484
  runButton?.addEventListener("click", () => void run("execute"));
289
485
  checkButton?.addEventListener("click", () => void run("typecheck"));
290
486
  cancelButton?.addEventListener("click", () => session.cancel());
487
+ element.addEventListener("keydown", (event) => {
488
+ if (event.target instanceof HTMLElement && event.target.matches("[role=\"tab\"]")) handleTabKeydown(element, event);
489
+ });
291
490
  element.addEventListener("click", (event) => {
292
491
  const target = event.target;
293
492
  if (!(target instanceof HTMLElement)) return;
@@ -306,13 +505,20 @@ function bindWidget(element, payload, client) {
306
505
  async function run(action) {
307
506
  await runPlayAction({
308
507
  action: () => action === "typecheck" ? session.typecheck() : session.run(),
309
- setBusy: (busy) => applyActionBusy(element, busy),
310
- onResult: (result) => paintResult(element, current, result),
311
- onError: (error) => paintResult(element, current, errorResult(errorMessage(error)))
508
+ setBusy: (busy) => {
509
+ applyActionBusy(element, busy);
510
+ if (busy) paintRunState(element, runningRunActionState(action));
511
+ },
512
+ onResult: (result) => paintResult(element, current, result, action),
513
+ onError: (error) => {
514
+ paintRunState(element, errorRunActionState(action, error));
515
+ paintResult(element, current, errorResult(errorMessage(error)), action);
516
+ }
312
517
  });
313
518
  }
314
519
  }
315
- function paintResult(element, _payload, result) {
520
+ function paintResult(element, _payload, result, action) {
521
+ paintRunState(element, resultRunActionState(action, result));
316
522
  const stdio = element.querySelector("[data-panel=\"stdio\"]");
317
523
  if (stdio) {
318
524
  stdio.innerHTML = `${renderDiagnosticsHtml(result)}${renderStdioHtml(result.stdio)}`;
@@ -333,11 +539,27 @@ function paintResult(element, _payload, result) {
333
539
  if (timing) timing.innerHTML = renderTimingHtml(result.timing);
334
540
  const stderr = element.querySelector("[data-panel=\"stderr\"]");
335
541
  if (stderr) stderr.innerHTML = renderStderrHtml(result);
336
- showPanel(element, Boolean(stderr) && (Boolean(result.stderr) || result.diagnostics.some((diagnostic) => diagnostic.severity === "error")) ? "stderr" : "stdio");
542
+ showPanel(element, resultPanelToShow(result, Boolean(stderr)));
543
+ }
544
+ function paintRunState(element, state) {
545
+ const widget = element.querySelector(".ox-code-play");
546
+ widget?.setAttribute("data-ox-run-state", state.phase);
547
+ widget?.setAttribute("aria-busy", state.phase === "running" ? "true" : "false");
548
+ const status = element.querySelector("[data-ox-status]");
549
+ if (status) status.textContent = renderRunStatusText(state);
550
+ }
551
+ function resultPanelToShow(result, hasStderrPanel) {
552
+ if (!hasStderrPanel) return "stdio";
553
+ if (result.diagnostics.some((diagnostic) => diagnostic.severity === "error") || result.status !== "ok" && Boolean(result.stderr)) return "stderr";
554
+ return "stdio";
337
555
  }
338
556
  function showPanel(element, panel) {
339
557
  const compact = Boolean(element.querySelector(".ox-code-play--compact"));
340
- for (const tab of element.querySelectorAll("[data-ox-panel]")) tab.setAttribute("aria-selected", tab.getAttribute("data-ox-panel") === panel ? "true" : "false");
558
+ for (const tab of element.querySelectorAll("[data-ox-panel]")) {
559
+ const selected = tab.getAttribute("data-ox-panel") === panel;
560
+ tab.setAttribute("aria-selected", selected ? "true" : "false");
561
+ tab.tabIndex = selected ? 0 : -1;
562
+ }
341
563
  for (const node of element.querySelectorAll(".ox-code-play__panel")) {
342
564
  const id = node.dataset.panel;
343
565
  if (compact && (id === "stdio" || id === "stderr")) {
@@ -347,6 +569,38 @@ function showPanel(element, panel) {
347
569
  node.hidden = id !== panel;
348
570
  }
349
571
  }
572
+ function handleTabKeydown(element, event) {
573
+ if (![
574
+ "ArrowLeft",
575
+ "ArrowRight",
576
+ "Home",
577
+ "End"
578
+ ].includes(event.key)) return;
579
+ const tabs = [...element.querySelectorAll("[data-ox-panel]")];
580
+ const current = tabs.indexOf(event.target);
581
+ if (current === -1) return;
582
+ event.preventDefault();
583
+ 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];
584
+ const panel = next?.dataset.oxPanel;
585
+ if (!next || !panel) return;
586
+ showPanel(element, panel);
587
+ next.focus();
588
+ }
589
+ function prepareA11y(element) {
590
+ const widget = element.querySelector(".ox-code-play");
591
+ if (!widget) return;
592
+ if (!widget.id) widget.id = `ox-code-play-${++widgetId}`;
593
+ for (const tab of element.querySelectorAll("[data-ox-panel]")) {
594
+ const panelName = tab.dataset.oxPanel;
595
+ const panel = panelName ? element.querySelector(`[data-panel="${panelName}"]`) : null;
596
+ if (!panelName || !panel) continue;
597
+ tab.id ||= `${widget.id}-${panelName}-tab`;
598
+ panel.id ||= `${widget.id}-${panelName}-panel`;
599
+ tab.setAttribute("aria-controls", panel.id);
600
+ panel.setAttribute("aria-labelledby", tab.id);
601
+ panel.removeAttribute("aria-label");
602
+ }
603
+ }
350
604
  function readForm(form) {
351
605
  const data = new FormData(form);
352
606
  const config = {};
@@ -359,7 +613,7 @@ function createCodePlayFromPayloads(root) {
359
613
  let endpoints;
360
614
  for (const element of queryWidgets(root)) try {
361
615
  const payload = decodePayload(element.getAttribute("data-ox-code-play") ?? "");
362
- languages[payload.language] = true;
616
+ languages[payload.language] = languageEnableFromPayload(payload);
363
617
  endpoints = payload.endpoints ?? endpoints;
364
618
  } catch {}
365
619
  return createCodePlay({
@@ -367,6 +621,15 @@ function createCodePlayFromPayloads(root) {
367
621
  endpoints
368
622
  });
369
623
  }
624
+ function languageEnableFromPayload(payload) {
625
+ const enable = {
626
+ execute: payload.capabilities.execute,
627
+ typecheck: payload.capabilities.typecheck,
628
+ config: payload.config
629
+ };
630
+ if (payload.endpoint) enable.endpoint = payload.endpoint;
631
+ return enable;
632
+ }
370
633
  function queryWidgets(root) {
371
634
  return [...root.querySelectorAll("[data-ox-code-play]")];
372
635
  }
@@ -382,6 +645,6 @@ function defaultRoot() {
382
645
  return document;
383
646
  }
384
647
  //#endregion
385
- export { renderDiagnosticsHtml as a, renderStdioHtml as c, renderConfigHtml as i, renderTimingHtml as l, mountCodePlay as n, renderProvenanceHtml as o, renderPlayUi as r, renderStderrHtml as s, hydrateCodePlay as t, CODE_PLAY_STYLES as u };
648
+ export { runPlayAction as _, renderRunStatusText as a, renderProvenanceHtml as c, renderTimingHtml as d, CODE_PLAY_STYLES as f, resultRunActionState as g, readPlayPayload as h, renderPlayUi as i, renderStderrHtml as l, idleRunActionState as m, mountCodePlay as n, renderConfigHtml as o, errorRunActionState as p, resultPanelToShow as r, renderDiagnosticsHtml as s, hydrateCodePlay as t, renderStdioHtml as u, runningRunActionState as v };
386
649
 
387
650
  //# sourceMappingURL=hydrate2.mjs.map