From b4264b727ef9e06abbfc53f125ee1f0c3cab0ebf Mon Sep 17 00:00:00 2001 From: Rizky Date: Wed, 20 Dec 2023 14:13:37 +0700 Subject: [PATCH] wip checkpoint --- app/srv/ws/sync/editor/parser/parse-js.ts | 22 +-- .../src/nova/ed/panel/popup/script/monaco.tsx | 5 +- .../nova/ed/panel/popup/script/pop-script.tsx | 1 - .../src/nova/ed/panel/popup/script/scope.tsx | 122 ---------------- .../ed/panel/popup/script/scope/add-scope.tsx | 33 +++++ .../panel/popup/script/scope/gen-import.tsx | 3 + .../ed/panel/popup/script/scope/modify.tsx | 133 ++++++++++++++++++ .../popup/script/scope/scope-children.tsx | 3 + .../popup/script/scope/scope-current.tsx | 3 + .../panel/popup/script/scope/scope-parent.tsx | 62 ++++++++ .../ed/panel/popup/script/scope/scope.tsx | 15 ++ .../nova/ed/panel/popup/script/scope/type.ts | 11 ++ .../nova/ed/panel/tree/node/item/action.tsx | 64 +++++++++ .../ed/panel/tree/node/item/action/hide.tsx | 2 +- app/web/src/nova/vi/render/render.tsx | 2 + app/web/src/utils/script/jscript.ts | 2 + 16 files changed, 346 insertions(+), 137 deletions(-) delete mode 100644 app/web/src/nova/ed/panel/popup/script/scope.tsx create mode 100644 app/web/src/nova/ed/panel/popup/script/scope/add-scope.tsx create mode 100644 app/web/src/nova/ed/panel/popup/script/scope/gen-import.tsx create mode 100644 app/web/src/nova/ed/panel/popup/script/scope/modify.tsx create mode 100644 app/web/src/nova/ed/panel/popup/script/scope/scope-children.tsx create mode 100644 app/web/src/nova/ed/panel/popup/script/scope/scope-current.tsx create mode 100644 app/web/src/nova/ed/panel/popup/script/scope/scope-parent.tsx create mode 100644 app/web/src/nova/ed/panel/popup/script/scope/scope.tsx create mode 100644 app/web/src/nova/ed/panel/popup/script/scope/type.ts diff --git a/app/srv/ws/sync/editor/parser/parse-js.ts b/app/srv/ws/sync/editor/parser/parse-js.ts index 51d3f7b0..69bd78e0 100644 --- a/app/srv/ws/sync/editor/parser/parse-js.ts +++ b/app/srv/ws/sync/editor/parser/parse-js.ts @@ -5,8 +5,11 @@ export type ParsedScope = Exclude, undefined>; export const parseJs = (code?: string) => { if (!code) return undefined; - const local = { name: "", value: "", index: 0 }; - const passprop: Record = {}; + const local = { name: "", value: "", start: 0, end: 0 }; + const passprop: Record< + string, + { value: string; start: number; end: number } + > = {}; const result = {} as { local?: typeof local | undefined; passprop?: typeof passprop | undefined; @@ -48,18 +51,16 @@ export const parseJs = (code?: string) => { attr.value.expression.properties.length - 1 ].loc?.end as any; - if ( - typeof start === "number" && - typeof end === "number" && - typeof loc.start.index === "number" - ) { + if (typeof start === "number" && typeof end === "number") { local.value = code.substring(start, end); - local.index = loc.start.index; + local.start = start; + local.end = end; } if (typeof start === "object" && typeof end === "object") { local.value = `{${code.substring(start.index, end.index)}}`; - local.index = loc.start.index; + local.start = loc.start.index; + local.end = loc.end.index; } } } @@ -72,7 +73,8 @@ export const parseJs = (code?: string) => { ) { passprop[attr.name.name] = { value: "0", - index: 0, + start: 0, + end: 0, }; // if (attr.value) { // if ( diff --git a/app/web/src/nova/ed/panel/popup/script/monaco.tsx b/app/web/src/nova/ed/panel/popup/script/monaco.tsx index 6b12ce49..45678327 100644 --- a/app/web/src/nova/ed/panel/popup/script/monaco.tsx +++ b/app/web/src/nova/ed/panel/popup/script/monaco.tsx @@ -4,15 +4,14 @@ import trim from "lodash.trim"; import { FC, useEffect } from "react"; import { compress } from "wasm-gzip"; import { useGlobal, useLocal } from "web-utils"; +import { ParsedScope } from "../../../../../../../srv/ws/sync/editor/parser/parse-js"; import { jscript } from "../../../../../utils/script/jscript"; import { jsMount } from "../../../../../utils/script/mount"; import { monacoTypings } from "../../../../../utils/script/typings"; import { getActiveMeta } from "../../../logic/active/get-meta"; import { EDGlobal, IMeta, active } from "../../../logic/ed-global"; import { edMonacoDefaultVal } from "./default-val"; -import { declareScope } from "./scope"; -import { ParsedScope } from "../../../../../../../srv/ws/sync/editor/parser/parse-js"; -import { ISimpleMeta } from "../../../../vi/utils/types"; +import { declareScope } from "./scope/scope"; const scriptEdit = { timeout: null as any, diff --git a/app/web/src/nova/ed/panel/popup/script/pop-script.tsx b/app/web/src/nova/ed/panel/popup/script/pop-script.tsx index b8657e9c..5561a356 100644 --- a/app/web/src/nova/ed/panel/popup/script/pop-script.tsx +++ b/app/web/src/nova/ed/panel/popup/script/pop-script.tsx @@ -5,7 +5,6 @@ import { Modal } from "../../../../../utils/ui/modal"; import { EDGlobal, active } from "../../../logic/ed-global"; import { propPopover } from "../../side/prop-master/prop-form"; import { EdScriptWorkbench } from "./workbench"; -import { treeRebuild } from "../../../logic/tree/build"; export const EdPopScript = () => { const p = useGlobal(EDGlobal, "EDITOR"); diff --git a/app/web/src/nova/ed/panel/popup/script/scope.tsx b/app/web/src/nova/ed/panel/popup/script/scope.tsx deleted file mode 100644 index c636397a..00000000 --- a/app/web/src/nova/ed/panel/popup/script/scope.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import type { OnMount } from "@monaco-editor/react"; -import { - EPage, - IMeta, - ISingleScope, - PG, - active, -} from "../../../logic/ed-global"; -type Monaco = Parameters[1]; -export type MonacoEditor = Parameters[0]; - -export const declareScope = async ( - p: PG, - meta: IMeta, - editor: MonacoEditor, - monaco: Monaco -) => { - const metas = active.comp_id - ? p.comp.list[active.comp_id]?.meta - : p.page.meta; - - const parents: IMeta[] = []; - let cur = meta; - if (cur && cur.parent) { - while (cur && cur.parent && cur.parent.id) { - if (cur.mitem) { - parents.unshift(cur); - } - cur = metas[cur.parent.id]; - } - } - - for (const m of parents) { - if (active.comp_id && m.parent?.id === "root" && active.instance) { - const meta = p.page.meta[active.instance.item_id]; - if (meta) { - if (!m.scope.def) m.scope.def = {}; - m.scope.def.props = meta.scope?.def?.props; - } - } - - const def = m.scope.def; - if (def) { - if (def.local) { - addScope({ - monaco, - loc: { - item_id: m.item.id, - comp_id: m.parent?.comp_id, - type: "item", - }, - source: `\ -export const {}; -const _local = ${def.local.value}; - -declare global { - const ${def.local.name}: typeof _local & { render: () =>void }; -}`, - }); - } else if (def.passprop) { - Object.keys(def.passprop).map((e) => { - if (e !== "idx" && e !== "key") { - addScope({ - monaco, - loc: { - item_id: m.item.id, - comp_id: m.parent?.comp_id, - type: "item", - }, - source: `\ -export const {}; -declare global { - const ${e} = null as any; -}`, - }); - } - }); - } else if (def.props) { - Object.keys(def.props).map((e) => { - addScope({ - monaco, - loc: { - item_id: m.item.id, - type: "prop", - comp_id: m.parent?.comp_id, - prop_name: e, - }, - source: `\ -export const {}; -declare global { - const ${e} = null as any; -}`, - }); - }); - } - } - } -}; - -const addScope = (arg: { - monaco: Monaco; - loc: { - item_id: string; - type: "prop" | "item"; - comp_id?: string; - prop_name?: string; - }; - source: string; -}) => { - const { monaco, source } = arg; - - const filename = `ts:scope~${JSON.stringify(arg.loc)}.d.ts`; - const model = monaco.editor.getModels().find((e) => { - return e.uri.toString() === filename; - }); - - if (model) { - model.setValue(source); - } else { - monaco.editor.createModel(source, "typescript", monaco.Uri.parse(filename)); - } -}; diff --git a/app/web/src/nova/ed/panel/popup/script/scope/add-scope.tsx b/app/web/src/nova/ed/panel/popup/script/scope/add-scope.tsx new file mode 100644 index 00000000..ea79ced3 --- /dev/null +++ b/app/web/src/nova/ed/panel/popup/script/scope/add-scope.tsx @@ -0,0 +1,33 @@ +import { PG } from "../../../../logic/ed-global"; +import { CodeLoc, Monaco } from "./type"; + +export const addScope = ( + p: PG, + type: "local" | "props" | "passprop", + arg: { + monaco: Monaco; + loc: CodeLoc; + source: string; + } +) => { + const { monaco, source } = arg; + + const filename = `ts:scope~${JSON.stringify(arg.loc)}.d.ts`; + const model = monaco.editor.getModels().find((e) => { + return e.uri.toString() === filename; + }); + + if (model) { + model.setValue(source); + } else { + const model = monaco.editor.createModel( + source, + "typescript", + monaco.Uri.parse(filename) + ); + model.onDidChangeContent((e) => { + const text = model.getValue(); + // modifyJS(p, type, arg.loc, text); + }); + } +}; diff --git a/app/web/src/nova/ed/panel/popup/script/scope/gen-import.tsx b/app/web/src/nova/ed/panel/popup/script/scope/gen-import.tsx new file mode 100644 index 00000000..abd3da24 --- /dev/null +++ b/app/web/src/nova/ed/panel/popup/script/scope/gen-import.tsx @@ -0,0 +1,3 @@ +export const genImport = () => { + return ``; +}; diff --git a/app/web/src/nova/ed/panel/popup/script/scope/modify.tsx b/app/web/src/nova/ed/panel/popup/script/scope/modify.tsx new file mode 100644 index 00000000..8413a71b --- /dev/null +++ b/app/web/src/nova/ed/panel/popup/script/scope/modify.tsx @@ -0,0 +1,133 @@ +import { Doc } from "yjs"; +import { ISimpleMeta } from "../../../../../vi/utils/types"; +import { + IMeta, + PG, + active +} from "../../../../logic/ed-global"; +import { CodeLoc } from "./type"; + +const modif = { + timeout: null as any, + pending: [] as { + type: "local" | "props" | "passprop"; + loc: CodeLoc; + src: string; + }[], +}; + +const modifyJS = ( + p: PG, + type: "local" | "props" | "passprop", + loc: CodeLoc, + src: string +) => { + modif.pending.push({ loc, type, src }); + + clearTimeout(modif.timeout); + modif.timeout = setTimeout(() => { + const map = new Map(); + + for (const item of modif.pending) { + const meta = getMetaByLoc(p, loc); + + if (meta && meta.mitem && meta.mitem.doc) { + let mapset = map.get(meta.mitem.doc); + if (!mapset) { + map.set(meta.mitem.doc, []); + mapset = map.get(meta.mitem.doc); + } + + if (mapset) { + mapset.push({ ...item, meta }); + } + } + } + + map.forEach((items, doc) => { + doc.transact(() => { + for (const item of items) { + const { meta, src, loc } = item; + if (loc.type === "item") { + const js = meta.item.adv?.js; + if (js) { + const text = extractText(type, src); + const def = meta.scope?.def?.[type]; + const smeta = getSMetaByLoc(p, loc); + + if (text) { + if ( + type === "local" && + def && + typeof def.start === "number" && + typeof def.end === "number" + ) { + const final = replaceRange(js, def.start, def.end, text); + def.end = def.start + text.length; + + if (smeta && smeta.scope?.local) { + smeta.scope.local.end = def.end; + } + } + } + } + } + } + }); + }); + modif.pending.length = 0; + }, 500); +}; + +const getSMetaByLoc = (p: PG, loc: CodeLoc) => { + let meta = undefined as ISimpleMeta | undefined; + if (!active.comp_id) { + meta = p.page.smeta[loc.item_id]; + } else { + const comp = p.comp.list[active.comp_id].comp.meta; + if (comp[loc.item_id]) { + meta = comp[loc.item_id]; + } + } + + if (loc.type === "item" && meta) { + return meta; + } +}; + +const getMetaByLoc = (p: PG, loc: CodeLoc) => { + let meta = undefined as IMeta | undefined; + if (!active.comp_id) { + meta = p.page.meta[loc.item_id]; + } else { + const comp = p.comp.list[active.comp_id].meta; + if (comp[loc.item_id]) { + meta = comp[loc.item_id]; + } + } + + if (loc.type === "item" && meta) { + return meta; + } +}; + +const extractText = (type: "local" | "props" | "passprop", src: string) => { + if (type === "local") { + const first = "type _local = "; + return src + .substring( + src.indexOf(first) + first.length, + src.indexOf("declare global {") + ) + .trim(); + } +}; + +function replaceRange( + s: string, + start: number, + end: number, + substitute: string +) { + return s.substring(0, start) + substitute + s.substring(end); +} diff --git a/app/web/src/nova/ed/panel/popup/script/scope/scope-children.tsx b/app/web/src/nova/ed/panel/popup/script/scope/scope-children.tsx new file mode 100644 index 00000000..9085fb1b --- /dev/null +++ b/app/web/src/nova/ed/panel/popup/script/scope/scope-children.tsx @@ -0,0 +1,3 @@ +import { IMeta, PG } from "../../../../logic/ed-global"; + +export const defineScopeChildren = (p: PG, meta: IMeta) => {}; diff --git a/app/web/src/nova/ed/panel/popup/script/scope/scope-current.tsx b/app/web/src/nova/ed/panel/popup/script/scope/scope-current.tsx new file mode 100644 index 00000000..65279f87 --- /dev/null +++ b/app/web/src/nova/ed/panel/popup/script/scope/scope-current.tsx @@ -0,0 +1,3 @@ +import { IMeta, PG } from "../../../../logic/ed-global"; + +export const defineScopeCurrent = (p: PG, meta: IMeta) => {}; diff --git a/app/web/src/nova/ed/panel/popup/script/scope/scope-parent.tsx b/app/web/src/nova/ed/panel/popup/script/scope/scope-parent.tsx new file mode 100644 index 00000000..8d4b5638 --- /dev/null +++ b/app/web/src/nova/ed/panel/popup/script/scope/scope-parent.tsx @@ -0,0 +1,62 @@ +import { IMeta, PG, active } from "../../../../logic/ed-global"; +import { addScope } from "./add-scope"; +import { Monaco } from "./type"; + +export const defineScopeParent = (p: PG, meta: IMeta, monaco: Monaco) => { + const metas = active.comp_id + ? p.comp.list[active.comp_id]?.meta + : p.page.meta; + + const parents: IMeta[] = []; + let cur = meta; + if (cur && cur.parent) { + while (cur && cur.parent && cur.parent.id) { + if (cur.mitem) { + parents.unshift(cur); + } + cur = metas[cur.parent.id]; + } + } + + for (const m of parents) { + if (active.comp_id && m.parent?.id === "root" && active.instance) { + const meta = p.page.meta[active.instance.item_id]; + if (meta) { + if (!m.scope.def) m.scope.def = {}; + m.scope.def.props = meta.scope?.def?.props; + } + } + const def = m.scope.def; + if (def) { + if (def.local) { + addScope(p, "local", { + monaco, + loc: { + item_id: m.item.id, + comp_id: m.parent?.comp_id, + type: "item", + }, + source: `\ +type _local = ${def.local.value}; +export const ${def.local.name}: _local & { render: () =>void }; +`, + }); + } else if (def.passprop) { + Object.entries(def.passprop).map(([e, v]) => { + if (e !== "idx" && e !== "key") { + addScope(p, "passprop", { + monaco, + loc: { + item_id: m.item.id, + comp_id: m.parent?.comp_id, + type: "item", + }, + source: `\ +export const ${e} = ${v.value};`, + }); + } + }); + } + } + } +}; diff --git a/app/web/src/nova/ed/panel/popup/script/scope/scope.tsx b/app/web/src/nova/ed/panel/popup/script/scope/scope.tsx new file mode 100644 index 00000000..55cd6ad4 --- /dev/null +++ b/app/web/src/nova/ed/panel/popup/script/scope/scope.tsx @@ -0,0 +1,15 @@ +import type { OnMount } from "@monaco-editor/react"; +import { IMeta, PG, active } from "../../../../logic/ed-global"; +import { addScope } from "./add-scope"; +import { defineScopeParent } from "./scope-parent"; +type Monaco = Parameters[1]; +export type MonacoEditor = Parameters[0]; + +export const declareScope = async ( + p: PG, + meta: IMeta, + editor: MonacoEditor, + monaco: Monaco +) => { + defineScopeParent(p, meta, monaco); +}; diff --git a/app/web/src/nova/ed/panel/popup/script/scope/type.ts b/app/web/src/nova/ed/panel/popup/script/scope/type.ts new file mode 100644 index 00000000..b890e1a7 --- /dev/null +++ b/app/web/src/nova/ed/panel/popup/script/scope/type.ts @@ -0,0 +1,11 @@ +import type { OnMount } from "@monaco-editor/react"; + +export type CodeLoc = { + item_id: string; + type: "prop" | "item"; + comp_id?: string; + prop_name?: string; +}; + +export type Monaco = Parameters[1]; +export type MonacoEditor = Parameters[0]; diff --git a/app/web/src/nova/ed/panel/tree/node/item/action.tsx b/app/web/src/nova/ed/panel/tree/node/item/action.tsx index 1500f1b6..ce431394 100644 --- a/app/web/src/nova/ed/panel/tree/node/item/action.tsx +++ b/app/web/src/nova/ed/panel/tree/node/item/action.tsx @@ -2,6 +2,7 @@ import { NodeModel, RenderParams } from "@minoru/react-dnd-treeview"; import { useGlobal } from "web-utils"; import { Tooltip } from "../../../../../../utils/ui/tooltip"; import { EDGlobal, IMeta, active } from "../../../../logic/ed-global"; +import { getMetaById } from "../../../../logic/active/get-meta"; export const EdTreeAction = ({ node, @@ -25,6 +26,35 @@ export const EdTreeAction = ({ return (
+ {item.hidden === "all" && ( + +
{ + e.stopPropagation(); + const meta = getMetaById(p, item.id); + if (meta) meta.mitem?.set("hidden", false); + }} + > + +
+
+ )} + {item.hidden === "only-editor" && ( + +
{ + e.stopPropagation(); + const meta = getMetaById(p, item.id); + if (meta) meta.mitem?.set("hidden", "all"); + }} + > + +
+
+ )} + {(!comp.enabled || (comp.enabled && comp.id === active.comp_id)) && ( ); }; + +const HideEditor = () => ( + + + +); + +const HideAll = () => ( + + + +); diff --git a/app/web/src/nova/ed/panel/tree/node/item/action/hide.tsx b/app/web/src/nova/ed/panel/tree/node/item/action/hide.tsx index 1939cbc4..e05009b3 100644 --- a/app/web/src/nova/ed/panel/tree/node/item/action/hide.tsx +++ b/app/web/src/nova/ed/panel/tree/node/item/action/hide.tsx @@ -11,7 +11,7 @@ export const edActionHide = ( if (mitem) { const hidden = mitem.get("hidden"); if (mode === "toggle") { - if (!hidden) mitem.set("hidden", "all"); + if (!hidden) mitem.set("hidden", "only-editor"); else mitem.delete("hidden"); } else { if (!hidden) mitem.set("hidden", "all"); diff --git a/app/web/src/nova/vi/render/render.tsx b/app/web/src/nova/vi/render/render.tsx index 20ce760a..9edc639d 100644 --- a/app/web/src/nova/vi/render/render.tsx +++ b/app/web/src/nova/vi/render/render.tsx @@ -12,6 +12,8 @@ export const ViRender: FC<{ }> = ({ meta, children }) => { if (!meta) return null; + if (meta.item.hidden) return null; + if (meta.item.adv?.js || meta.item.component?.id) { return {children}; } diff --git a/app/web/src/utils/script/jscript.ts b/app/web/src/utils/script/jscript.ts index 6834bb4f..4c92081e 100644 --- a/app/web/src/utils/script/jscript.ts +++ b/app/web/src/utils/script/jscript.ts @@ -2,6 +2,7 @@ import type { Editor as MonacoEditor } from "@monaco-editor/react"; import type Prettier from "prettier/standalone"; import type estree from "prettier/plugins/estree"; import type ts from "prettier/plugins/typescript"; +import { PG } from "../../nova/ed/logic/ed-global"; export type FBuild = ( entryFileName: string, src: string, @@ -15,6 +16,7 @@ export const initJS = async () => { }; export const jscript = { + reload: (p: PG) => {}, editor: null as typeof MonacoEditor | null, build: null as null | FBuild, pending: null as null | Promise,