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 ( - <> -
-
-
-

Player

- -
-
-
-
-

Broadcast

- - -
-
-
- - -); + {}} + /> +); \ No newline at end of file