fix monaco
This commit is contained in:
parent
bf19dbf50f
commit
a1c5166748
|
|
@ -9,7 +9,8 @@ import { EdMain } from "./panel/main/main";
|
|||
import { EdPane } from "./panel/main/pane-resize";
|
||||
import { EdPopCompGroup } from "./panel/popup/comp-group";
|
||||
import { EdPopSite } from "./panel/popup/site";
|
||||
import { jscript } from "../editor/panel/script/script-element";
|
||||
import { EdScriptInit } from "./panel/script/monaco/init";
|
||||
import { EdScriptSite } from "./panel/script/site";
|
||||
|
||||
export const EdBase = () => {
|
||||
const p = useGlobal(EDGlobal, "EDITOR");
|
||||
|
|
@ -33,7 +34,6 @@ export const EdBase = () => {
|
|||
);
|
||||
}
|
||||
|
||||
const Editor = jscript.editor;
|
||||
return (
|
||||
<div className="flex flex-col flex-1">
|
||||
<div className="flex justify-between"></div>
|
||||
|
|
@ -42,20 +42,11 @@ export const EdBase = () => {
|
|||
<EdPane type="left" />
|
||||
<EdMain />
|
||||
</div>
|
||||
|
||||
<>
|
||||
<EdPopSite />
|
||||
<EdPopCompGroup />
|
||||
{Editor && !jscript.ready && (
|
||||
<div className="hidden">
|
||||
<Editor
|
||||
onMount={() => {
|
||||
jscript.ready = true;
|
||||
p.render();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<EdScriptInit />
|
||||
<EdScriptSite />
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ export const EDGlobal = {
|
|||
| "ready",
|
||||
sync: null as unknown as Awaited<ReturnType<typeof clientStartSync>>,
|
||||
site: EmptySite,
|
||||
script: { siteTypes: {} as Record<string, string> },
|
||||
page: {
|
||||
cur: EmptyPage,
|
||||
doc: null as null | DPage,
|
||||
|
|
@ -86,6 +87,9 @@ export const EDGlobal = {
|
|||
group: {} as Record<string, Awaited<ReturnType<SAction["comp"]["group"]>>>,
|
||||
},
|
||||
ui: {
|
||||
script: {
|
||||
site: false,
|
||||
},
|
||||
layout: {
|
||||
left: parseInt(localStorage.getItem("prasi-layout-left") || "250"),
|
||||
right: parseInt(localStorage.getItem("prasi-layout-right") || "250"),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import init from "wasm-gzip";
|
||||
import { PG } from "./ed-global";
|
||||
import { jscript } from "../../editor/panel/script/script-element";
|
||||
import { jscript } from "../../../utils/script/jscript";
|
||||
|
||||
export const edInit = async (p: PG) => {
|
||||
await init();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
import { ReactNode } from "react";
|
||||
import { useLocal } from "web-utils";
|
||||
import { jscript } from "../../../../../utils/script/jscript";
|
||||
import { Loading } from "../../../../../utils/ui/loading";
|
||||
|
||||
export const EdScriptInit = () => {
|
||||
const Editor = jscript.editor;
|
||||
const local = useLocal({ editorLoaded: false }, () => {});
|
||||
|
||||
jscript.events.editorLoaded = () => {
|
||||
local.editorLoaded = true;
|
||||
local.render();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{Editor && local.editorLoaded && (
|
||||
<div className="hidden">
|
||||
<Editor
|
||||
onMount={() => {
|
||||
jscript.events.pendingDone();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const EdMonacoWrap = ({
|
||||
children,
|
||||
}: {
|
||||
children: (Editor: Exclude<typeof jscript.editor, null>) => ReactNode;
|
||||
}) => {
|
||||
const local = useLocal({});
|
||||
|
||||
if (jscript.pending && (!jscript.editor || !jscript.build)) {
|
||||
jscript.pending.then(() => {
|
||||
local.render();
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
"flex flex-1 absolute inset-[80px]",
|
||||
css`
|
||||
.monaco-editor {
|
||||
.mtk9 {
|
||||
color: #022f62;
|
||||
}
|
||||
.mtk1 {
|
||||
color: #022f62;
|
||||
}
|
||||
.mtk22 {
|
||||
color: #015cc5;
|
||||
}
|
||||
.mtk8 {
|
||||
color: #015cc5;
|
||||
}
|
||||
.mtk5 {
|
||||
color: #55bb8a;
|
||||
}
|
||||
.monaco-editor.showUnused .squiggly-inline-unnecessary {
|
||||
opacity: 0.4;
|
||||
}
|
||||
.jsx-expression-braces {
|
||||
color: #7c3813;
|
||||
}
|
||||
.jsx-tag-angle-bracket {
|
||||
color: #619ac3;
|
||||
}
|
||||
.jsx-tag-name {
|
||||
color: #619ac3;
|
||||
}
|
||||
.jsx-tag-order-1 {
|
||||
color: #23863a;
|
||||
}
|
||||
.jsx-tag-order-2 {
|
||||
color: #4e7ca1;
|
||||
}
|
||||
.jsx-tag-order-3 {
|
||||
color: #020360;
|
||||
}
|
||||
.jsx-tag-attribute-key {
|
||||
color: #6f42c1;
|
||||
}
|
||||
.jsx-text {
|
||||
color: #000000;
|
||||
}
|
||||
}
|
||||
`
|
||||
)}
|
||||
>
|
||||
{!jscript.editor || !jscript.build ? (
|
||||
<Loading note="script-cst" backdrop={false} />
|
||||
) : (
|
||||
children(jscript.editor)
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
import { jsMount } from "../../../../../utils/script/mount";
|
||||
import { monacoTypings } from "../../../../../utils/script/typings";
|
||||
import { Modal } from "../../../../../utils/ui/modal";
|
||||
import { EdMonacoWrap } from "./init";
|
||||
import type { Editor } from "@monaco-editor/react";
|
||||
|
||||
const monacoState = {} as Record<string, any>;
|
||||
export const EdMonaco = (arg: {
|
||||
id?: string;
|
||||
type: "js" | "html" | "css";
|
||||
filename: string;
|
||||
monaco: Parameters<typeof Editor>[0];
|
||||
onClose: () => void;
|
||||
prop?: {
|
||||
val: Record<string, any>;
|
||||
types: Record<string, string>;
|
||||
};
|
||||
}) => {
|
||||
const filename = arg.filename;
|
||||
const m = arg.monaco;
|
||||
const prop = { ...arg.monaco };
|
||||
|
||||
prop.options = {
|
||||
minimap: { enabled: false },
|
||||
wordWrap: "wordWrapColumn",
|
||||
autoClosingBrackets: "always",
|
||||
tabSize: 2,
|
||||
autoIndent: "full",
|
||||
formatOnPaste: true,
|
||||
formatOnType: true,
|
||||
useTabStops: true,
|
||||
};
|
||||
if (arg.type === "html") {
|
||||
prop.language = "html";
|
||||
}
|
||||
if (arg.type === "css") {
|
||||
prop.language = "scss";
|
||||
}
|
||||
if (arg.type === "js") {
|
||||
prop.language = "typescript";
|
||||
prop.onMount = async (editor, monaco) => {
|
||||
const value = editor.getValue();
|
||||
monaco.editor.getModels().forEach((model) => {
|
||||
if (model.uri.toString().startsWith("inmemory://model")) {
|
||||
model.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
let model = monaco.editor.createModel(
|
||||
value,
|
||||
"typescript",
|
||||
monaco.Uri.parse(`ts:${filename}`)
|
||||
);
|
||||
editor.setModel(model);
|
||||
|
||||
if (arg.id) {
|
||||
if (!monacoState[arg.id]) {
|
||||
editor.trigger("fold", "editor.foldAllMarkerRegions", null);
|
||||
} else {
|
||||
editor.restoreViewState(monacoState[arg.id]);
|
||||
}
|
||||
}
|
||||
|
||||
await jsMount(editor, monaco);
|
||||
|
||||
if (arg.prop)
|
||||
await monacoTypings(
|
||||
{
|
||||
script: {
|
||||
siteTypes: {},
|
||||
},
|
||||
site: {
|
||||
api_url: "",
|
||||
},
|
||||
site_dts: "",
|
||||
},
|
||||
monaco,
|
||||
{
|
||||
values: arg.prop.val,
|
||||
types: arg.prop.types,
|
||||
}
|
||||
);
|
||||
|
||||
if (m.onMount) m.onMount(editor, monaco);
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open
|
||||
onOpenChange={() => {
|
||||
arg.onClose();
|
||||
}}
|
||||
>
|
||||
<EdMonacoWrap>{(Editor) => <Editor {...prop} />}</EdMonacoWrap>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
import { useGlobal } from "web-utils";
|
||||
import { EdMonaco } from "./monaco/monaco";
|
||||
import { EDGlobal } from "../../logic/ed-global";
|
||||
|
||||
export const EdScriptSite = () => {
|
||||
const p = useGlobal(EDGlobal, "EDITOR");
|
||||
|
||||
if (!p.ui.script.site) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<EdMonaco
|
||||
id="script-site"
|
||||
type="js"
|
||||
filename="site.tsx"
|
||||
monaco={{
|
||||
value: p.site.js,
|
||||
onChange: (v) => {
|
||||
console.log(v);
|
||||
},
|
||||
}}
|
||||
prop={{
|
||||
val: {},
|
||||
types: {
|
||||
exports: "any",
|
||||
types: "any",
|
||||
render: "()=>void",
|
||||
},
|
||||
}}
|
||||
onClose={() => {
|
||||
p.ui.script.site = false;
|
||||
p.render();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
export const EdScriptSite = () => {
|
||||
|
||||
};
|
||||
|
|
@ -1,8 +1,18 @@
|
|||
import { useGlobal } from "web-utils";
|
||||
import { TopBtn } from "../top-btn";
|
||||
import { EDGlobal } from "../../../logic/ed-global";
|
||||
|
||||
export const EdSiteJS = () => {
|
||||
const p = useGlobal(EDGlobal, "EDITOR");
|
||||
return (
|
||||
<TopBtn style="slim" className="font-bold font-mono">
|
||||
<TopBtn
|
||||
style="slim"
|
||||
className="font-bold font-mono"
|
||||
onClick={() => {
|
||||
p.ui.script.site = true;
|
||||
p.render();
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
|
|
|
|||
|
|
@ -12,12 +12,12 @@ import {
|
|||
mergeScopeUpwards,
|
||||
treeScopeEval,
|
||||
} from "../logic/tree-scope";
|
||||
import { jscript } from "../panel/script/script-element";
|
||||
import { fillID } from "../tools/fill-id";
|
||||
import { newMap } from "../tools/yjs-tools";
|
||||
import { ComponentOver, ElProp, createElProp } from "./e-relprop";
|
||||
import { ETextInternal } from "./e-text";
|
||||
import { DefaultScript } from "../panel/script/monaco/monaco-el";
|
||||
import { jscript } from "../../../utils/script/jscript";
|
||||
|
||||
export const ERender: FC<{
|
||||
id: string;
|
||||
|
|
@ -185,9 +185,7 @@ export const ERender: FC<{
|
|||
meta.mitem
|
||||
) {
|
||||
if (!jscript.build) {
|
||||
jscript.init().then(() => {
|
||||
p.render();
|
||||
});
|
||||
jscript.init(p.render);
|
||||
return null;
|
||||
}
|
||||
jscript
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ import {
|
|||
} from "../../../utils/script/init-api";
|
||||
import { LSite } from "../../live/logic/global";
|
||||
import { validateLayout } from "../../live/logic/layout";
|
||||
import { jscript } from "../panel/script/script-element";
|
||||
import importModule from "../tools/dynamic-import";
|
||||
import { PG } from "./global";
|
||||
import { devLoader } from "../../live/dev-loader";
|
||||
import { jscript } from "../../../utils/script/jscript";
|
||||
|
||||
export const w = window as unknown as {
|
||||
basepath: string;
|
||||
|
|
@ -46,6 +46,10 @@ export const initEditor = async (p: PG, site_id: string) => {
|
|||
return "";
|
||||
};
|
||||
|
||||
if (!jscript.pending) {
|
||||
jscript.init(p.render);
|
||||
}
|
||||
|
||||
if (!p.item) return;
|
||||
|
||||
p.item.active = localStorage.getItem("prasi-item-active-id") || "";
|
||||
|
|
@ -147,10 +151,6 @@ export const initEditor = async (p: PG, site_id: string) => {
|
|||
|
||||
p.status = "ready";
|
||||
p.render();
|
||||
|
||||
if (!jscript.build) {
|
||||
jscript.init();
|
||||
}
|
||||
};
|
||||
|
||||
export const execSiteJS = (p: PG) => {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { mobileCSS } from "../elements/e-page";
|
|||
import { editorStyle } from "../elements/style";
|
||||
import { EditorGlobal } from "../logic/global";
|
||||
import { Toolbar } from "./toolbar/Toolbar";
|
||||
import { EdScriptInit } from "../../ed/panel/script/monaco/init";
|
||||
|
||||
const ETree = lazy(async () => ({
|
||||
default: (await import("./tree/tree")).ETree,
|
||||
|
|
@ -61,6 +62,7 @@ export const EMainEditor = () => {
|
|||
</div>
|
||||
{p.status === "ready" && (
|
||||
<Suspense fallback={<Loading note={`toolbar`} />}>
|
||||
<EdScriptInit />
|
||||
{p.manager.site && <SiteManager />}
|
||||
{p.manager.page && <PageManager />}
|
||||
{p.manager.comp && <CompManager />}
|
||||
|
|
|
|||
|
|
@ -5,17 +5,12 @@ import type {
|
|||
} from "@monaco-editor/react";
|
||||
import { FC } from "react";
|
||||
import { useGlobal, useLocal } from "web-utils";
|
||||
import { FBuild } from "../../../../../utils/script/jscript";
|
||||
import { EditorGlobal } from "../../../logic/global";
|
||||
import { jsMount } from "./mount";
|
||||
import { monacoTypings } from "./typings";
|
||||
export type MonacoEditor = Parameters<OnMount>[0];
|
||||
|
||||
export type FBuild = (
|
||||
entryFileName: string,
|
||||
src: string,
|
||||
files?: Record<string, string>
|
||||
) => Promise<string>;
|
||||
|
||||
export const customMonacoState: Record<string, any> = {};
|
||||
|
||||
export const ScriptMonacoCustom: FC<{
|
||||
|
|
|
|||
|
|
@ -7,18 +7,19 @@ import strDelta from "textdiff-create";
|
|||
import { useGlobal, useLocal } from "web-utils";
|
||||
import * as Y from "yjs";
|
||||
import { TypedMap } from "yjs-types";
|
||||
import { FBuild } from "../../../../../utils/script/jscript";
|
||||
import { FMCompDef, FNAdv } from "../../../../../utils/types/meta-fn";
|
||||
import { Button } from "../../../../../utils/ui/form/Button";
|
||||
import { Loading } from "../../../../../utils/ui/loading";
|
||||
import { Popover } from "../../../../../utils/ui/popover";
|
||||
import { EditorGlobal } from "../../../logic/global";
|
||||
import { mergeScopeUpwards } from "../../../logic/tree-scope";
|
||||
import { newMap } from "../../../tools/yjs-tools";
|
||||
import { MonacoElHistory } from "./monaco-el-history";
|
||||
import { MonacoElSnippet } from "./monaco-el-snippet";
|
||||
import { jsMount } from "./mount";
|
||||
import { MonacoScopeBar } from "./scope-bar";
|
||||
import { monacoTypings } from "./typings";
|
||||
import { MonacoElSnippet } from "./monaco-el-snippet";
|
||||
import { Button } from "../../../../../utils/ui/form/Button";
|
||||
import { Popover } from "../../../../../utils/ui/popover";
|
||||
import { MonacoElHistory } from "./monaco-el-history";
|
||||
|
||||
export type MonacoEditor = Parameters<OnMount>[0];
|
||||
export const DefaultScript = {
|
||||
|
|
@ -42,13 +43,6 @@ const w = window as unknown as {
|
|||
};
|
||||
};
|
||||
|
||||
export type FBuild = (
|
||||
entryFileName: string,
|
||||
src: string,
|
||||
files?: Record<string, string>,
|
||||
verbose?: boolean
|
||||
) => Promise<string>;
|
||||
|
||||
const monacoViewState = {} as Record<string, any>;
|
||||
|
||||
export const ScriptMonacoElement: FC<{
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { Loading } from "../../../../utils/ui/loading";
|
|||
import { EditorGlobal } from "../../logic/global";
|
||||
import { ScriptMonacoCustom } from "./monaco/monaco-custom";
|
||||
import { MonacoEditor } from "./monaco/typings";
|
||||
import { jscript } from "./script-element";
|
||||
import { jscript } from "../../../../utils/script/jscript";
|
||||
|
||||
export const EScriptCustom: FC<{
|
||||
monaco_id: string;
|
||||
|
|
@ -28,16 +28,8 @@ export const EScriptCustom: FC<{
|
|||
}) => {
|
||||
const p = useGlobal(EditorGlobal);
|
||||
|
||||
if (!jscript.editor) {
|
||||
Promise.all([
|
||||
import("@monaco-editor/react").then((e) => {
|
||||
jscript.editor = e.Editor;
|
||||
e.loader.config({ paths: { vs: "/min/vs" } });
|
||||
}),
|
||||
jscript.init(),
|
||||
]).then(() => {
|
||||
p.render();
|
||||
});
|
||||
if (!jscript.editor && jscript.pending) {
|
||||
jscript.pending.then(() => p.render());
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,70 +1,18 @@
|
|||
import type { Editor as MonacoEditor } from "@monaco-editor/react";
|
||||
import type { BuildOptions } from "esbuild-wasm";
|
||||
import { FC } from "react";
|
||||
import { useGlobal } from "web-utils";
|
||||
import * as Y from "yjs";
|
||||
import { jscript } from "../../../../utils/script/jscript";
|
||||
import { Loading } from "../../../../utils/ui/loading";
|
||||
import { Modal } from "../../../../utils/ui/modal";
|
||||
import { EditorGlobal } from "../../logic/global";
|
||||
import { rebuildTree } from "../../logic/tree-logic";
|
||||
import { initJS } from "./monaco/init";
|
||||
import { DefaultScript, FBuild, ScriptMonacoElement } from "./monaco/monaco-el";
|
||||
|
||||
export const jscript = {
|
||||
editor: null as typeof MonacoEditor | null,
|
||||
build: null as null | FBuild,
|
||||
_init: false as false | Promise<void>,
|
||||
ready: false,
|
||||
_editor: false,
|
||||
async init(render: () => void) {
|
||||
if (this._init) await this._init;
|
||||
if (!this._init) {
|
||||
this._init = new Promise<void>(async (resolve) => {
|
||||
const { sendIPC } = await import("./esbuild/ipc");
|
||||
await initJS();
|
||||
|
||||
if (!this._editor) {
|
||||
this._editor = true;
|
||||
const e = await import("@monaco-editor/react");
|
||||
jscript.editor = e.Editor;
|
||||
e.loader.config({ paths: { vs: "/min/vs" } });
|
||||
}
|
||||
|
||||
this.build = async (entry, src, files, verbose?: boolean) => {
|
||||
const options: BuildOptions = {
|
||||
entryPoints: [entry],
|
||||
jsx: "transform",
|
||||
bundle: true,
|
||||
format: "cjs",
|
||||
minify: true,
|
||||
};
|
||||
const res = await sendIPC({
|
||||
command_: "build",
|
||||
input_: { ...files, [entry]: src },
|
||||
options_: options,
|
||||
});
|
||||
|
||||
if (verbose && res.stderr_) {
|
||||
console.log(res.stderr_);
|
||||
}
|
||||
if (res.outputFiles_) return res.outputFiles_[0].text;
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
await this.build("el.tsx", `return ""`);
|
||||
render();
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
import { DefaultScript, ScriptMonacoElement } from "./monaco/monaco-el";
|
||||
|
||||
export const EScriptElement: FC<{}> = ({}) => {
|
||||
const p = useGlobal(EditorGlobal, "EDITOR");
|
||||
|
||||
if (!jscript.editor) {
|
||||
jscript.init(p.render);
|
||||
if (!jscript.editor && jscript.pending) {
|
||||
jscript.pending.then(() => p.render());
|
||||
}
|
||||
|
||||
if (!p.script.active) {
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@ import { editComp, loadComponent } from "../../../logic/comp";
|
|||
import { EditorGlobal, PG } from "../../../logic/global";
|
||||
import { rebuildTree } from "../../../logic/tree-logic";
|
||||
import { newMap } from "../../../tools/yjs-tools";
|
||||
import { jscript } from "../../script/script-element";
|
||||
import { CPJsx } from "./CPJsx";
|
||||
import { CPOption } from "./CPOption";
|
||||
import { CPText } from "./CPText";
|
||||
import { mergeScopeUpwards } from "../../../logic/tree-scope";
|
||||
import { treePropEval } from "../../../logic/tree-prop";
|
||||
import { jscript } from "../../../../../utils/script/jscript";
|
||||
|
||||
export const CPInstance: FC<{ mitem: MItem }> = ({ mitem }) => {
|
||||
const p = useGlobal(EditorGlobal, "EDITOR");
|
||||
|
|
@ -65,10 +65,7 @@ export const CPInstance: FC<{ mitem: MItem }> = ({ mitem }) => {
|
|||
}
|
||||
|
||||
if (!jscript.build) {
|
||||
jscript.init().then(() => {
|
||||
local.status = "ready";
|
||||
local.render();
|
||||
});
|
||||
jscript.init(p.render);
|
||||
} else {
|
||||
local.status = "ready";
|
||||
local.render();
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ import { EditorGlobal, NodeMeta } from "../../../logic/global";
|
|||
import { fillID } from "../../../tools/fill-id";
|
||||
import { flatTree } from "../../../tools/flat-tree";
|
||||
import { newMap } from "../../../tools/yjs-tools";
|
||||
import { jscript } from "../../script/script-element";
|
||||
import { detachComp } from "./action/detach";
|
||||
import { rebuildTree } from "../../../logic/tree-logic";
|
||||
import { jscript } from "../../../../../utils/script/jscript";
|
||||
|
||||
export const ETreeRightClick: FC<{
|
||||
node: NodeModel<NodeMeta>;
|
||||
|
|
@ -304,7 +304,7 @@ export const ETreeRightClick: FC<{
|
|||
label="Detach"
|
||||
onClick={async () => {
|
||||
if (!jscript.build) {
|
||||
await jscript.init();
|
||||
await jscript.init(p.render);
|
||||
}
|
||||
if (jscript.build && p.treeMeta[item.id]) {
|
||||
detachComp(
|
||||
|
|
@ -344,7 +344,7 @@ export const ETreeRightClick: FC<{
|
|||
comp_id: rootComp ? rootComp.id : undefined,
|
||||
group_id,
|
||||
})
|
||||
.then(async (e) => {
|
||||
.then(async (e: any) => {
|
||||
if (e) {
|
||||
await loadComponent(p, e.id);
|
||||
delete p.compLoading[item.id];
|
||||
|
|
|
|||
|
|
@ -0,0 +1,85 @@
|
|||
import type { Editor as MonacoEditor } from "@monaco-editor/react";
|
||||
import type { BuildOptions } from "esbuild-wasm";
|
||||
import type Prettier from "prettier/standalone";
|
||||
import type estree from "prettier/plugins/estree";
|
||||
import type ts from "prettier/plugins/typescript";
|
||||
export type FBuild = (
|
||||
entryFileName: string,
|
||||
src: string,
|
||||
files?: Record<string, string>,
|
||||
verbose?: boolean
|
||||
) => Promise<string>;
|
||||
|
||||
export const initJS = async () => {
|
||||
const { tryToSetCurrentVersion } = await import("./esbuild/versions");
|
||||
await tryToSetCurrentVersion("latest");
|
||||
};
|
||||
|
||||
export const jscript = {
|
||||
editor: null as typeof MonacoEditor | null,
|
||||
build: null as null | FBuild,
|
||||
pending: null as null | Promise<void>,
|
||||
events: {
|
||||
editorLoaded: () => {},
|
||||
esbuildLoaded: () => {},
|
||||
prettierLoaded: () => {},
|
||||
pendingDone: () => {},
|
||||
},
|
||||
prettier: {
|
||||
standalone: null as null | typeof Prettier,
|
||||
estree: null as null | typeof estree,
|
||||
ts: null as null | typeof ts,
|
||||
},
|
||||
async init(render: () => void) {
|
||||
if (this.pending) {
|
||||
await this.pending;
|
||||
render();
|
||||
}
|
||||
if (!this.pending) {
|
||||
this.pending = new Promise<void>(async (resolve) => {
|
||||
this.events.pendingDone = resolve;
|
||||
|
||||
const { sendIPC } = await import("./esbuild/ipc");
|
||||
await initJS();
|
||||
this.events.esbuildLoaded();
|
||||
|
||||
this.prettier.standalone = (
|
||||
await import("prettier/standalone")
|
||||
).default;
|
||||
this.prettier.estree = await import("prettier/plugins/estree");
|
||||
this.prettier.ts = await import("prettier/plugins/typescript");
|
||||
this.events.prettierLoaded();
|
||||
|
||||
const e = await import("@monaco-editor/react");
|
||||
jscript.editor = e.Editor;
|
||||
e.loader.config({ paths: { vs: "/min/vs" } });
|
||||
this.events.editorLoaded();
|
||||
|
||||
this.build = async (entry, src, files, verbose?: boolean) => {
|
||||
const options: BuildOptions = {
|
||||
entryPoints: [entry],
|
||||
jsx: "transform",
|
||||
bundle: true,
|
||||
format: "cjs",
|
||||
minify: true,
|
||||
};
|
||||
const res = await sendIPC({
|
||||
command_: "build",
|
||||
input_: { ...files, [entry]: src },
|
||||
options_: options,
|
||||
});
|
||||
|
||||
if (verbose && res.stderr_) {
|
||||
console.log(res.stderr_);
|
||||
}
|
||||
if (res.outputFiles_) return res.outputFiles_[0].text;
|
||||
|
||||
return "";
|
||||
};
|
||||
|
||||
await this.build("el.tsx", `return ""`);
|
||||
render();
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
import type { OnMount } from "@monaco-editor/react";
|
||||
import trim from "lodash.trim";
|
||||
import {
|
||||
MonacoJsxSyntaxHighlight,
|
||||
getWorker,
|
||||
} from "monaco-jsx-syntax-highlight-v2";
|
||||
import { jscript } from "./jscript";
|
||||
|
||||
export type MonacoEditor = Parameters<OnMount>[0];
|
||||
type Monaco = Parameters<OnMount>[1];
|
||||
type CompilerOptions = Parameters<
|
||||
Parameters<OnMount>[1]["languages"]["typescript"]["typescriptDefaults"]["setCompilerOptions"]
|
||||
>[0];
|
||||
|
||||
export const jsMount = async (editor: MonacoEditor, monaco: Monaco) => {
|
||||
const m = monaco as any;
|
||||
if (!m.customJSMounted) {
|
||||
m.customJSMounted = true;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
const compilerOptions: CompilerOptions = {
|
||||
jsx: monaco.languages.typescript.JsxEmit.React,
|
||||
jsxFactory: "React.createElement",
|
||||
jsxFragmentFactory: "React.Fragment",
|
||||
target: monaco.languages.typescript.ScriptTarget.ES2015,
|
||||
allowNonTsExtensions: true,
|
||||
lib: ["esnext", "dom"],
|
||||
module: monaco.languages.typescript.ModuleKind.ESNext,
|
||||
esModuleInterop: true,
|
||||
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
|
||||
};
|
||||
|
||||
const jsxHgController = new MonacoJsxSyntaxHighlight(getWorker(), monaco);
|
||||
const { highlighter } = jsxHgController.highlighterBuilder({
|
||||
editor: editor,
|
||||
});
|
||||
highlighter();
|
||||
editor.onDidChangeModelContent(() => {
|
||||
highlighter();
|
||||
});
|
||||
|
||||
monaco.languages.registerDocumentFormattingEditProvider("typescript", {
|
||||
async provideDocumentFormattingEdits(model, options, token) {
|
||||
const prettier = jscript.prettier.standalone;
|
||||
const prettier_ts = jscript.prettier.ts;
|
||||
const prettier_estree = jscript.prettier.estree;
|
||||
|
||||
if (prettier && prettier_estree && prettier_ts) {
|
||||
const text = trim(
|
||||
await prettier.format(model.getValue(), {
|
||||
parser: "typescript",
|
||||
plugins: [prettier_ts, prettier_estree],
|
||||
}),
|
||||
"; \n"
|
||||
);
|
||||
|
||||
return [
|
||||
{
|
||||
range: model.getFullModelRange(),
|
||||
text,
|
||||
},
|
||||
];
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
monaco.languages.registerCompletionItemProvider("typescript", {
|
||||
provideCompletionItems: (model, position) => {
|
||||
const word = model.getWordUntilPosition(position);
|
||||
return {
|
||||
suggestions: [
|
||||
{
|
||||
label: "log",
|
||||
kind: monaco.languages.CompletionItemKind.Snippet,
|
||||
documentation: "Add Console.log",
|
||||
insertText: `console.log($1)`,
|
||||
insertTextRules:
|
||||
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
||||
range: {
|
||||
startLineNumber: position.lineNumber,
|
||||
endLineNumber: position.lineNumber,
|
||||
startColumn: word.startColumn,
|
||||
endColumn: word.endColumn,
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "sfy",
|
||||
kind: monaco.languages.CompletionItemKind.Snippet,
|
||||
documentation: "Add JSON.stringify",
|
||||
insertText: `JSON.stringify($1)`,
|
||||
insertTextRules:
|
||||
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
||||
range: {
|
||||
startLineNumber: position.lineNumber,
|
||||
endLineNumber: position.lineNumber,
|
||||
startColumn: word.startColumn,
|
||||
endColumn: word.endColumn,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
monaco.languages.registerCompletionItemProvider("typescript", {
|
||||
triggerCharacters: [">"],
|
||||
provideCompletionItems: (model, position) => {
|
||||
const codePre: string = model.getValueInRange({
|
||||
startLineNumber: position.lineNumber,
|
||||
startColumn: 1,
|
||||
endLineNumber: position.lineNumber,
|
||||
endColumn: position.column,
|
||||
});
|
||||
|
||||
const tag = codePre.match(/.*<(\w+)>$/)?.[1];
|
||||
|
||||
if (!tag) {
|
||||
return;
|
||||
}
|
||||
|
||||
const word = model.getWordUntilPosition(position);
|
||||
|
||||
return {
|
||||
suggestions: [
|
||||
{
|
||||
label: `</${tag}>`,
|
||||
kind: monaco.languages.CompletionItemKind.EnumMember,
|
||||
insertText: `$1</${tag}>`,
|
||||
insertTextRules:
|
||||
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
|
||||
range: {
|
||||
startLineNumber: position.lineNumber,
|
||||
endLineNumber: position.lineNumber,
|
||||
startColumn: word.startColumn,
|
||||
endColumn: word.endColumn,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
monaco.languages.typescript.typescriptDefaults.setCompilerOptions(
|
||||
compilerOptions
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
editor.getAction("editor.action.formatDocument")?.run();
|
||||
}, 100);
|
||||
};
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
export const baseTypings = `
|
||||
type FC<T> = React.FC<T>;
|
||||
const Fragment = React.Fragment;
|
||||
const ReactNode = React.ReactNode;
|
||||
const useCallback = React.useCallback;
|
||||
const useMemo = React.useMemo;
|
||||
const ReactElement = React.ReactElement;
|
||||
const isValidElement = React.isValidElement;
|
||||
const useEffect = React.useEffect;
|
||||
const useState = React.useState;
|
||||
|
||||
const pathname: string;
|
||||
const isEditor: boolean;
|
||||
const isLayout: boolean;
|
||||
const isMobile: boolean;
|
||||
const isDesktop: boolean;
|
||||
const preload: (pathname: string) => void;
|
||||
const apiHeaders: Record<string, any>;
|
||||
const navigate: (url:string) => void;
|
||||
const params: any;
|
||||
const cx = (...classNames: any[]) => string;
|
||||
const css = (
|
||||
tag: CSSAttribute | TemplateStringsArray | string,
|
||||
...props: Array<string | number | boolean | undefined | null>
|
||||
) => string;
|
||||
|
||||
const props: {
|
||||
className: string;
|
||||
onPointerDown?: () => void;
|
||||
onPointerMove?: () => void;
|
||||
onPointerLeave?: () => void;
|
||||
};
|
||||
const children: ReactNode;
|
||||
|
||||
const PassProp: FC<Record<string,any> & {children: React.ReactNode; }>;
|
||||
const PassChild: FC<{name: string}>;
|
||||
const Preload: FC<{url: string[]}>;
|
||||
const apiurl: string;
|
||||
const pageid: string;
|
||||
type ITEM = {
|
||||
id: string
|
||||
name: string;
|
||||
type: 'item' | 'text';
|
||||
adv?: {
|
||||
js?: string;
|
||||
jsBuilt?: string;
|
||||
css?: string;
|
||||
html?: string;
|
||||
},
|
||||
text: string,
|
||||
html: string,
|
||||
component?: { id:string, props: Record<string, {
|
||||
value: string,
|
||||
valueBuilt: string,
|
||||
meta: { type: string }
|
||||
}>},
|
||||
childs: ITEM[]
|
||||
}
|
||||
const newElement: (gen?: (item: ITEM) => ITEM | ITEM[]) => React.ReactNode;
|
||||
const Local: <T extends Record<string, any>>(arg: {
|
||||
name: string;
|
||||
value: T;
|
||||
children: ((local: T & { render: () => void }) => any);
|
||||
deps?: any[];
|
||||
effect?: (
|
||||
local: T & { render: () => void }
|
||||
) => void | (() => void) | Promise<void | (() => void)>;
|
||||
hook?: (
|
||||
local: T & { render: () => void }
|
||||
) => void | (() => void) | Promise<void | (() => void)>;
|
||||
cache?: boolean;
|
||||
}) => ReactNode;
|
||||
|
||||
`;
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
import trim from "lodash.trim";
|
||||
import { isValidElement } from "react";
|
||||
|
||||
export const extractProp = (prop: {
|
||||
values: Record<string, any>;
|
||||
types: Record<string, string>;
|
||||
}) => {
|
||||
const propTypes: string[] = [];
|
||||
const props: Record<string, { val?: any; type?: string }> = {};
|
||||
|
||||
if (prop) {
|
||||
if (prop.values) {
|
||||
for (const [k, v] of Object.entries(prop.values)) {
|
||||
if (!props[k]) {
|
||||
props[k] = {};
|
||||
}
|
||||
|
||||
if (typeof v === "function") {
|
||||
if (isFunctionalComponent(v)) {
|
||||
props[k].type = "React.FC";
|
||||
} else if (isClassComponent(v)) {
|
||||
props[k].type = "React.Component";
|
||||
} else {
|
||||
props[k].type = "any";
|
||||
}
|
||||
} else if (v) {
|
||||
if (typeof v === "object" && v._jsx) {
|
||||
props[k].type = "React.ReactElement;";
|
||||
} else if (!!v.render && typeof v.$$typeof === "symbol") {
|
||||
props[k].type = "React.FC<Record<string,any> & {ref?:any}>";
|
||||
} else {
|
||||
props[k].val = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (prop.types) {
|
||||
for (const [k, v] of Object.entries(prop.types)) {
|
||||
if (!props[k]) {
|
||||
props[k] = {};
|
||||
}
|
||||
props[k].type = v;
|
||||
}
|
||||
}
|
||||
|
||||
for (const [k, v] of Object.entries(props)) {
|
||||
if (v.type) {
|
||||
propTypes.push(`const ${k}: ${trim(v.type, "; \n")};`);
|
||||
} else if (v.val) {
|
||||
if (typeof v.val === "object" && isValidElement(v.val)) {
|
||||
propTypes.push(`const ${k}: ReactElement;`);
|
||||
} else {
|
||||
try {
|
||||
let val = v.val;
|
||||
|
||||
if (typeof val === "object") {
|
||||
if (typeof val.render === "function") {
|
||||
val = { ...val, render: () => {} };
|
||||
}
|
||||
|
||||
propTypes.push(`const ${k}: ${recurseTypes(val)};`);
|
||||
} else {
|
||||
propTypes.push(`const ${k}: string;`);
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return propTypes;
|
||||
};
|
||||
|
||||
function recurseTypes(object: any) {
|
||||
const result: string[] = [];
|
||||
if (typeof object === "object") {
|
||||
if (object === null) return "null";
|
||||
if (Array.isArray(object)) {
|
||||
return `any[]`;
|
||||
}
|
||||
|
||||
for (const [k, v] of Object.entries(object)) {
|
||||
result.push(
|
||||
`${k}: ${typeof v === "object" && v ? recurseTypes(v) : typeof v}`
|
||||
);
|
||||
}
|
||||
|
||||
return `{
|
||||
${result.join(";\n ")}
|
||||
}`;
|
||||
}
|
||||
return typeof object;
|
||||
}
|
||||
|
||||
function isFunctionalComponent(Component: any) {
|
||||
return (
|
||||
typeof Component === "function" && // can be various things
|
||||
!(
|
||||
(
|
||||
Component.prototype && // native arrows don't have prototypes
|
||||
Component.prototype.isReactComponent
|
||||
) // special property
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function isClassComponent(Component: any) {
|
||||
return !!(
|
||||
typeof Component === "function" &&
|
||||
Component.prototype &&
|
||||
Component.prototype.isReactComponent
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
export const typeStringify = function (this: any, key: string, value: any) {
|
||||
if (typeof value === "function") {
|
||||
return `___FFF||any||FFF___`;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
export const typeReviver = (key: any, value: any) => {
|
||||
if (typeof key === "string" && key.indexOf("function ") === 0) {
|
||||
let functionTemplate = `(${value})`;
|
||||
return eval(functionTemplate);
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
import type { OnMount } from "@monaco-editor/react";
|
||||
import { w } from "../types/general";
|
||||
import { baseTypings } from "./types/base";
|
||||
import { extractProp } from "./types/prop";
|
||||
export type MonacoEditor = Parameters<OnMount>[0];
|
||||
type Monaco = Parameters<OnMount>[1];
|
||||
|
||||
const map = new WeakMap<any>();
|
||||
|
||||
export const monacoTypings = async (
|
||||
p: {
|
||||
site_dts: string;
|
||||
site: { api_url: string };
|
||||
script: { siteTypes: Record<string, string> };
|
||||
},
|
||||
monaco: Monaco,
|
||||
prop: { values: Record<string, any>; types: Record<string, string> }
|
||||
) => {
|
||||
if (!map.has(prop.values)) {
|
||||
map.set(prop.values, true);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (w.prasiApi[p.site.api_url] && w.prasiApi[p.site.api_url].prismaTypes) {
|
||||
const prisma = w.prasiApi[p.site.api_url].prismaTypes;
|
||||
|
||||
register(
|
||||
monaco,
|
||||
`\
|
||||
declare module "ts:runtime/index" {
|
||||
${prisma["runtime/index.d.ts"]}
|
||||
}`,
|
||||
`ts:runtime/index.d.ts`
|
||||
);
|
||||
|
||||
register(
|
||||
monaco,
|
||||
`\
|
||||
declare module "ts:runtime/library" {
|
||||
${prisma["runtime/library.d.ts"]}
|
||||
}`,
|
||||
`ts:runtime/library.d.ts`
|
||||
);
|
||||
|
||||
register(
|
||||
monaco,
|
||||
`\
|
||||
declare module "ts:prisma" {
|
||||
${prisma["prisma.d.ts"].replace(
|
||||
`import * as runtime from './runtime/library';`,
|
||||
`import * as runtime from 'ts:runtime/library';`
|
||||
)}
|
||||
}`,
|
||||
`ts:prisma.d.ts`
|
||||
);
|
||||
|
||||
register(monaco, w.prasiApi[p.site.api_url].apiTypes, "ts:api.d.ts");
|
||||
}
|
||||
|
||||
monaco.languages.typescript.typescriptDefaults.setExtraLibs([
|
||||
{
|
||||
filePath: "react.d.ts",
|
||||
content: await loadText(
|
||||
"https://cdn.jsdelivr.net/npm/@types/react@18.2.0/index.d.ts"
|
||||
),
|
||||
},
|
||||
{
|
||||
filePath: "jsx-runtime.d.ts",
|
||||
content: await loadText(
|
||||
"https://cdn.jsdelivr.net/npm/@types/react@18.2.0/jsx-runtime.d.ts"
|
||||
),
|
||||
},
|
||||
{
|
||||
filePath: "site.d.ts",
|
||||
content: p.site_dts.replaceAll("export declare const", "declare const"),
|
||||
},
|
||||
]);
|
||||
|
||||
const propText = extractProp({
|
||||
values: prop.values,
|
||||
types: { ...prop.types, ...p.script.siteTypes },
|
||||
});
|
||||
|
||||
const apiTypes = w.prasiApi[p.site.api_url]
|
||||
? w.prasiApi[p.site.api_url].apiTypes
|
||||
: "";
|
||||
|
||||
let apiPath = "app/gen/srv/api/srv";
|
||||
if (apiTypes && apiTypes.includes(`export * as srv from "gen/srv/api/srv"`)) {
|
||||
apiPath = "gen/srv/api/srv";
|
||||
}
|
||||
|
||||
register(
|
||||
monaco,
|
||||
`\
|
||||
import React from 'react';
|
||||
import prisma from 'ts:prisma';
|
||||
|
||||
${iftext(
|
||||
apiTypes,
|
||||
`\
|
||||
import "./api"
|
||||
import type * as SRVAPI from "${apiPath}";`
|
||||
)}
|
||||
|
||||
declare global {;
|
||||
const db: prisma.PrismaClient;
|
||||
|
||||
${baseTypings}
|
||||
|
||||
const moko: {nama: string};
|
||||
${propText.join("\n")}
|
||||
|
||||
${iftext(
|
||||
apiTypes,
|
||||
`
|
||||
type Api = typeof SRVAPI;
|
||||
type ApiName = keyof Api;
|
||||
const api: { [k in ApiName]: Awaited<Api[k]["handler"]>["_"]["api"] };
|
||||
`
|
||||
)}
|
||||
}
|
||||
|
||||
`,
|
||||
"ts:global.d.ts"
|
||||
);
|
||||
};
|
||||
|
||||
const loadText = async (url: string) => {
|
||||
try {
|
||||
const res = await fetch(url);
|
||||
return await res.text();
|
||||
} catch (e) {
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
export const iftext = (condition: any, text: string) => {
|
||||
if (condition) {
|
||||
return text;
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
export const register = (monaco: Monaco, source: string, uri: string) => {
|
||||
const model = monaco.editor.getModels().find((e) => {
|
||||
return e.uri.toString() === uri;
|
||||
});
|
||||
if (model) {
|
||||
model.setValue(source);
|
||||
} else {
|
||||
monaco.editor.createModel(source, "typescript", monaco.Uri.parse(uri));
|
||||
}
|
||||
};
|
||||
Loading…
Reference in New Issue