@ox-content/code-play 3.0.0-alpha.9 → 3.0.0-beta.0
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 +31 -3
- package/dist/browser.mjs +412 -44
- package/dist/client.mjs +17 -4
- 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 +3 -1
- package/dist/hydrate.d.mts.map +1 -1
- package/dist/hydrate2.mjs +395 -43
- package/dist/hydrate2.mjs.map +1 -1
- package/dist/index.d.mts +49 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +4 -4
- package/dist/payload.mjs +5 -1
- 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/browser.mjs
CHANGED
|
@@ -372,6 +372,9 @@ function escapeHtml(value) {
|
|
|
372
372
|
}
|
|
373
373
|
});
|
|
374
374
|
}
|
|
375
|
+
function escapeAttribute(value) {
|
|
376
|
+
return escapeHtml(value).replace(/`/g, "`");
|
|
377
|
+
}
|
|
375
378
|
//#endregion
|
|
376
379
|
//#region src/timing.ts
|
|
377
380
|
var PhaseTracker = class {
|
|
@@ -1197,7 +1200,6 @@ async function typecheckTypeScript(request) {
|
|
|
1197
1200
|
};
|
|
1198
1201
|
} catch (error) {
|
|
1199
1202
|
if (isAbortError(error) || request.signal?.aborted) throw error;
|
|
1200
|
-
executedRuntime = executionRuntimeFromError(error) ?? executedRuntime;
|
|
1201
1203
|
tracker.stop();
|
|
1202
1204
|
const message = error instanceof Error ? error.message : String(error);
|
|
1203
1205
|
return {
|
|
@@ -1433,9 +1435,12 @@ function errorMessage(error) {
|
|
|
1433
1435
|
}
|
|
1434
1436
|
function friendlyTransportMessage(error) {
|
|
1435
1437
|
const message = errorMessage(error);
|
|
1436
|
-
if ((error
|
|
1438
|
+
if (isOfflineError(error)) return "The executor is offline or unreachable from this page (for example, CORS). Set endpoints to a host that allows browser POST, or use the Vite dev proxy.";
|
|
1437
1439
|
return message;
|
|
1438
1440
|
}
|
|
1441
|
+
function transportFailureStatus(error) {
|
|
1442
|
+
return isOfflineError(error) ? "offline" : "error";
|
|
1443
|
+
}
|
|
1439
1444
|
function errorResult(message, source = "code-play", status = "error") {
|
|
1440
1445
|
return withStdioText({
|
|
1441
1446
|
status,
|
|
@@ -1449,6 +1454,14 @@ function errorResult(message, source = "code-play", status = "error") {
|
|
|
1449
1454
|
timing: emptyTiming()
|
|
1450
1455
|
});
|
|
1451
1456
|
}
|
|
1457
|
+
function isOfflineError(error) {
|
|
1458
|
+
const message = errorMessage(error);
|
|
1459
|
+
const name = error && typeof error === "object" && "name" in error ? String(error.name) : void 0;
|
|
1460
|
+
const typeError = error instanceof TypeError || name === "TypeError";
|
|
1461
|
+
if (name === "MissingTransportError") return true;
|
|
1462
|
+
if (typeError && /failed to fetch|networkerror|load failed|network request failed/i.test(message)) return true;
|
|
1463
|
+
return /\boffline\b|no code play transport|network request failed/i.test(message);
|
|
1464
|
+
}
|
|
1452
1465
|
//#endregion
|
|
1453
1466
|
//#region src/session.ts
|
|
1454
1467
|
var CodePlaySession = class {
|
|
@@ -1468,6 +1481,7 @@ var CodePlaySession = class {
|
|
|
1468
1481
|
timeoutMs;
|
|
1469
1482
|
transport;
|
|
1470
1483
|
endpoints;
|
|
1484
|
+
project;
|
|
1471
1485
|
loadTypeScript;
|
|
1472
1486
|
listeners = /* @__PURE__ */ new Map();
|
|
1473
1487
|
abort;
|
|
@@ -1479,6 +1493,7 @@ var CodePlaySession = class {
|
|
|
1479
1493
|
this.timeoutMs = input.timeoutMs;
|
|
1480
1494
|
this.transport = input.transport;
|
|
1481
1495
|
this.endpoints = input.endpoints;
|
|
1496
|
+
this.project = input.project;
|
|
1482
1497
|
this.loadTypeScript = input.loadTypeScript;
|
|
1483
1498
|
}
|
|
1484
1499
|
on(event, listener) {
|
|
@@ -1519,6 +1534,7 @@ var CodePlaySession = class {
|
|
|
1519
1534
|
transport: this.transport,
|
|
1520
1535
|
loadTypeScript: this.loadTypeScript,
|
|
1521
1536
|
endpoints: this.endpoints,
|
|
1537
|
+
project: this.project,
|
|
1522
1538
|
signal
|
|
1523
1539
|
};
|
|
1524
1540
|
try {
|
|
@@ -1526,7 +1542,7 @@ var CodePlaySession = class {
|
|
|
1526
1542
|
return this.finish(result);
|
|
1527
1543
|
} catch (error) {
|
|
1528
1544
|
if (signal.aborted || isAbortError(error)) return this.finish(errorResult("Run cancelled.", "code-play", "cancelled"));
|
|
1529
|
-
return this.finish(errorResult(friendlyTransportMessage(error)));
|
|
1545
|
+
return this.finish(errorResult(friendlyTransportMessage(error), "code-play", transportFailureStatus(error)));
|
|
1530
1546
|
}
|
|
1531
1547
|
}
|
|
1532
1548
|
finish(result) {
|
|
@@ -1611,62 +1627,221 @@ async function runPlayAction(input) {
|
|
|
1611
1627
|
input.setBusy(false);
|
|
1612
1628
|
}
|
|
1613
1629
|
}
|
|
1630
|
+
function idleRunActionState() {
|
|
1631
|
+
return { phase: "idle" };
|
|
1632
|
+
}
|
|
1633
|
+
function runningRunActionState(action, startedAtMs = Date.now()) {
|
|
1634
|
+
return {
|
|
1635
|
+
phase: "running",
|
|
1636
|
+
action,
|
|
1637
|
+
startedAtMs
|
|
1638
|
+
};
|
|
1639
|
+
}
|
|
1640
|
+
function resultRunActionState(action, result, finishedAtMs = Date.now()) {
|
|
1641
|
+
return {
|
|
1642
|
+
phase: result.status === "offline" ? "offline" : result.status === "ok" || result.status === "cancelled" ? "result" : "error",
|
|
1643
|
+
action,
|
|
1644
|
+
result,
|
|
1645
|
+
message: result.diagnostics[0]?.message,
|
|
1646
|
+
finishedAtMs
|
|
1647
|
+
};
|
|
1648
|
+
}
|
|
1649
|
+
function errorRunActionState(action, error, finishedAtMs = Date.now()) {
|
|
1650
|
+
return {
|
|
1651
|
+
phase: "error",
|
|
1652
|
+
action,
|
|
1653
|
+
message: errorMessage(error),
|
|
1654
|
+
finishedAtMs
|
|
1655
|
+
};
|
|
1656
|
+
}
|
|
1614
1657
|
//#endregion
|
|
1615
1658
|
//#region src/styles.ts
|
|
1616
1659
|
var CODE_PLAY_STYLES = `
|
|
1617
1660
|
.ox-code-play {
|
|
1618
1661
|
border: 1px solid var(--octc-color-border, color-mix(in srgb, currentColor 16%, transparent));
|
|
1619
|
-
border-radius:
|
|
1620
|
-
background: var(--octc-color-bg
|
|
1662
|
+
border-radius: 6px;
|
|
1663
|
+
background: var(--octc-color-bg, Canvas);
|
|
1621
1664
|
color: var(--octc-color-text, CanvasText);
|
|
1622
1665
|
overflow: hidden;
|
|
1623
|
-
margin:
|
|
1666
|
+
margin: 1rem 0;
|
|
1624
1667
|
}
|
|
1625
1668
|
.ox-code-play__toolbar {
|
|
1626
1669
|
display: flex;
|
|
1627
1670
|
flex-wrap: wrap;
|
|
1628
|
-
gap: 0.
|
|
1671
|
+
gap: 0.4rem;
|
|
1629
1672
|
align-items: center;
|
|
1630
|
-
padding: 0.
|
|
1673
|
+
padding: 0.45rem 0.6rem;
|
|
1631
1674
|
border-bottom: 1px solid var(--octc-color-border, color-mix(in srgb, currentColor 12%, transparent));
|
|
1675
|
+
background: color-mix(in srgb, var(--octc-color-bg-alt, var(--octc-color-bg, Canvas)) 64%, transparent);
|
|
1676
|
+
}
|
|
1677
|
+
.ox-code-play__summary {
|
|
1678
|
+
display: inline-flex;
|
|
1679
|
+
flex: 1 1 12rem;
|
|
1680
|
+
min-width: 9rem;
|
|
1681
|
+
gap: 0.45rem;
|
|
1682
|
+
align-items: baseline;
|
|
1632
1683
|
}
|
|
1633
1684
|
.ox-code-play__lang {
|
|
1634
1685
|
font: 600 0.8rem/1.2 ui-sans-serif, system-ui, sans-serif;
|
|
1635
|
-
margin-right: auto;
|
|
1636
1686
|
color: var(--octc-color-text, CanvasText);
|
|
1637
1687
|
}
|
|
1688
|
+
.ox-code-play__title {
|
|
1689
|
+
min-width: 0;
|
|
1690
|
+
overflow-wrap: anywhere;
|
|
1691
|
+
font: 500 0.78rem/1.25 ui-sans-serif, system-ui, sans-serif;
|
|
1692
|
+
opacity: 0.72;
|
|
1693
|
+
}
|
|
1694
|
+
.ox-code-play__status {
|
|
1695
|
+
min-width: 4.6rem;
|
|
1696
|
+
text-align: center;
|
|
1697
|
+
border: 1px solid var(--octc-color-border, color-mix(in srgb, currentColor 16%, transparent));
|
|
1698
|
+
border-radius: 999px;
|
|
1699
|
+
padding: 0.12rem 0.45rem;
|
|
1700
|
+
font: 650 0.68rem/1.25 ui-sans-serif, system-ui, sans-serif;
|
|
1701
|
+
color: var(--octc-color-text, CanvasText);
|
|
1702
|
+
background: var(--octc-color-bg, Canvas);
|
|
1703
|
+
}
|
|
1704
|
+
.ox-code-play[data-ox-run-state="running"] .ox-code-play__status {
|
|
1705
|
+
color: var(--octc-color-primary, var(--octc-accent, #4f46e5));
|
|
1706
|
+
}
|
|
1707
|
+
.ox-code-play[data-ox-run-state="error"] .ox-code-play__status,
|
|
1708
|
+
.ox-code-play[data-ox-run-state="offline"] .ox-code-play__status {
|
|
1709
|
+
color: var(--octc-danger, #b42318);
|
|
1710
|
+
}
|
|
1638
1711
|
.ox-code-play__toolbar button {
|
|
1639
1712
|
appearance: none;
|
|
1640
1713
|
border: 1px solid var(--octc-color-border, color-mix(in srgb, currentColor 20%, transparent));
|
|
1641
1714
|
background: color-mix(in srgb, var(--octc-color-text, CanvasText) 8%, transparent);
|
|
1642
1715
|
color: var(--octc-color-text, CanvasText);
|
|
1643
|
-
border-radius:
|
|
1644
|
-
|
|
1716
|
+
border-radius: 6px;
|
|
1717
|
+
min-height: 1.9rem;
|
|
1718
|
+
padding: 0.18rem 0.62rem;
|
|
1645
1719
|
font: 600 0.75rem/1.4 ui-sans-serif, system-ui, sans-serif;
|
|
1646
1720
|
cursor: pointer;
|
|
1647
1721
|
}
|
|
1648
1722
|
.ox-code-play__toolbar button:disabled { opacity: 0.55; cursor: progress; }
|
|
1723
|
+
.ox-code-play__toolbar button:focus-visible,
|
|
1724
|
+
.ox-code-play__tabs button:focus-visible,
|
|
1725
|
+
.ox-code-play__field input:focus-visible,
|
|
1726
|
+
.ox-code-play__field select:focus-visible,
|
|
1727
|
+
.ox-code-play__panel:focus-visible {
|
|
1728
|
+
outline: 2px solid var(--octc-color-primary, var(--octc-accent, #4f46e5));
|
|
1729
|
+
outline-offset: 2px;
|
|
1730
|
+
}
|
|
1731
|
+
.ox-code-play__runtime {
|
|
1732
|
+
display: flex;
|
|
1733
|
+
flex-wrap: wrap;
|
|
1734
|
+
gap: 0.4rem 0.7rem;
|
|
1735
|
+
align-items: center;
|
|
1736
|
+
padding: 0.38rem 0.6rem;
|
|
1737
|
+
border-bottom: 1px solid var(--octc-color-border, color-mix(in srgb, currentColor 10%, transparent));
|
|
1738
|
+
background: color-mix(in srgb, var(--octc-color-bg-alt, var(--octc-color-bg, Canvas)) 56%, transparent);
|
|
1739
|
+
}
|
|
1740
|
+
.ox-code-play__runtime-chip {
|
|
1741
|
+
display: inline-flex;
|
|
1742
|
+
gap: 0.3rem;
|
|
1743
|
+
align-items: baseline;
|
|
1744
|
+
min-width: 0;
|
|
1745
|
+
padding: 0;
|
|
1746
|
+
border: 0;
|
|
1747
|
+
background: transparent;
|
|
1748
|
+
}
|
|
1749
|
+
.ox-code-play__runtime-chip span {
|
|
1750
|
+
font: 650 0.62rem/1.1 ui-sans-serif, system-ui, sans-serif;
|
|
1751
|
+
text-transform: uppercase;
|
|
1752
|
+
letter-spacing: 0.03em;
|
|
1753
|
+
opacity: 0.62;
|
|
1754
|
+
}
|
|
1755
|
+
.ox-code-play__runtime-chip strong {
|
|
1756
|
+
font: 650 0.72rem/1.2 ui-sans-serif, system-ui, sans-serif;
|
|
1757
|
+
}
|
|
1758
|
+
.ox-code-play__runtime-chip--ok strong { color: var(--octc-color-primary, var(--octc-accent, #4f46e5)); }
|
|
1759
|
+
.ox-code-play__runtime-chip--warn strong { color: var(--octc-warning, #b54708); }
|
|
1760
|
+
.ox-code-play__runtime-chip--muted strong { opacity: 0.72; }
|
|
1761
|
+
.ox-code-play__project {
|
|
1762
|
+
display: inline-flex;
|
|
1763
|
+
flex: 1 1 18rem;
|
|
1764
|
+
min-width: min(100%, 16rem);
|
|
1765
|
+
gap: 0.45rem;
|
|
1766
|
+
align-items: center;
|
|
1767
|
+
flex-wrap: wrap;
|
|
1768
|
+
margin-left: auto;
|
|
1769
|
+
padding: 0.28rem 0.42rem;
|
|
1770
|
+
border: 1px solid var(--octc-color-border, color-mix(in srgb, currentColor 14%, transparent));
|
|
1771
|
+
border-radius: 6px;
|
|
1772
|
+
background: var(--octc-color-bg, Canvas);
|
|
1773
|
+
}
|
|
1774
|
+
.ox-code-play__project-main,
|
|
1775
|
+
.ox-code-play__project-entry {
|
|
1776
|
+
display: inline-grid;
|
|
1777
|
+
gap: 0.1rem;
|
|
1778
|
+
min-width: 0;
|
|
1779
|
+
}
|
|
1780
|
+
.ox-code-play__project-main { flex: 1 1 8.5rem; }
|
|
1781
|
+
.ox-code-play__project-entry { flex: 999 1 9rem; }
|
|
1782
|
+
.ox-code-play__project-main span,
|
|
1783
|
+
.ox-code-play__project-entry span {
|
|
1784
|
+
font: 600 0.62rem/1.1 ui-sans-serif, system-ui, sans-serif;
|
|
1785
|
+
text-transform: uppercase;
|
|
1786
|
+
letter-spacing: 0.04em;
|
|
1787
|
+
opacity: 0.62;
|
|
1788
|
+
}
|
|
1789
|
+
.ox-code-play__project-main strong,
|
|
1790
|
+
.ox-code-play__project-entry strong {
|
|
1791
|
+
min-width: 0;
|
|
1792
|
+
overflow-wrap: anywhere;
|
|
1793
|
+
font: 650 0.78rem/1.2 ui-sans-serif, system-ui, sans-serif;
|
|
1794
|
+
}
|
|
1795
|
+
.ox-code-play__project-files,
|
|
1796
|
+
.ox-code-play__project-warning,
|
|
1797
|
+
.ox-code-play__project-link {
|
|
1798
|
+
flex: 0 0 auto;
|
|
1799
|
+
border-radius: 6px;
|
|
1800
|
+
padding: 0.18rem 0.45rem;
|
|
1801
|
+
font: 650 0.68rem/1.25 ui-sans-serif, system-ui, sans-serif;
|
|
1802
|
+
}
|
|
1803
|
+
.ox-code-play__project-files {
|
|
1804
|
+
background: color-mix(in srgb, var(--octc-color-text, CanvasText) 8%, transparent);
|
|
1805
|
+
}
|
|
1806
|
+
.ox-code-play__project-warning {
|
|
1807
|
+
color: var(--octc-warning, #b54708);
|
|
1808
|
+
background: color-mix(in srgb, var(--octc-warning, #b54708) 12%, transparent);
|
|
1809
|
+
}
|
|
1810
|
+
.ox-code-play__project-link {
|
|
1811
|
+
color: var(--octc-color-primary, var(--octc-accent, #4f46e5));
|
|
1812
|
+
background: color-mix(in srgb, var(--octc-color-primary, var(--octc-accent, #4f46e5)) 12%, transparent);
|
|
1813
|
+
text-decoration: none;
|
|
1814
|
+
}
|
|
1815
|
+
.ox-code-play__project-link:focus-visible {
|
|
1816
|
+
outline: 2px solid var(--octc-color-primary, var(--octc-accent, #4f46e5));
|
|
1817
|
+
outline-offset: 2px;
|
|
1818
|
+
}
|
|
1649
1819
|
.ox-code-play .ox-code { margin: 0; }
|
|
1650
|
-
.ox-code-play__source pre {
|
|
1820
|
+
.ox-code-play__source pre {
|
|
1821
|
+
margin: 0;
|
|
1822
|
+
border: 0;
|
|
1823
|
+
border-radius: 0;
|
|
1824
|
+
padding: 0.75rem 0.8rem;
|
|
1825
|
+
}
|
|
1651
1826
|
.ox-code-play__tabs {
|
|
1652
1827
|
display: flex;
|
|
1653
|
-
gap: 0.
|
|
1654
|
-
padding: 0.
|
|
1828
|
+
gap: 0.15rem;
|
|
1829
|
+
padding: 0.35rem 0.55rem 0;
|
|
1655
1830
|
}
|
|
1656
1831
|
.ox-code-play__tabs button {
|
|
1657
1832
|
appearance: none;
|
|
1658
1833
|
border: 0;
|
|
1659
1834
|
background: transparent;
|
|
1660
1835
|
color: var(--octc-color-text, CanvasText);
|
|
1661
|
-
padding: 0.
|
|
1662
|
-
border-radius:
|
|
1836
|
+
padding: 0.28rem 0.48rem;
|
|
1837
|
+
border-radius: 5px 5px 0 0;
|
|
1663
1838
|
font: 600 0.75rem/1.2 ui-sans-serif, system-ui, sans-serif;
|
|
1664
1839
|
cursor: pointer;
|
|
1665
1840
|
}
|
|
1666
1841
|
.ox-code-play__tabs button[aria-selected="true"] {
|
|
1667
1842
|
background: color-mix(in srgb, var(--octc-color-text, CanvasText) 10%, transparent);
|
|
1668
1843
|
}
|
|
1669
|
-
.ox-code-play__panel { padding: 0.
|
|
1844
|
+
.ox-code-play__panel { padding: 0.55rem 0.65rem 0.7rem; color: var(--octc-color-text, CanvasText); }
|
|
1670
1845
|
.ox-code-play__panel pre {
|
|
1671
1846
|
background: transparent !important;
|
|
1672
1847
|
color: inherit !important;
|
|
@@ -1677,7 +1852,7 @@ var CODE_PLAY_STYLES = `
|
|
|
1677
1852
|
}
|
|
1678
1853
|
.ox-code-play__empty { margin: 0; opacity: 0.7; font-size: 0.85rem; }
|
|
1679
1854
|
.ox-code-play__stdio { font: 12px/1.45 ui-monospace, SFMono-Regular, monospace; }
|
|
1680
|
-
.ox-code-play__stdio-line { display: grid; grid-template-columns:
|
|
1855
|
+
.ox-code-play__stdio-line { display: grid; grid-template-columns: 5.4rem 1fr; gap: 0.55rem; white-space: pre-wrap; }
|
|
1681
1856
|
.ox-code-play__stdio-line--stderr { color: var(--octc-danger, #b42318); }
|
|
1682
1857
|
.ox-code-play__stdio-line--stdin { opacity: 0.75; }
|
|
1683
1858
|
.ox-code-play__stdio-meta { opacity: 0.6; }
|
|
@@ -1686,7 +1861,7 @@ var CODE_PLAY_STYLES = `
|
|
|
1686
1861
|
.ox-code-play__field input, .ox-code-play__field select {
|
|
1687
1862
|
font: inherit;
|
|
1688
1863
|
padding: 0.3rem 0.45rem;
|
|
1689
|
-
border-radius:
|
|
1864
|
+
border-radius: 6px;
|
|
1690
1865
|
border: 1px solid var(--octc-color-border, color-mix(in srgb, currentColor 18%, transparent));
|
|
1691
1866
|
background: var(--octc-color-bg, Canvas);
|
|
1692
1867
|
color: var(--octc-color-text, CanvasText);
|
|
@@ -1708,6 +1883,20 @@ var CODE_PLAY_STYLES = `
|
|
|
1708
1883
|
.ox-code-play__diag--warning { color: var(--octc-warning, #b54708); }
|
|
1709
1884
|
.ox-code-play--compact .ox-code-play__tabs { display: none; }
|
|
1710
1885
|
.ox-code-play--compact .ox-code-play__panel[data-panel]:not([data-panel="stdio"]):not([data-panel="stderr"]) { display: none; }
|
|
1886
|
+
@media (max-width: 640px) {
|
|
1887
|
+
.ox-code-play__toolbar {
|
|
1888
|
+
align-items: flex-start;
|
|
1889
|
+
}
|
|
1890
|
+
.ox-code-play__runtime,
|
|
1891
|
+
.ox-code-play__tabs,
|
|
1892
|
+
.ox-code-play__panel {
|
|
1893
|
+
padding-inline: 0.55rem;
|
|
1894
|
+
}
|
|
1895
|
+
.ox-code-play__stdio-line {
|
|
1896
|
+
grid-template-columns: 1fr;
|
|
1897
|
+
gap: 0.15rem;
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1711
1900
|
`;
|
|
1712
1901
|
//#endregion
|
|
1713
1902
|
//#region src/viewers.ts
|
|
@@ -1781,42 +1970,141 @@ function renderPlayUi(state) {
|
|
|
1781
1970
|
if (preset === "headless") return "";
|
|
1782
1971
|
const panel = state.panel ?? "stdio";
|
|
1783
1972
|
const canTypecheck = state.payload.capabilities.typecheck;
|
|
1973
|
+
const runState = state.runState ?? (state.busy ? {
|
|
1974
|
+
phase: "running",
|
|
1975
|
+
action: "execute"
|
|
1976
|
+
} : idleRunActionState());
|
|
1977
|
+
const isBusy = Boolean(state.busy) || runState.phase === "running";
|
|
1784
1978
|
const tabs = renderTabs(state, panel);
|
|
1785
1979
|
const viewers = state.payload.viewers;
|
|
1786
|
-
|
|
1980
|
+
const label = widgetLabel(state.payload, definition);
|
|
1981
|
+
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"}">
|
|
1787
1982
|
<div class="ox-code-play__toolbar">
|
|
1788
|
-
<span class="ox-code-play__lang">${escapeHtml(definition?.name ?? state.payload.language)}</span>
|
|
1789
|
-
<
|
|
1790
|
-
|
|
1791
|
-
|
|
1983
|
+
<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>
|
|
1984
|
+
<span class="ox-code-play__status" data-ox-status role="status" aria-live="polite">${renderRunStatusText(runState)}</span>
|
|
1985
|
+
<button type="button" data-ox-action="run" aria-label="${escapeHtml(`Run ${label}`)}"${actionButtonAttrs("run", Boolean(state.busy))}>Run</button>
|
|
1986
|
+
${canTypecheck ? `<button type="button" data-ox-action="typecheck" aria-label="${escapeHtml(`Typecheck ${label}`)}"${actionButtonAttrs("typecheck", Boolean(state.busy))}>Typecheck</button>` : ""}
|
|
1987
|
+
<button type="button" data-ox-action="cancel" aria-label="${escapeHtml(`Cancel ${label}`)}"${actionButtonAttrs("cancel", Boolean(state.busy))}>Cancel</button>
|
|
1792
1988
|
</div>
|
|
1989
|
+
${renderRuntimeStrip(state.payload, definition)}
|
|
1793
1990
|
<div class="ox-code-play__source"></div>
|
|
1794
1991
|
${tabs}
|
|
1795
|
-
${viewers.stdio ?
|
|
1796
|
-
${viewers.stderr ?
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1992
|
+
${viewers.stdio ? renderPanel("stdio", "Stdio", panel, preset, `${renderDiagnosticsHtml(state.result)}${renderStdioHtml(state.result?.stdio ?? [])}`) : ""}
|
|
1993
|
+
${viewers.stderr ? renderPanel("stderr", "Stderr", panel, preset, renderStderrHtml(state.result)) : ""}
|
|
1994
|
+
${viewers.config ? renderPanel("config", "Config", panel, preset, renderConfigHtml(definition?.configSchema ?? [], state.payload.config)) : ""}
|
|
1995
|
+
${viewers.provenance ? renderPanel("provenance", "Provenance", panel, preset, renderProvenanceHtml(state.result?.provenance)) : ""}
|
|
1996
|
+
${viewers.timing ? renderPanel("timing", "Timing", panel, preset, renderTimingHtml(state.result?.timing)) : ""}
|
|
1800
1997
|
</div>`;
|
|
1801
1998
|
}
|
|
1999
|
+
function renderRunStatusText(state) {
|
|
2000
|
+
if (state.phase === "running") return state.action === "typecheck" ? "Typechecking" : "Running";
|
|
2001
|
+
if (state.phase === "offline") return "Offline";
|
|
2002
|
+
if (state.phase === "error") {
|
|
2003
|
+
if (state.result?.status === "timeout") return "Timed out";
|
|
2004
|
+
if (state.result?.status === "unsupported") return "Unsupported";
|
|
2005
|
+
return "Error";
|
|
2006
|
+
}
|
|
2007
|
+
if (state.phase === "result") {
|
|
2008
|
+
if (state.result?.status === "cancelled") return "Cancelled";
|
|
2009
|
+
return "Done";
|
|
2010
|
+
}
|
|
2011
|
+
return "Ready";
|
|
2012
|
+
}
|
|
2013
|
+
function renderRuntimeStrip(payload, definition) {
|
|
2014
|
+
const runtime = runtimeLabel(payload, definition);
|
|
2015
|
+
const executor = executorLabel(payload, definition);
|
|
2016
|
+
const checks = payload.capabilities.typecheck ? "Typecheck ready" : "Run only";
|
|
2017
|
+
return `<div class="ox-code-play__runtime" aria-label="Code Play runtime">${[
|
|
2018
|
+
runtimeChip("Runtime", runtime.label, runtime.kind),
|
|
2019
|
+
runtimeChip("Executor", executor.label, executor.kind),
|
|
2020
|
+
runtimeChip("Checks", checks, payload.capabilities.typecheck ? "ok" : "muted")
|
|
2021
|
+
].join("")}${renderProjectSandboxHtml(payload.project)}</div>`;
|
|
2022
|
+
}
|
|
2023
|
+
function runtimeLabel(payload, definition) {
|
|
2024
|
+
switch (definition?.backend) {
|
|
2025
|
+
case "javascript": return {
|
|
2026
|
+
label: "Browser sandbox",
|
|
2027
|
+
kind: "ok"
|
|
2028
|
+
};
|
|
2029
|
+
case "typescript": return {
|
|
2030
|
+
label: "TypeScript sandbox",
|
|
2031
|
+
kind: "ok"
|
|
2032
|
+
};
|
|
2033
|
+
case "framework": return {
|
|
2034
|
+
label: `${definition.name} iframe preview`,
|
|
2035
|
+
kind: "ok"
|
|
2036
|
+
};
|
|
2037
|
+
case "rust-playground": return {
|
|
2038
|
+
label: "Rust Playground",
|
|
2039
|
+
kind: payload.endpoints?.rust ? "ok" : "warn"
|
|
2040
|
+
};
|
|
2041
|
+
case "go-playground": return {
|
|
2042
|
+
label: "Go Playground",
|
|
2043
|
+
kind: payload.endpoints?.go ? "ok" : "warn"
|
|
2044
|
+
};
|
|
2045
|
+
case "remote": return payload.endpoint ? {
|
|
2046
|
+
label: "Piston-compatible",
|
|
2047
|
+
kind: "ok"
|
|
2048
|
+
} : {
|
|
2049
|
+
label: "Endpoint missing",
|
|
2050
|
+
kind: "warn"
|
|
2051
|
+
};
|
|
2052
|
+
default: return {
|
|
2053
|
+
label: definition?.name ?? payload.language,
|
|
2054
|
+
kind: "muted"
|
|
2055
|
+
};
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
function executorLabel(payload, definition) {
|
|
2059
|
+
if (!payload.capabilities.execute) return {
|
|
2060
|
+
label: "Disabled",
|
|
2061
|
+
kind: "warn"
|
|
2062
|
+
};
|
|
2063
|
+
if (definition?.backend === "remote" && !payload.endpoint) return {
|
|
2064
|
+
label: "Configure endpoint",
|
|
2065
|
+
kind: "warn"
|
|
2066
|
+
};
|
|
2067
|
+
return {
|
|
2068
|
+
label: "On demand",
|
|
2069
|
+
kind: "ok"
|
|
2070
|
+
};
|
|
2071
|
+
}
|
|
2072
|
+
function runtimeChip(label, value, kind) {
|
|
2073
|
+
return `<span class="ox-code-play__runtime-chip ox-code-play__runtime-chip--${kind}"><span>${escapeHtml(label)}</span><strong>${escapeHtml(value)}</strong></span>`;
|
|
2074
|
+
}
|
|
2075
|
+
function renderProjectSandboxHtml(project) {
|
|
2076
|
+
if (!project) return "";
|
|
2077
|
+
const fileCount = `${project.files.length} ${project.files.length === 1 ? "file" : "files"}`;
|
|
2078
|
+
const target = project.target === "browser" ? "Browser project" : project.target === "node" ? "Node-like project" : "External project";
|
|
2079
|
+
const url = project.openUrl ?? project.fallbackUrl;
|
|
2080
|
+
const warnings = project.warnings?.length ? `<span class="ox-code-play__project-warning" title="${escapeAttribute(project.warnings.join("\n"))}">Warnings</span>` : "";
|
|
2081
|
+
const link = url ? `<a class="ox-code-play__project-link" href="${escapeAttribute(url)}" target="_blank" rel="noopener noreferrer">Open</a>` : "";
|
|
2082
|
+
return `<span class="ox-code-play__project" data-ox-project-provider="${escapeAttribute(project.provider)}">
|
|
2083
|
+
<span class="ox-code-play__project-main"><span>${escapeHtml(project.label)}</span><strong>${escapeHtml(target)}</strong></span>
|
|
2084
|
+
${project.entry ? `<span class="ox-code-play__project-entry"><span>Entry</span><strong>${escapeHtml(project.entry)}</strong></span>` : ""}
|
|
2085
|
+
<span class="ox-code-play__project-files">${escapeHtml(fileCount)}</span>
|
|
2086
|
+
${warnings}${link}
|
|
2087
|
+
</span>`;
|
|
2088
|
+
}
|
|
1802
2089
|
function renderTabs(state, panel) {
|
|
1803
2090
|
if (state.payload.ui === "compact") return "";
|
|
1804
2091
|
const viewers = state.payload.viewers;
|
|
1805
2092
|
const buttons = [
|
|
1806
|
-
viewers.stdio ? tab("stdio", "
|
|
1807
|
-
viewers.stderr ? tab("stderr", "
|
|
1808
|
-
viewers.config ? tab("config", "
|
|
1809
|
-
viewers.provenance ? tab("provenance", "
|
|
1810
|
-
viewers.timing ? tab("timing", "
|
|
2093
|
+
viewers.stdio ? tab("stdio", "Stdio", panel) : "",
|
|
2094
|
+
viewers.stderr ? tab("stderr", "Stderr", panel) : "",
|
|
2095
|
+
viewers.config ? tab("config", "Config", panel) : "",
|
|
2096
|
+
viewers.provenance ? tab("provenance", "Provenance", panel) : "",
|
|
2097
|
+
viewers.timing ? tab("timing", "Timing", panel) : ""
|
|
1811
2098
|
].filter(Boolean).join("");
|
|
1812
2099
|
return buttons ? `<div class="ox-code-play__tabs" role="tablist">${buttons}</div>` : "";
|
|
1813
2100
|
}
|
|
1814
2101
|
function tab(id, label, selected) {
|
|
1815
|
-
|
|
2102
|
+
const isSelected = selected === id;
|
|
2103
|
+
return `<button type="button" role="tab" data-ox-panel="${id}" aria-selected="${isSelected ? "true" : "false"}" tabindex="${isSelected ? "0" : "-1"}">${label}</button>`;
|
|
1816
2104
|
}
|
|
1817
2105
|
function actionButtonAttrs(action, busy) {
|
|
1818
2106
|
const state = actionBusyState(action, busy);
|
|
1819
|
-
return `${state.disabled ? " disabled" : ""}${state.hidden ? " hidden" : ""}`;
|
|
2107
|
+
return `${state.disabled ? " disabled aria-disabled=\"true\"" : ""}${state.hidden ? " hidden" : ""}`;
|
|
1820
2108
|
}
|
|
1821
2109
|
function actionBusyState(action, busy) {
|
|
1822
2110
|
if (action === "cancel") return {
|
|
@@ -1833,15 +2121,30 @@ function applyActionBusy(root, busy) {
|
|
|
1833
2121
|
const state = actionBusyState(button.dataset.oxAction ?? "", busy);
|
|
1834
2122
|
button.disabled = state.disabled;
|
|
1835
2123
|
button.hidden = state.hidden;
|
|
2124
|
+
button.setAttribute("aria-disabled", state.disabled ? "true" : "false");
|
|
1836
2125
|
}
|
|
2126
|
+
for (const widget of queryPlayWidgets(root)) widget.setAttribute("aria-busy", busy ? "true" : "false");
|
|
2127
|
+
}
|
|
2128
|
+
function renderPanel(id, label, current, preset, html) {
|
|
2129
|
+
return `<div class="ox-code-play__panel" role="tabpanel" tabindex="0" aria-label="${escapeHtml(label)}" data-panel="${id}"${hidden(current, id, preset)}>${html}</div>`;
|
|
1837
2130
|
}
|
|
1838
2131
|
function hidden(current, id, preset) {
|
|
1839
2132
|
if (preset === "compact" && (id === "stdio" || id === "stderr")) return "";
|
|
1840
2133
|
return current === id ? "" : " hidden";
|
|
1841
2134
|
}
|
|
2135
|
+
function widgetLabel(payload, definition) {
|
|
2136
|
+
const language = definition?.name ?? payload.language;
|
|
2137
|
+
return payload.title ? `${payload.title} (${language})` : `${language} Code Play`;
|
|
2138
|
+
}
|
|
2139
|
+
function queryPlayWidgets(root) {
|
|
2140
|
+
const widgets = [...root.querySelectorAll(".ox-code-play")];
|
|
2141
|
+
if (typeof HTMLElement !== "undefined" && root instanceof HTMLElement && root.matches(".ox-code-play")) widgets.unshift(root);
|
|
2142
|
+
return widgets;
|
|
2143
|
+
}
|
|
1842
2144
|
//#endregion
|
|
1843
2145
|
//#region src/hydrate.ts
|
|
1844
2146
|
var STYLE_ID = "ox-code-play-styles";
|
|
2147
|
+
var widgetId = 0;
|
|
1845
2148
|
function hydrateCodePlay(root = defaultRoot(), options = {}) {
|
|
1846
2149
|
ensureStyles();
|
|
1847
2150
|
const client = options.client ?? createCodePlayFromPayloads(root);
|
|
@@ -1854,7 +2157,7 @@ function mountCodePlay(element, options = {}) {
|
|
|
1854
2157
|
const payload = readPlayPayload(element.getAttribute("data-ox-code-play") ?? "");
|
|
1855
2158
|
if (!payload || payload.ui === "headless") return;
|
|
1856
2159
|
const client = options.client ?? createCodePlay({
|
|
1857
|
-
languages: { [payload.language]:
|
|
2160
|
+
languages: { [payload.language]: languageEnableFromPayload(payload) },
|
|
1858
2161
|
endpoints: payload.endpoints
|
|
1859
2162
|
});
|
|
1860
2163
|
const source = element.innerHTML;
|
|
@@ -1866,14 +2169,17 @@ function mountCodePlay(element, options = {}) {
|
|
|
1866
2169
|
if (sourceSlot) sourceSlot.innerHTML = source;
|
|
1867
2170
|
element.replaceChildren(widget);
|
|
1868
2171
|
element.dataset.oxCodePlayMounted = "true";
|
|
2172
|
+
element.removeAttribute("inert");
|
|
1869
2173
|
bindWidget(element, payload, client);
|
|
2174
|
+
prepareA11y(element);
|
|
1870
2175
|
}
|
|
1871
2176
|
function bindWidget(element, payload, client) {
|
|
1872
2177
|
let current = payload;
|
|
1873
2178
|
const session = client.createSession({
|
|
1874
2179
|
language: payload.language,
|
|
1875
2180
|
code: payload.code,
|
|
1876
|
-
config: payload.config
|
|
2181
|
+
config: payload.config,
|
|
2182
|
+
project: payload.project
|
|
1877
2183
|
});
|
|
1878
2184
|
const runButton = element.querySelector("[data-ox-action=\"run\"]");
|
|
1879
2185
|
const checkButton = element.querySelector("[data-ox-action=\"typecheck\"]");
|
|
@@ -1881,6 +2187,9 @@ function bindWidget(element, payload, client) {
|
|
|
1881
2187
|
runButton?.addEventListener("click", () => void run("execute"));
|
|
1882
2188
|
checkButton?.addEventListener("click", () => void run("typecheck"));
|
|
1883
2189
|
cancelButton?.addEventListener("click", () => session.cancel());
|
|
2190
|
+
element.addEventListener("keydown", (event) => {
|
|
2191
|
+
if (event.target instanceof HTMLElement && event.target.matches("[role=\"tab\"]")) handleTabKeydown(element, event);
|
|
2192
|
+
});
|
|
1884
2193
|
element.addEventListener("click", (event) => {
|
|
1885
2194
|
const target = event.target;
|
|
1886
2195
|
if (!(target instanceof HTMLElement)) return;
|
|
@@ -1899,13 +2208,20 @@ function bindWidget(element, payload, client) {
|
|
|
1899
2208
|
async function run(action) {
|
|
1900
2209
|
await runPlayAction({
|
|
1901
2210
|
action: () => action === "typecheck" ? session.typecheck() : session.run(),
|
|
1902
|
-
setBusy: (busy) =>
|
|
1903
|
-
|
|
1904
|
-
|
|
2211
|
+
setBusy: (busy) => {
|
|
2212
|
+
applyActionBusy(element, busy);
|
|
2213
|
+
if (busy) paintRunState(element, runningRunActionState(action));
|
|
2214
|
+
},
|
|
2215
|
+
onResult: (result) => paintResult(element, current, result, action),
|
|
2216
|
+
onError: (error) => {
|
|
2217
|
+
paintRunState(element, errorRunActionState(action, error));
|
|
2218
|
+
paintResult(element, current, errorResult(errorMessage(error)), action);
|
|
2219
|
+
}
|
|
1905
2220
|
});
|
|
1906
2221
|
}
|
|
1907
2222
|
}
|
|
1908
|
-
function paintResult(element, _payload, result) {
|
|
2223
|
+
function paintResult(element, _payload, result, action) {
|
|
2224
|
+
paintRunState(element, resultRunActionState(action, result));
|
|
1909
2225
|
const stdio = element.querySelector("[data-panel=\"stdio\"]");
|
|
1910
2226
|
if (stdio) {
|
|
1911
2227
|
stdio.innerHTML = `${renderDiagnosticsHtml(result)}${renderStdioHtml(result.stdio)}`;
|
|
@@ -1928,6 +2244,13 @@ function paintResult(element, _payload, result) {
|
|
|
1928
2244
|
if (stderr) stderr.innerHTML = renderStderrHtml(result);
|
|
1929
2245
|
showPanel(element, resultPanelToShow(result, Boolean(stderr)));
|
|
1930
2246
|
}
|
|
2247
|
+
function paintRunState(element, state) {
|
|
2248
|
+
const widget = element.querySelector(".ox-code-play");
|
|
2249
|
+
widget?.setAttribute("data-ox-run-state", state.phase);
|
|
2250
|
+
widget?.setAttribute("aria-busy", state.phase === "running" ? "true" : "false");
|
|
2251
|
+
const status = element.querySelector("[data-ox-status]");
|
|
2252
|
+
if (status) status.textContent = renderRunStatusText(state);
|
|
2253
|
+
}
|
|
1931
2254
|
function resultPanelToShow(result, hasStderrPanel) {
|
|
1932
2255
|
if (!hasStderrPanel) return "stdio";
|
|
1933
2256
|
if (result.diagnostics.some((diagnostic) => diagnostic.severity === "error") || result.status !== "ok" && Boolean(result.stderr)) return "stderr";
|
|
@@ -1935,7 +2258,11 @@ function resultPanelToShow(result, hasStderrPanel) {
|
|
|
1935
2258
|
}
|
|
1936
2259
|
function showPanel(element, panel) {
|
|
1937
2260
|
const compact = Boolean(element.querySelector(".ox-code-play--compact"));
|
|
1938
|
-
for (const tab of element.querySelectorAll("[data-ox-panel]"))
|
|
2261
|
+
for (const tab of element.querySelectorAll("[data-ox-panel]")) {
|
|
2262
|
+
const selected = tab.getAttribute("data-ox-panel") === panel;
|
|
2263
|
+
tab.setAttribute("aria-selected", selected ? "true" : "false");
|
|
2264
|
+
tab.tabIndex = selected ? 0 : -1;
|
|
2265
|
+
}
|
|
1939
2266
|
for (const node of element.querySelectorAll(".ox-code-play__panel")) {
|
|
1940
2267
|
const id = node.dataset.panel;
|
|
1941
2268
|
if (compact && (id === "stdio" || id === "stderr")) {
|
|
@@ -1945,6 +2272,38 @@ function showPanel(element, panel) {
|
|
|
1945
2272
|
node.hidden = id !== panel;
|
|
1946
2273
|
}
|
|
1947
2274
|
}
|
|
2275
|
+
function handleTabKeydown(element, event) {
|
|
2276
|
+
if (![
|
|
2277
|
+
"ArrowLeft",
|
|
2278
|
+
"ArrowRight",
|
|
2279
|
+
"Home",
|
|
2280
|
+
"End"
|
|
2281
|
+
].includes(event.key)) return;
|
|
2282
|
+
const tabs = [...element.querySelectorAll("[data-ox-panel]")];
|
|
2283
|
+
const current = tabs.indexOf(event.target);
|
|
2284
|
+
if (current === -1) return;
|
|
2285
|
+
event.preventDefault();
|
|
2286
|
+
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];
|
|
2287
|
+
const panel = next?.dataset.oxPanel;
|
|
2288
|
+
if (!next || !panel) return;
|
|
2289
|
+
showPanel(element, panel);
|
|
2290
|
+
next.focus();
|
|
2291
|
+
}
|
|
2292
|
+
function prepareA11y(element) {
|
|
2293
|
+
const widget = element.querySelector(".ox-code-play");
|
|
2294
|
+
if (!widget) return;
|
|
2295
|
+
if (!widget.id) widget.id = `ox-code-play-${++widgetId}`;
|
|
2296
|
+
for (const tab of element.querySelectorAll("[data-ox-panel]")) {
|
|
2297
|
+
const panelName = tab.dataset.oxPanel;
|
|
2298
|
+
const panel = panelName ? element.querySelector(`[data-panel="${panelName}"]`) : null;
|
|
2299
|
+
if (!panelName || !panel) continue;
|
|
2300
|
+
tab.id ||= `${widget.id}-${panelName}-tab`;
|
|
2301
|
+
panel.id ||= `${widget.id}-${panelName}-panel`;
|
|
2302
|
+
tab.setAttribute("aria-controls", panel.id);
|
|
2303
|
+
panel.setAttribute("aria-labelledby", tab.id);
|
|
2304
|
+
panel.removeAttribute("aria-label");
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
1948
2307
|
function readForm(form) {
|
|
1949
2308
|
const data = new FormData(form);
|
|
1950
2309
|
const config = {};
|
|
@@ -1957,7 +2316,7 @@ function createCodePlayFromPayloads(root) {
|
|
|
1957
2316
|
let endpoints;
|
|
1958
2317
|
for (const element of queryWidgets(root)) try {
|
|
1959
2318
|
const payload = decodePayload(element.getAttribute("data-ox-code-play") ?? "");
|
|
1960
|
-
languages[payload.language] =
|
|
2319
|
+
languages[payload.language] = languageEnableFromPayload(payload);
|
|
1961
2320
|
endpoints = payload.endpoints ?? endpoints;
|
|
1962
2321
|
} catch {}
|
|
1963
2322
|
return createCodePlay({
|
|
@@ -1965,6 +2324,15 @@ function createCodePlayFromPayloads(root) {
|
|
|
1965
2324
|
endpoints
|
|
1966
2325
|
});
|
|
1967
2326
|
}
|
|
2327
|
+
function languageEnableFromPayload(payload) {
|
|
2328
|
+
const enable = {
|
|
2329
|
+
execute: payload.capabilities.execute,
|
|
2330
|
+
typecheck: payload.capabilities.typecheck,
|
|
2331
|
+
config: payload.config
|
|
2332
|
+
};
|
|
2333
|
+
if (payload.endpoint) enable.endpoint = payload.endpoint;
|
|
2334
|
+
return enable;
|
|
2335
|
+
}
|
|
1968
2336
|
function queryWidgets(root) {
|
|
1969
2337
|
return [...root.querySelectorAll("[data-ox-code-play]")];
|
|
1970
2338
|
}
|