From 24a6678f2f0832e6b0e9fd4e604579cbe1ba2c8c Mon Sep 17 00:00:00 2001
From: Elliot Braem <16282460+elliotBraem@users.noreply.github.com>
Date: Wed, 29 May 2024 03:43:24 -0500
Subject: [PATCH] adds editor
---
gateway/package.json | 1 +
gateway/src/App.js | 4 +
gateway/src/components/Editor/Editor.js | 912 +++++++++++++++++++
gateway/src/components/Editor/FileTab.js | 172 ++++
gateway/src/components/Editor/OpenModal.js | 62 ++
gateway/src/components/Editor/RenameModal.js | 49 +
gateway/src/stores/bos-loader.js | 9 +
gateway/yarn.lock | 19 +
pnpm-lock.yaml | 37 +
widget/page/sandbox.jsx | 59 +-
10 files changed, 1290 insertions(+), 34 deletions(-)
create mode 100644 gateway/src/components/Editor/Editor.js
create mode 100644 gateway/src/components/Editor/FileTab.js
create mode 100644 gateway/src/components/Editor/OpenModal.js
create mode 100644 gateway/src/components/Editor/RenameModal.js
create mode 100644 gateway/src/stores/bos-loader.js
diff --git a/gateway/package.json b/gateway/package.json
index 7df246d..8d4f259 100644
--- a/gateway/package.json
+++ b/gateway/package.json
@@ -17,6 +17,7 @@
"dependencies": {
"@braintree/sanitize-url": "^6.0.2",
"@livepeer/react": "^4.1.0",
+ "@monaco-editor/react": "^4.6.0",
"@playwright/test": "^1.38.1",
"@radix-ui/react-popover": "^1.0.7",
"big.js": "^6.1.1",
diff --git a/gateway/src/App.js b/gateway/src/App.js
index 7896750..79ac3a9 100644
--- a/gateway/src/App.js
+++ b/gateway/src/App.js
@@ -18,6 +18,7 @@ import { BroadcastComponent } from "./components/Broadcast/Broadcast";
import { VideoPlayer } from "./components/Player/Player";
import useRedirectMap from "./useRedirectMap";
+import Editor from "./components/Editor/Editor";
function Viewer({ widgetSrc, code, initialProps }) {
const { components: redirectMap } = useRedirectMap();
@@ -121,6 +122,9 @@ function App(props) {
"Broadcast.ApiKey": (props) => {
return ;
},
+ "Editor": (props) => {
+ return ;
+ }
},
features: {
enableComponentSrcDataKey: true,
diff --git a/gateway/src/components/Editor/Editor.js b/gateway/src/components/Editor/Editor.js
new file mode 100644
index 0000000..e9ebf55
--- /dev/null
+++ b/gateway/src/components/Editor/Editor.js
@@ -0,0 +1,912 @@
+import React, { useCallback, useEffect, useMemo, useState } from "react";
+import ls from "local-storage";
+import prettier from "prettier";
+import parserBabel from "prettier/parser-babel";
+import { useNavigate, useParams } from "react-router-dom";
+import Editor, { useMonaco } from "@monaco-editor/react";
+import {
+ Widget,
+ useCache,
+ useNear,
+ CommitButton,
+ useAccountId,
+} from "near-social-vm";
+import { Nav, OverlayTrigger, Tooltip } from "react-bootstrap";
+import RenameModal from "./RenameModal";
+import OpenModal from "./OpenModal";
+import {
+ FileTab,
+ Filetype,
+ StorageDomain,
+ StorageType,
+ toPath,
+} from "./FileTab";
+import vmTypesDeclaration from "raw-loader!near-social-vm-types";
+import styled from "styled-components";
+import { useBosLoaderStore } from "../../stores/bos-loader";
+
+const LsKey = "social.near:v01:";
+const EditorLayoutKey = LsKey + "editorLayout:";
+const WidgetPropsKey = LsKey + "widgetProps:";
+const EditorUncommittedPreviewsKey = LsKey + "editorUncommittedPreviews:";
+
+const DefaultEditorCode = "return
Hello World
;";
+
+const NavPill = styled.button`
+ all: unset;
+
+ display: flex;
+ padding: 10px 20px;
+ justify-content: center;
+ align-items: center;
+ gap: 10px;
+
+ border-radius: 8px;
+ background: var(--bg-2, #23242b);
+
+ color: var(--white-100, #fff);
+
+ font-size: 0.875rem;
+ font-style: normal;
+ font-weight: 500;
+ line-height: normal;
+`;
+
+const Container = styled.div`
+ background: #000000;
+ color: #fff;
+
+ min-height: 100%;
+`;
+
+const CustomSearch = styled.div`
+ .form-control {
+ background: #23242b;
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ color: #fff;
+ }
+ input::placeholder {
+ color: #c7c7c7;
+ }
+`;
+
+const Button = styled.button`
+ all: unset;
+
+ display: flex;
+ padding: 10px 20px;
+ justify-content: center;
+ align-items: center;
+ gap: 4px;
+
+ /* Other/Button_text */
+ font-family: Satoshi, sans-serif;
+ font-size: 0.875rem;
+ font-style: normal;
+ font-weight: 500;
+ line-height: normal;
+
+ border-radius: 8px;
+ background: #51b6ff;
+ color: black;
+`;
+
+const Tab = {
+ Editor: "Editor",
+ Props: "Props",
+ Metadata: "Metadata",
+ Widget: "Widget",
+};
+
+const Layout = {
+ Tabs: "Tabs",
+ Split: "Split",
+};
+
+export default function EditorPage(props) {
+ const { widgetSrc } = useParams();
+ const navigate = useNavigate();
+ const redirectMapStore = useBosLoaderStore();
+ const setWidgetSrc = props.setWidgetSrc;
+
+ const [loading, setLoading] = useState(false);
+ const [code, setCode] = useState(undefined);
+ const [path, setPath] = useState(undefined);
+ const [files, setFiles] = useState(undefined);
+ const [lastPath, setLastPath] = useState(undefined);
+ const [showRenameModal, setShowRenameModal] = useState(false);
+ const [showOpenModal, setShowOpenModal] = useState(false);
+ const [allSaved, setAllSaved] = useState({});
+ const [uncommittedPreviews] = useState(
+ ls.get(EditorUncommittedPreviewsKey) ?? false,
+ );
+ const [widgetConfig, setWidgetConfig] = useState(undefined);
+
+ const [renderCode, setRenderCode] = useState(code);
+ const [widgetProps, setWidgetProps] = useState(
+ ls.get(WidgetPropsKey) || "{}",
+ );
+ const [parsedWidgetProps, setParsedWidgetProps] = useState({});
+ const [propsError, setPropsError] = useState(null);
+ const [metadata, setMetadata] = useState(undefined);
+ const near = useNear();
+ const cache = useCache();
+ const accountId = useAccountId();
+
+ const [tab, setTab] = useState(Tab.Editor);
+ const [layout, setLayoutState] = useState(
+ ls.get(EditorLayoutKey) || Layout.Tabs,
+ );
+ const [previewKey, setPreviewKey] = useState("");
+
+ const monaco = useMonaco();
+
+ useEffect(() => {
+ if (monaco) {
+ monaco.languages.typescript.javascriptDefaults.setEagerModelSync(true);
+ monaco.languages.typescript.javascriptDefaults.addExtraLib(
+ vmTypesDeclaration,
+ );
+ }
+ }, [monaco]);
+
+ const setLayout = useCallback(
+ (layout) => {
+ ls.set(EditorLayoutKey, layout);
+ setLayoutState(layout);
+ },
+ [setLayoutState],
+ );
+
+ useEffect(() => {
+ setWidgetSrc({
+ edit: null,
+ view: widgetSrc,
+ });
+ }, [widgetSrc, setWidgetSrc]);
+
+ const updateCode = useCallback(
+ (path, code) => {
+ cache.localStorageSet(
+ StorageDomain,
+ {
+ path,
+ type: StorageType.Code,
+ },
+ {
+ code,
+ time: Date.now(),
+ },
+ );
+ setCode(code);
+ },
+ [cache, setCode],
+ );
+
+ useEffect(() => {
+ ls.set(WidgetPropsKey, widgetProps);
+ try {
+ const parsedWidgetProps = JSON.parse(widgetProps);
+ setParsedWidgetProps(parsedWidgetProps);
+ setPropsError(null);
+ } catch (e) {
+ setParsedWidgetProps({});
+ setPropsError(e.message);
+ }
+ }, [widgetProps]);
+
+ const removeFromFiles = useCallback(
+ (path) => {
+ path = JSON.stringify(path);
+ setFiles((files) =>
+ files.filter((file) => JSON.stringify(file) !== path),
+ );
+ setLastPath(path);
+ },
+ [setFiles, setLastPath],
+ );
+
+ const addToFiles = useCallback(
+ (path) => {
+ const jpath = JSON.stringify(path);
+ setFiles((files) => {
+ const newFiles = [...files];
+ if (!files.find((file) => JSON.stringify(file) === jpath)) {
+ newFiles.push(path);
+ }
+ return newFiles;
+ });
+ setLastPath(path);
+ },
+ [setFiles, setLastPath],
+ );
+
+ useEffect(() => {
+ if (files && lastPath) {
+ cache.localStorageSet(
+ StorageDomain,
+ {
+ type: StorageType.Files,
+ },
+ { files, lastPath },
+ );
+ }
+ }, [files, lastPath, cache]);
+
+ const openFile = useCallback(
+ (path, code) => {
+ setPath(path);
+ addToFiles(path);
+ setMetadata(undefined);
+ setRenderCode(null);
+ if (code !== undefined) {
+ updateCode(path, code);
+ } else {
+ setLoading(true);
+ cache
+ .asyncLocalStorageGet(StorageDomain, {
+ path,
+ type: StorageType.Code,
+ })
+ .then(({ code }) => {
+ updateCode(path, code);
+ })
+ .finally(() => {
+ setLoading(false);
+ });
+ }
+ },
+ [updateCode, addToFiles],
+ );
+
+ const updateSaved = useCallback((jp, saved, localCode) => {
+ setAllSaved((allSaved) => {
+ return Object.assign({}, allSaved, { [jp]: saved || localCode });
+ });
+ }, []);
+
+ const loadFile = useCallback(
+ (nameOrPath) => {
+ if (!near) {
+ return;
+ }
+ const widgetSrc =
+ nameOrPath.indexOf("/") >= 0
+ ? nameOrPath
+ : `${accountId}/widget/${nameOrPath}`;
+ const c = () => {
+ const code = cache.socialGet(
+ near,
+ widgetSrc,
+ false,
+ undefined,
+ undefined,
+ c,
+ );
+ if (code) {
+ openFile(toPath(Filetype.Widget, widgetSrc), code);
+ }
+ };
+
+ c();
+ },
+ [accountId, openFile, toPath, near, cache],
+ );
+
+ const generateNewName = useCallback(
+ (type) => {
+ for (let i = 0; ; i++) {
+ const name = `Draft-${i}`;
+ const path = toPath(type, name);
+ path.unnamed = true;
+ const jPath = JSON.stringify(path);
+ if (!files?.find((file) => JSON.stringify(file) === jPath)) {
+ return path;
+ }
+ }
+ },
+ [toPath, files],
+ );
+
+ const createFile = useCallback(
+ (type) => {
+ const path = generateNewName(type);
+ openFile(path, DefaultEditorCode);
+ },
+ [generateNewName, openFile],
+ );
+
+ const renameFile = useCallback(
+ (newName, code) => {
+ const newPath = toPath(path.type, newName);
+ const jNewPath = JSON.stringify(newPath);
+ const jPath = JSON.stringify(path);
+ setFiles((files) => {
+ const newFiles = files.filter(
+ (file) => JSON.stringify(file) !== jNewPath,
+ );
+ const i = newFiles.findIndex((file) => JSON.stringify(file) === jPath);
+ if (i >= 0) {
+ newFiles[i] = newPath;
+ }
+ return newFiles;
+ });
+ setLastPath(newPath);
+ setPath(newPath);
+ updateCode(newPath, code);
+ },
+ [path, toPath, updateCode],
+ );
+
+ useEffect(() => {
+ cache
+ .asyncLocalStorageGet(StorageDomain, { type: StorageType.Files })
+ .then((value) => {
+ const { files, lastPath } = value || {};
+ setFiles(files || []);
+ setLastPath(lastPath);
+ });
+ }, [cache]);
+
+ useEffect(() => {
+ if (!near || !files) {
+ return;
+ }
+ if (widgetSrc) {
+ if (widgetSrc === "new") {
+ createFile(Filetype.Widget);
+ } else {
+ loadFile(widgetSrc);
+ }
+ navigate.replace(`/edit/`);
+ } else if (path === undefined) {
+ if (files.length === 0) {
+ createFile(Filetype.Widget);
+ } else {
+ openFile(lastPath, undefined);
+ }
+ }
+ }, [near, createFile, lastPath, files, path, widgetSrc, openFile, loadFile]);
+
+ const reformat = useCallback(
+ (path, code) => {
+ try {
+ const formattedCode = prettier.format(code, {
+ parser: "babel",
+ plugins: [parserBabel],
+ });
+ updateCode(path, formattedCode);
+ } catch (e) {
+ console.log(e);
+ }
+ },
+ [updateCode],
+ );
+
+ const reformatProps = useCallback(
+ (props) => {
+ try {
+ const formattedProps = JSON.stringify(JSON.parse(props), null, 2);
+ setWidgetProps(formattedProps);
+ } catch (e) {
+ console.log(e);
+ }
+ },
+ [setWidgetProps],
+ );
+
+ const closeCommitted = useCallback(
+ (path, allSaved) => {
+ setFiles((files) => {
+ files = files.filter((file) => allSaved[JSON.stringify(file)] !== true);
+ if (allSaved[JSON.stringify(path)] === true) {
+ if (files.length > 0) {
+ openFile(files[files.length - 1], undefined);
+ } else {
+ createFile(Filetype.Widget);
+ }
+ }
+ return files;
+ });
+ },
+ [openFile, createFile],
+ );
+
+ const layoutClass = layout === Layout.Split ? "w-50" : "";
+
+ const onLayoutChange = useCallback(
+ (e) => {
+ const layout = e.target.value;
+ if (layout === Layout.Split && tab === Tab.Widget) {
+ setTab(Tab.Editor);
+ }
+ setLayout(layout);
+ },
+ [setLayout, tab, setTab],
+ );
+
+ const pathToSrc = useCallback(
+ (path) => {
+ return `${accountId}/${path?.type}/${path?.name}`;
+ },
+ [accountId],
+ );
+
+ const generateWidgetConfig = useCallback(
+ (uncommittedPreviews) => {
+ return uncommittedPreviews
+ ? {
+ redirectMap: Object.fromEntries(
+ Object.entries(allSaved)
+ .filter(([jpath, code]) => code !== true)
+ .map(([jpath, code]) => {
+ const path = JSON.parse(jpath);
+ return [
+ pathToSrc(path),
+ {
+ code,
+ },
+ ];
+ }),
+ ),
+ }
+ : undefined;
+ },
+ [allSaved, pathToSrc],
+ );
+
+ const widgetName = path?.name;
+
+ const commitButton = (
+
+ Save Widget
+
+ );
+
+ const widgetPath = `${accountId}/${path?.type}/${path?.name}`;
+ const jpath = JSON.stringify(path);
+
+ const renderPreview = (code) => {
+ setWidgetConfig(generateWidgetConfig(uncommittedPreviews));
+ setRenderCode(code);
+ setPreviewKey(`preview-${Date.now()}`);
+ };
+
+ return (
+
+ renameFile(newName, code)}
+ onHide={() => setShowRenameModal(false)}
+ />
+ loadFile(newName)}
+ onNew={(newName) =>
+ newName
+ ? openFile(toPath(Filetype.Widget, newName), DefaultEditorCode)
+ : createFile(Filetype.Widget)
+ }
+ onHide={() => setShowOpenModal(false)}
+ />
+
+
+ {props.widgets.editorComponentSearch && (
+
+ ({
+ extraButtons: ({ widgetName, widgetPath, onHide }) => (
+
+ {`Open "${widgetName}" component in the editor`}
+
+ }
+ >
+
+
+ ),
+ }),
+ [loadFile],
+ )}
+ />
+
+ )}
+
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+ {props.widgets.widgetMetadataEditor && (
+ -
+
+
+ )}
+ {layout === Layout.Tabs && (
+ -
+
+
+ )}
+
+
+
+
+ updateCode(path, code)}
+ wrapperProps={{
+ onBlur: () => reformat(path, code),
+ }}
+ />
+
+
+
+ {!path?.unnamed && commitButton}
+
+
+
+
+
+ setWidgetProps(props)}
+ wrapperProps={{
+ onBlur: () => reformatProps(widgetProps),
+ }}
+ />
+
+
^^ Props for debugging (in JSON)
+ {propsError && (
+
{propsError}
+ )}
+
+
+
+ ({
+ widgetPath,
+ onChange: setMetadata,
+ }),
+ [widgetPath],
+ )}
+ />
+
+
{commitButton}
+
+
+
+
+
+
+ {renderCode ? (
+
+ ) : (
+ 'Click "Render preview" button to render the widget'
+ )}
+
+
+
+
+
+
+
+
+ ({ metadata, accountId, widgetName }),
+ [metadata, accountId, widgetName],
+ )}
+ />
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/gateway/src/components/Editor/FileTab.js b/gateway/src/components/Editor/FileTab.js
new file mode 100644
index 0000000..db5e861
--- /dev/null
+++ b/gateway/src/components/Editor/FileTab.js
@@ -0,0 +1,172 @@
+import { Nav } from "react-bootstrap";
+import React, { useEffect, useState } from "react";
+import { useAccountId, useCache, useNear } from "near-social-vm";
+import styled from "styled-components";
+
+export const Filetype = {
+ Widget: "widget",
+ Module: "module",
+};
+
+export const StorageDomain = {
+ page: "editor",
+};
+
+export const StorageType = {
+ Code: "code",
+ Files: "files",
+};
+
+export function toPath(type, nameOrPath) {
+ const name =
+ nameOrPath.indexOf("/") >= 0
+ ? nameOrPath.split("/").slice(2).join("/")
+ : nameOrPath;
+ return { type, name };
+}
+
+export function FileTab(props) {
+ const {
+ files,
+ p,
+ active,
+ idx,
+ removeFromFiles,
+ openFile,
+ createFile,
+ code,
+ updateSaved,
+ } = props;
+ const cache = useCache();
+ const near = useNear();
+ const accountId = useAccountId();
+ const [localCode, setLocalCode] = useState(null);
+ const [chainCode, setChainCode] = useState(null);
+ const [saved, setSaved] = useState(false);
+
+ const jp = JSON.stringify(p);
+
+ useEffect(() => {
+ if (code !== undefined) {
+ setLocalCode(code);
+ return;
+ }
+ cache
+ .asyncLocalStorageGet(StorageDomain, {
+ path: p,
+ type: StorageType.Code,
+ })
+ .then(({ code }) => {
+ setLocalCode(code);
+ });
+ }, [code, cache, p]);
+
+ useEffect(() => {
+ const widgetSrc = `${accountId}/${p?.type}/${p?.name}`;
+ const c = () => {
+ const code = cache.socialGet(
+ near,
+ widgetSrc,
+ false,
+ undefined,
+ undefined,
+ c,
+ );
+ setChainCode(code);
+ };
+ c();
+ }, [cache, near, p, accountId]);
+
+ useEffect(() => {
+ const unsaved = localCode !== chainCode;
+ setSaved(unsaved);
+ }, [localCode, chainCode]);
+
+ useEffect(() => {
+ updateSaved && updateSaved(jp, !saved, localCode);
+ }, [saved, updateSaved, localCode]);
+
+ const Button = styled.button`
+ all: unset;
+
+ display: flex;
+ padding: 10px 20px;
+ justify-content: center;
+ align-items: center;
+ gap: 4px;
+
+ color: var(--black-100, #000);
+ /* Other/Button_text */
+ font-family: Satoshi, sans-serif;
+ font-size: 0.875rem;
+ font-style: normal;
+ font-weight: 500;
+ line-height: normal;
+
+ border-radius: 8px;
+ background: var(--Blue, #51b6ff);
+
+ ${active && "box-shadow:0px 0px 0px 2px #fff inset;"}
+ `;
+
+ const CloseButton = styled.button`
+ all: unset;
+ padding: 4px;
+ font-size: 12px;
+
+ height: 8px;
+ width: 8px;
+
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ color: #000;
+ border-radius: 50%;
+ transition: all 300ms;
+
+ &:hover {
+ background-color: #fff;
+ }
+ `;
+
+ return (
+
+
+
+ );
+}
diff --git a/gateway/src/components/Editor/OpenModal.js b/gateway/src/components/Editor/OpenModal.js
new file mode 100644
index 0000000..0d66aab
--- /dev/null
+++ b/gateway/src/components/Editor/OpenModal.js
@@ -0,0 +1,62 @@
+import React, { useState } from "react";
+import Modal from "react-bootstrap/Modal";
+
+export default function OpenModal(props) {
+ const onHide = props.onHide;
+ const onOpen = props.onOpen;
+ const onNew = props.onNew;
+ const show = props.show;
+
+ const [widgetSrc, setWidgetSrc] = useState("");
+
+ return (
+
+
+ Open widget
+
+
+
+
+ setWidgetSrc(e.target.value.replaceAll(/[^a-zA-Z0-9_.\-\/]/g, ""))
+ }
+ />
+
+
+
+
+
+
+
+ );
+}
diff --git a/gateway/src/components/Editor/RenameModal.js b/gateway/src/components/Editor/RenameModal.js
new file mode 100644
index 0000000..026ff11
--- /dev/null
+++ b/gateway/src/components/Editor/RenameModal.js
@@ -0,0 +1,49 @@
+import React, { useState } from "react";
+import Modal from "react-bootstrap/Modal";
+
+export default function RenameModal(props) {
+ const onHide = props.onHide;
+ const name = props.name;
+ const onRename = props.onRename;
+ const show = props.show;
+
+ const [newName, setNewName] = useState(name);
+
+ return (
+
+
+ Rename
+
+
+
+
+ setNewName(e.target.value.replaceAll(/[^a-zA-Z0-9_.\-]/g, ""))
+ }
+ />
+
+
+
+
+
+
+ );
+}
diff --git a/gateway/src/stores/bos-loader.js b/gateway/src/stores/bos-loader.js
new file mode 100644
index 0000000..4914a4e
--- /dev/null
+++ b/gateway/src/stores/bos-loader.js
@@ -0,0 +1,9 @@
+import { create } from "zustand";
+
+export const useBosLoaderStore = create((set) => ({
+ failedToLoad: false,
+ hasResolved: false,
+ loaderUrl: "",
+ redirectMap: {},
+ set: (state) => set((previousState) => ({ ...previousState, ...state })),
+}));
diff --git a/gateway/yarn.lock b/gateway/yarn.lock
index 1704c9b..c39f93b 100644
--- a/gateway/yarn.lock
+++ b/gateway/yarn.lock
@@ -2003,6 +2003,20 @@
"@radix-ui/react-use-layout-effect" "^1.0.1"
zustand "^4.5.0"
+"@monaco-editor/loader@^1.4.0":
+ version "1.4.0"
+ resolved "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.4.0.tgz#f08227057331ec890fa1e903912a5b711a2ad558"
+ integrity sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==
+ dependencies:
+ state-local "^1.0.6"
+
+"@monaco-editor/react@^4.6.0":
+ version "4.6.0"
+ resolved "https://registry.npmjs.org/@monaco-editor/react/-/react-4.6.0.tgz#bcc68671e358a21c3814566b865a54b191e24119"
+ integrity sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw==
+ dependencies:
+ "@monaco-editor/loader" "^1.4.0"
+
"@multiformats/blake2@^1.0.13":
version "1.0.13"
resolved "https://registry.yarnpkg.com/@multiformats/blake2/-/blake2-1.0.13.tgz#ab0c471da28df55eb7c16339e25ba9571259c2d7"
@@ -9928,6 +9942,11 @@ stable@^0.1.8:
resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
+state-local@^1.0.6:
+ version "1.0.7"
+ resolved "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz#da50211d07f05748d53009bee46307a37db386d5"
+ integrity sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==
+
static-eval@2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.0.2.tgz#2d1759306b1befa688938454c546b7871f806a42"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 5221a36..8ba95b1 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -8,6 +8,9 @@ importers:
.:
dependencies:
+ '@monaco-editor/react':
+ specifier: ^4.6.0
+ version: 4.6.0(monaco-editor@0.49.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-popover':
specifier: 1.0.7
version: 1.0.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -74,6 +77,18 @@ packages:
'@jridgewell/trace-mapping@0.3.25':
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
+ '@monaco-editor/loader@1.4.0':
+ resolution: {integrity: sha512-00ioBig0x642hytVspPl7DbQyaSWRaolYie/UFNjoTdvoKPzo6xrXLhTk9ixgIKcLH5b5vDOjVNiGyY+uDCUlg==}
+ peerDependencies:
+ monaco-editor: '>= 0.21.0 < 1'
+
+ '@monaco-editor/react@4.6.0':
+ resolution: {integrity: sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw==}
+ peerDependencies:
+ monaco-editor: '>= 0.25.0 < 1'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+
'@near-js/crypto@1.2.3':
resolution: {integrity: sha512-BuNE+tdcxwImxktFtuAxLiVejFDtn1X92kejcDcYc6f7e0ku9yMntdw98LMb+5ls+xlRuF1UDoi/hUF1LPVpyQ==}
@@ -854,6 +869,9 @@ packages:
engines: {node: '>=10'}
hasBin: true
+ monaco-editor@0.49.0:
+ resolution: {integrity: sha512-2I8/T3X/hLxB2oPHgqcNYUVdA/ZEFShT7IAujifIPMfKkNbLOqY8XCoyHCXrsdjb36dW9MwoTwBCFpXKMwNwaQ==}
+
ms@2.0.0:
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
@@ -1086,6 +1104,9 @@ packages:
spawn-command@0.0.2:
resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==}
+ state-local@1.0.7:
+ resolution: {integrity: sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==}
+
statuses@1.5.0:
resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==}
engines: {node: '>= 0.6'}
@@ -1314,6 +1335,18 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.4.15
+ '@monaco-editor/loader@1.4.0(monaco-editor@0.49.0)':
+ dependencies:
+ monaco-editor: 0.49.0
+ state-local: 1.0.7
+
+ '@monaco-editor/react@4.6.0(monaco-editor@0.49.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@monaco-editor/loader': 1.4.0(monaco-editor@0.49.0)
+ monaco-editor: 0.49.0
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+
'@near-js/crypto@1.2.3':
dependencies:
'@near-js/types': 0.2.0
@@ -2128,6 +2161,8 @@ snapshots:
mkdirp@1.0.4: {}
+ monaco-editor@0.49.0: {}
+
ms@2.0.0: {}
ms@2.1.2: {}
@@ -2369,6 +2404,8 @@ snapshots:
spawn-command@0.0.2: {}
+ state-local@1.0.7: {}
+
statuses@1.5.0: {}
statuses@2.0.1: {}
diff --git a/widget/page/sandbox.jsx b/widget/page/sandbox.jsx
index 28076f3..166e334 100644
--- a/widget/page/sandbox.jsx
+++ b/widget/page/sandbox.jsx
@@ -1,35 +1,26 @@
-const playerData = {
- key: "value",
- playerProps: {
- playbackId: "8b3bdqjtdj4jsjwa",
- },
-};
-
-const broadcastData = {};
-
return (
- <>
-
-
- >
-);
+ {}}
+ />
+);
\ No newline at end of file