diff --git a/app/srv/ws/edit/action/get-comp.ts b/app/srv/ws/edit/action/get-comp.ts index 44a0b809..e5b5d340 100644 --- a/app/srv/ws/edit/action/get-comp.ts +++ b/app/srv/ws/edit/action/get-comp.ts @@ -1,15 +1,19 @@ -import { Websocket } from "hyper-express"; +import { ServerWebSocket } from "bun"; import { compress } from "lz-string"; import { syncronize } from "y-pojo"; import * as Y from "yjs"; +import { WSData } from "../../../../../pkgs/core/server/create"; +import { SingleComp, eg } from "../edit-global"; import { WS_MSG_GET_COMP, WS_MSG_SET_COMP, WS_MSG_SV_LOCAL, -} from "../../../web/src/utils/types/ws"; -import { SingleComp, eg } from "../edit-global"; +} from "../../../../web/src/utils/types/ws"; -export const getComp = async (ws: Websocket, msg: WS_MSG_GET_COMP) => { +export const getComp = async ( + ws: ServerWebSocket, + msg: WS_MSG_GET_COMP +) => { const comp_id = msg.comp_id; if (!eg.edit.comp[comp_id]) { @@ -17,6 +21,15 @@ export const getComp = async (ws: Websocket, msg: WS_MSG_GET_COMP) => { where: { id: comp_id, }, + select: { + component_group: true, + content_tree: true, + id: true, + id_component_group: true, + name: true, + props: true, + type: true, + }, }); if (!rawComp) { @@ -34,7 +47,7 @@ export const getComp = async (ws: Websocket, msg: WS_MSG_GET_COMP) => { const map = ydoc.getMap("map"); syncronize(map as any, rawComp); - const ws = new Set(); + const ws = new Set>(); const um = new Y.UndoManager(map, { ignoreRemoteMapChanges: true }); const broadcast = () => { const sv_local = compress(Y.encodeStateVector(ydoc as any).toString()); diff --git a/app/srv/ws/edit/tools/validate-tree-page.ts b/app/srv/ws/edit/tools/validate-tree-page.ts deleted file mode 100644 index ad2f7e1f..00000000 --- a/app/srv/ws/edit/tools/validate-tree-page.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Websocket } from "hyper-express"; -import { syncronize } from "y-pojo"; -import * as Y from "yjs"; -import { fillID } from "../../../web/src/utils/page/tools/fill-id"; -import { IContent, MContent } from "../../../web/src/utils/types/general"; -import { IItem } from "../../../web/src/utils/types/item"; -import { IRoot } from "../../../web/src/utils/types/root"; -import { getComp } from "../action/get-comp"; -import { eg } from "../edit-global"; - -const MAX_STRING_LENGTH = 15000; -export const validateTreeMap = async ( - ws: Websocket, - item: MContent, - changed?: boolean -) => { - let _changed = changed; - const type = item.get("type") as IContent["type"] | "root"; - if (type !== "root") { - item.forEach((val, key, map) => { - if (typeof val === "string") { - if (val.length > MAX_STRING_LENGTH) { - map.set(key, ""); - _changed = true; - } - } else { - if (typeof val === "object" && val instanceof Y.Map) { - val._map.forEach((ival, ikey, imap) => { - if (typeof ival === "string") { - if ((ival as string).length > MAX_STRING_LENGTH) { - imap.set(ikey, "" as any); - _changed = true; - } - } - }); - } - } - }); - } - - if (item) { - if (type !== "text") { - const childs = item.get("childs"); - if (childs) { - for (const c of childs) { - if (await validateTreeMap(ws, c)) { - _changed = true; - } - } - } - } - } - return !!_changed; -}; diff --git a/app/web/src/base/page/editor.tsx b/app/web/src/base/page/editor.tsx index 90d56ed8..ecb312fe 100644 --- a/app/web/src/base/page/editor.tsx +++ b/app/web/src/base/page/editor.tsx @@ -1,19 +1,19 @@ -import { Suspense, lazy, useEffect } from "react"; -import { page, useLocal } from "web-utils"; +import { FC, useEffect } from "react"; +import { page, useGlobal, useLocal } from "web-utils"; +import { EditorGlobal } from "../../render/editor/logic/global"; import { Loading } from "../../utils/ui/loading"; -const Editor = lazy(async () => ({ - default: (await import("../../render/editor/editor")).Editor, -})); - export default page({ url: "/editor/:site_id/:page_id", component: ({}) => { + const p = useGlobal(EditorGlobal, "EDITOR"); + const local = useLocal({ loading: true, session: null as any, notfound: false, init: false, + Editor: null as null | FC, }); const site_id = params.site_id === "_" ? "" : params.site_id; const page_id = params.page_id === "_" ? "" : params.page_id; @@ -21,6 +21,10 @@ export default page({ useEffect(() => { if (!local.init) { (async () => { + if (!local.Editor) { + local.Editor = (await import("../../render/editor/editor")).Editor; + } + let ses: any = null; try { ses = JSON.parse(localStorage.getItem("prasi-session") || ""); @@ -129,12 +133,11 @@ export default page({ } }, [local.init]); - if (local.loading) return ; + const Editor = local.Editor; + if (local.loading || !Editor) return ; return ( - }> - - + ); }, }); diff --git a/app/web/src/base/root.tsx b/app/web/src/base/root.tsx index 2c156cbd..c240ce98 100644 --- a/app/web/src/base/root.tsx +++ b/app/web/src/base/root.tsx @@ -27,6 +27,7 @@ export const Root: FC<{}> = ({}) => { ); prasiContext.render = local.render; + const Provider = GlobalContext.Provider as FC<{ value: any; children: any }>; const found = local.router.lookup(location.pathname); diff --git a/app/web/src/render/editor/editor.tsx b/app/web/src/render/editor/editor.tsx index 7326d137..6602c042 100644 --- a/app/web/src/render/editor/editor.tsx +++ b/app/web/src/render/editor/editor.tsx @@ -79,7 +79,7 @@ export const Editor: FC<{ site_id: string; page_id: string; session: any }> = ({ useEffect(() => { if (p.status !== "init" && w.prasiApi) { for (const [k, v] of Object.entries(deepClone(EditorGlobal))) { - if (k === "session" || k === "site") continue; + if (k === "session" || k === "site" || "status") continue; (p as any)[k] = v; } @@ -99,6 +99,7 @@ export const Editor: FC<{ site_id: string; page_id: string; session: any }> = ({ } if (p.status === "init") { + (window as any).mok = ((window as any).mok || 0) + 1; p.ui.loading = ; p.ui.preload = ; p.ui.notfound = ( @@ -109,6 +110,7 @@ export const Editor: FC<{ site_id: string; page_id: string; session: any }> = ({ PREVIEW ERROR ); + p.status = "loading"; initEditor(p, site_id); } diff --git a/app/web/src/render/editor/logic/init.ts b/app/web/src/render/editor/logic/init.ts index e3cb024d..273b8320 100644 --- a/app/web/src/render/editor/logic/init.ts +++ b/app/web/src/render/editor/logic/init.ts @@ -32,119 +32,119 @@ export const w = window as unknown as { }; export const initEditor = async (p: PG, site_id: string) => { - if (p.status === "init") { - p.status = "loading"; - w.isEditor = true; - if (typeof w.isLayout === "undefined") { - w.isLayout = false; - } - w.isMobile = p.mode === "mobile"; - w.isDesktop = p.mode === "desktop"; - w.apiHeaders = {}; - w.preload = () => {}; + w.isEditor = true; + if (typeof w.isLayout === "undefined") { + w.isLayout = false; + } + w.isMobile = p.mode === "mobile"; + w.isDesktop = p.mode === "desktop"; + w.apiHeaders = {}; + w.preload = () => {}; - w.navigateOverride = (_href) => { - if (_href.startsWith("/ed")) return _href; - return ""; - }; + w.navigateOverride = (_href) => { + if (_href.startsWith("/ed")) return _href; + return ""; + }; - p.item.active = localStorage.getItem("prasi-item-active-id") || ""; - p.item.activeOriginalId = - localStorage.getItem("prasi-item-active-oid") || ""; - const comp: any = { - id: localStorage.getItem("prasi-comp-active-id"), - instance_id: localStorage.getItem("prasi-comp-instance-id"), - last: localStorage.getItem("prasi-comp-active-last"), - props: localStorage.getItem("prasi-comp-active-props"), - }; - if (comp.last) { - comp.last = JSON.parse(comp.last); + p.item.active = localStorage.getItem("prasi-item-active-id") || ""; + p.item.activeOriginalId = localStorage.getItem("prasi-item-active-oid") || ""; + const comp: any = { + id: localStorage.getItem("prasi-comp-active-id"), + instance_id: localStorage.getItem("prasi-comp-instance-id"), + last: localStorage.getItem("prasi-comp-active-last"), + props: localStorage.getItem("prasi-comp-active-props"), + }; + if (comp.last) { + comp.last = JSON.parse(comp.last); + } + if (comp.props) { + comp.props = JSON.parse(comp.props); + } + if (comp.id) { + p.comp = comp; + } + + let site = null as any; + try { + site = JSON.parse(localStorage.getItem(`prasi-site-${site_id}`) || ""); + } catch (e) {} + + const querySite = async () => { + const site = await defaultLoader.site(p as any, { id: site_id }); + + localStorage.setItem(`prasi-site-${site_id}`, JSON.stringify(site)); + return site; + }; + const processSite = async (site: LSite) => { + if (!w.exports) { + w.exports = {}; } - if (comp.props) { - comp.props = JSON.parse(comp.props); - } - if (comp.id) { - p.comp = comp; + if (site.cgroup_ids) { + for (const id of site.cgroup_ids) { + await importModule(`${serverurl}/npm/site/${id}/site.js`); + } } - let site = null as any; - try { - site = JSON.parse(localStorage.getItem(`prasi-site-${site_id}`) || ""); - } catch (e) {} + await importModule(`${serverurl}/npm/site/${site.id}/site.js`); + p.lsite = site; + p.site.id = site.id; + p.site.js = site.js || ""; + p.site.js_compiled = site.js_compiled || ""; + p.site.name = site.name; + p.site.domain = site.domain; + p.site.responsive = site.responsive as any; + p.site.layout = site.layout; + p.site.layout_id = site.layout_id; - const querySite = async () => { - const site = await defaultLoader.site(p as any, { id: site_id }); - localStorage.setItem(`prasi-site-${site_id}`, JSON.stringify(site)); - return site; + await validateLayout(p); + + w.externalAPI = { + mode: (localStorage.getItem(`prasi-ext-api-mode-${p.site.id}`) || + "prod") as any, + devUrl: localStorage.getItem(`prasi-ext-dev-url-${p.site.id}`) || "", + prodUrl: localStorage.getItem(`prasi-ext-prod-url-${p.site.id}`) || "", }; - const processSite = async (site: LSite) => { - if (!w.exports) { - w.exports = {}; - } - if (site.cgroup_ids) { - for (const id of site.cgroup_ids) { - await importModule(`${serverurl}/npm/site/${id}/site.js`); - } - } - await importModule(`${serverurl}/npm/site/${site.id}/site.js`); - p.lsite = site; - p.site.id = site.id; - p.site.js = site.js || ""; - p.site.js_compiled = site.js_compiled || ""; - p.site.name = site.name; - p.site.domain = site.domain; - p.site.responsive = site.responsive as any; - p.site.layout = site.layout; - p.site.layout_id = site.layout_id; + p.site.api_url = await initApi(site.config); - await validateLayout(p); + if (w.externalAPI.prodUrl !== p.site.api_url) { + w.externalAPI.prodUrl = p.site.api_url; + localStorage.setItem(`prasi-ext-prod-url-${p.site.id}`, p.site.api_url); + } + if (w.externalAPI.mode === "dev" && w.externalAPI.devUrl) { + p.site.api_url = w.externalAPI.devUrl; + await reloadDBAPI(w.externalAPI.devUrl); + } - w.externalAPI = { - mode: (localStorage.getItem(`prasi-ext-api-mode-${p.site.id}`) || - "prod") as any, - devUrl: localStorage.getItem(`prasi-ext-dev-url-${p.site.id}`) || "", - prodUrl: localStorage.getItem(`prasi-ext-prod-url-${p.site.id}`) || "", - }; + w.apiurl = p.site.api_url; + api.site_dts(p.site.id).then((e: any) => { + p.site_dts = e || ""; + p.render(); + }); + const configLocal: any = get(site, "config.prasi"); + if (configLocal) { + p.site.api_prasi.db = configLocal.dburl ? configLocal.dburl : ""; + p.site.api_prasi.port = configLocal.port ? configLocal.port : ""; + } + execSiteJS(p); + }; - p.site.api_url = await initApi(site.config); - - if (w.externalAPI.prodUrl !== p.site.api_url) { - w.externalAPI.prodUrl = p.site.api_url; - localStorage.setItem(`prasi-ext-prod-url-${p.site.id}`, p.site.api_url); - } - if (w.externalAPI.mode === "dev" && w.externalAPI.devUrl) { - p.site.api_url = w.externalAPI.devUrl; - await reloadDBAPI(w.externalAPI.devUrl); - } - - w.apiurl = p.site.api_url; - api.site_dts(p.site.id).then((e) => { - p.site_dts = e || ""; - p.render(); - }); - const configLocal: any = get(site, "config.prasi"); - if (configLocal) { - p.site.api_prasi.db = configLocal.dburl ? configLocal.dburl : ""; - p.site.api_prasi.port = configLocal.port ? configLocal.port : ""; - } - execSiteJS(p); - }; - if (!site || (site && !site.id)) { - const site = await querySite(); + if (!site || (site && !site.id)) { + const site = await querySite(); + if (site) { await processSite(site); - } else { - await processSite(site); - querySite(); } + } else { + await processSite(site); + querySite(); + } - p.status = "ready"; - p.render(); + p.status = "ready"; + p.render(); - if (!jscript.build) { - jscript.init(); - } + if (!jscript.build) { + jscript.init(); } }; diff --git a/app/web/src/render/live/logic/default-loader.tsx b/app/web/src/render/live/logic/default-loader.tsx index 53d1d5fb..7124fe7f 100644 --- a/app/web/src/render/live/logic/default-loader.tsx +++ b/app/web/src/render/live/logic/default-loader.tsx @@ -18,6 +18,10 @@ export const defaultLoader: Loader = { }, })) as unknown as LSite; + if (!site) { + return null; + } + const cgroups = await db.site_use_comp.findMany({ where: { id_site: site.id }, select: { use_id_site: true }, diff --git a/app/web/src/render/live/logic/ws.ts b/app/web/src/render/live/logic/ws.ts index 08ea109c..df89c6ec 100644 --- a/app/web/src/render/live/logic/ws.ts +++ b/app/web/src/render/live/logic/ws.ts @@ -83,9 +83,8 @@ export const liveWS = async (p: PG) => { console.clear(); console.log( - `🔥 Page updated: ${ - p.page?.url - } ${new Date().toLocaleString()}` + `🔥 Page updated: ${p.page + ?.url} ${new Date().toLocaleString()}` ); } }) @@ -179,49 +178,6 @@ export const liveWS = async (p: PG) => { } } break; - case "sitejs_reload": - if (msg.js) { - p.site.js = msg.js; - - const exec = (fn: string, scopes: any) => { - if (p) { - if (!p.script.api) p.script.api = createAPI(p.site.api_url); - if (!p.script.db) p.script.db = createDB(p.site.api_url); - - scopes["db"] = p.script.db; - scopes["api"] = p.script.api; - const f = new Function(...Object.keys(scopes), fn); - const res = f(...Object.values(scopes)); - return res; - } - return null; - }; - const w = window as any; - const scope = { - types: {}, - exports: w.exports, - load: importModule, - render: p.render, - module: { - exports: {} as any, - }, - }; - - p.status = "init"; - console.log( - `🔥 Site JS Reloaded: ${new Date().toLocaleString()}` - ); - exec(p.site.js, scope); - - if (scope.module.exports) { - for (const [k, v] of Object.entries(scope.module.exports)) { - w.exports[k] = v; - } - } - - p.render(); - } - break; case "undo": case "redo": case "new_comp": diff --git a/pkgs/core/utils/parcel.ts b/pkgs/core/utils/parcel.ts index 0f352fb6..dc0b96e7 100644 --- a/pkgs/core/utils/parcel.ts +++ b/pkgs/core/utils/parcel.ts @@ -3,6 +3,7 @@ import { dir } from "./dir"; import { g } from "./global"; import { spawn } from "bun"; +const decoder = new TextDecoder(); export const parcelBuild = async () => { await dirAsync("app/static"); const args = [ @@ -20,53 +21,60 @@ export const parcelBuild = async () => { await removeAsync(dir.path("app/web/.parcel-cache")); const parcel = spawn({ - cmd: args, + cmd: args, cwd: dir.path("app/web"), stdio: ["ignore", "inherit", "inherit"], }); await parcel.exited; } else { - const parcel = spawn({ - cmd: args, - cwd: dir.path("app/web"), - stdio: ["ignore", "pipe", "pipe"], - }); + await new Promise((resolve) => { + const parcel = spawn({ + cmd: args, + cwd: dir.path("app/web"), + stdio: ["ignore", "pipe", "pipe"], + }); - g.parcel = parcel; + g.parcel = parcel; - let output = true; - (async () => { - if (parcel.stdout) { - for await (const chunk of parcel.stdout) { - if (output) process.stdout.write(chunk); + let output = true; + let decoded = false; + (async () => { + if (parcel.stdout) { + for await (const chunk of parcel.stdout) { + if (!decoded && decoder.decode(chunk).includes("✨")) { + resolve(); + decoded = true; + } + if (output) process.stdout.write(chunk); + } } - } - })(); + })(); - (async () => { - if (parcel.stderr) { - for await (const chunk of parcel.stderr) { - if (output) process.stderr.write(chunk); + (async () => { + if (parcel.stderr) { + for await (const chunk of parcel.stderr) { + if (output) process.stderr.write(chunk); + } } - } - })(); + })(); - const cleanup = async () => { - output = false; - }; + const cleanup = async () => { + output = false; + }; - process.on("SIGINT", async () => { - await cleanup(); - process.exit(); - }); + process.on("SIGINT", async () => { + await cleanup(); + process.exit(); + }); - process.on("SIGTERM", async () => { - await cleanup(); - process.exit(); - }); + process.on("SIGTERM", async () => { + await cleanup(); + process.exit(); + }); - process.on("beforeExit", async () => { - await cleanup(); + process.on("beforeExit", async () => { + await cleanup(); + }); }); } }; diff --git a/pkgs/web-utils/src/use-global.ts b/pkgs/web-utils/src/use-global.ts index e89cd5b4..8755bbc4 100644 --- a/pkgs/web-utils/src/use-global.ts +++ b/pkgs/web-utils/src/use-global.ts @@ -39,7 +39,6 @@ export const useGlobal = ( if (!_id) { _id = "GLOBAL_DEFAULT"; } - const ctx = useContext(GlobalContext); const { global, render } = ctx;