mirror of
https://github.com/astral-sh/ruff.git
synced 2025-11-28 23:38:00 +00:00
[red-knot] Add run panel (#17002)
## Summary This PR adds a new secondary panel to the red knot playground that allows running the python code (current file) with [pyodide](https://pyodide.org/en/stable/index.html) (currently Python 3.12 only). ## Test Plan https://github.com/user-attachments/assets/7bda8ef7-19fb-4c2f-8e62-8e49a1416be1
This commit is contained in:
parent
338fed98a4
commit
43ca85a351
8 changed files with 414 additions and 22 deletions
|
|
@ -19,6 +19,7 @@
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
"lz-string": "^1.5.0",
|
"lz-string": "^1.5.0",
|
||||||
"monaco-editor": "^0.52.2",
|
"monaco-editor": "^0.52.2",
|
||||||
|
"pyodide": "^0.27.4",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-resizable-panels": "^2.1.7",
|
"react-resizable-panels": "^2.1.7",
|
||||||
|
|
@ -31,5 +32,8 @@
|
||||||
"react": "$react",
|
"react": "$react",
|
||||||
"react-dom": "$react-dom"
|
"react-dom": "$react-dom"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"vite-plugin-static-copy": "^2.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ import {
|
||||||
Theme,
|
Theme,
|
||||||
VerticalResizeHandle,
|
VerticalResizeHandle,
|
||||||
} from "shared";
|
} from "shared";
|
||||||
import { Diagnostic, Workspace } from "red_knot_wasm";
|
import type { Diagnostic, Workspace } from "red_knot_wasm";
|
||||||
import { Panel, PanelGroup } from "react-resizable-panels";
|
import { Panel, PanelGroup } from "react-resizable-panels";
|
||||||
import { Files } from "./Files";
|
import { Files } from "./Files";
|
||||||
import SecondarySideBar from "./SecondarySideBar";
|
import SecondarySideBar from "./SecondarySideBar";
|
||||||
|
|
@ -181,6 +181,7 @@ export default function Chrome({
|
||||||
minSize={10}
|
minSize={10}
|
||||||
>
|
>
|
||||||
<SecondaryPanel
|
<SecondaryPanel
|
||||||
|
files={files}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
tool={secondaryTool}
|
tool={secondaryTool}
|
||||||
result={checkResult.secondary}
|
result={checkResult.secondary}
|
||||||
|
|
@ -247,6 +248,13 @@ function useCheckResult(
|
||||||
content: workspace.tokens(currentHandle),
|
content: workspace.tokens(currentHandle),
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "Run":
|
||||||
|
secondary = {
|
||||||
|
status: "ok",
|
||||||
|
content: "",
|
||||||
|
};
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
secondary = {
|
secondary = {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,14 @@
|
||||||
import MonacoEditor from "@monaco-editor/react";
|
import MonacoEditor from "@monaco-editor/react";
|
||||||
import { Theme } from "shared";
|
import { AstralButton, Theme } from "shared";
|
||||||
|
import { ReadonlyFiles } from "../Playground";
|
||||||
|
import { Suspense, use, useState } from "react";
|
||||||
|
import { loadPyodide, PyodideInterface } from "pyodide";
|
||||||
|
import classNames from "classnames";
|
||||||
|
|
||||||
export enum SecondaryTool {
|
export enum SecondaryTool {
|
||||||
"AST" = "AST",
|
"AST" = "AST",
|
||||||
"Tokens" = "Tokens",
|
"Tokens" = "Tokens",
|
||||||
|
"Run" = "Run",
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SecondaryPanelResult =
|
export type SecondaryPanelResult =
|
||||||
|
|
@ -12,6 +17,7 @@ export type SecondaryPanelResult =
|
||||||
| { status: "error"; error: string };
|
| { status: "error"; error: string };
|
||||||
|
|
||||||
export interface SecondaryPanelProps {
|
export interface SecondaryPanelProps {
|
||||||
|
files: ReadonlyFiles;
|
||||||
tool: SecondaryTool;
|
tool: SecondaryTool;
|
||||||
result: SecondaryPanelResult;
|
result: SecondaryPanelResult;
|
||||||
theme: Theme;
|
theme: Theme;
|
||||||
|
|
@ -20,23 +26,32 @@ export interface SecondaryPanelProps {
|
||||||
export default function SecondaryPanel({
|
export default function SecondaryPanel({
|
||||||
tool,
|
tool,
|
||||||
result,
|
result,
|
||||||
|
files,
|
||||||
theme,
|
theme,
|
||||||
}: SecondaryPanelProps) {
|
}: SecondaryPanelProps) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full">
|
<div className="flex flex-col h-full">
|
||||||
<div className="flex-grow">
|
<Content
|
||||||
<Content tool={tool} result={result} theme={theme} />
|
tool={tool}
|
||||||
</div>
|
result={result}
|
||||||
|
theme={theme}
|
||||||
|
files={files}
|
||||||
|
revision={files.revision}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Content({
|
function Content({
|
||||||
|
files,
|
||||||
tool,
|
tool,
|
||||||
result,
|
result,
|
||||||
theme,
|
theme,
|
||||||
|
revision,
|
||||||
}: {
|
}: {
|
||||||
tool: SecondaryTool;
|
tool: SecondaryTool;
|
||||||
|
files: ReadonlyFiles;
|
||||||
|
revision: number;
|
||||||
result: SecondaryPanelResult;
|
result: SecondaryPanelResult;
|
||||||
theme: Theme;
|
theme: Theme;
|
||||||
}) {
|
}) {
|
||||||
|
|
@ -54,25 +69,124 @@ function Content({
|
||||||
case "Tokens":
|
case "Tokens":
|
||||||
language = "RustPythonTokens";
|
language = "RustPythonTokens";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case "Run":
|
||||||
|
return <Run theme={theme} files={files} key={`${revision}`} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MonacoEditor
|
<div className="flex-grow">
|
||||||
options={{
|
<MonacoEditor
|
||||||
readOnly: true,
|
options={{
|
||||||
minimap: { enabled: false },
|
readOnly: true,
|
||||||
fontSize: 14,
|
minimap: { enabled: false },
|
||||||
roundedSelection: false,
|
fontSize: 14,
|
||||||
scrollBeyondLastLine: false,
|
roundedSelection: false,
|
||||||
contextmenu: false,
|
scrollBeyondLastLine: false,
|
||||||
}}
|
contextmenu: false,
|
||||||
language={language}
|
}}
|
||||||
value={result.content}
|
language={language}
|
||||||
theme={theme === "light" ? "Ayu-Light" : "Ayu-Dark"}
|
value={result.content}
|
||||||
/>
|
theme={theme === "light" ? "Ayu-Light" : "Ayu-Dark"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
case "error":
|
case "error":
|
||||||
return <code className="whitespace-pre-wrap">{result.error}</code>;
|
return (
|
||||||
|
<div className="flex-grow">
|
||||||
|
<code className="whitespace-pre-wrap">{result.error}</code>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let pyodidePromise: Promise<PyodideInterface> | null = null;
|
||||||
|
|
||||||
|
function Run({ files, theme }: { files: ReadonlyFiles; theme: Theme }) {
|
||||||
|
if (pyodidePromise == null) {
|
||||||
|
pyodidePromise = loadPyodide();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Suspense fallback={<div className="text-center">Loading</div>}>
|
||||||
|
<RunWithPyiodide
|
||||||
|
theme={theme}
|
||||||
|
files={files}
|
||||||
|
pyodidePromise={pyodidePromise}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function RunWithPyiodide({
|
||||||
|
files,
|
||||||
|
pyodidePromise,
|
||||||
|
theme,
|
||||||
|
}: {
|
||||||
|
files: ReadonlyFiles;
|
||||||
|
theme: Theme;
|
||||||
|
pyodidePromise: Promise<PyodideInterface>;
|
||||||
|
}) {
|
||||||
|
const pyodide = use(pyodidePromise);
|
||||||
|
|
||||||
|
const [output, setOutput] = useState<string | null>(null);
|
||||||
|
|
||||||
|
if (output == null) {
|
||||||
|
const handleRun = () => {
|
||||||
|
let stdout = "";
|
||||||
|
|
||||||
|
pyodide.setStdout({
|
||||||
|
batched(output) {
|
||||||
|
stdout += output + "\n";
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const main = files.selected == null ? "" : files.contents[files.selected];
|
||||||
|
|
||||||
|
for (const file of files.index) {
|
||||||
|
pyodide.FS.writeFile(file.name, files.contents[file.id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Patch up reveal types
|
||||||
|
pyodide.runPython(`
|
||||||
|
import builtins
|
||||||
|
builtins.reveal_type = print`);
|
||||||
|
|
||||||
|
pyodide.runPython(main);
|
||||||
|
setOutput(stdout);
|
||||||
|
} catch (e) {
|
||||||
|
setOutput(`Failed to run Python script: ${e}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<div className="flex flex-auto flex-col justify-center items-center">
|
||||||
|
<AstralButton
|
||||||
|
type="button"
|
||||||
|
className="flex-none leading-6 py-1.5 px-3 shadow-xs"
|
||||||
|
onClick={handleRun}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
className="inset-0 flex items-center justify-center"
|
||||||
|
aria-hidden="false"
|
||||||
|
>
|
||||||
|
Run...
|
||||||
|
</span>
|
||||||
|
</AstralButton>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<pre
|
||||||
|
className={classNames(
|
||||||
|
"m-2",
|
||||||
|
"text-sm",
|
||||||
|
"whitespace-pre",
|
||||||
|
theme === "dark" ? "text-white" : null,
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{output}
|
||||||
|
</pre>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { SecondaryTool } from "./SecondaryPanel";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
selected: SecondaryTool | null;
|
selected: SecondaryTool | null;
|
||||||
|
|
||||||
onSelected(tool: SecondaryTool): void;
|
onSelected(tool: SecondaryTool): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -26,6 +27,14 @@ export default function SecondarySideBar({ selected, onSelected }: Props) {
|
||||||
>
|
>
|
||||||
<Icons.Token />
|
<Icons.Token />
|
||||||
</SideBarEntry>
|
</SideBarEntry>
|
||||||
|
<SideBarEntry
|
||||||
|
title="Run"
|
||||||
|
position={"right"}
|
||||||
|
selected={selected === SecondaryTool.Run}
|
||||||
|
onClick={() => onSelected(SecondaryTool.Run)}
|
||||||
|
>
|
||||||
|
<Icons.Run />
|
||||||
|
</SideBarEntry>
|
||||||
</SideBar>
|
</SideBar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ import {
|
||||||
import { ErrorMessage, Header, setupMonaco, useTheme } from "shared";
|
import { ErrorMessage, Header, setupMonaco, useTheme } from "shared";
|
||||||
import { FileHandle, Workspace } from "red_knot_wasm";
|
import { FileHandle, Workspace } from "red_knot_wasm";
|
||||||
import { persist, persistLocal, restore } from "./Editor/persist";
|
import { persist, persistLocal, restore } from "./Editor/persist";
|
||||||
import initRedKnot from "../red_knot_wasm";
|
|
||||||
import { loader } from "@monaco-editor/react";
|
import { loader } from "@monaco-editor/react";
|
||||||
import knotSchema from "../../../knot.schema.json";
|
import knotSchema from "../../../knot.schema.json";
|
||||||
import Chrome, { formatError } from "./Editor/Chrome";
|
import Chrome, { formatError } from "./Editor/Chrome";
|
||||||
|
|
@ -399,7 +398,8 @@ export interface InitializedPlayground {
|
||||||
|
|
||||||
// Run once during startup. Initializes monaco, loads the wasm file, and restores the previous editor state.
|
// Run once during startup. Initializes monaco, loads the wasm file, and restores the previous editor state.
|
||||||
async function startPlayground(): Promise<InitializedPlayground> {
|
async function startPlayground(): Promise<InitializedPlayground> {
|
||||||
await initRedKnot();
|
const red_knot = await import("../red_knot_wasm");
|
||||||
|
await red_knot.default();
|
||||||
const monaco = await loader.init();
|
const monaco = await loader.init();
|
||||||
|
|
||||||
setupMonaco(monaco, {
|
setupMonaco(monaco, {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,31 @@
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import tailwindcss from "@tailwindcss/vite";
|
import tailwindcss from "@tailwindcss/vite";
|
||||||
import react from "@vitejs/plugin-react-swc";
|
import react from "@vitejs/plugin-react-swc";
|
||||||
|
import { dirname, join } from "path";
|
||||||
|
import { fileURLToPath } from "url";
|
||||||
|
import { viteStaticCopy } from "vite-plugin-static-copy";
|
||||||
|
|
||||||
|
const PYODIDE_EXCLUDE = [
|
||||||
|
"!**/*.{md,html}",
|
||||||
|
"!**/*.d.ts",
|
||||||
|
"!**/*.whl",
|
||||||
|
"!**/node_modules",
|
||||||
|
];
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [react(), tailwindcss()],
|
plugins: [react(), tailwindcss(), viteStaticCopyPyodide()],
|
||||||
|
optimizeDeps: { exclude: ["pyodide"] },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export function viteStaticCopyPyodide() {
|
||||||
|
const pyodideDir = dirname(fileURLToPath(import.meta.resolve("pyodide")));
|
||||||
|
return viteStaticCopy({
|
||||||
|
targets: [
|
||||||
|
{
|
||||||
|
src: [join(pyodideDir, "*"), ...PYODIDE_EXCLUDE],
|
||||||
|
dest: "assets",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
||||||
209
playground/package-lock.json
generated
209
playground/package-lock.json
generated
|
|
@ -38,12 +38,16 @@
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
"lz-string": "^1.5.0",
|
"lz-string": "^1.5.0",
|
||||||
"monaco-editor": "^0.52.2",
|
"monaco-editor": "^0.52.2",
|
||||||
|
"pyodide": "^0.27.4",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"react-resizable-panels": "^2.1.7",
|
"react-resizable-panels": "^2.1.7",
|
||||||
"red_knot_wasm": "file:red_knot_wasm",
|
"red_knot_wasm": "file:red_knot_wasm",
|
||||||
"shared": "0.0.0",
|
"shared": "0.0.0",
|
||||||
"smol-toml": "^1.3.1"
|
"smol-toml": "^1.3.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"vite-plugin-static-copy": "^2.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"knot/red_knot_wasm": {
|
"knot/red_knot_wasm": {
|
||||||
|
|
@ -1806,6 +1810,20 @@
|
||||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/anymatch": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||||
|
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"normalize-path": "^3.0.0",
|
||||||
|
"picomatch": "^2.0.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/argparse": {
|
"node_modules/argparse": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||||
|
|
@ -2014,6 +2032,19 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/binary-extensions": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/binary-install": {
|
"node_modules/binary-install": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/binary-install/-/binary-install-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/binary-install/-/binary-install-1.1.0.tgz",
|
||||||
|
|
@ -2130,6 +2161,44 @@
|
||||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/chokidar": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"anymatch": "~3.1.2",
|
||||||
|
"braces": "~3.0.2",
|
||||||
|
"glob-parent": "~5.1.2",
|
||||||
|
"is-binary-path": "~2.1.0",
|
||||||
|
"is-glob": "~4.0.1",
|
||||||
|
"normalize-path": "~3.0.0",
|
||||||
|
"readdirp": "~3.6.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8.10.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://paulmillr.com/funding/"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "~2.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/chokidar/node_modules/glob-parent": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"is-glob": "^4.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/chownr": {
|
"node_modules/chownr": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
|
||||||
|
|
@ -3065,6 +3134,21 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/fs-extra": {
|
||||||
|
"version": "11.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
|
||||||
|
"integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"graceful-fs": "^4.2.0",
|
||||||
|
"jsonfile": "^6.0.1",
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fs-minipass": {
|
"node_modules/fs-minipass": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
|
||||||
|
|
@ -3522,6 +3606,19 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-binary-path": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"binary-extensions": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/is-boolean-object": {
|
"node_modules/is-boolean-object": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
|
||||||
|
|
@ -3942,6 +4039,19 @@
|
||||||
"json5": "lib/cli.js"
|
"json5": "lib/cli.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jsonfile": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"graceful-fs": "^4.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jsx-ast-utils": {
|
"node_modules/jsx-ast-utils": {
|
||||||
"version": "3.3.5",
|
"version": "3.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
|
||||||
|
|
@ -4416,6 +4526,16 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/normalize-path": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/object-assign": {
|
"node_modules/object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
|
|
@ -4617,6 +4737,19 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/p-map": {
|
||||||
|
"version": "7.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz",
|
||||||
|
"integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/parent-module": {
|
"node_modules/parent-module": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||||
|
|
@ -4774,6 +4907,18 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pyodide": {
|
||||||
|
"version": "0.27.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/pyodide/-/pyodide-0.27.4.tgz",
|
||||||
|
"integrity": "sha512-2y3ySHCBmyzYDUlB939SaU3n7RxYQxwnGHgdakW/CPrNFX2L9fC+4nfJWQJH8a0ruQa8bBZSKCImMt/cq15RiQ==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"ws": "^8.5.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/queue-microtask": {
|
"node_modules/queue-microtask": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||||
|
|
@ -4833,6 +4978,19 @@
|
||||||
"react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
"react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/readdirp": {
|
||||||
|
"version": "3.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
|
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"picomatch": "^2.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/red_knot_wasm": {
|
"node_modules/red_knot_wasm": {
|
||||||
"resolved": "knot/red_knot_wasm",
|
"resolved": "knot/red_knot_wasm",
|
||||||
"link": true
|
"link": true
|
||||||
|
|
@ -5630,6 +5788,16 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/universalify": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/uri-js": {
|
"node_modules/uri-js": {
|
||||||
"version": "4.4.1",
|
"version": "4.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||||
|
|
@ -5712,6 +5880,26 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vite-plugin-static-copy": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-LLKwhhHetGaCnWz4mas4qqjjguDka6/6b4+SeIohRroj8aCE7QTfiZECfPecslFQkWZ3HdQuq5kOPmWZjNYlKA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"chokidar": "^3.5.3",
|
||||||
|
"fast-glob": "^3.2.11",
|
||||||
|
"fs-extra": "^11.1.0",
|
||||||
|
"p-map": "^7.0.3",
|
||||||
|
"picocolors": "^1.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.0.0 || >=20.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vite": "^5.0.0 || ^6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/wasm-pack": {
|
"node_modules/wasm-pack": {
|
||||||
"version": "0.13.1",
|
"version": "0.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/wasm-pack/-/wasm-pack-0.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/wasm-pack/-/wasm-pack-0.13.1.tgz",
|
||||||
|
|
@ -5848,6 +6036,27 @@
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/ws": {
|
||||||
|
"version": "8.18.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz",
|
||||||
|
"integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": ">=5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/yallist": {
|
"node_modules/yallist": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,31 @@ export function File({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function Run({
|
||||||
|
width = 24,
|
||||||
|
height = 24,
|
||||||
|
}: {
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M4.00024 2V14.4805L12.9149 8.24024L4.00024 2ZM11.1812 8.24024L4.99524 12.5684V3.91209L11.1812 8.24024Z"
|
||||||
|
fill="#ffffff"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function Settings() {
|
export function Settings() {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue