diff --git a/app/srv/ws/sync/actions.ts b/app/srv/ws/sync/actions.ts index 1a542b6b..3129f09a 100644 --- a/app/srv/ws/sync/actions.ts +++ b/app/srv/ws/sync/actions.ts @@ -125,7 +125,7 @@ export const SyncActions = { prop_name: string; value: Uint8Array; } - ) => ({}) as boolean | ParsedScope, + ) => ({}) as boolean | ParsedScope | string, parse: async (code: string | Record) => ({}) as Record>, }, diff --git a/app/srv/ws/sync/actions/code_edit.ts b/app/srv/ws/sync/actions/code_edit.ts index 8a58617c..76652dfc 100644 --- a/app/srv/ws/sync/actions/code_edit.ts +++ b/app/srv/ws/sync/actions/code_edit.ts @@ -41,41 +41,45 @@ export const code_edit: SAction["code"]["edit"] = async function ( if (mitem) { if (arg.type === "adv") { - const res = await transform(`render(${src})`, { - jsx: "transform", - format: "cjs", - loader: "tsx", - }); - doc?.transact(() => { - const mode = arg.mode; - let adv = mitem.get("adv"); - if (!adv) { - mitem.set("adv", new Y.Map() as any); - adv = mitem.get("adv"); - } + try { + const res = await transform(`render(${src})`, { + jsx: "transform", + format: "cjs", + loader: "tsx", + }); + doc?.transact(() => { + const mode = arg.mode; + let adv = mitem.get("adv"); + if (!adv) { + mitem.set("adv", new Y.Map() as any); + adv = mitem.get("adv"); + } - if (adv) { - try { - if (adv) { - adv.set(mode, src); - if (mode === "js") { - adv.set("jsBuilt", res.code); + if (adv) { + try { + if (adv) { + adv.set(mode, src); + if (mode === "js") { + adv.set("jsBuilt", res.code); + } + } + } catch (e) { + g.log.error(e); + } + + if (mode === "js") { + const res = parseJs(adv.get("js")) || false; + if (res) { + mitem.set("script", res); + } else { + mitem.delete("script"); } } - } catch (e) { - g.log.error(e); } - - if (mode === "js") { - const res = parseJs(adv.get("js")) || false; - if (res) { - mitem.set("script", res); - } else { - mitem.delete("script"); - } - } - } - }); + }); + } catch (e: any) { + return e.message.toString(); + } } else { const mprop = mitem .get("component") @@ -94,7 +98,9 @@ export const code_edit: SAction["code"]["edit"] = async function ( mprop.set("value", src); mprop.set("valueBuilt", res.code.substring(6)); }); - } catch (e) {} + } catch (e: any) { + return e.message.toString(); + } } } } @@ -134,8 +140,8 @@ export const code_edit: SAction["code"]["edit"] = async function ( } } }); - } catch (e) { - g.log.error(e); + } catch (e: any) { + return e.message.toString(); } } } diff --git a/app/web/src/nova/ed/ed-base.tsx b/app/web/src/nova/ed/ed-base.tsx index 5685a53a..8e528e2b 100644 --- a/app/web/src/nova/ed/ed-base.tsx +++ b/app/web/src/nova/ed/ed-base.tsx @@ -28,7 +28,7 @@ export const EdBase = () => { edRoute(p); - if (p.status === "post-init") { + if (p.status === "load-site") { return ; } if (p.status === "site-not-found" || p.status === "page-not-found") { @@ -47,7 +47,12 @@ export const EdBase = () => {
-
+
@@ -138,3 +143,18 @@ const style = css` } } `; + +export const mobileCSS = css` + background-color: white; + background-image: linear-gradient(45deg, #fafafa 25%, transparent 25%), + linear-gradient(-45deg, #fafafa 25%, transparent 25%), + linear-gradient(45deg, transparent 75%, #fafafa 75%), + linear-gradient(-45deg, transparent 75%, #fafafa 75%); + + background-size: 20px 20px; + background-position: + 0 0, + 0 10px, + 10px -10px, + -10px 0px; +`; diff --git a/app/web/src/nova/ed/ed-right.tsx b/app/web/src/nova/ed/ed-right.tsx index 3b0f4fdd..bdb9468b 100644 --- a/app/web/src/nova/ed/ed-right.tsx +++ b/app/web/src/nova/ed/ed-right.tsx @@ -19,7 +19,7 @@ export const EdRight = () => { css` width: ${p.ui.layout.right}px; `, - "border-l flex flex-col" + "border-l flex flex-col bg-white" )} > {!meta ? ( diff --git a/app/web/src/nova/ed/logic/ed-global.ts b/app/web/src/nova/ed/logic/ed-global.ts index 42f14d76..f26017d3 100644 --- a/app/web/src/nova/ed/logic/ed-global.ts +++ b/app/web/src/nova/ed/logic/ed-global.ts @@ -132,7 +132,7 @@ export const EDGlobal = { clients: {} as Record, status: "init" as | "init" - | "post-init" + | "load-site" | "reload" | "site-not-found" | "page-not-found" @@ -228,6 +228,8 @@ export const EDGlobal = { prop_kind: "" as PropFieldKind, prop_name: "", on_close: () => {}, + typings: { status: "ok" as "ok" | "loading" | "error", err_msg: "" }, + wb_render: () => {}, }, site: null as null | ((site_id: string) => void | Promise), site_form: null as null | { diff --git a/app/web/src/nova/ed/logic/ed-route.ts b/app/web/src/nova/ed/logic/ed-route.ts index 5a138d84..785b7ead 100644 --- a/app/web/src/nova/ed/logic/ed-route.ts +++ b/app/web/src/nova/ed/logic/ed-route.ts @@ -8,7 +8,7 @@ import { treeRebuild } from "./tree/build"; export const edRoute = async (p: PG) => { if (p.status === "ready" || p.status === "init") { if (!p.site.domain && !p.site.name) { - p.status = "post-init"; + p.status = "load-site"; const site = await p.sync.site.load(p.site.id); if (!site) { p.status = "site-not-found"; diff --git a/app/web/src/nova/ed/panel/header/right/responsive-toggle.tsx b/app/web/src/nova/ed/panel/header/right/responsive-toggle.tsx index 14275ef8..fcf3f8ce 100644 --- a/app/web/src/nova/ed/panel/header/right/responsive-toggle.tsx +++ b/app/web/src/nova/ed/panel/header/right/responsive-toggle.tsx @@ -7,63 +7,70 @@ export const ResponsiveToggle = () => { const c = useGlobal(EDGlobal, "EDITOR"); const mode = c.mode; const activeModeClassName = "border-b-2 border-blue-500"; - return ( - - - - ), - }, - { - onClick() { - c.mode = "desktop"; - w.isMobile = false; - w.isDesktop = true; - localStorage.setItem("prasi-editor-mode", "desktop"); - c.render(); - }, - className: cx(mode === "desktop" && activeModeClassName), - content: ( - - - - ), - }, - ]} - /> - ); + + const box = { + mobile: { + onClick() { + c.mode = "mobile"; + w.isMobile = true; + w.isDesktop = false; + localStorage.setItem("prasi-editor-mode", "mobile"); + c.render(); + }, + className: cx(mode === "mobile" && activeModeClassName), + content: ( + + + + ), + }, + desktop: { + onClick() { + c.mode = "desktop"; + w.isMobile = false; + w.isDesktop = true; + localStorage.setItem("prasi-editor-mode", "desktop"); + c.render(); + }, + className: cx(mode === "desktop" && activeModeClassName), + content: ( + + + + ), + }, + }; + const items: any[] = []; + if (c.site.responsive === "mobile-only") { + items.push(box.mobile); + } else if (c.site.responsive === "desktop-only") { + items.push(box.desktop); + } else { + items.push(box.mobile); + items.push(box.desktop); + } + + return ; }; diff --git a/app/web/src/nova/ed/panel/main/main-per-item.tsx b/app/web/src/nova/ed/panel/main/main-per-item.tsx index 22a24db7..df931330 100644 --- a/app/web/src/nova/ed/panel/main/main-per-item.tsx +++ b/app/web/src/nova/ed/panel/main/main-per-item.tsx @@ -75,6 +75,9 @@ export const mainPerItemVisit = ( } } }; + if (parts.props.className && !parts.props.className.includes("el-active")) { + parts.props.className += " el-active"; + } prop.onInput = (e) => { e.stopPropagation(); @@ -160,10 +163,6 @@ export const mainPerItemVisit = ( }; }; -function getCaret(el: any) { - return getSelectionOffset(el); -} - function setCaret(el: any, offset: any) { setSelectionOffset(el, offset[0], offset[1]); } diff --git a/app/web/src/nova/ed/panel/main/main.tsx b/app/web/src/nova/ed/panel/main/main.tsx index f350bda7..43a0a3d2 100644 --- a/app/web/src/nova/ed/panel/main/main.tsx +++ b/app/web/src/nova/ed/panel/main/main.tsx @@ -60,9 +60,7 @@ export const EdMain = () => {
{ if (el) { @@ -75,23 +73,39 @@ export const EdMain = () => { } }} > - {meta &&
{local.cache}
} +
{local.cache}
); }; -const mainStyle = ( - p: PG, - meta: IMeta, - local: { width: number; height: number } -) => { +const mainStyle = (p: PG, meta?: IMeta) => { let is_active = meta ? isMetaActive(p, meta) : false; const scale = parseInt(p.ui.zoom.replace("%", "")) / 100; + + let width = `${(1 / scale) * 100}%`; + if (p.mode === "mobile") { + width = `${(1 / scale) * 375}px`; + } + return cx( - "absolute inset-0 flex", + "absolute flex", css` - width: ${(1 / scale) * 100}%; + contain: content; + `, + p.mode === "mobile" + ? css` + border-left: 1px solid #ccc; + border-right: 1px solid #ccc; + background: white; + top: 0px; + overflow-x: hidden; + overflow-y: auto; + bottom: 0px; + ` + : "inset-0", + css` + width: ${width}; transform: scale(${scale}); transform-origin: 0% 0% 0px; `, 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 5f910647..00b9d6b2 100644 --- a/app/web/src/nova/ed/panel/popup/script/monaco.tsx +++ b/app/web/src/nova/ed/panel/popup/script/monaco.tsx @@ -288,6 +288,8 @@ export const EdScriptMonaco: FC<{}> = () => { } onChange={(value) => { const stype = p.ui.popup.script.type; + p.ui.popup.script.typings.status = "loading"; + p.ui.popup.script.wb_render(); if ((value || "").includes(IMPORT_SEPARATOR)) { const valparts = (value || "").split(IMPORT_SEPARATOR + "\n"); if (valparts.length === 2) local.value = valparts[1]; @@ -301,7 +303,7 @@ export const EdScriptMonaco: FC<{}> = () => { `export const ${p.ui.popup.script.prop_name} = `.length ); } - } else { + } else { local.value = value || ""; } local.render(); @@ -318,6 +320,8 @@ export const EdScriptMonaco: FC<{}> = () => { arg.page_id = p.page.cur.id; } let scope: boolean | ParsedScope = false; + p.ui.popup.script.typings.status = "ok"; + p.ui.popup.script.typings.err_msg = ""; if (stype === "prop-master") { p.sync.code.edit({ type: "prop-master", @@ -327,7 +331,7 @@ export const EdScriptMonaco: FC<{}> = () => { ...arg, }); } else if (stype === "prop-instance") { - scope = await p.sync.code.edit({ + const code_result = await p.sync.code.edit({ type: "prop-instance", mode: mode, prop_name: p.ui.popup.script.prop_name, @@ -335,18 +339,32 @@ export const EdScriptMonaco: FC<{}> = () => { value: compress(encode.encode(value || "")), ...arg, }); + if (typeof code_result === "string") { + p.ui.popup.script.typings.status = "error"; + p.ui.popup.script.typings.err_msg = code_result; + } else if (typeof code_result === "object") { + scope = code_result; + } } else { - scope = await p.sync.code.edit({ + const code_result = await p.sync.code.edit({ type: "adv", mode: mode, item_id: active.item_id, value: compress(encode.encode(value || "")), ...arg, }); + + if (typeof code_result === "string") { + p.ui.popup.script.typings.status = "error"; + p.ui.popup.script.typings.err_msg = code_result; + } else if (typeof code_result === "object") { + scope = code_result; + } } if (typeof scope === "object") { meta.item.script = scope; } + p.ui.popup.script.wb_render(); } }; p.ui.popup.script.on_close = () => { diff --git a/app/web/src/nova/ed/panel/popup/script/workbench.tsx b/app/web/src/nova/ed/panel/popup/script/workbench.tsx index 55a6f41c..b4a7ce20 100644 --- a/app/web/src/nova/ed/panel/popup/script/workbench.tsx +++ b/app/web/src/nova/ed/panel/popup/script/workbench.tsx @@ -5,10 +5,12 @@ import { EdScriptMonaco } from "./monaco"; import { EdScriptSnippet } from "./snippet"; import { useEffect } from "react"; import { Loading } from "../../../../../utils/ui/loading"; +import { Tooltip } from "../../../../../utils/ui/tooltip"; export const EdScriptWorkbench = () => { const p = useGlobal(EDGlobal, "EDITOR"); const local = useLocal({ active_id: "" }); + p.ui.popup.script.wb_render = local.render; useEffect(() => { if (!local.active_id) { @@ -27,103 +29,144 @@ export const EdScriptWorkbench = () => { canBack: active.script_nav.list.length > 0, }; + const is_error = + p.ui.popup.script.typings.status === "error" && + p.ui.popup.script.mode === "js"; return (
-
-
-
{ - if (scriptNav.canBack) { - active.script_nav.idx = active.script_nav.idx - 1; - const item = active.script_nav.list[active.script_nav.idx]; - - if (item) { - active.item_id = item.item_id; - active.comp_id = item.comp_id || ""; - active.instance = { - item_id: item.instance?.item_id || "", - comp_id: item.instance?.comp_id || "", - }; - p.render(); - } - } - }} - > - -
-
{ - if (scriptNav.canNext) { - active.script_nav.idx = active.script_nav.idx + 1; - const item = active.script_nav.list[active.script_nav.idx]; - - if (item) { - active.item_id = item.item_id; - active.comp_id = item.comp_id || ""; - active.instance = { - item_id: item.instance?.item_id || "", - comp_id: item.instance?.comp_id || "", - }; - p.render(); - } - } - }} - > - -
-
- {p.ui.popup.script.type === "prop-master" && } - {p.ui.popup.script.type === "prop-instance" && } - {p.ui.popup.script.type === "item" && ( - <> -
- {[ - { type: "js", color: "#e9522c" }, - { type: "css", color: "#188228" }, - { type: "html", color: "#2c3e83" }, - ].map((e) => { - return ( -
{ - p.ui.popup.script.mode = e.type as any; - p.render(); - }} - > - {e.type} -
- ); - })} -
- {p.ui.popup.script.mode === "js" && - p.ui.popup.script.type === "item" && } - +
+
+
+
{ + if (scriptNav.canBack) { + active.script_nav.idx = active.script_nav.idx - 1; + const item = active.script_nav.list[active.script_nav.idx]; + + if (item) { + active.item_id = item.item_id; + active.comp_id = item.comp_id || ""; + active.instance = { + item_id: item.instance?.item_id || "", + comp_id: item.instance?.comp_id || "", + }; + p.render(); + } + } + }} + > + +
+
{ + if (scriptNav.canNext) { + active.script_nav.idx = active.script_nav.idx + 1; + const item = active.script_nav.list[active.script_nav.idx]; + + if (item) { + active.item_id = item.item_id; + active.comp_id = item.comp_id || ""; + active.instance = { + item_id: item.instance?.item_id || "", + comp_id: item.instance?.comp_id || "", + }; + p.render(); + } + } + }} + > + +
+
+ {p.ui.popup.script.type === "prop-master" && } + {p.ui.popup.script.type === "prop-instance" && ( + + )} + {p.ui.popup.script.type === "item" && ( + <> +
+ {[ + { type: "js", color: "#e9522c" }, + { type: "css", color: "#188228" }, + { type: "html", color: "#2c3e83" }, + ].map((e) => { + return ( +
{ + p.ui.popup.script.mode = e.type as any; + p.render(); + }} + > + {e.type} +
+ ); + })} +
+ {p.ui.popup.script.mode === "js" && + p.ui.popup.script.type === "item" && } + + )} +
+
+ {p.ui.popup.script.mode === "js" && ( +
+ { + ( + { + ok:
Typings: OK
, + loading:
Loading ⌛
, + error: ( + + {p.ui.popup.script.typings.err_msg} +
+ } + delay={0} + > +
+ ⚠️ Typings: ERROR +
+ + ), + } as any + )[p.ui.popup.script.typings.status] + } +
+ )} +
+
{local.active_id === active.item_id ? ( diff --git a/app/web/src/nova/ed/panel/tree/node/item/action/del.tsx b/app/web/src/nova/ed/panel/tree/node/item/action/del.tsx index ebf1226c..550a9427 100644 --- a/app/web/src/nova/ed/panel/tree/node/item/action/del.tsx +++ b/app/web/src/nova/ed/panel/tree/node/item/action/del.tsx @@ -9,11 +9,15 @@ export const edActionDelete = async (p: PG, item: IContent) => { if (meta) { const mitem = meta.mitem; if (mitem) { - mitem.parent.forEach((e, k) => { - if (e == mitem) { - deleteByParent(p, mitem, k); - } + mitem.doc?.transact(() => { + mitem.parent.forEach((e, k) => { + if (e == mitem) { + deleteByParent(p, mitem, k); + } + }); }); + await treeRebuild(p); + p.render(); } } }; @@ -23,17 +27,19 @@ export const edActionDeleteById = async (p: PG, id: string) => { if (meta) { const mitem = meta.mitem; if (mitem) { - mitem.parent.forEach((e, k) => { - if (e == mitem) { - deleteByParent(p, mitem, k); - } + mitem.doc?.transact(() => { + mitem.parent.forEach((e, k) => { + if (e == mitem) { + deleteByParent(p, mitem, k); + } + }); }); + await treeRebuild(p); + p.render(); } } }; const deleteByParent = (p: PG, mitem: MItem, index: number) => { - const mchild = mitem.parent.get(index); mitem.parent.delete(index); - treeRebuild(p); }; diff --git a/app/web/src/nova/ed/panel/tree/node/render.tsx b/app/web/src/nova/ed/panel/tree/node/render.tsx index d813cfb4..cd929647 100644 --- a/app/web/src/nova/ed/panel/tree/node/render.tsx +++ b/app/web/src/nova/ed/panel/tree/node/render.tsx @@ -122,12 +122,14 @@ export const nodeRender: NodeRender = (node, prm) => { active.item_id = item.id; p.ui.tree.search = ""; p.render(); + if ((item as IContent).type === "text") { setTimeout(() => { if (document.activeElement?.tagName === "INPUT") { return; } const el_active = document.querySelector(".el-active") as any; + if (el_active) { setEndOfContenteditable(el_active); }