From 943861acca395bdd113ab776be68880917063d8e Mon Sep 17 00:00:00 2001 From: cruxcode Date: Thu, 23 Feb 2023 21:33:21 -0800 Subject: [PATCH 01/28] bugfix: before_app_load to unset the comp id state --- packages/component-style-layer/src/hooks/useShowTab.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/component-style-layer/src/hooks/useShowTab.ts b/packages/component-style-layer/src/hooks/useShowTab.ts index 6c2af53c1..6584258d0 100644 --- a/packages/component-style-layer/src/hooks/useShowTab.ts +++ b/packages/component-style-layer/src/hooks/useShowTab.ts @@ -82,6 +82,12 @@ export const useShowTab = (compTree: Tree) => { }); return unsub; }, []); + + useEffect(() => { + return subscribeEditorMachine("before_app_load", () => { + setId(null); + }); + }, []); return { showTab, alias, From 921875efd088b29c4bb2b82f18e6bc062fad0585 Mon Sep 17 00:00:00 2001 From: cruxcode Date: Fri, 24 Feb 2023 14:26:01 -0800 Subject: [PATCH 02/28] handleBreakpointChange & bugfix --- packages/core/src/types.tsx | 4 +-- .../core/src/utils/createComponentFromNode.ts | 1 + packages/pwa-builder-manager/src/api.ts | 10 +++---- .../src/handleBreakpointChange.ts | 28 +++++++++++++++++++ packages/pwa-builder-manager/src/index.ts | 1 + packages/pwa-builder-server/src/index.ts | 4 +-- packages/pwa-builder-server/src/utils.ts | 6 ++-- 7 files changed, 41 insertions(+), 13 deletions(-) create mode 100644 packages/pwa-builder-manager/src/handleBreakpointChange.ts diff --git a/packages/core/src/types.tsx b/packages/core/src/types.tsx index 9def76770..ab36f5dec 100644 --- a/packages/core/src/types.tsx +++ b/packages/core/src/types.tsx @@ -400,9 +400,9 @@ export interface ClientToServerEvents { urlPath: string, callback: (events: AnyEvent[]) => void ) => void; - saveEvent: ( + saveEvents: ( urlPath: string, - event: AnyEvent, + events: AnyEvent[], callback: (success: boolean) => void ) => void; } diff --git a/packages/core/src/utils/createComponentFromNode.ts b/packages/core/src/utils/createComponentFromNode.ts index 5756d98ff..8b1224cf6 100644 --- a/packages/core/src/utils/createComponentFromNode.ts +++ b/packages/core/src/utils/createComponentFromNode.ts @@ -22,6 +22,7 @@ function createPropsFromManifestComponent( if (tree.links[compId] && tree.links[compId].childId) { const propNodeId = tree.links[compId].childId; // convention that state.property field in tree contains the value + console.log(compId, propNodeId, treeId); const value = tree.nodes[propNodeId].state.property; const breakpoints = tree.nodes[propNodeId].state.breakpoints; // temporary fix: handle breakpoint for styles prop only diff --git a/packages/pwa-builder-manager/src/api.ts b/packages/pwa-builder-manager/src/api.ts index a757c7e45..2308b8619 100644 --- a/packages/pwa-builder-manager/src/api.ts +++ b/packages/pwa-builder-manager/src/api.ts @@ -61,12 +61,10 @@ function postNewEvents( if (forest) { forest.handleEvents(data); const { events } = data; - events.forEach((event) => { - socket.emit("saveEvent", routeObjectPath, event, (success) => { - if (!success) { - console.log("Failed to send event to backend"); - } - }); + socket.emit("saveEvents", routeObjectPath, events, (success) => { + if (!success) { + console.log("Failed to send event to backend"); + } }); } } diff --git a/packages/pwa-builder-manager/src/handleBreakpointChange.ts b/packages/pwa-builder-manager/src/handleBreakpointChange.ts new file mode 100644 index 000000000..241d0daac --- /dev/null +++ b/packages/pwa-builder-manager/src/handleBreakpointChange.ts @@ -0,0 +1,28 @@ +import { BrowserForestManager, createComponentFromNode } from "@atrilabs/core"; +import { breakpointApi } from "./breakpointApi"; +import ComponentTreeId from "@atrilabs/app-design-forest/src/componentTree?id"; +import { editorAppMachineInterpreter } from "./init"; + +breakpointApi.subscribeBreakpointChange(() => { + const compTree = BrowserForestManager.currentForest.tree(ComponentTreeId); + if (compTree) { + const compIds = Object.keys(compTree.nodes); + const compDatas = compIds.map((compId) => { + const node = compTree.nodes[compId]; + const componentData = createComponentFromNode( + node, + breakpointApi.getActiveBreakpoint() + ); + return componentData; + }); + compDatas.forEach((compData) => { + if (compData?.props) { + editorAppMachineInterpreter.machine.context.canvasWindow?.postMessage( + { type: "UPDATE_PROPS", payload: compData }, + // @ts-ignore + "*" + ); + } + }); + } +}); diff --git a/packages/pwa-builder-manager/src/index.ts b/packages/pwa-builder-manager/src/index.ts index a8490b3a9..b8f36a910 100644 --- a/packages/pwa-builder-manager/src/index.ts +++ b/packages/pwa-builder-manager/src/index.ts @@ -5,3 +5,4 @@ export * from "./breakpointApi"; export * from "./componentApi"; import "./handleBrowserForestManagerUpdates"; import "./attachAllKeyboardShortcuts"; +import "./handleBreakpointChange"; diff --git a/packages/pwa-builder-server/src/index.ts b/packages/pwa-builder-server/src/index.ts index af5bed1df..277286f34 100644 --- a/packages/pwa-builder-server/src/index.ts +++ b/packages/pwa-builder-server/src/index.ts @@ -47,10 +47,10 @@ io.on("connection", (socket) => { cb(JSON.parse(events.toString())); } }); - socket.on("saveEvent", async (urlPath, event, cb) => { + socket.on("saveEvents", async (urlPath, events, cb) => { const pageInfo = await getMatchedPageInfo(urlPath); if (pageInfo) { - saveEventsForPage(resolvePages(pageInfo.unixFilepath), event); + saveEventsForPage(resolvePages(pageInfo.unixFilepath), events); cb(true); } else { cb(false); diff --git a/packages/pwa-builder-server/src/utils.ts b/packages/pwa-builder-server/src/utils.ts index 2ce8a556f..4bcd42b89 100644 --- a/packages/pwa-builder-server/src/utils.ts +++ b/packages/pwa-builder-server/src/utils.ts @@ -72,13 +72,13 @@ export function loadEventsForPage(unixFilepath: string) { return Buffer.from(JSON.stringify([])); } -export function saveEventsForPage(unixFilepath: string, event: AnyEvent) { +export function saveEventsForPage(unixFilepath: string, events: AnyEvent[]) { const filename = getEventsJSONFilename(unixFilepath); if (fs.existsSync(filename)) { const parsed = JSON.parse(fs.readFileSync(filename).toString()); - parsed.push(event); + parsed.push(...events); fs.writeFileSync(filename, JSON.stringify(parsed, null, 2)); } else { - fs.writeFileSync(filename, JSON.stringify([event], null, 2)); + fs.writeFileSync(filename, JSON.stringify(events, null, 2)); } } From 1ce5ddd8dae4513b17a1bb4b0cea1004fb044653 Mon Sep 17 00:00:00 2001 From: cruxcode Date: Fri, 24 Feb 2023 14:37:34 -0800 Subject: [PATCH 03/28] removed console.log --- packages/core/src/utils/createComponentFromNode.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/core/src/utils/createComponentFromNode.ts b/packages/core/src/utils/createComponentFromNode.ts index 8b1224cf6..5756d98ff 100644 --- a/packages/core/src/utils/createComponentFromNode.ts +++ b/packages/core/src/utils/createComponentFromNode.ts @@ -22,7 +22,6 @@ function createPropsFromManifestComponent( if (tree.links[compId] && tree.links[compId].childId) { const propNodeId = tree.links[compId].childId; // convention that state.property field in tree contains the value - console.log(compId, propNodeId, treeId); const value = tree.nodes[propNodeId].state.property; const breakpoints = tree.nodes[propNodeId].state.breakpoints; // temporary fix: handle breakpoint for styles prop only From 7b9ec332fd50294c87eb614eb18096c4e3995dda Mon Sep 17 00:00:00 2001 From: cruxcode Date: Fri, 24 Feb 2023 19:39:49 -0800 Subject: [PATCH 04/28] select overlay done --- .../CanvasOverlay/CanvasOverlay.tsx | 3 +- .../CanvasOverlay/components/FilledLine.tsx | 11 ++ .../CanvasOverlay/components/OpacityBox.tsx | 16 +++ .../CanvasOverlay/hooks/index.ts | 2 + .../CanvasOverlay/hooks/useSelectHints.tsx | 135 ++++++++++++++++++ .../CanvasZoneRenderer/CanvasZoneRenderer.tsx | 9 ++ .../VisualHints/HintOverlayBox.tsx | 95 ++++++++++++ .../VisualHints/hintOverlays.ts | 65 +++++++++ .../editor-components/VisualHints/types.ts | 43 ++++++ .../VisualHints/useHintOverlays.tsx | 42 ++++++ packages/atri-app-core/src/utils/getId.ts | 5 + 11 files changed, 425 insertions(+), 1 deletion(-) create mode 100644 packages/atri-app-core/src/editor-components/CanvasOverlay/components/FilledLine.tsx create mode 100644 packages/atri-app-core/src/editor-components/CanvasOverlay/components/OpacityBox.tsx create mode 100644 packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/index.ts create mode 100644 packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/useSelectHints.tsx create mode 100644 packages/atri-app-core/src/editor-components/VisualHints/HintOverlayBox.tsx create mode 100644 packages/atri-app-core/src/editor-components/VisualHints/hintOverlays.ts create mode 100644 packages/atri-app-core/src/editor-components/VisualHints/types.ts create mode 100644 packages/atri-app-core/src/editor-components/VisualHints/useHintOverlays.tsx create mode 100644 packages/atri-app-core/src/utils/getId.ts diff --git a/packages/atri-app-core/src/editor-components/CanvasOverlay/CanvasOverlay.tsx b/packages/atri-app-core/src/editor-components/CanvasOverlay/CanvasOverlay.tsx index 159f0d917..897bcd2b9 100644 --- a/packages/atri-app-core/src/editor-components/CanvasOverlay/CanvasOverlay.tsx +++ b/packages/atri-app-core/src/editor-components/CanvasOverlay/CanvasOverlay.tsx @@ -1,7 +1,8 @@ -import { useDragDrop } from "./hooks/useDragDrop"; +import { useDragDrop, useSelectHints } from "./hooks"; export function CanvasOverlay() { const { dragFC, dragOverlayStyle } = useDragDrop(); + useSelectHints(); return ( <> {dragFC?.Comp && dragOverlayStyle ? ( diff --git a/packages/atri-app-core/src/editor-components/CanvasOverlay/components/FilledLine.tsx b/packages/atri-app-core/src/editor-components/CanvasOverlay/components/FilledLine.tsx new file mode 100644 index 000000000..e493bd06a --- /dev/null +++ b/packages/atri-app-core/src/editor-components/CanvasOverlay/components/FilledLine.tsx @@ -0,0 +1,11 @@ +export type FilledLineProps = { + fill: string; +}; + +export const FilledLine: React.FC = (props) => { + return ( +
+ ); +}; diff --git a/packages/atri-app-core/src/editor-components/CanvasOverlay/components/OpacityBox.tsx b/packages/atri-app-core/src/editor-components/CanvasOverlay/components/OpacityBox.tsx new file mode 100644 index 000000000..b118294f0 --- /dev/null +++ b/packages/atri-app-core/src/editor-components/CanvasOverlay/components/OpacityBox.tsx @@ -0,0 +1,16 @@ +export type OpacityBoxProps = { + opacity: number; +}; + +export const OpacityBox: React.FC = (props) => { + return ( +
+ ); +}; diff --git a/packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/index.ts b/packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/index.ts new file mode 100644 index 000000000..1228be6b6 --- /dev/null +++ b/packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/index.ts @@ -0,0 +1,2 @@ +export * from "./useDragDrop"; +export * from "./useSelectHints"; diff --git a/packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/useSelectHints.tsx b/packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/useSelectHints.tsx new file mode 100644 index 000000000..38b3eac8f --- /dev/null +++ b/packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/useSelectHints.tsx @@ -0,0 +1,135 @@ +import { orange600 } from "@atrilabs/design-system"; +import { useEffect, useRef, useCallback } from "react"; +import { subscribeCanvasMachine } from "../../../api"; +import { getId } from "../../../utils/getId"; +import { + removeHintOverlays, + addOrModifyHintOverlays, +} from "../../VisualHints/hintOverlays"; +import { FilledLine } from "../components/FilledLine"; + +const thickness = 2; + +export function useSelectHints() { + const topLineHoverId = useRef(null); + const rightLineHoverId = useRef(null); + const bottomLineHoverId = useRef(null); + const leftLineHoverId = useRef(null); + const compId = useRef(null); + + const clearOverlay = useCallback(() => { + if (topLineHoverId.current) { + removeHintOverlays([topLineHoverId.current]); + topLineHoverId.current = null; + } + if (rightLineHoverId.current) { + removeHintOverlays([rightLineHoverId.current]); + rightLineHoverId.current = null; + } + if (bottomLineHoverId.current) { + removeHintOverlays([bottomLineHoverId.current]); + bottomLineHoverId.current = null; + } + if (leftLineHoverId.current) { + removeHintOverlays([leftLineHoverId.current]); + leftLineHoverId.current = null; + } + }, []); + + const renderFn = useCallback(() => { + if ( + topLineHoverId.current && + rightLineHoverId.current && + bottomLineHoverId.current && + leftLineHoverId.current && + compId.current + ) { + // top line + addOrModifyHintOverlays({ + [topLineHoverId.current]: { + compId: compId.current, + comp: , + overlayId: topLineHoverId.current, + box: (dim) => { + return { + dimension: { + width: dim.dimension.width, + height: thickness, + }, + position: { top: -thickness, left: 0 }, + }; + }, + }, + }); + // right line + addOrModifyHintOverlays({ + [rightLineHoverId.current]: { + compId: compId.current, + comp: , + overlayId: rightLineHoverId.current, + box: (dim) => { + return { + dimension: { + width: thickness, + height: dim.dimension.height + 2 * thickness, + }, + position: { top: -thickness, left: dim.dimension.width }, + }; + }, + }, + }); + // bottom line + addOrModifyHintOverlays({ + [bottomLineHoverId.current]: { + compId: compId.current, + comp: , + overlayId: bottomLineHoverId.current, + box: (dim) => { + return { + dimension: { + width: dim.dimension.width, + height: thickness, + }, + position: { top: dim.dimension.height, left: 0 }, + }; + }, + }, + }); + // left line + addOrModifyHintOverlays({ + [leftLineHoverId.current]: { + compId: compId.current, + comp: , + overlayId: leftLineHoverId.current, + box: (dim) => { + return { + dimension: { + width: thickness, + height: dim.dimension.height + 2 * thickness, + }, + position: { top: -thickness, left: -thickness }, + }; + }, + }, + }); + } + }, []); + + useEffect(() => { + return subscribeCanvasMachine("select", (context, event) => { + topLineHoverId.current = getId(); + rightLineHoverId.current = getId(); + bottomLineHoverId.current = getId(); + leftLineHoverId.current = getId(); + compId.current = context.selected; + renderFn(); + }); + }, []); + + useEffect(() => { + return subscribeCanvasMachine("selectEnd", (context, event) => { + clearOverlay(); + compId.current = null; + }); + }, []); +} diff --git a/packages/atri-app-core/src/editor-components/CanvasZoneRenderer/CanvasZoneRenderer.tsx b/packages/atri-app-core/src/editor-components/CanvasZoneRenderer/CanvasZoneRenderer.tsx index 701a69f22..0789e335d 100644 --- a/packages/atri-app-core/src/editor-components/CanvasZoneRenderer/CanvasZoneRenderer.tsx +++ b/packages/atri-app-core/src/editor-components/CanvasZoneRenderer/CanvasZoneRenderer.tsx @@ -6,6 +6,7 @@ import { componentStoreApi } from "../../api"; import { NormalComponentRenderer } from "../NormalComponentRenderer/NormalComponentRenderer"; import { ParentComponentRenderer } from "../ParentComponentRenderer/ParentComponentRenderer"; import { RepeatingComponentRenderer } from "../RepeatingComponentRenderer/RepeatingComponentRenderer"; +import { useHintOverlays } from "../VisualHints/useHintOverlays"; export function CanvasZoneRenderer(props: CanvasZoneRendererProps) { const { childCompIds } = useCanvasZoneEventSubscriber({ @@ -17,9 +18,14 @@ export function CanvasZoneRenderer(props: CanvasZoneRendererProps) { ...props.styles, height: "200px", border: `1px solid ${gray500}`, + position: "relative" as const, }; } + return { ...props.styles, position: "relative" as const }; }, [props.styles, childCompIds]); + + const hintOverlayNodes = useHintOverlays(props.canvasZoneId); + return (
{childCompIds.map((childCompId) => { @@ -35,6 +41,9 @@ export function CanvasZoneRenderer(props: CanvasZoneRendererProps) { ); })} +
+ {hintOverlayNodes} +
); } diff --git a/packages/atri-app-core/src/editor-components/VisualHints/HintOverlayBox.tsx b/packages/atri-app-core/src/editor-components/VisualHints/HintOverlayBox.tsx new file mode 100644 index 000000000..f109c936c --- /dev/null +++ b/packages/atri-app-core/src/editor-components/VisualHints/HintOverlayBox.tsx @@ -0,0 +1,95 @@ +import React, { useMemo } from "react"; +import { componentStoreApi } from "../../api"; +import { getCSSBoxCoords } from "../../api/utils"; +import { HintOverlay, HintOverlayDimension } from "./types"; + +function calculateBoxDimensions(props: HintOverlay): HintOverlayDimension { + const canvasComponent = componentStoreApi.getComponent(props.compId); + if (canvasComponent?.ref.current) { + const comp = canvasComponent.ref.current!; + const canvasZoneId = canvasComponent.parent.canvasZoneId; + const canvasZone = componentStoreApi.getCanvasZoneComponent(canvasZoneId); + if (!canvasZone) { + throw Error(`Canvas zone with canvas zone id ${canvasZoneId} not found.`); + } + const canvasZoneCoords = getCSSBoxCoords(canvasZone); + const compCoords = getCSSBoxCoords(comp); + const { + marginBottom, + marginTop, + marginLeft, + marginRight, + paddingLeft, + paddingRight, + paddingTop, + paddingBottom, + } = getComputedStyle(comp); + const box = props.box({ + dimension: { + height: compCoords.height, + width: compCoords.width, + marginBottom: parseFloat(marginBottom), + marginLeft: parseFloat(marginLeft), + marginRight: parseFloat(marginRight), + marginTop: parseFloat(marginTop), + paddingBottom: parseFloat(paddingBottom), + paddingTop: parseFloat(paddingTop), + paddingLeft: parseFloat(paddingLeft), + paddingRight: parseFloat(paddingRight), + }, + }); + return { box, canvasZoneCoords, compCoords }; + } + + return { + box: { position: { top: 0, left: 0 }, dimension: { width: 0, height: 0 } }, + canvasZoneCoords: { top: 0, left: 0, height: 0, width: 0 }, + compCoords: { top: 0, left: 0, width: 0, height: 0 }, + }; +} + +export const HintOverlayBox: React.FC = ( + props +) => { + const { box, canvasZoneCoords, compCoords } = useMemo(() => { + return calculateBoxDimensions(props); + }, [props]); + + const { top, left, width, height } = useMemo(() => { + const canvasZonePosition = { + top: canvasZoneCoords.top, + left: canvasZoneCoords.left, + }; + const compPosition = { top: compCoords.top, left: compCoords.left }; + const top = + (compPosition.top - canvasZonePosition.top + box.position.top) / + props.scale; + const left = + (compPosition.left - canvasZonePosition.left + box.position.left) / + props.scale; + const width = box.dimension.width / props.scale; + const height = box.dimension.height / props.scale; + + return { top, left, width, height }; + }, [box, canvasZoneCoords, compCoords, props.scale]); + + return ( + + {box ? ( +
+ {props.comp} +
+ ) : null} +
+ ); +}; diff --git a/packages/atri-app-core/src/editor-components/VisualHints/hintOverlays.ts b/packages/atri-app-core/src/editor-components/VisualHints/hintOverlays.ts new file mode 100644 index 000000000..1be69efca --- /dev/null +++ b/packages/atri-app-core/src/editor-components/VisualHints/hintOverlays.ts @@ -0,0 +1,65 @@ +import { componentStoreApi } from "../../api"; +import { HintOverlay } from "./types"; + +const hintOverlays: { [overlayId: string]: HintOverlay } = {}; +const overlayCanvasZoneMap: { [canvasZoneId: string]: string[] } = {}; +const hintOverlaySubscribers: { + [canvasZoneId: string]: (() => void) | undefined; +} = {}; + +export function addOrModifyHintOverlays(overlays: { + [overlayId: string]: HintOverlay; +}) { + const overlayIds = Object.keys(overlays); + const affectedCanvasZones: Set = new Set(); + overlayIds.forEach((overlayId) => { + const overlay = overlays[overlayId]!; + hintOverlays[overlayId] = overlay; + const canvasZoneId = componentStoreApi.getComponent(overlay.compId)!.parent + .canvasZoneId; + affectedCanvasZones.add(canvasZoneId); + overlayCanvasZoneMap[canvasZoneId] + ? overlayCanvasZoneMap[canvasZoneId].push(overlayId) + : (overlayCanvasZoneMap[canvasZoneId] = [overlayId]); + }); + affectedCanvasZones.forEach((canvasZoneId) => { + hintOverlaySubscribers[canvasZoneId]?.(); + }); +} + +export function removeHintOverlays(overlayIds: string[]) { + const affectedCanvasZones: Set = new Set(); + overlayIds.forEach((overlayId) => { + if (!hintOverlays[overlayId]) { + return; + } + const overlay = hintOverlays[overlayId]!; + const canvasZoneId = componentStoreApi.getComponent(overlay.compId)!.parent + .canvasZoneId; + affectedCanvasZones.add(canvasZoneId); + if (hintOverlays[overlayId]) { + delete hintOverlays[overlayId]; + } + const foundIndex = overlayCanvasZoneMap[canvasZoneId].findIndex( + (curr) => curr === overlayId + ); + if (foundIndex >= 0) { + overlayCanvasZoneMap[canvasZoneId].splice(foundIndex, 1); + } + }); + affectedCanvasZones.forEach((canvasZoneId) => { + hintOverlaySubscribers[canvasZoneId]?.(); + }); +} + +export function getHintOverlayIds(canvasZoneId: string) { + return overlayCanvasZoneMap[canvasZoneId] || []; +} + +export function getHintOverlays() { + return { ...hintOverlays }; +} + +export function subscribeHintOverlay(canvasZoneId: string, cb: () => void) { + hintOverlaySubscribers[canvasZoneId] = cb; +} diff --git a/packages/atri-app-core/src/editor-components/VisualHints/types.ts b/packages/atri-app-core/src/editor-components/VisualHints/types.ts new file mode 100644 index 000000000..a04ce21a8 --- /dev/null +++ b/packages/atri-app-core/src/editor-components/VisualHints/types.ts @@ -0,0 +1,43 @@ +export type ComponentCoords = { + top: number; + left: number; + width: number; + height: number; +}; + +export type Position = { top: number; left: number }; + +export type BoxDimension = { + // border and padding included + width: number; + // border and padding included + height: number; + marginTop: number; + marginBottom: number; + marginLeft: number; + marginRight: number; + paddingLeft: number; + paddingRight: number; + paddingTop: number; + paddingBottom: number; +}; + +export type HintDimension = { + position: Position; + dimension: { width: number; height: number }; +}; + +export type BoxOverlay = (comp: { dimension: BoxDimension }) => HintDimension; + +export type HintOverlay = { + overlayId: string; + compId: string; + comp: React.ReactNode; + box: BoxOverlay; +}; + +export type HintOverlayDimension = { + box: HintDimension; + canvasZoneCoords: ComponentCoords; + compCoords: ComponentCoords; +}; diff --git a/packages/atri-app-core/src/editor-components/VisualHints/useHintOverlays.tsx b/packages/atri-app-core/src/editor-components/VisualHints/useHintOverlays.tsx new file mode 100644 index 000000000..b60b8f9a8 --- /dev/null +++ b/packages/atri-app-core/src/editor-components/VisualHints/useHintOverlays.tsx @@ -0,0 +1,42 @@ +import { useState, useCallback, useEffect } from "react"; +import { + getHintOverlays, + subscribeHintOverlay, + getHintOverlayIds, +} from "./hintOverlays"; +import { HintOverlayBox } from "./HintOverlayBox"; + +export const useHintOverlays = (canvasZoneId: string) => { + const [hintNodes, setHintNodes] = useState([]); + + const setNodesCb = useCallback(() => { + const hintOverlays = getHintOverlays(); + const hintHoverlayIds = getHintOverlayIds(canvasZoneId); + const hintNodes = hintHoverlayIds.map((hoverlayId) => { + const hintOverlay = hintOverlays[hoverlayId]; + return ( + + ); + }); + setHintNodes(hintNodes); + }, [canvasZoneId]); + + useEffect(() => { + // set nodes whenever a overlay data structure is changed + subscribeHintOverlay(canvasZoneId, () => { + setNodesCb(); + }); + }, [setNodesCb, canvasZoneId]); + + useEffect(() => { + // set nodes whenever dimension of screen changes + setNodesCb(); + }, [setNodesCb]); + return hintNodes; +}; diff --git a/packages/atri-app-core/src/utils/getId.ts b/packages/atri-app-core/src/utils/getId.ts new file mode 100644 index 000000000..fc58f7a55 --- /dev/null +++ b/packages/atri-app-core/src/utils/getId.ts @@ -0,0 +1,5 @@ +import { v4 as uuidv4 } from "uuid"; + +export function getId() { + return uuidv4(); +} From 34467fff009c578b608243fa6abbd32cb1792494 Mon Sep 17 00:00:00 2001 From: cruxcode Date: Fri, 24 Feb 2023 19:54:49 -0800 Subject: [PATCH 05/28] overlay for hover done --- packages/atri-app-core/src/canvasMachine.ts | 21 ++- .../CanvasOverlay/CanvasOverlay.tsx | 2 + .../CanvasOverlay/hooks/useHoverEvents.tsx | 154 ++++++++++++++++++ 3 files changed, 172 insertions(+), 5 deletions(-) create mode 100644 packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/useHoverEvents.tsx diff --git a/packages/atri-app-core/src/canvasMachine.ts b/packages/atri-app-core/src/canvasMachine.ts index 937b75326..417fef97d 100644 --- a/packages/atri-app-core/src/canvasMachine.ts +++ b/packages/atri-app-core/src/canvasMachine.ts @@ -375,10 +375,6 @@ function mouseIsNotBackInTheRepositionComponent( context: CanvasMachineContext, event: MOUSE_UP_EVENT ) { - console.log( - "mouseIsNotBackInTheRepositionComponent", - !mouseIsBackInTheRepositionComponent(context, event) - ); return !mouseIsBackInTheRepositionComponent(context, event); } @@ -409,7 +405,9 @@ type SubscribeStates = | typeof COMPONENT_REWIRED | typeof PROPS_UPDATED | typeof KEY_UP - | typeof KEY_DOWN; + | typeof KEY_DOWN + | "hoverWhileSelected" + | "hoverWhileSelectedEnd"; export function createCanvasMachine(id: string) { const subscribers: { [key in SubscribeStates]: Callback[] } = { @@ -436,6 +434,8 @@ export function createCanvasMachine(id: string) { [PROPS_UPDATED]: [], [KEY_UP]: [], [KEY_DOWN]: [], + hoverWhileSelected: [], + hoverWhileSelectedEnd: [], }; function subscribeCanvasMachine(state: SubscribeStates, cb: Callback) { subscribers[state].push(cb); @@ -653,6 +653,7 @@ export function createCanvasMachine(id: string) { [MOUSE_MOVE]: { target: hoverWhileSelected, cond: selectedDifferentComponent, + actions: ["setHoverComponent"], }, }, }, @@ -665,6 +666,16 @@ export function createCanvasMachine(id: string) { [SCROLL]: { target: selectIdle }, [OUTSIDE_CANVAS]: { target: selectIdle }, }, + entry: (context, event) => { + callSubscribers("hoverWhileSelected", context, event); + }, + exit: (context, event) => { + callSubscribers( + "hoverWhileSelectedEnd", + context, + event + ); + }, }, }, }, diff --git a/packages/atri-app-core/src/editor-components/CanvasOverlay/CanvasOverlay.tsx b/packages/atri-app-core/src/editor-components/CanvasOverlay/CanvasOverlay.tsx index 897bcd2b9..e742b8093 100644 --- a/packages/atri-app-core/src/editor-components/CanvasOverlay/CanvasOverlay.tsx +++ b/packages/atri-app-core/src/editor-components/CanvasOverlay/CanvasOverlay.tsx @@ -1,8 +1,10 @@ import { useDragDrop, useSelectHints } from "./hooks"; +import { useHoverHints } from "./hooks/useHoverEvents"; export function CanvasOverlay() { const { dragFC, dragOverlayStyle } = useDragDrop(); useSelectHints(); + useHoverHints(); return ( <> {dragFC?.Comp && dragOverlayStyle ? ( diff --git a/packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/useHoverEvents.tsx b/packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/useHoverEvents.tsx new file mode 100644 index 000000000..65c7d5386 --- /dev/null +++ b/packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/useHoverEvents.tsx @@ -0,0 +1,154 @@ +import { orange600 } from "@atrilabs/design-system"; +import { useEffect, useRef, useCallback } from "react"; +import { subscribeCanvasMachine } from "../../../api"; +import { getId } from "../../../utils/getId"; +import { + removeHintOverlays, + addOrModifyHintOverlays, +} from "../../VisualHints/hintOverlays"; +import { FilledLine } from "../components/FilledLine"; + +const thickness = 2; + +export function useHoverHints() { + const topLineHoverId = useRef(null); + const rightLineHoverId = useRef(null); + const bottomLineHoverId = useRef(null); + const leftLineHoverId = useRef(null); + const compId = useRef(null); + + const clearOverlay = useCallback(() => { + if (topLineHoverId.current) { + removeHintOverlays([topLineHoverId.current]); + topLineHoverId.current = null; + } + if (rightLineHoverId.current) { + removeHintOverlays([rightLineHoverId.current]); + rightLineHoverId.current = null; + } + if (bottomLineHoverId.current) { + removeHintOverlays([bottomLineHoverId.current]); + bottomLineHoverId.current = null; + } + if (leftLineHoverId.current) { + removeHintOverlays([leftLineHoverId.current]); + leftLineHoverId.current = null; + } + }, []); + + const renderFn = useCallback(() => { + if ( + topLineHoverId.current && + rightLineHoverId.current && + bottomLineHoverId.current && + leftLineHoverId.current && + compId.current + ) { + // top line + addOrModifyHintOverlays({ + [topLineHoverId.current]: { + compId: compId.current, + comp: , + overlayId: topLineHoverId.current, + box: (dim) => { + return { + dimension: { + width: dim.dimension.width, + height: thickness, + }, + position: { top: -thickness, left: 0 }, + }; + }, + }, + }); + // right line + addOrModifyHintOverlays({ + [rightLineHoverId.current]: { + compId: compId.current, + comp: , + overlayId: rightLineHoverId.current, + box: (dim) => { + return { + dimension: { + width: thickness, + height: dim.dimension.height + 2 * thickness, + }, + position: { top: -thickness, left: dim.dimension.width }, + }; + }, + }, + }); + // bottom line + addOrModifyHintOverlays({ + [bottomLineHoverId.current]: { + compId: compId.current, + comp: , + overlayId: bottomLineHoverId.current, + box: (dim) => { + return { + dimension: { + width: dim.dimension.width, + height: thickness, + }, + position: { top: dim.dimension.height, left: 0 }, + }; + }, + }, + }); + // left line + addOrModifyHintOverlays({ + [leftLineHoverId.current]: { + compId: compId.current, + comp: , + overlayId: leftLineHoverId.current, + box: (dim) => { + return { + dimension: { + width: thickness, + height: dim.dimension.height + 2 * thickness, + }, + position: { top: -thickness, left: -thickness }, + }; + }, + }, + }); + } + }, []); + + useEffect(() => { + return subscribeCanvasMachine("hover", (context) => { + topLineHoverId.current = getId(); + rightLineHoverId.current = getId(); + bottomLineHoverId.current = getId(); + leftLineHoverId.current = getId(); + console.log(context.hovered); + compId.current = context.hovered; + renderFn(); + }); + }, []); + + useEffect(() => { + return subscribeCanvasMachine("hoverEnd", () => { + clearOverlay(); + compId.current = null; + }); + }, []); + + useEffect(() => { + return subscribeCanvasMachine("hoverWhileSelected", (context) => { + topLineHoverId.current = getId(); + rightLineHoverId.current = getId(); + bottomLineHoverId.current = getId(); + leftLineHoverId.current = getId(); + compId.current = context.hovered; + renderFn(); + }); + }, []); + + useEffect(() => { + return subscribeCanvasMachine("hoverWhileSelectedEnd", () => { + clearOverlay(); + compId.current = null; + }); + }, []); +} From 5c5d5f6951081a53bd810ea4e6cfd1a5bc0dea5b Mon Sep 17 00:00:00 2001 From: cruxcode Date: Fri, 24 Feb 2023 20:00:31 -0800 Subject: [PATCH 06/28] bugfix: update hovered on re-hoverWhileSelected --- packages/atri-app-core/src/canvasMachine.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/atri-app-core/src/canvasMachine.ts b/packages/atri-app-core/src/canvasMachine.ts index 417fef97d..57b2b0291 100644 --- a/packages/atri-app-core/src/canvasMachine.ts +++ b/packages/atri-app-core/src/canvasMachine.ts @@ -662,6 +662,7 @@ export function createCanvasMachine(id: string) { [MOUSE_MOVE]: { target: hoverWhileSelected, cond: selectedDifferentComponent, + actions: ["setHoverComponent"], }, [SCROLL]: { target: selectIdle }, [OUTSIDE_CANVAS]: { target: selectIdle }, From 72e4aa4dd8ed9d8ff6cf4786f0f41470bba31118 Mon Sep 17 00:00:00 2001 From: cruxcode Date: Fri, 24 Feb 2023 20:25:31 -0800 Subject: [PATCH 07/28] removed console.log --- .../src/editor-components/CanvasOverlay/CanvasOverlay.tsx | 2 +- .../hooks/{useHoverEvents.tsx => useHoverHints.tsx} | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) rename packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/{useHoverEvents.tsx => useHoverHints.tsx} (99%) diff --git a/packages/atri-app-core/src/editor-components/CanvasOverlay/CanvasOverlay.tsx b/packages/atri-app-core/src/editor-components/CanvasOverlay/CanvasOverlay.tsx index e742b8093..004144948 100644 --- a/packages/atri-app-core/src/editor-components/CanvasOverlay/CanvasOverlay.tsx +++ b/packages/atri-app-core/src/editor-components/CanvasOverlay/CanvasOverlay.tsx @@ -1,5 +1,5 @@ import { useDragDrop, useSelectHints } from "./hooks"; -import { useHoverHints } from "./hooks/useHoverEvents"; +import { useHoverHints } from "./hooks/useHoverHints"; export function CanvasOverlay() { const { dragFC, dragOverlayStyle } = useDragDrop(); diff --git a/packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/useHoverEvents.tsx b/packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/useHoverHints.tsx similarity index 99% rename from packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/useHoverEvents.tsx rename to packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/useHoverHints.tsx index 65c7d5386..fbcef2072 100644 --- a/packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/useHoverEvents.tsx +++ b/packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/useHoverHints.tsx @@ -121,7 +121,6 @@ export function useHoverHints() { rightLineHoverId.current = getId(); bottomLineHoverId.current = getId(); leftLineHoverId.current = getId(); - console.log(context.hovered); compId.current = context.hovered; renderFn(); }); From ab9aa53969810e9fa5d28d92aad562e74c9edc96 Mon Sep 17 00:00:00 2001 From: cruxcode Date: Sat, 25 Feb 2023 14:47:32 -0800 Subject: [PATCH 08/28] overlay on reposition --- .../CanvasOverlay/CanvasOverlay.tsx | 9 ++- .../CanvasOverlay/hooks/index.ts | 2 + .../CanvasOverlay/hooks/useDraggedOverlay.tsx | 68 +++++++++++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/useDraggedOverlay.tsx diff --git a/packages/atri-app-core/src/editor-components/CanvasOverlay/CanvasOverlay.tsx b/packages/atri-app-core/src/editor-components/CanvasOverlay/CanvasOverlay.tsx index 004144948..d08345366 100644 --- a/packages/atri-app-core/src/editor-components/CanvasOverlay/CanvasOverlay.tsx +++ b/packages/atri-app-core/src/editor-components/CanvasOverlay/CanvasOverlay.tsx @@ -1,10 +1,15 @@ -import { useDragDrop, useSelectHints } from "./hooks"; -import { useHoverHints } from "./hooks/useHoverHints"; +import { + useDragDrop, + useSelectHints, + useHoverHints, + useDraggedOverlay, +} from "./hooks"; export function CanvasOverlay() { const { dragFC, dragOverlayStyle } = useDragDrop(); useSelectHints(); useHoverHints(); + useDraggedOverlay(); return ( <> {dragFC?.Comp && dragOverlayStyle ? ( diff --git a/packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/index.ts b/packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/index.ts index 1228be6b6..5d4620e71 100644 --- a/packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/index.ts +++ b/packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/index.ts @@ -1,2 +1,4 @@ export * from "./useDragDrop"; export * from "./useSelectHints"; +export * from "./useHoverHints"; +export * from "./useDraggedOverlay"; diff --git a/packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/useDraggedOverlay.tsx b/packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/useDraggedOverlay.tsx new file mode 100644 index 000000000..78ab7d7eb --- /dev/null +++ b/packages/atri-app-core/src/editor-components/CanvasOverlay/hooks/useDraggedOverlay.tsx @@ -0,0 +1,68 @@ +import { subscribeCanvasMachine } from "../../../api"; +import { getId } from "../../../utils/getId"; +import { + removeHintOverlays, + addOrModifyHintOverlays, +} from "../../VisualHints/hintOverlays"; +import { useCallback, useEffect, useRef } from "react"; +import { OpacityBox } from "../components/OpacityBox"; + +export const useDraggedOverlay = () => { + const boxOverlayId = useRef(null); + const compId = useRef(null); + + const clearOverlay = useCallback(() => { + if (boxOverlayId.current) { + removeHintOverlays([boxOverlayId.current]); + boxOverlayId.current = null; + } + }, []); + + const renderFn = useCallback(() => { + if (boxOverlayId.current && compId.current) { + // top line + addOrModifyHintOverlays({ + [boxOverlayId.current]: { + compId: compId.current, + comp: , + overlayId: boxOverlayId.current, + box: (dim) => { + return { + dimension: { + width: dim.dimension.width, + height: dim.dimension.height, + }, + position: { top: 0, left: 0 }, + }; + }, + }, + }); + } + }, []); + + useEffect(() => { + const unsub = subscribeCanvasMachine("reposition", (context) => { + if (!context.repositionComponent) { + return; + } + boxOverlayId.current = getId(); + compId.current = context.repositionComponent; + renderFn(); + }); + return unsub; + }, [renderFn]); + + useEffect(() => { + const unsub = subscribeCanvasMachine("repositionSuccess", () => { + clearOverlay(); + }); + return unsub; + }, [clearOverlay]); + + useEffect(() => { + const unsub = subscribeCanvasMachine("repositionFailed", () => { + clearOverlay(); + }); + return unsub; + }, [clearOverlay]); +}; From 38d266331fb3e0f344cb6785223831b70df29649 Mon Sep 17 00:00:00 2001 From: cruxcode Date: Sat, 25 Feb 2023 23:49:33 -0800 Subject: [PATCH 09/28] python files are generated from react manifest --- .../src/utils/extractParams.ts | 2 +- .../scripts/gen-py-classes/buildManifests.ts | 2 +- .../scripts/gen-py-classes/gen-py-classes.ts | 67 ++++++++++++++++++- .../src/scripts/gen-py-classes/utils.ts | 8 ++- 4 files changed, 73 insertions(+), 6 deletions(-) diff --git a/packages/commands-builder/src/utils/extractParams.ts b/packages/commands-builder/src/utils/extractParams.ts index dad690125..28b56a206 100644 --- a/packages/commands-builder/src/utils/extractParams.ts +++ b/packages/commands-builder/src/utils/extractParams.ts @@ -22,7 +22,7 @@ export function processArgs() { .option("f", { alias: "outputFilename", type: "string", - default: "main", + default: "main.js", description: "name of the output bundle file", }) .option("s", { alias: "appSrc", type: "string", default: "./src" }) diff --git a/packages/commands/src/scripts/gen-py-classes/buildManifests.ts b/packages/commands/src/scripts/gen-py-classes/buildManifests.ts index 698ea3387..9022d5ee5 100644 --- a/packages/commands/src/scripts/gen-py-classes/buildManifests.ts +++ b/packages/commands/src/scripts/gen-py-classes/buildManifests.ts @@ -47,7 +47,7 @@ export function buildManifests(options: { ...params, entry: createManifestsEntry, moduleFileExtensions, - outputFilename: "component.registry.js", + outputFilename: params.outputFilename, babel: { plugins: [ [ diff --git a/packages/commands/src/scripts/gen-py-classes/gen-py-classes.ts b/packages/commands/src/scripts/gen-py-classes/gen-py-classes.ts index 76403f3bf..60cab6054 100644 --- a/packages/commands/src/scripts/gen-py-classes/gen-py-classes.ts +++ b/packages/commands/src/scripts/gen-py-classes/gen-py-classes.ts @@ -2,6 +2,14 @@ import { extractParams } from "@atrilabs/commands-builder"; import { buildManifests } from "./buildManifests"; +import fs from "fs"; +import path from "path"; +import { ReactComponentManifestSchema } from "@atrilabs/react-component-manifest-schema"; +import pkgUp from "pkg-up"; +import { createComponentClassFile } from "./utils"; + +const atriPyPkgOutputDir = "atri-py-pkg"; + /** * This script has to be run in a package that * exports manifests or an Atri app with manifests directory. @@ -11,7 +19,64 @@ import { buildManifests } from "./buildManifests"; async function main() { const params = extractParams(); buildManifests({ params }) - .then(() => {}) + .then(() => { + const outputFilepath = path.resolve( + params.paths.outputDir, + params.outputFilename + ); + // check if build of manifest registry was success + if (fs.existsSync(outputFilepath)) { + fs.mkdirSync(path.resolve(params.paths.outputDir, atriPyPkgOutputDir), { + recursive: true, + }); + // @ts-ignore + const registry = __non_webpack_require__(outputFilepath).default as { + manifests: { [schema: string]: any }; + }[]; + const ReactComponentManifestSchemaId = + "@atrilabs/react-component-manifest-schema/src/index.ts"; + const result = registry.map(({ manifests }) => { + const reactManifest: ReactComponentManifestSchema | undefined = + manifests[ReactComponentManifestSchemaId]; + if (reactManifest) { + // @ts-ignore + const nodePkg = __non_webpack_require__(pkgUp.sync())["name"]; + const compKey = reactManifest.meta.key; + const callbacks = Object.keys(reactManifest.dev.attachCallbacks); + const customProps = Object.keys( + reactManifest.dev.attachProps[ + "atrilabs/app-design-forest/src/customPropsTree" + ] || {} + ); + return { + content: createComponentClassFile({ + compKey, + nodePkg, + callbacks, + customProps, + }), + outputFilename: compKey, + }; + } else { + throw Error( + "The react manifest schema isn't exported for some component." + ); + } + }); + result.forEach(({ outputFilename, content }) => + fs.writeFileSync( + path.resolve( + params.paths.outputDir, + atriPyPkgOutputDir, + `${outputFilename}.py` + ), + content + ) + ); + } else { + throw Error(`Missing bundle ${outputFilepath}`); + } + }) .catch((err) => { console.log(err); }); diff --git a/packages/commands/src/scripts/gen-py-classes/utils.ts b/packages/commands/src/scripts/gen-py-classes/utils.ts index 19c672da6..d0edacbe9 100644 --- a/packages/commands/src/scripts/gen-py-classes/utils.ts +++ b/packages/commands/src/scripts/gen-py-classes/utils.ts @@ -42,9 +42,11 @@ class ${compKey}(AtriComponent): self._setter_access_tracker = {} self.compKey = "${compKey}" self.nodePkg = "${nodePkg}" - ${callbacks.map((callback) => { - return `self.${callback} = False`; - })} + ${callbacks + .map((callback) => { + return `self.${callback} = False`; + }) + .join("\n\t\t")} self.custom = state["custom"] if state != None and "custom" in state else None self._setter_access_tracker = {} self._getter_access_tracker = {} From 3d70aac965d1b61fb87a72e2cb1b5d56eafd3026 Mon Sep 17 00:00:00 2001 From: cruxcode Date: Sun, 26 Feb 2023 00:10:20 -0800 Subject: [PATCH 10/28] generate __init__.py file --- .../scripts/gen-py-classes/gen-py-classes.ts | 31 ++++++++++++------- .../src/scripts/gen-py-classes/utils.ts | 6 ++++ 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/packages/commands/src/scripts/gen-py-classes/gen-py-classes.ts b/packages/commands/src/scripts/gen-py-classes/gen-py-classes.ts index 60cab6054..b6b49eea9 100644 --- a/packages/commands/src/scripts/gen-py-classes/gen-py-classes.ts +++ b/packages/commands/src/scripts/gen-py-classes/gen-py-classes.ts @@ -6,7 +6,7 @@ import fs from "fs"; import path from "path"; import { ReactComponentManifestSchema } from "@atrilabs/react-component-manifest-schema"; import pkgUp from "pkg-up"; -import { createComponentClassFile } from "./utils"; +import { createComponentClassFile, createInitPyFile } from "./utils"; const atriPyPkgOutputDir = "atri-py-pkg"; @@ -26,9 +26,16 @@ async function main() { ); // check if build of manifest registry was success if (fs.existsSync(outputFilepath)) { - fs.mkdirSync(path.resolve(params.paths.outputDir, atriPyPkgOutputDir), { - recursive: true, - }); + const componentsDir = path.resolve( + params.paths.outputDir, + atriPyPkgOutputDir, + "src", + "components" + ); + if (!fs.existsSync(componentsDir)) + fs.mkdirSync(componentsDir, { + recursive: true, + }); // @ts-ignore const registry = __non_webpack_require__(outputFilepath).default as { manifests: { [schema: string]: any }; @@ -55,7 +62,7 @@ async function main() { callbacks, customProps, }), - outputFilename: compKey, + compKey, }; } else { throw Error( @@ -63,16 +70,18 @@ async function main() { ); } }); - result.forEach(({ outputFilename, content }) => + // write components py file + result.forEach(({ compKey, content }) => fs.writeFileSync( - path.resolve( - params.paths.outputDir, - atriPyPkgOutputDir, - `${outputFilename}.py` - ), + path.resolve(componentsDir, `${compKey}.py`), content ) ); + // write __init__.py file + fs.writeFileSync( + path.resolve(componentsDir, "..", `__init__.py`), + createInitPyFile(result.map((r) => r.compKey)) + ); } else { throw Error(`Missing bundle ${outputFilepath}`); } diff --git a/packages/commands/src/scripts/gen-py-classes/utils.ts b/packages/commands/src/scripts/gen-py-classes/utils.ts index d0edacbe9..3a4352c34 100644 --- a/packages/commands/src/scripts/gen-py-classes/utils.ts +++ b/packages/commands/src/scripts/gen-py-classes/utils.ts @@ -82,3 +82,9 @@ export function createComponentClassFile(options: { options.callbacks )}`; } + +export function createInitPyFile(compKeys: string[]) { + return compKeys + .map((compKey) => `from components.${compKey} import ${compKey}`) + .join("\n"); +} From 5ba74829d790a06ce35017bb4ca032f57f9f6948 Mon Sep 17 00:00:00 2001 From: cruxcode Date: Sun, 26 Feb 2023 01:14:49 -0800 Subject: [PATCH 11/28] atri_core is publishable --- .../src/scripts/gen-py-classes/utils.ts | 2 +- python-packages/atri-core/pyproject.toml | 2 +- python-packages/atri-core/setup.py | 23 +++---------------- python-packages/atri-core/src/__init__.py | 0 .../src/{ => atri_core}/AtriComponent.py | 2 +- .../src/{ => atri_core}/AtriStyles.py | 0 .../atri-core/src/atri_core/__init__.py | 2 ++ 7 files changed, 8 insertions(+), 23 deletions(-) delete mode 100644 python-packages/atri-core/src/__init__.py rename python-packages/atri-core/src/{ => atri_core}/AtriComponent.py (94%) rename python-packages/atri-core/src/{ => atri_core}/AtriStyles.py (100%) create mode 100644 python-packages/atri-core/src/atri_core/__init__.py diff --git a/packages/commands/src/scripts/gen-py-classes/utils.ts b/packages/commands/src/scripts/gen-py-classes/utils.ts index 3a4352c34..5a823c600 100644 --- a/packages/commands/src/scripts/gen-py-classes/utils.ts +++ b/packages/commands/src/scripts/gen-py-classes/utils.ts @@ -1,5 +1,5 @@ const imports = `from typing import Any, Union -from atri_core.AtriComponent import AtriComponent`; +from atri_core import AtriComponent`; function createCustomClass(compKey: string, props: string[]) { return `class ${compKey}CustomClass: diff --git a/python-packages/atri-core/pyproject.toml b/python-packages/atri-core/pyproject.toml index 1e31dbc98..7fd26b970 100644 --- a/python-packages/atri-core/pyproject.toml +++ b/python-packages/atri-core/pyproject.toml @@ -1,3 +1,3 @@ [build-system] -requires = ["setuptools", "pipenv"] +requires = ["setuptools"] build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/python-packages/atri-core/setup.py b/python-packages/atri-core/setup.py index 183fd6dde..762319848 100644 --- a/python-packages/atri-core/setup.py +++ b/python-packages/atri-core/setup.py @@ -4,18 +4,6 @@ EDITABLE_BUILD = os.getenv("PIP_EDITABLE_BUILD", None) == "true" -try: - from pipenv.project import Project - try: - # Pipenv 2022.4.8 - from pipenv.utils.dependencies import convert_deps_to_pip - except: - # Older Pipenv - from pipenv.utils import convert_deps_to_pip -except: - err_msg = "Please install pipenv and try again." - sys.exit(err_msg) - import subprocess import re @@ -40,7 +28,7 @@ def get_git_tag(): return git_tag -NAME = "atri" +NAME = "atri-core" VERSION = "1.0.0" if EDITABLE_BUILD else get_git_tag()[1:] @@ -52,10 +40,6 @@ def get_git_tag(): "It (semi-)automates many things out of the box such as CDN caching, browser caching etc." ) -pipfile = Project().parsed_pipfile -packages = pipfile["packages"].copy() -requirements = convert_deps_to_pip(packages) - setuptools.setup( name=NAME, version=VERSION, @@ -71,8 +55,7 @@ def get_git_tag(): license="GPLv3", package_dir={"": "src"}, packages=setuptools.find_packages(where="src"), - install_requires=requirements, + install_requires=[], zip_safe=False, - include_package_data=True, - entry_points={"console_scripts": ["atri=atri.cli:main"]} + include_package_data=True ) \ No newline at end of file diff --git a/python-packages/atri-core/src/__init__.py b/python-packages/atri-core/src/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/python-packages/atri-core/src/AtriComponent.py b/python-packages/atri-core/src/atri_core/AtriComponent.py similarity index 94% rename from python-packages/atri-core/src/AtriComponent.py rename to python-packages/atri-core/src/atri_core/AtriComponent.py index 1702f3ca0..537534f74 100644 --- a/python-packages/atri-core/src/AtriComponent.py +++ b/python-packages/atri-core/src/atri_core/AtriComponent.py @@ -1,5 +1,5 @@ from typing import Any, Union -from AtriStyles import AtriStyles +from .AtriStyles import AtriStyles class AtriComponent: def __init__(self, state: Union[Any, None]): diff --git a/python-packages/atri-core/src/AtriStyles.py b/python-packages/atri-core/src/atri_core/AtriStyles.py similarity index 100% rename from python-packages/atri-core/src/AtriStyles.py rename to python-packages/atri-core/src/atri_core/AtriStyles.py diff --git a/python-packages/atri-core/src/atri_core/__init__.py b/python-packages/atri-core/src/atri_core/__init__.py new file mode 100644 index 000000000..a353b779d --- /dev/null +++ b/python-packages/atri-core/src/atri_core/__init__.py @@ -0,0 +1,2 @@ +from .AtriComponent import AtriComponent +from .AtriStyles import AtriStyles \ No newline at end of file From 72c8f039cd0875ff30b22b35b708021bc0b8b7cc Mon Sep 17 00:00:00 2001 From: cruxcode Date: Sun, 26 Feb 2023 13:57:23 -0800 Subject: [PATCH 12/28] python manifest project built using package.json --- .../scripts/gen-py-classes/gen-py-classes.ts | 96 ++++++++++++++++--- .../gen-py-classes/templates/pyproject.toml | 3 + .../scripts/gen-py-classes/templates/setup.py | 43 +++++++++ .../src/scripts/gen-py-classes/utils.ts | 2 +- .../react-component-manifests/package.json | 3 + 5 files changed, 134 insertions(+), 13 deletions(-) create mode 100644 packages/commands/src/scripts/gen-py-classes/templates/pyproject.toml create mode 100644 packages/commands/src/scripts/gen-py-classes/templates/setup.py diff --git a/packages/commands/src/scripts/gen-py-classes/gen-py-classes.ts b/packages/commands/src/scripts/gen-py-classes/gen-py-classes.ts index b6b49eea9..ec8ec35a4 100644 --- a/packages/commands/src/scripts/gen-py-classes/gen-py-classes.ts +++ b/packages/commands/src/scripts/gen-py-classes/gen-py-classes.ts @@ -18,6 +18,88 @@ const atriPyPkgOutputDir = "atri-py-pkg"; */ async function main() { const params = extractParams(); + // @ts-ignore + const packageJSON = __non_webpack_require__(pkgUp.sync()); + if ( + !packageJSON["atriConfig"] && + !packageJSON["atriConfig"]["pythonPackageName"] + ) { + throw Error("The package.json doesn't have valid atriConfig field."); + } + + const componentsDir = path.resolve( + params.paths.outputDir, + atriPyPkgOutputDir, + "src", + packageJSON["atriConfig"]["pythonPackageName"] + ); + + if (!fs.existsSync(componentsDir)) + fs.mkdirSync(componentsDir, { + recursive: true, + }); + + const configDir = path.resolve( + params.paths.outputDir, + atriPyPkgOutputDir, + "src", + "config" + ); + + if (!fs.existsSync(configDir)) + fs.mkdirSync(configDir, { + recursive: true, + }); + + // write package.json to atri-py-pkg/config directory + fs.writeFileSync( + path.resolve(configDir, "package.json"), + JSON.stringify(packageJSON, null, 2) + ); + fs.writeFileSync(path.resolve(configDir, "__init__.py"), ""); + + const setupPyFilepath = path.resolve( + params.paths.outputDir, + atriPyPkgOutputDir, + "setup.py" + ); + + if (!fs.existsSync(setupPyFilepath)) { + fs.copyFileSync( + path.resolve( + __dirname, + "..", + "src", + "scripts", + "gen-py-classes", + "templates", + "setup.py" + ), + setupPyFilepath + ); + } + + const pyProjectTomlFilepath = path.resolve( + params.paths.outputDir, + atriPyPkgOutputDir, + "pyproject.toml" + ); + + if (!fs.existsSync(pyProjectTomlFilepath)) { + fs.copyFileSync( + path.resolve( + __dirname, + "..", + "src", + "scripts", + "gen-py-classes", + "templates", + "pyproject.toml" + ), + pyProjectTomlFilepath + ); + } + buildManifests({ params }) .then(() => { const outputFilepath = path.resolve( @@ -26,16 +108,6 @@ async function main() { ); // check if build of manifest registry was success if (fs.existsSync(outputFilepath)) { - const componentsDir = path.resolve( - params.paths.outputDir, - atriPyPkgOutputDir, - "src", - "components" - ); - if (!fs.existsSync(componentsDir)) - fs.mkdirSync(componentsDir, { - recursive: true, - }); // @ts-ignore const registry = __non_webpack_require__(outputFilepath).default as { manifests: { [schema: string]: any }; @@ -47,7 +119,7 @@ async function main() { manifests[ReactComponentManifestSchemaId]; if (reactManifest) { // @ts-ignore - const nodePkg = __non_webpack_require__(pkgUp.sync())["name"]; + const nodePkg = packageJSON["name"]; const compKey = reactManifest.meta.key; const callbacks = Object.keys(reactManifest.dev.attachCallbacks); const customProps = Object.keys( @@ -79,7 +151,7 @@ async function main() { ); // write __init__.py file fs.writeFileSync( - path.resolve(componentsDir, "..", `__init__.py`), + path.resolve(componentsDir, `__init__.py`), createInitPyFile(result.map((r) => r.compKey)) ); } else { diff --git a/packages/commands/src/scripts/gen-py-classes/templates/pyproject.toml b/packages/commands/src/scripts/gen-py-classes/templates/pyproject.toml new file mode 100644 index 000000000..7fd26b970 --- /dev/null +++ b/packages/commands/src/scripts/gen-py-classes/templates/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/packages/commands/src/scripts/gen-py-classes/templates/setup.py b/packages/commands/src/scripts/gen-py-classes/templates/setup.py new file mode 100644 index 000000000..2bc5eb3a6 --- /dev/null +++ b/packages/commands/src/scripts/gen-py-classes/templates/setup.py @@ -0,0 +1,43 @@ +import setuptools +import json +from pathlib import Path + +with open(str(Path.cwd() / "src" / "config" / "package.json")) as f: + package_json = json.load(f) + +name = package_json["atriConfig"]["pythonPackageName"] + +version = package_json["version"] + +description = package_json["description"] + +long_description = package_json["description"] + +[author, author_email] = package_json["author"].split(" ") + +author_email = author_email.replace("<", "").replace(">", "") + +url = package_json["homepage"] + +source = package_json["repository"]["url"] + +setuptools.setup( + name=name, + version=version, + description=description, + long_description=long_description, + url=url, + project_urls={ + "Source": source + }, + author=author, + author_email=author_email, + python_requires=">=3.7", + license="GPLv3", + package_dir={"": "src", "config": "src/config"}, + packages=setuptools.find_packages(where="src"), + install_requires=["atri_core"], + zip_safe=False, + include_package_data=True, + package_data={'': ["*.json"]} +) \ No newline at end of file diff --git a/packages/commands/src/scripts/gen-py-classes/utils.ts b/packages/commands/src/scripts/gen-py-classes/utils.ts index 5a823c600..eb8a682e8 100644 --- a/packages/commands/src/scripts/gen-py-classes/utils.ts +++ b/packages/commands/src/scripts/gen-py-classes/utils.ts @@ -85,6 +85,6 @@ export function createComponentClassFile(options: { export function createInitPyFile(compKeys: string[]) { return compKeys - .map((compKey) => `from components.${compKey} import ${compKey}`) + .map((compKey) => `from .${compKey} import ${compKey}`) .join("\n"); } diff --git a/packages/react-component-manifests/package.json b/packages/react-component-manifests/package.json index 2acaa3d02..3504d66bd 100644 --- a/packages/react-component-manifests/package.json +++ b/packages/react-component-manifests/package.json @@ -55,5 +55,8 @@ "react": "18.0.0", "react-dom": "18.0.0", "react-router-dom": "^6.3.0" + }, + "atriConfig": { + "pythonPackageName": "atri_react" } } From 58254a8229f7e79dfc1188fedf671f8d9f41ddd8 Mon Sep 17 00:00:00 2001 From: cruxcode Date: Sun, 26 Feb 2023 14:30:24 -0800 Subject: [PATCH 13/28] bugfix: call join after map --- .../scripts/gen-py-classes/gen-py-classes.ts | 4 ++-- .../src/scripts/gen-py-classes/utils.ts | 22 +++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/packages/commands/src/scripts/gen-py-classes/gen-py-classes.ts b/packages/commands/src/scripts/gen-py-classes/gen-py-classes.ts index ec8ec35a4..8b412fdc5 100644 --- a/packages/commands/src/scripts/gen-py-classes/gen-py-classes.ts +++ b/packages/commands/src/scripts/gen-py-classes/gen-py-classes.ts @@ -123,8 +123,8 @@ async function main() { const compKey = reactManifest.meta.key; const callbacks = Object.keys(reactManifest.dev.attachCallbacks); const customProps = Object.keys( - reactManifest.dev.attachProps[ - "atrilabs/app-design-forest/src/customPropsTree" + reactManifest.dev.attachProps["custom"]?.["treeOptions"][ + "dataTypes" ] || {} ); return { diff --git a/packages/commands/src/scripts/gen-py-classes/utils.ts b/packages/commands/src/scripts/gen-py-classes/utils.ts index eb8a682e8..81bbbb951 100644 --- a/packages/commands/src/scripts/gen-py-classes/utils.ts +++ b/packages/commands/src/scripts/gen-py-classes/utils.ts @@ -5,29 +5,33 @@ function createCustomClass(compKey: string, props: string[]) { return `class ${compKey}CustomClass: def __init__(self, state: Union[Any, None]): self._setter_access_tracker = {} - ${props.map((prop) => { - return `self.${prop}: Union[str, None] = state["${prop}"] if state != None and "${prop}" in state else None`; - })} + ${props + .map((prop) => { + return `self.${prop}: Union[str, None] = state["${prop}"] if state != None and "${prop}" in state else None`; + }) + .join("\n\t\t")} self._setter_access_tracker = {} self._getter_access_tracker = {} - ${props.map((prop) => { - return `@property + ${props + .map((prop) => { + return `@property def ${prop}(self): self._getter_access_tracker["${prop}"] = {} return self._${prop} - @text.setter - def text(self, state): + @${prop}.setter + def ${prop}(self, state): self._setter_access_tracker["${prop}"] = {} self._${prop} = state`; - })} + }) + .join("\n\t")} def _to_json_fields(self): return {\n${props .map((prop) => { return `\t\t\t"${prop}": self._${prop}`; }) - .join(",")}\n\t\t\t}`; + .join(",\n")}\n\t\t\t}`; } function createComponentClass( From 53ac0c9acfdcea3b137b3f8641be8c8bfedb0036 Mon Sep 17 00:00:00 2001 From: cruxcode Date: Sun, 26 Feb 2023 17:33:29 -0800 Subject: [PATCH 14/28] wip: generate python model for each page --- packages/commands/package.json | 6 ++- .../src/commons/generatePythonPageModel.ts | 54 +++++++++++++++++++ packages/commands/src/consts.ts | 2 + .../src/scripts/gen-py-app/gen-py-app.ts | 32 +++++++++++ .../commands/src/scripts/gen-py-app/utils.ts | 46 ---------------- .../scripts/gen-py-classes/getManifestIRs.ts | 2 +- 6 files changed, 93 insertions(+), 49 deletions(-) create mode 100644 packages/commands/src/commons/generatePythonPageModel.ts create mode 100644 packages/commands/src/scripts/gen-py-app/gen-py-app.ts delete mode 100644 packages/commands/src/scripts/gen-py-app/utils.ts diff --git a/packages/commands/package.json b/packages/commands/package.json index eb6610d43..6e1d2eb8f 100644 --- a/packages/commands/package.json +++ b/packages/commands/package.json @@ -11,7 +11,8 @@ "dev-atri-app": "dist/dev.js", "dev-editor": "dist/dev-editor.js", "build-manifest-registry": "dist/build-manifest-registry.js", - "gen-py-classes": "dist/gen-py-classes.js" + "gen-py-classes": "dist/gen-py-classes.js", + "gen-py-app": "dist/gen-py-app.js" }, "directories": { "test": "__tests__" @@ -32,7 +33,8 @@ "build-editor": "build-node-app -e ./src/scripts/dev-editor/dev-editor.ts -f dev-editor.js -n '../../node_modules' -i '@atrilabs/atri-app-core:@atrilabs/design-system:@atrilabs/manifest-registry' -a '@atrilabs/atri-app-core:@atrilabs/design-system:@atrilabs/manifest-registry'", "build-manifest-registry": "build-node-app -e ./src/scripts/build-manifest-registry/build-manifest-registry.ts -f build-manifest-registry.js -n '../../node_modules' -i '@atrilabs/atri-app-core:@atrilabs/design-system' -a '@atrilabs/atri-app-core:@atrilabs/design-system'", "build-gen-py-classes": "build-node-app -e ./src/scripts/gen-py-classes/gen-py-classes.ts -f gen-py-classes.js -n '../../node_modules' -i '@atrilabs/atri-app-core:@atrilabs/design-system:@atrilabs/manifest-registry' -a '@atrilabs/atri-app-core:@atrilabs/design-system:@atrilabs/manifest-registry'", - "build": "yarn build-app-dev & yarn build-editor & yarn build-manifest-registry & yarn build-gen-py-classes", + "build-gen-py-app": "build-node-app -e ./src/scripts/gen-py-app/gen-py-app.ts -f gen-py-app.js -n '../../node_modules' -i '@atrilabs/atri-app-core:@atrilabs/design-system:@atrilabs/manifest-registry' -a '@atrilabs/atri-app-core:@atrilabs/design-system:@atrilabs/manifest-registry'", + "build": "yarn build-app-dev & yarn build-editor & yarn build-manifest-registry & yarn build-gen-py-classes & yarn build-gen-py-app", "test": "jest --detectOpenHandles", "test:watch": "jest --watch --detectOpenHandles" }, diff --git a/packages/commands/src/commons/generatePythonPageModel.ts b/packages/commands/src/commons/generatePythonPageModel.ts new file mode 100644 index 000000000..605d1ce24 --- /dev/null +++ b/packages/commands/src/commons/generatePythonPageModel.ts @@ -0,0 +1,54 @@ +export function generatePythonPageModel( + compAlias: { alias: string; compKey: string; pythonPkg: string }[] +) { + const importSets = new Set(); + compAlias.forEach(({ compKey, pythonPkg }) => { + importSets.add(`from ${pythonPkg} import ${compKey}`); + }); + + return `from typing import Union, Any + ${Array.from(importSets).join("\n")} + + class Page: + def __init__(self, state: Union[Any, None]): + self.event_data = None + self.event_alias = None + self._setter_access_tracker = {} + ${compAlias + .map(({ alias }) => { + return `self.${alias} = state["${alias}"]`; + }) + .join("\n")} + self._setter_access_tracker = {} + self._getter_access_tracker = {} + + def set_event(self, event): + self.event_data = event["event_data"] + self.event_alias = event["alias"] + callback_name = event["callback_name"] + comp = getattr(self, self.event_alias) + setattr(comp, callback_name, True) + + ${compAlias + .map(({ alias, compKey }) => { + return `@property + def ${alias}(self): + self._getter_access_tracker["${alias}"] = {} + return self._${alias} + @${alias}.setter + def ${alias}(self, new_state): + self._setter_access_tracker["${alias}"] = {} + self._${alias} = ${compKey}(new_state)`; + }) + .join("\n")} + + def _to_json_fields(self): + return { + ${compAlias + .map(({ alias }) => { + return `"${alias}": self._${alias}`; + }) + .join(",\n")} + } + `; +} diff --git a/packages/commands/src/consts.ts b/packages/commands/src/consts.ts index ba03a24e5..c2966b05f 100644 --- a/packages/commands/src/consts.ts +++ b/packages/commands/src/consts.ts @@ -1,6 +1,8 @@ import path from "path"; export const PAGE_DIR = path.resolve("pages"); +export const CONTROLLERS_DIR = path.resolve("controllers"); +export const ROUTES_DIR = path.resolve(CONTROLLERS_DIR, "routes"); function resolvePage(relativePath: string[]) { return path.resolve(PAGE_DIR, ...relativePath); diff --git a/packages/commands/src/scripts/gen-py-app/gen-py-app.ts b/packages/commands/src/scripts/gen-py-app/gen-py-app.ts new file mode 100644 index 000000000..d020c42ec --- /dev/null +++ b/packages/commands/src/scripts/gen-py-app/gen-py-app.ts @@ -0,0 +1,32 @@ +#!/usr/bin/env node +import { + dirStructureToIR, + IRToUnixFilePath, + readDirStructure, +} from "@atrilabs/atri-app-core"; +import { PAGE_DIR, ROUTES_DIR } from "../../consts"; +import path from "path"; +import fs from "fs"; + +async function main() { + const filePaths = await readDirStructure(PAGE_DIR); + const irs = dirStructureToIR(filePaths); + irs.map((ir) => { + const unixFilepath = IRToUnixFilePath(ir); + const modelPath = path.resolve( + ROUTES_DIR, + unixFilepath.replace(/^\//, "") + ".model.py" + ); + const modelDir = path.dirname(modelPath); + if (!fs.existsSync(modelDir)) { + fs.mkdirSync(modelDir, { recursive: true }); + } + // TODO: + // create forest from events.json file + // get alias, compKey and nodePkg + // read package.json of nodePkg to get pythonPkg + fs.writeFileSync(modelPath, ""); + }); +} + +main().catch((err) => console.log(err)); diff --git a/packages/commands/src/scripts/gen-py-app/utils.ts b/packages/commands/src/scripts/gen-py-app/utils.ts deleted file mode 100644 index cd045b50d..000000000 --- a/packages/commands/src/scripts/gen-py-app/utils.ts +++ /dev/null @@ -1,46 +0,0 @@ -export function generatePage( - compAlias: { alias: string; compKey: string; pythonPkg: string }[] -) { - const importSets = new Set(); - compAlias.forEach(({ compKey, pythonPkg }) => { - importSets.add(`from ${pythonPkg} import ${compKey}`); - }); - - return `from typing import Union, Any -${Array.from(importSets).join("\n")} - -class Page: - def __init__(self, state: Union[Any, None]): - self.event_data = None - self.event_alias = None - self._setter_access_tracker = {} - ${compAlias.map(({ alias }) => { - return `self.${alias} = state["${alias}"]`; - })} - self._setter_access_tracker = {} - self._getter_access_tracker = {} - - def set_event(self, event): - self.event_data = event["event_data"] - self.event_alias = event["alias"] - callback_name = event["callback_name"] - comp = getattr(self, self.event_alias) - setattr(comp, callback_name, True) - - ${compAlias.map(({ alias, compKey }) => { - return `@property - def ${alias}(self): - self._getter_access_tracker["${alias}"] = {} - return self._${alias} - @${alias}.setter - def ${alias}(self, new_state): - self._setter_access_tracker["${alias}"] = {} - self._${alias} = ${compKey}(new_state)`; - })} - - def _to_json_fields(self): - return { - "Button1": self._Button1, - } -`; -} diff --git a/packages/commands/src/scripts/gen-py-classes/getManifestIRs.ts b/packages/commands/src/scripts/gen-py-classes/getManifestIRs.ts index 866d5bbcf..3dcc89b8c 100644 --- a/packages/commands/src/scripts/gen-py-classes/getManifestIRs.ts +++ b/packages/commands/src/scripts/gen-py-classes/getManifestIRs.ts @@ -4,7 +4,7 @@ import pkgUp from "pkg-up"; import path from "path"; /** - * This script can be run inside a packages or Atri app. + * This function can be run inside a packages or Atri app. * * 1. If ran inside a Atri app: * From 3177ea5be2b935302b494a820a7b3ca1728f5fef Mon Sep 17 00:00:00 2001 From: cruxcode Date: Sun, 26 Feb 2023 19:25:42 -0800 Subject: [PATCH 15/28] python page model gets generated --- packages/commands/package.json | 2 +- .../src/commons/generatePythonPageModel.ts | 83 ++++++++++--------- .../src/scripts/gen-py-app/forestDef.ts | 71 ++++++++++++++++ .../src/scripts/gen-py-app/gen-py-app.ts | 38 +++++++-- 4 files changed, 144 insertions(+), 50 deletions(-) create mode 100644 packages/commands/src/scripts/gen-py-app/forestDef.ts diff --git a/packages/commands/package.json b/packages/commands/package.json index 6e1d2eb8f..a24337fac 100644 --- a/packages/commands/package.json +++ b/packages/commands/package.json @@ -33,7 +33,7 @@ "build-editor": "build-node-app -e ./src/scripts/dev-editor/dev-editor.ts -f dev-editor.js -n '../../node_modules' -i '@atrilabs/atri-app-core:@atrilabs/design-system:@atrilabs/manifest-registry' -a '@atrilabs/atri-app-core:@atrilabs/design-system:@atrilabs/manifest-registry'", "build-manifest-registry": "build-node-app -e ./src/scripts/build-manifest-registry/build-manifest-registry.ts -f build-manifest-registry.js -n '../../node_modules' -i '@atrilabs/atri-app-core:@atrilabs/design-system' -a '@atrilabs/atri-app-core:@atrilabs/design-system'", "build-gen-py-classes": "build-node-app -e ./src/scripts/gen-py-classes/gen-py-classes.ts -f gen-py-classes.js -n '../../node_modules' -i '@atrilabs/atri-app-core:@atrilabs/design-system:@atrilabs/manifest-registry' -a '@atrilabs/atri-app-core:@atrilabs/design-system:@atrilabs/manifest-registry'", - "build-gen-py-app": "build-node-app -e ./src/scripts/gen-py-app/gen-py-app.ts -f gen-py-app.js -n '../../node_modules' -i '@atrilabs/atri-app-core:@atrilabs/design-system:@atrilabs/manifest-registry' -a '@atrilabs/atri-app-core:@atrilabs/design-system:@atrilabs/manifest-registry'", + "build-gen-py-app": "build-node-app -e ./src/scripts/gen-py-app/gen-py-app.ts -f gen-py-app.js -n '../../node_modules' -i '@atrilabs/atri-app-core:@atrilabs/design-system:@atrilabs/manifest-registry:@atrilabs/forest' -a '@atrilabs/atri-app-core:@atrilabs/design-system:@atrilabs/manifest-registry:@atrilabs/forest'", "build": "yarn build-app-dev & yarn build-editor & yarn build-manifest-registry & yarn build-gen-py-classes & yarn build-gen-py-app", "test": "jest --detectOpenHandles", "test:watch": "jest --watch --detectOpenHandles" diff --git a/packages/commands/src/commons/generatePythonPageModel.ts b/packages/commands/src/commons/generatePythonPageModel.ts index 605d1ce24..6f99347a5 100644 --- a/packages/commands/src/commons/generatePythonPageModel.ts +++ b/packages/commands/src/commons/generatePythonPageModel.ts @@ -1,54 +1,55 @@ export function generatePythonPageModel( - compAlias: { alias: string; compKey: string; pythonPkg: string }[] + compDefs: { alias: string; compKey: string; pythonPkg: string }[] ) { const importSets = new Set(); - compAlias.forEach(({ compKey, pythonPkg }) => { + compDefs.forEach(({ compKey, pythonPkg }) => { importSets.add(`from ${pythonPkg} import ${compKey}`); }); return `from typing import Union, Any - ${Array.from(importSets).join("\n")} +${Array.from(importSets).join("\n")} - class Page: - def __init__(self, state: Union[Any, None]): - self.event_data = None - self.event_alias = None - self._setter_access_tracker = {} - ${compAlias - .map(({ alias }) => { - return `self.${alias} = state["${alias}"]`; - }) - .join("\n")} - self._setter_access_tracker = {} - self._getter_access_tracker = {} +class Page: + def __init__(self, state: Union[Any, None]): + self.event_data = None + self.event_alias = None + self._setter_access_tracker = {} +${compDefs + .map(({ alias }) => { + return `\t\tself.${alias} = state["${alias}"]`; + }) + .join("\n")} + self._setter_access_tracker = {} + self._getter_access_tracker = {} - def set_event(self, event): - self.event_data = event["event_data"] - self.event_alias = event["alias"] - callback_name = event["callback_name"] - comp = getattr(self, self.event_alias) - setattr(comp, callback_name, True) + def set_event(self, event): + self.event_data = event["event_data"] + self.event_alias = event["alias"] + callback_name = event["callback_name"] + comp = getattr(self, self.event_alias) + setattr(comp, callback_name, True) - ${compAlias - .map(({ alias, compKey }) => { - return `@property - def ${alias}(self): - self._getter_access_tracker["${alias}"] = {} - return self._${alias} - @${alias}.setter - def ${alias}(self, new_state): - self._setter_access_tracker["${alias}"] = {} - self._${alias} = ${compKey}(new_state)`; - }) - .join("\n")} + ${compDefs + .map(({ alias, compKey }) => { + return ` + @property + def ${alias}(self): + self._getter_access_tracker["${alias}"] = {} + return self._${alias} + @${alias}.setter + def ${alias}(self, new_state): + self._setter_access_tracker["${alias}"] = {} + self._${alias} = ${compKey}(new_state)`; + }) + .join("\n")} - def _to_json_fields(self): - return { - ${compAlias - .map(({ alias }) => { - return `"${alias}": self._${alias}`; - }) - .join(",\n")} - } + def _to_json_fields(self): + return { +${compDefs + .map(({ alias }) => { + return `\t\t\t"${alias}": self._${alias}`; + }) + .join(",\n")} + } `; } diff --git a/packages/commands/src/scripts/gen-py-app/forestDef.ts b/packages/commands/src/scripts/gen-py-app/forestDef.ts new file mode 100644 index 000000000..8d2ac0251 --- /dev/null +++ b/packages/commands/src/scripts/gen-py-app/forestDef.ts @@ -0,0 +1,71 @@ +import { TreeDef, ForestDef } from "@atrilabs/forest"; + +export const componentTreeDef: TreeDef = { + id: "@atrilabs/app-design-forest/src/componentTree", + modulePath: "@atrilabs/app-design-forest/src/componentTree", + defFn: () => { + return { + validateCreate(_event) { + return true; + }, + validatePatch(_event) { + return true; + }, + onCreate(_event) {}, + }; + }, +}; + +export const cssTreeDef: TreeDef = { + id: "@atrilabs/app-design-forest/src/cssTree", + modulePath: "@atrilabs/app-design-forest/src/cssTree", + defFn: () => { + return { + validateCreate(_event) { + return true; + }, + validatePatch(_event) { + return true; + }, + onCreate(_event) {}, + }; + }, +}; + +export const callbackTreeDef: TreeDef = { + id: "@atrilabs/app-design-forest/src/callbackHandlerTree", + modulePath: "@atrilabs/app-design-forest/src/callbackHandlerTree", + defFn: () => { + return { + validateCreate(_event) { + return true; + }, + validatePatch(_event) { + return true; + }, + onCreate(_event) {}, + }; + }, +}; + +export const customPropsTreeDef: TreeDef = { + id: "@atrilabs/app-design-forest/src/customPropsTree", + modulePath: "@atrilabs/app-design-forest/src/customPropsTree", + defFn: () => { + return { + validateCreate(_event) { + return true; + }, + validatePatch(_event) { + return true; + }, + onCreate(_event) {}, + }; + }, +}; + +export const forestDef: ForestDef = { + id: "forestId", + pkg: "forestPkg", + trees: [componentTreeDef, cssTreeDef, callbackTreeDef, customPropsTreeDef], +}; diff --git a/packages/commands/src/scripts/gen-py-app/gen-py-app.ts b/packages/commands/src/scripts/gen-py-app/gen-py-app.ts index d020c42ec..3e2d832e1 100644 --- a/packages/commands/src/scripts/gen-py-app/gen-py-app.ts +++ b/packages/commands/src/scripts/gen-py-app/gen-py-app.ts @@ -7,25 +7,47 @@ import { import { PAGE_DIR, ROUTES_DIR } from "../../consts"; import path from "path"; import fs from "fs"; +import { AnyEvent, createForest } from "@atrilabs/forest"; +import { componentTreeDef, forestDef } from "./forestDef"; +import { generatePythonPageModel } from "../../commons/generatePythonPageModel"; async function main() { const filePaths = await readDirStructure(PAGE_DIR); const irs = dirStructureToIR(filePaths); irs.map((ir) => { - const unixFilepath = IRToUnixFilePath(ir); - const modelPath = path.resolve( - ROUTES_DIR, - unixFilepath.replace(/^\//, "") + ".model.py" - ); + const unixFilepath = IRToUnixFilePath(ir).replace(/^\//, ""); + const modelPath = path.resolve(ROUTES_DIR, unixFilepath + ".model.py"); const modelDir = path.dirname(modelPath); if (!fs.existsSync(modelDir)) { fs.mkdirSync(modelDir, { recursive: true }); } // TODO: // create forest from events.json file - // get alias, compKey and nodePkg - // read package.json of nodePkg to get pythonPkg - fs.writeFileSync(modelPath, ""); + const eventsFilepath = path.resolve( + PAGE_DIR, + unixFilepath + ".events.json" + ); + if (fs.existsSync(eventsFilepath)) { + const events: AnyEvent[] = JSON.parse( + fs.readFileSync(eventsFilepath).toString() + ); + const forest = createForest(forestDef); + forest.handleEvents({ name: "", events, meta: { agent: "server-sent" } }); + const compNodes = Object.values(forest.tree(componentTreeDef.id)!.nodes); + const compDefs = compNodes.map((compNode) => { + // get alias, compKey and nodePkg + const compKey = compNode.meta.key as string; + const nodePkg = compNode.meta.pkg as string; + const alias = compNode.state["alias"] as string; + // read package.json of nodePkg to get pythonPkg + // @ts-ignore + const packageJSON = __non_webpack_require__(nodePkg + "/package.json"); + const pythonPkg = packageJSON["atriConfig"]["pythonPackageName"]; + return { alias, compKey, pythonPkg }; + }); + const modelContent = generatePythonPageModel(compDefs); + fs.writeFileSync(modelPath, modelContent); + } }); } From a27c8258499f83f0d8517f92a4ba3bf22a50b500 Mon Sep 17 00:00:00 2001 From: cruxcode Date: Sun, 26 Feb 2023 23:10:38 -0800 Subject: [PATCH 16/28] generate python models in dev mode --- packages/commands/package.json | 1 + .../src/commons/generatePythonPageModels.ts | 50 ++++++++++++++ .../src/scripts/dev-py-app/dev-py-app.ts | 43 ++++++++++++ .../dev-py-app/generatePythonRoutes.ts | 65 +++++++++++++++++++ .../src/scripts/gen-py-app/gen-py-app.ts | 50 +------------- 5 files changed, 161 insertions(+), 48 deletions(-) create mode 100644 packages/commands/src/commons/generatePythonPageModels.ts create mode 100644 packages/commands/src/scripts/dev-py-app/dev-py-app.ts create mode 100644 packages/commands/src/scripts/dev-py-app/generatePythonRoutes.ts diff --git a/packages/commands/package.json b/packages/commands/package.json index a24337fac..962ff3991 100644 --- a/packages/commands/package.json +++ b/packages/commands/package.json @@ -64,6 +64,7 @@ "bfj": "^7.0.2", "case-sensitive-paths-webpack-plugin": "^2.4.0", "chalk": "^4.1.2", + "chokidar": "^3.5.3", "express": "^4.18.2", "find-process": "^1.4.7", "fs-extra": "^11.1.0", diff --git a/packages/commands/src/commons/generatePythonPageModels.ts b/packages/commands/src/commons/generatePythonPageModels.ts new file mode 100644 index 000000000..52cee036f --- /dev/null +++ b/packages/commands/src/commons/generatePythonPageModels.ts @@ -0,0 +1,50 @@ +import { + dirStructureToIR, + IRToUnixFilePath, + PathIR, + readDirStructure, +} from "@atrilabs/atri-app-core"; +import { PAGE_DIR, ROUTES_DIR } from "../consts"; +import path from "path"; +import fs from "fs"; +import { AnyEvent, createForest } from "@atrilabs/forest"; +import { componentTreeDef, forestDef } from "../scripts/gen-py-app/forestDef"; +import { generatePythonPageModel } from "./generatePythonPageModel"; + +export function generatePythonModelForAPage(ir: PathIR) { + const unixFilepath = IRToUnixFilePath(ir).replace(/^\//, ""); + const modelPath = path.resolve(ROUTES_DIR, unixFilepath + "_model.py"); + const modelDir = path.dirname(modelPath); + if (!fs.existsSync(modelDir)) { + fs.mkdirSync(modelDir, { recursive: true }); + } + // create forest from events.json file + const eventsFilepath = path.resolve(PAGE_DIR, unixFilepath + ".events.json"); + const events: AnyEvent[] = fs.existsSync(eventsFilepath) + ? JSON.parse(fs.readFileSync(eventsFilepath).toString()) + : []; + const forest = createForest(forestDef); + forest.handleEvents({ name: "", events, meta: { agent: "server-sent" } }); + const compNodes = Object.values(forest.tree(componentTreeDef.id)!.nodes); + const compDefs = compNodes.map((compNode) => { + // get alias, compKey and nodePkg + const compKey = compNode.meta.key as string; + const nodePkg = compNode.meta.pkg as string; + const alias = compNode.state["alias"] as string; + // read package.json of nodePkg to get pythonPkg + // @ts-ignore + const packageJSON = __non_webpack_require__(nodePkg + "/package.json"); + const pythonPkg = packageJSON["atriConfig"]["pythonPackageName"]; + return { alias, compKey, pythonPkg }; + }); + const modelContent = generatePythonPageModel(compDefs); + fs.writeFileSync(modelPath, modelContent); +} + +export async function generatePythonPageModels() { + const filePaths = await readDirStructure(PAGE_DIR); + const irs = dirStructureToIR(filePaths); + irs.map((ir) => { + generatePythonModelForAPage(ir); + }); +} diff --git a/packages/commands/src/scripts/dev-py-app/dev-py-app.ts b/packages/commands/src/scripts/dev-py-app/dev-py-app.ts new file mode 100644 index 000000000..d3b586070 --- /dev/null +++ b/packages/commands/src/scripts/dev-py-app/dev-py-app.ts @@ -0,0 +1,43 @@ +#!/usr/bin/env node + +import { dirStructureToIR } from "@atrilabs/atri-app-core"; +import { watch } from "chokidar"; +import { + generatePythonModelForAPage, + generatePythonPageModels, +} from "../../commons/generatePythonPageModels"; +import { PAGE_DIR } from "../../consts"; +import { generatePythonRoutes } from "./generatePythonRoutes"; +import path from "path"; + +/** + * 1. Generate .model.py files on startup. + * + * 2. Generate ${page}.py files on startup. + * + * 3. Watch the pages directory for new pages (and maybe new .events.json file?). + * + * 4. Watch the pages directory for changes in .events.json. + */ + +async function main() { + await generatePythonPageModels(); + await generatePythonRoutes(); + const watcher = watch(PAGE_DIR, { ignoreInitial: true }); + watcher.on("add", async (_filepath, stats) => { + if (stats?.isDirectory() === false) { + await generatePythonPageModels(); + await generatePythonRoutes(); + } + }); + watcher.on("change", (filepath, stats) => { + if (stats?.isDirectory() === false && filepath.endsWith(".events.json")) { + const ir = dirStructureToIR([ + path.relative(PAGE_DIR, filepath).replace(/(\.events\.json)$/, ""), + ])[0]!; + generatePythonModelForAPage(ir); + } + }); +} + +main().catch(console.log); diff --git a/packages/commands/src/scripts/dev-py-app/generatePythonRoutes.ts b/packages/commands/src/scripts/dev-py-app/generatePythonRoutes.ts new file mode 100644 index 000000000..867b283a6 --- /dev/null +++ b/packages/commands/src/scripts/dev-py-app/generatePythonRoutes.ts @@ -0,0 +1,65 @@ +import { + dirStructureToIR, + IRToUnixFilePath, + readDirStructure, +} from "@atrilabs/atri-app-core"; +import { PAGE_DIR, ROUTES_DIR } from "../../consts"; +import path from "path"; +import fs from "fs"; + +export function generatePythonRoute(unixFilepath: string) { + return `from .${path.basename(unixFilepath)}_model import Page +from fastapi import Request, Response + +def init_state(at: Atri): + """ + This function is called everytime "Publish" button is hit in the editor. + The argument "at" is a dictionary that has initial values set from visual editor. + Changing values in this dictionary will modify the intial state of the app. + """ + pass + +def handle_page_request(at: Atri, req: Request, res: Response, query: str): + """ + This function is called whenever a user loads this route in the browser. + """ + pass + +def handle_event(at: Atri, req: Request, res: Response): + """ + This function is called whenever an event is received. An event occurs when user + performs some action such as click button. + """ + pass`; +} + +export async function generatePythonRoutes() { + const filePaths = await readDirStructure(PAGE_DIR); + const irs = dirStructureToIR(filePaths); + irs + .filter((ir) => { + const unixFilepath = IRToUnixFilePath(ir).replace(/^\//, ""); + const routeControllerPath = path.resolve( + ROUTES_DIR, + unixFilepath + ".py" + ); + // do nothing if the route file is already generated previously + if (fs.existsSync(routeControllerPath)) { + return false; + } + return true; + }) + .forEach((ir) => { + const unixFilepath = IRToUnixFilePath(ir).replace(/^\//, ""); + const routeControllerPath = path.resolve( + ROUTES_DIR, + unixFilepath + ".py" + ); + const routeControllerDir = path.dirname(routeControllerPath); + if (!fs.existsSync(routeControllerDir)) { + fs.mkdirSync(routeControllerDir, { recursive: true }); + } + const content = generatePythonRoute(unixFilepath); + fs.writeFileSync(routeControllerPath, content); + }); +} diff --git a/packages/commands/src/scripts/gen-py-app/gen-py-app.ts b/packages/commands/src/scripts/gen-py-app/gen-py-app.ts index 3e2d832e1..bceaa1ba1 100644 --- a/packages/commands/src/scripts/gen-py-app/gen-py-app.ts +++ b/packages/commands/src/scripts/gen-py-app/gen-py-app.ts @@ -1,54 +1,8 @@ #!/usr/bin/env node -import { - dirStructureToIR, - IRToUnixFilePath, - readDirStructure, -} from "@atrilabs/atri-app-core"; -import { PAGE_DIR, ROUTES_DIR } from "../../consts"; -import path from "path"; -import fs from "fs"; -import { AnyEvent, createForest } from "@atrilabs/forest"; -import { componentTreeDef, forestDef } from "./forestDef"; -import { generatePythonPageModel } from "../../commons/generatePythonPageModel"; +import { generatePythonPageModels } from "../../commons/generatePythonPageModels"; async function main() { - const filePaths = await readDirStructure(PAGE_DIR); - const irs = dirStructureToIR(filePaths); - irs.map((ir) => { - const unixFilepath = IRToUnixFilePath(ir).replace(/^\//, ""); - const modelPath = path.resolve(ROUTES_DIR, unixFilepath + ".model.py"); - const modelDir = path.dirname(modelPath); - if (!fs.existsSync(modelDir)) { - fs.mkdirSync(modelDir, { recursive: true }); - } - // TODO: - // create forest from events.json file - const eventsFilepath = path.resolve( - PAGE_DIR, - unixFilepath + ".events.json" - ); - if (fs.existsSync(eventsFilepath)) { - const events: AnyEvent[] = JSON.parse( - fs.readFileSync(eventsFilepath).toString() - ); - const forest = createForest(forestDef); - forest.handleEvents({ name: "", events, meta: { agent: "server-sent" } }); - const compNodes = Object.values(forest.tree(componentTreeDef.id)!.nodes); - const compDefs = compNodes.map((compNode) => { - // get alias, compKey and nodePkg - const compKey = compNode.meta.key as string; - const nodePkg = compNode.meta.pkg as string; - const alias = compNode.state["alias"] as string; - // read package.json of nodePkg to get pythonPkg - // @ts-ignore - const packageJSON = __non_webpack_require__(nodePkg + "/package.json"); - const pythonPkg = packageJSON["atriConfig"]["pythonPackageName"]; - return { alias, compKey, pythonPkg }; - }); - const modelContent = generatePythonPageModel(compDefs); - fs.writeFileSync(modelPath, modelContent); - } - }); + return generatePythonPageModels(); } main().catch((err) => console.log(err)); From 7d673f0cea85ae8da62449e4bca4729f0ec8db2c Mon Sep 17 00:00:00 2001 From: cruxcode Date: Mon, 27 Feb 2023 12:19:29 -0800 Subject: [PATCH 17/28] dev mode for generate python routes done --- packages/commands/package.json | 6 ++++-- .../src/scripts/dev-py-app/dev-py-app.ts | 17 ++++++++++++++++- .../scripts/dev-py-app/generatePythonRoutes.ts | 6 +++--- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/packages/commands/package.json b/packages/commands/package.json index 962ff3991..3eaa67356 100644 --- a/packages/commands/package.json +++ b/packages/commands/package.json @@ -12,7 +12,8 @@ "dev-editor": "dist/dev-editor.js", "build-manifest-registry": "dist/build-manifest-registry.js", "gen-py-classes": "dist/gen-py-classes.js", - "gen-py-app": "dist/gen-py-app.js" + "gen-py-app": "dist/gen-py-app.js", + "dev-py-app": "dist/dev-py-app.js" }, "directories": { "test": "__tests__" @@ -34,7 +35,8 @@ "build-manifest-registry": "build-node-app -e ./src/scripts/build-manifest-registry/build-manifest-registry.ts -f build-manifest-registry.js -n '../../node_modules' -i '@atrilabs/atri-app-core:@atrilabs/design-system' -a '@atrilabs/atri-app-core:@atrilabs/design-system'", "build-gen-py-classes": "build-node-app -e ./src/scripts/gen-py-classes/gen-py-classes.ts -f gen-py-classes.js -n '../../node_modules' -i '@atrilabs/atri-app-core:@atrilabs/design-system:@atrilabs/manifest-registry' -a '@atrilabs/atri-app-core:@atrilabs/design-system:@atrilabs/manifest-registry'", "build-gen-py-app": "build-node-app -e ./src/scripts/gen-py-app/gen-py-app.ts -f gen-py-app.js -n '../../node_modules' -i '@atrilabs/atri-app-core:@atrilabs/design-system:@atrilabs/manifest-registry:@atrilabs/forest' -a '@atrilabs/atri-app-core:@atrilabs/design-system:@atrilabs/manifest-registry:@atrilabs/forest'", - "build": "yarn build-app-dev & yarn build-editor & yarn build-manifest-registry & yarn build-gen-py-classes & yarn build-gen-py-app", + "build-dev-py-app": "build-node-app -e ./src/scripts/dev-py-app/dev-py-app.ts -f dev-py-app.js -n '../../node_modules' -i '@atrilabs/atri-app-core:@atrilabs/design-system:@atrilabs/manifest-registry:@atrilabs/forest' -a '@atrilabs/atri-app-core:@atrilabs/design-system:@atrilabs/manifest-registry:@atrilabs/forest'", + "build": "yarn build-app-dev & yarn build-editor & yarn build-manifest-registry & yarn build-gen-py-classes & yarn build-gen-py-app & yarn build-dev-py-app", "test": "jest --detectOpenHandles", "test:watch": "jest --watch --detectOpenHandles" }, diff --git a/packages/commands/src/scripts/dev-py-app/dev-py-app.ts b/packages/commands/src/scripts/dev-py-app/dev-py-app.ts index d3b586070..963390ada 100644 --- a/packages/commands/src/scripts/dev-py-app/dev-py-app.ts +++ b/packages/commands/src/scripts/dev-py-app/dev-py-app.ts @@ -23,7 +23,7 @@ import path from "path"; async function main() { await generatePythonPageModels(); await generatePythonRoutes(); - const watcher = watch(PAGE_DIR, { ignoreInitial: true }); + const watcher = watch(PAGE_DIR, { ignoreInitial: true, alwaysStat: true }); watcher.on("add", async (_filepath, stats) => { if (stats?.isDirectory() === false) { await generatePythonPageModels(); @@ -38,6 +38,21 @@ async function main() { generatePythonModelForAPage(ir); } }); + + ["SIGINT", "SIGTERM"].forEach(function (sig) { + process.on(sig, function () { + watcher.close(); + process.exit(); + }); + }); + + if (process.env["CI"] !== "true") { + // Gracefully exit when stdin ends + process.stdin.on("end", function () { + watcher.close(); + process.exit(); + }); + } } main().catch(console.log); diff --git a/packages/commands/src/scripts/dev-py-app/generatePythonRoutes.ts b/packages/commands/src/scripts/dev-py-app/generatePythonRoutes.ts index 867b283a6..34b79d0a9 100644 --- a/packages/commands/src/scripts/dev-py-app/generatePythonRoutes.ts +++ b/packages/commands/src/scripts/dev-py-app/generatePythonRoutes.ts @@ -11,7 +11,7 @@ export function generatePythonRoute(unixFilepath: string) { return `from .${path.basename(unixFilepath)}_model import Page from fastapi import Request, Response -def init_state(at: Atri): +def init_state(at: Page): """ This function is called everytime "Publish" button is hit in the editor. The argument "at" is a dictionary that has initial values set from visual editor. @@ -19,13 +19,13 @@ def init_state(at: Atri): """ pass -def handle_page_request(at: Atri, req: Request, res: Response, query: str): +def handle_page_request(at: Page, req: Request, res: Response, query: str): """ This function is called whenever a user loads this route in the browser. """ pass -def handle_event(at: Atri, req: Request, res: Response): +def handle_event(at: Page, req: Request, res: Response): """ This function is called whenever an event is received. An event occurs when user performs some action such as click button. From dadd9eeb3c5f90f54d05247a75d52b469e30573a Mon Sep 17 00:00:00 2001 From: cruxcode Date: Mon, 27 Feb 2023 17:21:38 -0800 Subject: [PATCH 18/28] create alias on drop done --- packages/pwa-builder-manager/package.json | 1 + packages/pwa-builder-manager/src/aliasApi.ts | 136 ++++++++++++++++++ packages/pwa-builder-manager/src/canvasApi.ts | 10 ++ packages/pwa-builder-manager/src/index.ts | 1 + yarn.lock | 19 +++ 5 files changed, 167 insertions(+) create mode 100644 packages/pwa-builder-manager/src/aliasApi.ts diff --git a/packages/pwa-builder-manager/package.json b/packages/pwa-builder-manager/package.json index 4a9eb5e5d..9fa08c68c 100644 --- a/packages/pwa-builder-manager/package.json +++ b/packages/pwa-builder-manager/package.json @@ -23,6 +23,7 @@ "dependencies": { "@atrilabs/atri-app-core": "^0.0.90", "@atrilabs/core": "^0.0.90", + "trie-search": "^1.3.6", "xstate": "^4.35.4" } } diff --git a/packages/pwa-builder-manager/src/aliasApi.ts b/packages/pwa-builder-manager/src/aliasApi.ts new file mode 100644 index 000000000..55231d9bf --- /dev/null +++ b/packages/pwa-builder-manager/src/aliasApi.ts @@ -0,0 +1,136 @@ +import { BrowserForestManager } from "@atrilabs/core"; +import ComponentTreeId from "@atrilabs/app-design-forest/src/componentTree?id"; +import { subscribeEditorMachine } from "./init"; +const TrieSearch = require("trie-search"); +import { api } from "./api"; + +const trie = new TrieSearch(); + +async function assingAliasFromPrefix(options: { + prefix: string; + id: string; + postData: Parameters[2]; +}) { + const { prefix, id, postData } = options; + const results = trie.search(prefix); + let largestNumber = 1; + if (Array.isArray(results) && results.length > 0) { + results.forEach((result: { value: { alias: string } }) => { + const matches = result.value.alias.match(/((\d+)$)/); + if (matches) { + const num = parseInt(matches[0]); + if (num >= largestNumber) { + largestNumber = num + 1; + } + } + }); + } + const alias = prefix + largestNumber; + const { forestPkgId, forestId } = BrowserForestManager.currentForest; + postData.events.push({ + type: `PATCH$$${ComponentTreeId}`, + id, + slice: { alias }, + }); + api.postNewEvents(forestPkgId, forestId, postData); +} + +async function maybeAssignAlias(options: { + alias: string; + id: string; + postData: Parameters[2]; +}) { + const { alias, id, postData } = options; + const compId = await getCompIdForAlias(alias); + if (compId !== undefined && compId !== id) { + const err = Error(); + err.name = "DUPLICATE"; + err.message = `The alias ${alias} is already taken.`; + throw err; + } else if (compId === id) { + // do nothing + } else { + const { forestPkgId, forestId } = BrowserForestManager.currentForest; + postData.events.push({ + type: `PATCH$$${ComponentTreeId}`, + id, + slice: { alias }, + }); + api.postNewEvents(forestPkgId, forestId, postData); + } +} + +async function getCompIdForAlias(alias: string) { + const result = trie.search(alias); + if ( + Array.isArray(result) && + result[0] && + result[0].alias && + result[0].alias === alias && + result[0].deleted === false + ) { + return result[0].compId as string; + } +} + +function createOrUndangle(alias: string, id: string) { + const result = trie.search(alias); + if ( + Array.isArray(result) && + result[0] && + result[0].value.alias && + result[0].value.alias === alias + ) { + result[0].deleted = false; + result[0].id = id; + } else { + trie.addFromObject({ [alias]: { id, alias, deleted: false } }); + } +} + +function dangleAlias(alias: string) { + const result = trie.search(alias); + if ( + Array.isArray(result) && + result[0] && + result[0].alias && + result[0].alias === alias + ) { + result[0].deleted = true; + } +} + +subscribeEditorMachine("before_app_load", () => { + trie.reset(); +}); + +BrowserForestManager.currentForest.subscribeForest((update) => { + if (update.type === "wire" && update.treeId === ComponentTreeId) { + const compId = update.id; + const compNode = + BrowserForestManager.currentForest.tree(ComponentTreeId)!.nodes[compId]!; + const alias = compNode.state.alias; + if (alias) { + createOrUndangle(alias, compId); + } + } + if (update.type === "dewire" && update.treeId === ComponentTreeId) { + const alias = update.deletedNode.state.alias; + dangleAlias(alias); + } + if (update.type === "change" && update.treeId === ComponentTreeId) { + const compId = update.id; + const compNode = + BrowserForestManager.currentForest.tree(ComponentTreeId)!.nodes[compId]!; + const alias = compNode.state.alias; + const oldAlias = update.oldState.alias; + if (oldAlias) dangleAlias(oldAlias); + createOrUndangle(alias, compId); + } +}); + +export const aliasApi = { + assingAliasFromPrefix, + getCompIdForAlias, + maybeAssignAlias, +}; diff --git a/packages/pwa-builder-manager/src/canvasApi.ts b/packages/pwa-builder-manager/src/canvasApi.ts index 5a212ca45..54554c97c 100644 --- a/packages/pwa-builder-manager/src/canvasApi.ts +++ b/packages/pwa-builder-manager/src/canvasApi.ts @@ -8,6 +8,7 @@ import { import { api } from "./api"; import ComponentTreeId from "@atrilabs/app-design-forest/src/componentTree?id"; import { PatchEvent } from "@atrilabs/forest"; +import { aliasApi } from "./aliasApi"; window.addEventListener("message", (ev) => { if ( @@ -161,6 +162,15 @@ subscribeEditorMachine("DRAG_SUCCESS", (context, event) => { name: "NEW_DROP", meta: { agent: "browser" }, }); + aliasApi.assingAliasFromPrefix({ + prefix: key, + id, + postData: { + name: "NEW_DROP_ALIAS", + meta: { agent: "browser" }, + events: [], + }, + }); } } }); diff --git a/packages/pwa-builder-manager/src/index.ts b/packages/pwa-builder-manager/src/index.ts index b8f36a910..9bc71e0ee 100644 --- a/packages/pwa-builder-manager/src/index.ts +++ b/packages/pwa-builder-manager/src/index.ts @@ -6,3 +6,4 @@ export * from "./componentApi"; import "./handleBrowserForestManagerUpdates"; import "./attachAllKeyboardShortcuts"; import "./handleBreakpointChange"; +export * from "./aliasApi"; diff --git a/yarn.lock b/yarn.lock index 3a0cc45dd..0508715c3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10594,6 +10594,13 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +hasharray@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/hasharray/-/hasharray-1.1.2.tgz#ba5aa8656d26af92c9e4525315b62b5f651039d3" + integrity sha512-7w3idwaVXX9gL9LiTCBSNKRGTBcp2WI/kf13UYeZ9+trOGBHVYHei6qtMY6DVnwGOouVUSRg0+L2xf4Q2/CmzA== + dependencies: + jclass "^1.0.1" + he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -11350,6 +11357,11 @@ jake@^10.8.5: filelist "^1.0.1" minimatch "^3.0.4" +jclass@^1.0.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/jclass/-/jclass-1.2.1.tgz#eaafeec0dd6a5bf8b3ea43c04e010c637638768b" + integrity sha512-mRx8uv1qJLOtxbRf3IWOQIH2ro7VIPn6ZkhbTcUJvJEslLzYA7BSATXDi/GR1yKYV9DASsjTZL+0YJPdqSMznw== + jest-changed-files@^28.0.2: version "28.0.2" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-28.0.2.tgz#7d7810660a5bd043af9e9cfbe4d58adb05e91531" @@ -17079,6 +17091,13 @@ treeverse@^2.0.0: resolved "https://registry.yarnpkg.com/treeverse/-/treeverse-2.0.0.tgz#036dcef04bc3fd79a9b79a68d4da03e882d8a9ca" integrity sha512-N5gJCkLu1aXccpOTtqV6ddSEi6ZmGkh3hjmbu1IjcavJK4qyOVQmi0myQKM7z5jVGmD68SJoliaVrMmVObhj6A== +trie-search@^1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/trie-search/-/trie-search-1.3.6.tgz#a2fb5f53bf74ace71b2ba8388efd9416c51b4da9" + integrity sha512-CsQZ3F/K9ee1WofZjLgw/KBo2CKQ+o4uq2QWqJ1f4GlBZO2wCplkHPeT9cyZfLXF5+jPWVLwyYdAIy4hGk8VbQ== + dependencies: + hasharray "^1.1.1" + trim-newlines@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" From 468e68a35262196883344baa50b33443382398ec Mon Sep 17 00:00:00 2001 From: cruxcode Date: Mon, 27 Feb 2023 17:46:30 -0800 Subject: [PATCH 19/28] change alias using aliasApi done --- .../src/hooks/useShowTab.ts | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/packages/component-style-layer/src/hooks/useShowTab.ts b/packages/component-style-layer/src/hooks/useShowTab.ts index 6584258d0..a0d335d91 100644 --- a/packages/component-style-layer/src/hooks/useShowTab.ts +++ b/packages/component-style-layer/src/hooks/useShowTab.ts @@ -1,9 +1,11 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { BrowserForestManager } from "@atrilabs/core"; -import ComponentTreeId from "@atrilabs/app-design-forest/src/componentTree?id"; import ReactManifestSchemaId from "@atrilabs/react-component-manifest-schema?id"; -import { PatchEvent, Tree } from "@atrilabs/forest"; -import { api, subscribeEditorMachine } from "@atrilabs/pwa-builder-manager"; +import { Tree } from "@atrilabs/forest"; +import { + subscribeEditorMachine, + aliasApi, +} from "@atrilabs/pwa-builder-manager"; export const useShowTab = (compTree: Tree) => { const [showTab, setShowTab] = useState(false); @@ -13,22 +15,15 @@ export const useShowTab = (compTree: Tree) => { const setAliasCb = useCallback( (event: React.ChangeEvent) => { if (id) { - const forestPkgId = BrowserForestManager.currentForest.forestPkgId; - const forestId = BrowserForestManager.currentForest.forestId; const alias = event.target.value.replace(/\s+/g, "_"); - const patchEvent: PatchEvent = { - type: `PATCH$$${ComponentTreeId}`, + aliasApi.maybeAssignAlias({ + alias, id, - slice: { - alias, + postData: { + events: [], + meta: { agent: "browser" }, + name: "SET_ALIAS", }, - }; - api.postNewEvents(forestPkgId, forestId, { - events: [patchEvent], - meta: { - agent: "browser", - }, - name: "SET_ALIAS", }); } }, From 0c2a1609973647b4c247840410ea08d038c778d0 Mon Sep 17 00:00:00 2001 From: cruxcode Date: Mon, 27 Feb 2023 19:23:20 -0800 Subject: [PATCH 20/28] action layer added --- packages/action-layer/package.json | 1 + packages/action-layer/src/TabBody.tsx | 23 ++++++-------- .../src/hooks/useManageActionLayer.ts | 5 +-- .../action-layer/src/hooks/usePageRoutes.tsx | 31 +++++++++++-------- packages/action-layer/src/hooks/useShowTab.ts | 31 ++++++++----------- packages/action-layer/src/types.ts | 4 +++ packages/pwa-builder/src/App.tsx | 4 +-- 7 files changed, 51 insertions(+), 48 deletions(-) create mode 100644 packages/action-layer/src/types.ts diff --git a/packages/action-layer/package.json b/packages/action-layer/package.json index 05e11db6b..fed4d3065 100644 --- a/packages/action-layer/package.json +++ b/packages/action-layer/package.json @@ -30,6 +30,7 @@ "@atrilabs/core": "^0.0.90", "@atrilabs/design-system": "^0.0.90", "@atrilabs/layer-types": "^0.0.90", + "@atrilabs/pwa-builder-manager": "^0.0.90", "@atrilabs/react-component-manifest-schema": "^0.0.90", "@atrilabs/scripts": "^0.0.90", "typescript": "^4.6.3" diff --git a/packages/action-layer/src/TabBody.tsx b/packages/action-layer/src/TabBody.tsx index 72dd166be..0f11be674 100644 --- a/packages/action-layer/src/TabBody.tsx +++ b/packages/action-layer/src/TabBody.tsx @@ -11,7 +11,7 @@ import { CallbackHandler, NavigationCallbackHandler, } from "@atrilabs/react-component-manifest-schema"; -import { useCallback, useEffect, useMemo } from "react"; +import { useCallback, useMemo } from "react"; import { ReactComponent as AddIcon } from "./assets/add.svg"; import { useFileUploadAliases } from "./hooks/useFileUploadAliases"; import { usePageRoutes } from "./hooks/usePageRoutes"; @@ -52,7 +52,7 @@ function surelyReutrnArray(arr: any[]) { export const TabBody: React.FC = (props) => { const { fileUploadActions } = useFileUploadAliases(); - const { routes } = usePageRoutes(); + const { pagesInfo } = usePageRoutes(); const options = useMemo(() => { const options: { action: CallbackHandler["0"]; value: number }[] = []; @@ -74,12 +74,13 @@ export const TabBody: React.FC = (props) => { } }); // internal navigation - routes.forEach((route) => { - const action: CallbackHandler["0"] = { - navigate: { type: "internal", url: route }, - }; - options.push({ action, value: options.length + 1 }); - }); + if (pagesInfo) + pagesInfo.forEach((info) => { + const action: CallbackHandler["0"] = { + navigate: { type: "internal", url: info.routeObjectPath }, + }; + options.push({ action, value: options.length + 1 }); + }); // external navigation options.push({ action: { navigate: { type: "external", url: "", target: "_blank" } }, @@ -91,7 +92,7 @@ export const TabBody: React.FC = (props) => { value: options.length + 1, }); return options; - }, [fileUploadActions, props, routes]); + }, [fileUploadActions, props, pagesInfo]); const onChangeAction = useCallback( (callbackName: string, index: number, value: number) => { @@ -169,10 +170,6 @@ export const TabBody: React.FC = (props) => { [props] ); - useEffect(() => { - console.log(surelyReutrnArray); - }, []); - return (
{ const compTree = useTree(ComponentTreeId); diff --git a/packages/action-layer/src/hooks/usePageRoutes.tsx b/packages/action-layer/src/hooks/usePageRoutes.tsx index 77a282bff..efdefd444 100644 --- a/packages/action-layer/src/hooks/usePageRoutes.tsx +++ b/packages/action-layer/src/hooks/usePageRoutes.tsx @@ -1,18 +1,23 @@ -import { api, BrowserForestManager } from "@atrilabs/core"; import { useEffect, useState } from "react"; +import { + editorAppMachineInterpreter, + subscribeEditorMachine, +} from "@atrilabs/pwa-builder-manager"; +import { PageInfo } from "../types"; + +export function usePageRoutes() { + const [pagesInfo, setPagesInfo] = useState(null); -export const usePageRoutes = () => { - const [routes, setRoutes] = useState([]); useEffect(() => { - const forestPkgId = BrowserForestManager.currentForest.forestPkgId; - api.getPages(forestPkgId, (pages) => { - const routes: string[] = []; - const pageIds = Object.keys(pages); - pageIds.forEach((pageId) => { - routes.push(pages[pageId].route); + if (editorAppMachineInterpreter.getSnapshot().value !== "booting") { + const pagesInfo = editorAppMachineInterpreter.machine.context.pagesInfo; + setPagesInfo(pagesInfo); + } else { + return subscribeEditorMachine("afterbootup", (context) => { + setPagesInfo(context.pagesInfo); }); - setRoutes(routes); - }); + } }, []); - return { routes }; -}; + + return { pagesInfo }; +} diff --git a/packages/action-layer/src/hooks/useShowTab.ts b/packages/action-layer/src/hooks/useShowTab.ts index 683ca6c78..6433db170 100644 --- a/packages/action-layer/src/hooks/useShowTab.ts +++ b/packages/action-layer/src/hooks/useShowTab.ts @@ -1,9 +1,9 @@ import { useCallback, useEffect, useState } from "react"; -import { api, BrowserForestManager, useTree } from "@atrilabs/core"; -import { subscribeCanvasActivity } from "@atrilabs/canvas-runtime"; +import { BrowserForestManager, useTree } from "@atrilabs/core"; import ComponentTreeId from "@atrilabs/app-design-forest/src/componentTree?id"; import ReactManifestSchemaId from "@atrilabs/react-component-manifest-schema?id"; import { PatchEvent } from "@atrilabs/forest"; +import { api, subscribeEditorMachine } from "@atrilabs/pwa-builder-manager"; export const useShowTab = () => { const [showTab, setShowTab] = useState(false); @@ -52,32 +52,27 @@ export const useShowTab = () => { }, [tree]); useEffect(() => { - const unsub = subscribeCanvasActivity("select", (context) => { - const id = context.select!.id; - if ( - tree.nodes[id] && - tree.nodes[id].meta.manifestSchemaId === ReactManifestSchemaId - ) { - const alias = tree.nodes[id].state.alias; - // When a new component is dropped, it is automatically selected, hence, it might - // be that no alias has been created till now. - if (alias === undefined) { - setAlias(""); - } else { - setAlias(alias); - } + const unsub = subscribeEditorMachine("SELECT", (_context, event) => { + if (event.type === "SELECT") { + const id = event.id; setId(id); setShowTab(true); } }); return unsub; - }, [tree]); + }, []); useEffect(() => { - const unsub = subscribeCanvasActivity("selectEnd", (context) => { + const unsub = subscribeEditorMachine("SELECT_END", (_context) => { setShowTab(false); setId(null); }); return unsub; }, []); + + useEffect(() => { + return subscribeEditorMachine("before_app_load", () => { + setId(null); + }); + }, []); return { showTab, alias, setAliasCb, id }; }; diff --git a/packages/action-layer/src/types.ts b/packages/action-layer/src/types.ts new file mode 100644 index 000000000..0a6dfe02d --- /dev/null +++ b/packages/action-layer/src/types.ts @@ -0,0 +1,4 @@ +export type PageInfo = { + routeObjectPath: string; + unixFilepath: string; +}[]; diff --git a/packages/pwa-builder/src/App.tsx b/packages/pwa-builder/src/App.tsx index 4cc8716ed..0495da31c 100644 --- a/packages/pwa-builder/src/App.tsx +++ b/packages/pwa-builder/src/App.tsx @@ -12,7 +12,7 @@ import ComponentStyleLayer from "@atrilabs/component-style-layer"; import CustomPropsLayer from "@atrilabs/custom-props-layer"; // import AssetManagerLayer from "@atrilabs/asset-manager-layer"; // import AppTemplateLayer from "@atrilabs/app-template-layer"; -// import ActionLayer from "@atrilabs/action-layer"; +import ActionLayer from "@atrilabs/action-layer"; // import ResourceProcessLayer from "@atrilabs/resource-processor-layer"; // import UndoRedoLayer from "@atrilabs/undo-redo-layer"; // import ComponentNavigatorLayer from "@atrilabs/component-navigator"; @@ -35,7 +35,7 @@ export default function App() { {/* */} {/* */} - {/* */} + {/* */} {/* */} {/* */} From 8cbea19e48491870e00ce7c2de5170f5614a0a43 Mon Sep 17 00:00:00 2001 From: cruxcode Date: Mon, 27 Feb 2023 19:49:59 -0800 Subject: [PATCH 21/28] bugfix: add .jsx random extension for IRToUnixFilepath to work --- packages/commands/src/scripts/dev-py-app/dev-py-app.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/commands/src/scripts/dev-py-app/dev-py-app.ts b/packages/commands/src/scripts/dev-py-app/dev-py-app.ts index 963390ada..f47faa8c1 100644 --- a/packages/commands/src/scripts/dev-py-app/dev-py-app.ts +++ b/packages/commands/src/scripts/dev-py-app/dev-py-app.ts @@ -32,9 +32,12 @@ async function main() { }); watcher.on("change", (filepath, stats) => { if (stats?.isDirectory() === false && filepath.endsWith(".events.json")) { - const ir = dirStructureToIR([ - path.relative(PAGE_DIR, filepath).replace(/(\.events\.json)$/, ""), - ])[0]!; + // WARN: adding .jsx without confirming that the actual + // extension is .jsx. + const unixfilepath = `/${path + .relative(PAGE_DIR, filepath) + .replace(/(\.events\.json)$/, "")}.jsx`; + const ir = dirStructureToIR([unixfilepath])[0]!; generatePythonModelForAPage(ir); } }); From 650a59a35af8bf2caf385f73839931360171bfee Mon Sep 17 00:00:00 2001 From: cruxcode Date: Tue, 28 Feb 2023 09:43:14 -0800 Subject: [PATCH 22/28] removed canvas-zone-manager package --- packages/canvas-zone-manager/.eslintrc.json | 6 ----- packages/canvas-zone-manager/README.md | 11 -------- packages/canvas-zone-manager/package.json | 29 --------------------- packages/canvas-zone-manager/src/index.ts | 1 - packages/canvas-zone-manager/src/machine.ts | 7 ----- packages/canvas-zone-manager/tsconfig.json | 25 ------------------ 6 files changed, 79 deletions(-) delete mode 100644 packages/canvas-zone-manager/.eslintrc.json delete mode 100644 packages/canvas-zone-manager/README.md delete mode 100644 packages/canvas-zone-manager/package.json delete mode 100644 packages/canvas-zone-manager/src/index.ts delete mode 100644 packages/canvas-zone-manager/src/machine.ts delete mode 100644 packages/canvas-zone-manager/tsconfig.json diff --git a/packages/canvas-zone-manager/.eslintrc.json b/packages/canvas-zone-manager/.eslintrc.json deleted file mode 100644 index 57d0fa18a..000000000 --- a/packages/canvas-zone-manager/.eslintrc.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": ["react-app", "prettier"], - "rules": { - "import/no-anonymous-default-export": "off" - } -} diff --git a/packages/canvas-zone-manager/README.md b/packages/canvas-zone-manager/README.md deleted file mode 100644 index 8989e3c6b..000000000 --- a/packages/canvas-zone-manager/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# `@atrilabs/canvas-zone-manager` - -> TODO: description - -## Usage - -``` -const canvasZoneManager = require('@atrilabs/canvas-zone-manager'); - -// TODO: DEMONSTRATE API -``` diff --git a/packages/canvas-zone-manager/package.json b/packages/canvas-zone-manager/package.json deleted file mode 100644 index 0e14c4e49..000000000 --- a/packages/canvas-zone-manager/package.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "@atrilabs/canvas-zone-manager", - "version": "0.0.90", - "description": "> TODO: description", - "author": "cruxcode ", - "homepage": "https://github.com/cruxcode/atrilabs-engine#readme", - "license": "ISC", - "main": "src/index.ts", - "directories": { - "lib": "lib", - "test": "__tests__" - }, - "files": [ - "lib" - ], - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/cruxcode/atrilabs-engine.git" - }, - "scripts": { - "test": "echo \"Error: run tests from root\" && exit 1" - }, - "bugs": { - "url": "https://github.com/cruxcode/atrilabs-engine/issues" - } -} diff --git a/packages/canvas-zone-manager/src/index.ts b/packages/canvas-zone-manager/src/index.ts deleted file mode 100644 index 1cf18cd7d..000000000 --- a/packages/canvas-zone-manager/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./machine"; diff --git a/packages/canvas-zone-manager/src/machine.ts b/packages/canvas-zone-manager/src/machine.ts deleted file mode 100644 index aa369400c..000000000 --- a/packages/canvas-zone-manager/src/machine.ts +++ /dev/null @@ -1,7 +0,0 @@ -// page loaded (send page loaded message to parent) -// page-live-reloaded -// processing event request -// drag in progress (gets this message from parent) -> no zones active -> zone active -// zone active -> mouse up -> event captured by some parent component (send drag success message to parent) -// zone inactive -> mouse up (send drag failed message to parent) -export {}; diff --git a/packages/canvas-zone-manager/tsconfig.json b/packages/canvas-zone-manager/tsconfig.json deleted file mode 100644 index 524bdb507..000000000 --- a/packages/canvas-zone-manager/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "module": "es6", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": false, - "jsx": "react-jsx", - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "rootDir": "src", - "outDir": "lib" - }, - "include": ["src"] -} From f123eba48619b8a0ce4ca2f69c301eb0ee77822b Mon Sep 17 00:00:00 2001 From: cruxcode Date: Tue, 28 Feb 2023 10:04:19 -0800 Subject: [PATCH 23/28] support for shared libs in tool --- packages/commands/src/scripts/dev-editor/dev-editor.ts | 6 ++++++ packages/core/src/types.tsx | 3 +++ packages/pwa-builder/package.json | 2 +- packages/pwa-builder/src/tool.config.js | 3 +++ 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/commands/src/scripts/dev-editor/dev-editor.ts b/packages/commands/src/scripts/dev-editor/dev-editor.ts index 06d780811..6849b86aa 100644 --- a/packages/commands/src/scripts/dev-editor/dev-editor.ts +++ b/packages/commands/src/scripts/dev-editor/dev-editor.ts @@ -65,6 +65,12 @@ function main() { __non_webpack_require__.resolve(pkg) ); }), + ...toolConfig.shared.map(({ pkg }) => { + return path.dirname( + // @ts-ignore + __non_webpack_require__.resolve(pkg) + ); + }), ...toolConfig.runtimes.map(({ pkg }) => { return path.dirname( // @ts-ignore diff --git a/packages/core/src/types.tsx b/packages/core/src/types.tsx index ab36f5dec..7625fbd98 100644 --- a/packages/core/src/types.tsx +++ b/packages/core/src/types.tsx @@ -142,6 +142,9 @@ export type ToolConfig = { resources: { path: string; }; + shared: { + pkg: string; + }[]; }; // type for manifest.schema.config.js diff --git a/packages/pwa-builder/package.json b/packages/pwa-builder/package.json index c7373a7f1..a041318dd 100644 --- a/packages/pwa-builder/package.json +++ b/packages/pwa-builder/package.json @@ -21,7 +21,7 @@ "url": "git+https://github.com/cruxcode/atrilabs-engine.git" }, "scripts": { - "dev": "dev-editor -i '@atrilabs/shared-layer-lib:@atrilabs/canvas-runtime-utils:@atrilabs/pwa-builder-manager:@atrilabs/design-system:@atrilabs/forest:@atrilabs/app-design-forest'", + "dev": "dev-editor -i '@atrilabs/canvas-runtime-utils:@atrilabs/pwa-builder-manager:@atrilabs/design-system:@atrilabs/forest:@atrilabs/app-design-forest'", "build-manifest-registry": "build-manifest-registry" }, "bugs": { diff --git a/packages/pwa-builder/src/tool.config.js b/packages/pwa-builder/src/tool.config.js index 30360a124..cfa7a6dc4 100644 --- a/packages/pwa-builder/src/tool.config.js +++ b/packages/pwa-builder/src/tool.config.js @@ -57,6 +57,8 @@ const runtimes = [{ pkg: "@atrilabs/canvas-runtime" }]; const manifestDirs = [{ pkg: "@atrilabs/react-component-manifests" }]; +const shared = [{ pkg: "@atrilabs/shared-layer-lib" }]; + module.exports = { forests, clients, @@ -64,4 +66,5 @@ module.exports = { layers, runtimes, manifestDirs, + shared, }; From fea06c5160160f6e47fb3d3179dd1641b72d46c2 Mon Sep 17 00:00:00 2001 From: cruxcode Date: Tue, 28 Feb 2023 12:09:33 -0800 Subject: [PATCH 24/28] asset manager integrated --- .../src/components/InputAudio.tsx | 32 --- .../src/components/InputImage.tsx | 41 --- .../src/components/InputVideo.tsx | 32 --- .../upload-container/UploadContainer.tsx | 254 ------------------ .../upload-container/hooks/useGetAssetList.ts | 53 ---- packages/asset-manager-layer/src/index.tsx | 2 +- .../src/scripts/dev-editor/dev-editor.ts | 5 +- packages/core/src/types.tsx | 18 ++ packages/pwa-builder-manager/src/api.ts | 8 +- packages/pwa-builder-server/package.json | 8 +- .../pwa-builder-server/src/handleAssets.ts | 60 +++++ packages/pwa-builder-server/src/index.ts | 23 ++ packages/pwa-builder/src/App.tsx | 4 +- yarn.lock | 7 +- 14 files changed, 127 insertions(+), 420 deletions(-) delete mode 100644 packages/asset-manager-layer/src/components/InputAudio.tsx delete mode 100644 packages/asset-manager-layer/src/components/InputImage.tsx delete mode 100644 packages/asset-manager-layer/src/components/InputVideo.tsx delete mode 100644 packages/asset-manager-layer/src/components/upload-container/UploadContainer.tsx delete mode 100644 packages/asset-manager-layer/src/components/upload-container/hooks/useGetAssetList.ts create mode 100644 packages/pwa-builder-server/src/handleAssets.ts diff --git a/packages/asset-manager-layer/src/components/InputAudio.tsx b/packages/asset-manager-layer/src/components/InputAudio.tsx deleted file mode 100644 index 7fe16057a..000000000 --- a/packages/asset-manager-layer/src/components/InputAudio.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { gray200, smallText } from "@atrilabs/design-system"; -import React from "react"; -import { ReactComponent as AudIp } from "../assets/audio-icon.svg"; - -export const styles: { [key: string]: React.CSSProperties } = { - container: { - display: "flex", - borderBottom: "1px solid rgba(31, 41, 55, 0.5)", - padding: "0 0.5rem", - alignItems: "center", - }, - audioIcon: { - padding: "0.5rem", - }, - audioText: { - ...smallText, - color: gray200, - }, -}; - -function InputAudio(props: { url: string; audioText: string }) { - return ( -
-
- -
- {props.audioText} -
- ); -} - -export default InputAudio; diff --git a/packages/asset-manager-layer/src/components/InputImage.tsx b/packages/asset-manager-layer/src/components/InputImage.tsx deleted file mode 100644 index 20df8caf6..000000000 --- a/packages/asset-manager-layer/src/components/InputImage.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { gray200, smallText } from "@atrilabs/design-system"; -import React from "react"; - -export const styles: { [key: string]: React.CSSProperties } = { - imageBox: { - textAlign: "left", - marginBottom: "10px", - }, - imageContainer: { - border: "1px solid rgba(31, 41, 55, 0.5)", - borderRadius: "2px", - width: "5.5rem", - height: "3rem", - }, - image: { - width: "6rem", - height: "3rem", - objectFit: "fill", - }, - imageText: { - width: "6rem", - ...smallText, - color: gray200, - whiteSpace: "nowrap", - overflow: "hidden", - textOverflow: "ellipsis", - }, -}; - -function InputImage(props: { url: string; imageText: string }) { - return ( -
-
- -
-

{props.imageText}

-
- ); -} - -export default InputImage; diff --git a/packages/asset-manager-layer/src/components/InputVideo.tsx b/packages/asset-manager-layer/src/components/InputVideo.tsx deleted file mode 100644 index ea8864de6..000000000 --- a/packages/asset-manager-layer/src/components/InputVideo.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { gray200, smallText } from "@atrilabs/design-system"; -import React from "react"; -import { ReactComponent as VidIp } from "../assets/video-icon.svg"; - -export const styles: { [key: string]: React.CSSProperties } = { - container: { - display: "flex", - borderBottom: "1px solid rgba(31, 41, 55, 0.5)", - padding: "0 0.5rem", - alignItems: "center", - }, - videoIcon: { - padding: "0.5rem", - }, - videoText: { - ...smallText, - color: gray200, - }, -}; - -function InputVideo(props: { url: string; videoText: string }) { - return ( -
-
- -
- {props.videoText} -
- ); -} - -export default InputVideo; diff --git a/packages/asset-manager-layer/src/components/upload-container/UploadContainer.tsx b/packages/asset-manager-layer/src/components/upload-container/UploadContainer.tsx deleted file mode 100644 index efecb4d7d..000000000 --- a/packages/asset-manager-layer/src/components/upload-container/UploadContainer.tsx +++ /dev/null @@ -1,254 +0,0 @@ -import { api } from "@atrilabs/core"; -import { - amber300, - gray300, - gray700, - gray900, - h1Heading, - h4Heading, -} from "@atrilabs/design-system"; -import React, { useCallback, useRef, useState } from "react"; -import InputAudio from "../InputAudio"; -import InputImage from "../InputImage"; -import InputVideo from "../InputVideo"; -import { Cross } from "../../assets/Cross"; -import { useGetAssetsInfo } from "./hooks/useGetAssetList"; - -export type UploadContainerProps = { - onCrossClicked?: () => void; - onUploadSuccess?: (url: string) => void; - onUploadFailed?: () => void; - onUploadMultipleSuccess?: (urls: string[]) => void; - onSelect?: (url: string) => void; - // upload mode for allowing upload of new files - modes?: ("upload" | "upload_multiple" | "draggable" | "select")[]; -}; - -export const styles: { [key: string]: React.CSSProperties } = { - dropContainerItem: { - backgroundColor: gray700, - border: "1px solid rgba(31, 41, 55, 0.5)", - width: "15rem", - minHeight: "100%", - userSelect: "none", - display: "flex", - flexDirection: "column", - boxSizing: "border-box", - }, - dropContainerItemHeader: { - display: "flex", - justifyContent: "space-between", - padding: "0.5rem 1rem", - }, - dropContainerItemHeaderH4: { - ...h1Heading, - color: gray300, - margin: "0px", - }, - icons: { - display: "flex", - alignItems: "center", - height: "100%", - }, - iconsSpan: { - display: "flex", - justifyContent: "center", - alignItems: "center", - cursor: "pointer", - width: "1.3rem", - }, - uploadBox: { - display: "none", - }, - selectMediaTypeBtn: { - ...h4Heading, - border: "none", - outline: "none", - background: amber300, - borderRadius: "4px", - color: gray900, - padding: "6px 0", - textAlign: "center", - width: "13rem", - margin: "11px 0", - }, - selectMediaTypeDiv: { - background: gray900, - width: "100%", - marginBottom: "11px", - }, - selectMediaTypeSelect: { - background: gray900, - color: gray300, - margin: "0 1rem", - border: "none", - outline: "none", - padding: "0.5rem 0", - width: "35%", - }, - dropDownItems: { - outline: "none", - }, - container: { - display: "grid", - padding: "0 1rem", - gridTemplateColumns: "auto auto", - columnGap: "15px", - }, -}; - -export const UploadContainer: React.FC = (props) => { - const [mediaType, setMediaType] = useState("images"); - const { assetsInfo, getAssetsInfo } = useGetAssetsInfo(); - - const onCrossClickCb = useCallback(() => { - if (props.onCrossClicked) props.onCrossClicked(); - }, [props]); - - const onFileBrowsedCb: React.ChangeEventHandler = - useCallback( - async (e) => { - // don't allow upload if upload mode is not set - if (props.modes && props.modes.includes("upload_multiple")) { - const files = e.target.files; - if (files) { - const assets: { - name: string; - data: ArrayBuffer; - size: number; - mime: string; - }[] = []; - for (let i = 0; i < files.length; i++) { - const file = files[i]; - const data = await file.arrayBuffer(); - const name = file.name; - const size = file.size; - const mime = file.type; - assets.push({ name, data, size, mime }); - } - api.uploadAssets(assets, (success, urls) => { - if (!success) { - if (props.onUploadFailed) props.onUploadFailed(); - } else { - getAssetsInfo(); - if (props.onUploadMultipleSuccess) { - props.onUploadMultipleSuccess(urls); - } - } - }); - } - } - - if (props.modes && props.modes.includes("upload")) { - const files = e.target.files; - if (files && files.length === 1) { - const assets: { - name: string; - data: ArrayBuffer; - size: number; - mime: string; - }[] = []; - const file = files[0]; - const data = await file.arrayBuffer(); - const name = file.name; - const size = file.size; - const mime = file.type; - assets.push({ name, data, size, mime }); - api.uploadAssets(assets, (success, urls) => { - if (!success) { - if (props.onUploadFailed) props.onUploadFailed(); - } else { - getAssetsInfo(); - if (props.onUploadSuccess) { - props.onUploadSuccess(urls[0]); - } - } - }); - } - } - }, - [props, getAssetsInfo] - ); - - const refEle = useRef(null); - - const handleInputClick = useCallback(() => { - refEle.current?.click(); - }, []); - - const handleTypeChange = useCallback( - (e: React.ChangeEvent) => { - setMediaType(e.target.value); - }, - [] - ); - - return ( -
-
-

Assets

-
- - - -
-
-
- - - -
- -
-
- {mediaType === "images" ? ( -
- {assetsInfo["images"].map((i) => ( - - ))} -
- ) : mediaType === "audio" ? ( - assetsInfo["audio"].map((i) => ( - - )) - ) : ( - assetsInfo["video"].map((i) => ( - - )) - )} -
-
-
- ); -}; diff --git a/packages/asset-manager-layer/src/components/upload-container/hooks/useGetAssetList.ts b/packages/asset-manager-layer/src/components/upload-container/hooks/useGetAssetList.ts deleted file mode 100644 index f2e8e8d90..000000000 --- a/packages/asset-manager-layer/src/components/upload-container/hooks/useGetAssetList.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { api } from "@atrilabs/core"; -import { useCallback, useEffect, useState } from "react"; - -export type FormattedAssetsInfo = { - images: { name: string; url: string; mime: string }[]; - video: { name: string; url: string; mime: string }[]; - audio: { name: string; url: string; mime: string }[]; -}; - -export const useGetAssetsInfo = () => { - const [assetsInfo, setAssetsInfo] = useState({ - images: [], - video: [], - audio: [], - }); - - const getAssetsInfo = useCallback(() => { - api.getAssetsInfo((assets) => { - const assetsInfo: FormattedAssetsInfo = { - images: [], - video: [], - audio: [], - }; - Object.keys(assets).forEach((key) => { - if (assets[key].mime.includes("image")) { - assetsInfo["images"].push({ - name: key, - url: assets[key].url, - mime: assets[key].mime, - }); - } else if (assets[key].mime.includes("audio")) { - assetsInfo["audio"].push({ - name: key, - url: assets[key].url, - mime: assets[key].mime, - }); - } else if (assets[key].mime.includes("video")) { - assetsInfo["video"].push({ - name: key, - url: assets[key].url, - mime: assets[key].mime, - }); - } - }); - setAssetsInfo(assetsInfo); - }); - }, []); - - useEffect(() => { - getAssetsInfo(); - }, [getAssetsInfo]); - return { assetsInfo, getAssetsInfo }; -}; diff --git a/packages/asset-manager-layer/src/index.tsx b/packages/asset-manager-layer/src/index.tsx index ce97b5a40..c8ba5c788 100644 --- a/packages/asset-manager-layer/src/index.tsx +++ b/packages/asset-manager-layer/src/index.tsx @@ -4,7 +4,7 @@ import { useCallback, useState } from "react"; import { UploadContainer, UploadContainerProps, -} from "./components/upload-container/UploadContainer"; +} from "@atrilabs/shared-layer-lib"; import { ReactComponent as AssetIcon } from "./assets/asset-icon.svg"; import "./styles.css"; const styles: { [key: string]: React.CSSProperties } = { diff --git a/packages/commands/src/scripts/dev-editor/dev-editor.ts b/packages/commands/src/scripts/dev-editor/dev-editor.ts index 6849b86aa..6bc940dd5 100644 --- a/packages/commands/src/scripts/dev-editor/dev-editor.ts +++ b/packages/commands/src/scripts/dev-editor/dev-editor.ts @@ -199,9 +199,12 @@ function main() { generateIndexHtml: true, proxy: { "/socket.io": { - target: "http://localhost:4000/socket.io", + target: "http://localhost:4000", ws: true, }, + "/assets": { + target: "http://localhost:4000", + }, }, babel: { plugins: [ diff --git a/packages/core/src/types.tsx b/packages/core/src/types.tsx index 7625fbd98..a1ee5bc48 100644 --- a/packages/core/src/types.tsx +++ b/packages/core/src/types.tsx @@ -408,6 +408,24 @@ export interface ClientToServerEvents { events: AnyEvent[], callback: (success: boolean) => void ) => void; + /** + * + * @param files Each property of file is derived from the Web API File. + */ + uploadAssets( + files: { + name: string; + data: ArrayBuffer; + size: number; + mime: string; + }[], + callback: (success: boolean, urls: string[]) => void + ): void; + getAssetsInfo: ( + callback: (assets: { + [name: string]: { url: string; mime: string }; + }) => void + ) => void; } export interface InterServerEvents {} diff --git a/packages/pwa-builder-manager/src/api.ts b/packages/pwa-builder-manager/src/api.ts index 2308b8619..facf08194 100644 --- a/packages/pwa-builder-manager/src/api.ts +++ b/packages/pwa-builder-manager/src/api.ts @@ -90,10 +90,14 @@ function uploadAssets( mime: string; }[], callback: (success: boolean, urls: string[]) => void -) {} +) { + socket.emit("uploadAssets", files, callback); +} function getAssetsInfo( callback: (assets: { [name: string]: { url: string; mime: string } }) => void -) {} +) { + socket.emit("getAssetsInfo", callback); +} export const api = { postNewEvents, diff --git a/packages/pwa-builder-server/package.json b/packages/pwa-builder-server/package.json index a560e8487..8e2cc4c35 100644 --- a/packages/pwa-builder-server/package.json +++ b/packages/pwa-builder-server/package.json @@ -25,6 +25,12 @@ "dependencies": { "@atrilabs/atri-app-core": "^0.0.90", "@atrilabs/commands-builder": "^0.0.90", - "@atrilabs/core": "^0.0.90" + "@atrilabs/core": "^0.0.90", + "mime-types": "^2.1.35", + "recursive-readdir": "^2.2.3", + "upath": "^2.0.1" + }, + "devDependencies": { + "@types/mime-types": "^2.1.1" } } diff --git a/packages/pwa-builder-server/src/handleAssets.ts b/packages/pwa-builder-server/src/handleAssets.ts new file mode 100644 index 000000000..d958e1672 --- /dev/null +++ b/packages/pwa-builder-server/src/handleAssets.ts @@ -0,0 +1,60 @@ +import path from "path"; +import recursive from "recursive-readdir"; +import { toUnix } from "upath"; +import mimetypes from "mime-types"; +import fs from "fs"; + +export const PUBLIC_DIR = path.resolve("public"); +const ASSETS_DIR = path.resolve("public", "assets"); + +function createAssetsDir() { + if (!fs.existsSync(ASSETS_DIR)) { + fs.mkdirSync(ASSETS_DIR, { recursive: true }); + } +} + +createAssetsDir(); + +function createUrl(assetPath: string) { + const prefixRemovedPath = assetPath.replace(PUBLIC_DIR, ""); + return process.platform === "win32" + ? toUnix(prefixRemovedPath) + : prefixRemovedPath; +} + +export function saveAssets( + files: { + name: string; + data: ArrayBuffer; + size: number; + mime: string; + }[] +): Promise { + return Promise.all( + files.map((file) => { + return new Promise((res, rej) => { + const filepath = path.resolve(ASSETS_DIR, file.name); + fs.createWriteStream(filepath).write(file.data, (err) => { + if (err) { + rej(err); + } else { + res(createUrl(filepath)); + } + }); + }); + }) + ); +} + +export function getAllAssetsInfo(): Promise<{ + [name: string]: { url: string; mime: string }; +}> { + return recursive(ASSETS_DIR).then((files) => { + return files.reduce((curr, file) => { + const url = createUrl(file); + const mime = mimetypes.lookup(file) || "application/octet-stream"; + curr[url] = { url, mime }; + return curr; + }, {} as { [name: string]: { url: string; mime: string } }); + }); +} diff --git a/packages/pwa-builder-server/src/index.ts b/packages/pwa-builder-server/src/index.ts index 277286f34..62c620384 100644 --- a/packages/pwa-builder-server/src/index.ts +++ b/packages/pwa-builder-server/src/index.ts @@ -16,6 +16,7 @@ import { resolvePages, saveEventsForPage, } from "./utils"; +import { saveAssets, getAllAssetsInfo, PUBLIC_DIR } from "./handleAssets"; const app = express(); const server = http.createServer(app); @@ -56,8 +57,30 @@ io.on("connection", (socket) => { cb(false); } }); + socket.on("uploadAssets", (files, cb) => { + saveAssets(files) + .then((urls) => { + cb(true, urls); + }) + .catch((err) => { + cb(false, []); + console.log(err); + }); + }); + socket.on("getAssetsInfo", (cb) => { + getAllAssetsInfo() + .then((info) => { + cb(info); + }) + .catch((err) => { + cb({}); + console.log(err); + }); + }); }); +app.use(express.static(PUBLIC_DIR)); + const port = process.env["PORT"] ? parseInt(process.env["PORT"]) : 4000; const host = process.env["HOST"] ? process.env["HOST"] : "0.0.0.0"; diff --git a/packages/pwa-builder/src/App.tsx b/packages/pwa-builder/src/App.tsx index 0495da31c..cd11c3f57 100644 --- a/packages/pwa-builder/src/App.tsx +++ b/packages/pwa-builder/src/App.tsx @@ -10,7 +10,7 @@ import ComponentStyleLayer from "@atrilabs/component-style-layer"; // import OverlayHintsLayer from "@atrilabs/overlay-hints-layer"; // import PublishAppLayer from "@atrilabs/publish-app-layer"; import CustomPropsLayer from "@atrilabs/custom-props-layer"; -// import AssetManagerLayer from "@atrilabs/asset-manager-layer"; +import AssetManagerLayer from "@atrilabs/asset-manager-layer"; // import AppTemplateLayer from "@atrilabs/app-template-layer"; import ActionLayer from "@atrilabs/action-layer"; // import ResourceProcessLayer from "@atrilabs/resource-processor-layer"; @@ -33,7 +33,7 @@ export default function App() { {/* */} - {/* */} + {/* */} {/* */} diff --git a/yarn.lock b/yarn.lock index 0508715c3..6b8b0c432 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6043,6 +6043,11 @@ dependencies: "@types/lodash" "*" +"@types/mime-types@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@types/mime-types/-/mime-types-2.1.1.tgz#d9ba43490fa3a3df958759adf69396c3532cf2c1" + integrity sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw== + "@types/mime@*": version "3.0.1" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" @@ -13106,7 +13111,7 @@ mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@^2.1.35, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== From 0f6798bf33ae79d5364768a2897fbe618763054a Mon Sep 17 00:00:00 2001 From: cruxcode Date: Tue, 28 Feb 2023 12:22:50 -0800 Subject: [PATCH 25/28] removed react-shim manifest-shims packages --- packages/manifest-shims/README.md | 5 -- packages/manifest-shims/package.json | 32 ------------- packages/manifest-shims/src/react/index.ts | 11 ----- .../manifest-shims/src/react/manifests.ts | 39 ---------------- packages/manifest-shims/src/react/shims.ts | 4 -- packages/manifest-shims/tsconfig.json | 25 ---------- packages/react-shim/README.md | 11 ----- packages/react-shim/dist/251.js | 1 - packages/react-shim/dist/251.js.LICENSE.txt | 19 -------- packages/react-shim/dist/main.js | 1 - packages/react-shim/package.json | 46 ------------------- packages/react-shim/server/index.js | 24 ---------- packages/react-shim/src/Button.tsx | 13 ------ packages/react-shim/src/index.ts | 11 ----- packages/react-shim/src/manifests.ts | 3 -- packages/react-shim/src/shims.ts | 4 -- packages/react-shim/tsconfig.json | 25 ---------- packages/react-shim/webpack.config.js | 34 -------------- 18 files changed, 308 deletions(-) delete mode 100644 packages/manifest-shims/README.md delete mode 100644 packages/manifest-shims/package.json delete mode 100644 packages/manifest-shims/src/react/index.ts delete mode 100644 packages/manifest-shims/src/react/manifests.ts delete mode 100644 packages/manifest-shims/src/react/shims.ts delete mode 100644 packages/manifest-shims/tsconfig.json delete mode 100644 packages/react-shim/README.md delete mode 100644 packages/react-shim/dist/251.js delete mode 100644 packages/react-shim/dist/251.js.LICENSE.txt delete mode 100644 packages/react-shim/dist/main.js delete mode 100644 packages/react-shim/package.json delete mode 100644 packages/react-shim/server/index.js delete mode 100644 packages/react-shim/src/Button.tsx delete mode 100644 packages/react-shim/src/index.ts delete mode 100644 packages/react-shim/src/manifests.ts delete mode 100644 packages/react-shim/src/shims.ts delete mode 100644 packages/react-shim/tsconfig.json delete mode 100644 packages/react-shim/webpack.config.js diff --git a/packages/manifest-shims/README.md b/packages/manifest-shims/README.md deleted file mode 100644 index dd02a5534..000000000 --- a/packages/manifest-shims/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# `manifest-shims` - -This package contains shims required for different build types of the manifest packages. - -The child directories of src are named the same as build type. For example, src/react contains the react shim. The shims are expected to be copied directly to another location to become part of a build job. diff --git a/packages/manifest-shims/package.json b/packages/manifest-shims/package.json deleted file mode 100644 index 24264b76f..000000000 --- a/packages/manifest-shims/package.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "@atrilabs/manifest-shims", - "version": "0.0.90", - "description": "> TODO: description", - "author": "cruxcode ", - "homepage": "https://github.com/cruxcode/atrilabs-engine#readme", - "license": "ISC", - "directories": { - "lib": "lib" - }, - "files": [ - "lib" - ], - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/cruxcode/atrilabs-engine.git" - }, - "scripts": { - "clean": "rimraf lib", - "build": "tsc", - "prepublishOnly": "yarn run build" - }, - "bugs": { - "url": "https://github.com/cruxcode/atrilabs-engine/issues" - }, - "dependencies": { - "typescript": "^4.6.4" - } -} diff --git a/packages/manifest-shims/src/react/index.ts b/packages/manifest-shims/src/react/index.ts deleted file mode 100644 index c8f5a0bf5..000000000 --- a/packages/manifest-shims/src/react/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Shims } from "./shims"; - -export function setup( - registry: (comps: any) => void, - React: any, - ReactRuntime: any -) { - Shims.React = React; - Shims.ReactRuntime = ReactRuntime; - import("./manifests").then((mod) => registry(mod.manifests)); -} diff --git a/packages/manifest-shims/src/react/manifests.ts b/packages/manifest-shims/src/react/manifests.ts deleted file mode 100644 index e494db660..000000000 --- a/packages/manifest-shims/src/react/manifests.ts +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Default exports from each file goes in here. - * The expected schema of default export is: - * { - * manifests: { - * [manifestId]: component[]; - * } - * } - * The schema validation is expected to be carried out client side. - * The array is expected to be filled by some automated code before build. - */ -const defaultImports: any[] = []; - -function defaultImportsToRegistry() { - const manifests: { [manifestId: string]: { components: any[] } } = {}; - for (let i = 0; i < defaultImports.length; i++) { - const defaultImport = defaultImports[i]!; - if (defaultImport && defaultImport["manifests"]) { - const currManifests = defaultImport["manifests"]; - const manifestIds = Object.keys(currManifests); - manifestIds.forEach((manifestId) => { - if ( - currManifests[manifestId] && - !Array.isArray(currManifests[manifestId]) - ) { - return; - } - if (manifests[manifestId]) { - manifests[manifestId].components.push(...currManifests[manifestId]); - } else { - manifests[manifestId] = { components: currManifests[manifestId] }; - } - }); - } - } - return manifests; -} - -export const manifests = defaultImportsToRegistry(); diff --git a/packages/manifest-shims/src/react/shims.ts b/packages/manifest-shims/src/react/shims.ts deleted file mode 100644 index 9a733e931..000000000 --- a/packages/manifest-shims/src/react/shims.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const Shims = { - React: {}, - ReactRuntime: {}, -}; diff --git a/packages/manifest-shims/tsconfig.json b/packages/manifest-shims/tsconfig.json deleted file mode 100644 index 0b08a0807..000000000 --- a/packages/manifest-shims/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "module": "es2020", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": false, - "jsx": "react-jsx", - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "rootDir": "src", - "outDir": "lib" - }, - "include": ["src"] -} diff --git a/packages/react-shim/README.md b/packages/react-shim/README.md deleted file mode 100644 index 60d8aa6f3..000000000 --- a/packages/react-shim/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# `@atrilabs/react-shim` - -> TODO: description - -## Usage - -``` -const reactShim = require('@atrilabs/react-shim'); - -// TODO: DEMONSTRATE API -``` diff --git a/packages/react-shim/dist/251.js b/packages/react-shim/dist/251.js deleted file mode 100644 index 5803bf4bd..000000000 --- a/packages/react-shim/dist/251.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkmanifestcomps=self.webpackChunkmanifestcomps||[]).push([[251],{251:(n,t,e)=>{e.r(t),e.d(t,{comps:()=>u});var r=e(42),c=function(){return c=Object.assign||function(n){for(var t,e=1,r=arguments.length;e{"use strict";var e,t,r={42:(e,t,r)=>{r.d(t,{E:()=>o});var o={React:{},ReactRuntime:{}}}},o={};function n(e){var t=o[e];if(void 0!==t)return t.exports;var a=o[e]={exports:{}};return r[e](a,a.exports,n),a.exports}n.m=r,n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce(((t,r)=>(n.f[r](e,t),t)),[])),n.u=e=>e+".js",n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),e={},t="manifestcomps:",n.l=(r,o,a,i)=>{if(e[r])e[r].push(o);else{var c,s;if(void 0!==a)for(var u=document.getElementsByTagName("script"),l=0;l{c.onerror=c.onload=null,clearTimeout(f);var n=e[r];if(delete e[r],c.parentNode&&c.parentNode.removeChild(c),n&&n.forEach((e=>e(o))),t)return t(o)},f=setTimeout(d.bind(null,void 0,{type:"timeout",target:c}),12e4);c.onerror=d.bind(null,c.onerror),c.onload=d.bind(null,c.onload),s&&document.head.appendChild(c)}},n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{var e;n.g.importScripts&&(e=n.g.location+"");var t=n.g.document;if(!e&&t&&(t.currentScript&&(e=t.currentScript.src),!e)){var r=t.getElementsByTagName("script");r.length&&(e=r[r.length-1].src)}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),n.p=e})(),(()=>{var e={179:0};n.f.j=(t,r)=>{var o=n.o(e,t)?e[t]:void 0;if(0!==o)if(o)r.push(o[2]);else{var a=new Promise(((r,n)=>o=e[t]=[r,n]));r.push(o[2]=a);var i=n.p+n.u(t),c=new Error;n.l(i,(r=>{if(n.o(e,t)&&(0!==(o=e[t])&&(e[t]=void 0),o)){var a=r&&("load"===r.type?"missing":r.type),i=r&&r.target&&r.target.src;c.message="Loading chunk "+t+" failed.\n("+a+": "+i+")",c.name="ChunkLoadError",c.type=a,c.request=i,o[1](c)}}),"chunk-"+t,t)}};var t=(t,r)=>{var o,a,[i,c,s]=r,u=0;if(i.some((t=>0!==e[t]))){for(o in c)n.o(c,o)&&(n.m[o]=c[o]);s&&s(n)}for(t&&t(r);u{n.r(a),n.d(a,{setup:()=>t});var e=n(42);function t(t,r,o){e.E.React=r,e.E.ReactRuntime=o,n.e(251).then(n.bind(n,251)).then((function(e){return t(e.comps)}))}})(),manifestcomps=a})(); \ No newline at end of file diff --git a/packages/react-shim/package.json b/packages/react-shim/package.json deleted file mode 100644 index 5f932cd3b..000000000 --- a/packages/react-shim/package.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "@atrilabs/react-shim", - "version": "0.0.90", - "description": "> TODO: description", - "author": "cruxcode ", - "homepage": "https://github.com/cruxcode/atrilabs-engine#readme", - "license": "ISC", - "main": "dist/main.js", - "directories": { - "lib": "lib", - "test": "__tests__" - }, - "files": [ - "lib", - "dist" - ], - "publishConfig": { - "access": "public" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/cruxcode/atrilabs-engine.git" - }, - "scripts": { - "clean": "rimraf lib", - "build": "tsc", - "webpack": "webpack", - "prepublishOnly": "yarn run build" - }, - "bugs": { - "url": "https://github.com/cruxcode/atrilabs-engine/issues" - }, - "devDependencies": { - "@babel/core": "^7.17.12", - "@babel/preset-env": "^7.17.12", - "babel-loader": "^8.2.5", - "react": "18.0.0", - "react-dom": "18.0.0", - "webpack": "^5.72.1", - "webpack-cli": "^4.9.2" - }, - "dependencies": { - "cors": "^2.8.5", - "express": "^4.18.1" - } -} diff --git a/packages/react-shim/server/index.js b/packages/react-shim/server/index.js deleted file mode 100644 index d735c9949..000000000 --- a/packages/react-shim/server/index.js +++ /dev/null @@ -1,24 +0,0 @@ -const express = require("express"); -const app = express(); -const cors = require("cors"); -app.use(cors({ origin: "*" })); -const path = require("path"); - -app.use((req, res, next) => { - console.log("rec"); - next(); -}); -app.get("/main.js", (req, res) => { - res.sendFile(path.resolve(__dirname, "..", "dist", "main.js")); -}); - -app.get("/251.js", (req, res) => { - res.sendFile(path.resolve(__dirname, "..", "dist", "251.js")); -}); -const listener = app.listen(4003, "localhost", function () { - console.log( - "... port %d in %s mode", - listener.address().port, - listener.address().address - ); -}); diff --git a/packages/react-shim/src/Button.tsx b/packages/react-shim/src/Button.tsx deleted file mode 100644 index 9f2b2701d..000000000 --- a/packages/react-shim/src/Button.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import React, { useState } from "react"; - -export const Button = () => { - const [count, setcount] = useState(0); - console.log(count); - return ; -}; - -const manifest = { - comp: Button, -}; - -export default manifest; diff --git a/packages/react-shim/src/index.ts b/packages/react-shim/src/index.ts deleted file mode 100644 index 7a5d6d344..000000000 --- a/packages/react-shim/src/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Shims } from "./shims"; - -export function setup( - registry: (comps: any[]) => void, - React: any, - ReactRuntime: any -) { - Shims.React = React; - Shims.ReactRuntime = ReactRuntime; - import("./manifests").then((mod) => registry(mod.comps)); -} diff --git a/packages/react-shim/src/manifests.ts b/packages/react-shim/src/manifests.ts deleted file mode 100644 index 0b8cea224..000000000 --- a/packages/react-shim/src/manifests.ts +++ /dev/null @@ -1,3 +0,0 @@ -import ButtonManifest from "./Button"; - -export const comps = [ButtonManifest.comp]; diff --git a/packages/react-shim/src/shims.ts b/packages/react-shim/src/shims.ts deleted file mode 100644 index 9a733e931..000000000 --- a/packages/react-shim/src/shims.ts +++ /dev/null @@ -1,4 +0,0 @@ -export const Shims = { - React: {}, - ReactRuntime: {}, -}; diff --git a/packages/react-shim/tsconfig.json b/packages/react-shim/tsconfig.json deleted file mode 100644 index 0b08a0807..000000000 --- a/packages/react-shim/tsconfig.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, - "skipLibCheck": true, - "esModuleInterop": true, - "allowSyntheticDefaultImports": true, - "strict": true, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "module": "es2020", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "noEmit": false, - "jsx": "react-jsx", - "declaration": true, - "declarationMap": true, - "sourceMap": true, - "rootDir": "src", - "outDir": "lib" - }, - "include": ["src"] -} diff --git a/packages/react-shim/webpack.config.js b/packages/react-shim/webpack.config.js deleted file mode 100644 index 21c25b949..000000000 --- a/packages/react-shim/webpack.config.js +++ /dev/null @@ -1,34 +0,0 @@ -const path = require("path"); - -module.exports = { - mode: "production", - entry: "./lib/index.js", - output: { - path: path.resolve(__dirname, "dist"), - library: "manifestcomps", - }, - module: { - rules: [ - { - oneOf: [ - { - test: /\.(js|mjs|jsx|ts|tsx)$/, - use: [ - { - loader: require.resolve("babel-loader"), - options: { - presets: ["@babel/preset-env"], - babelrc: false, - configFile: false, - }, - }, - ], - }, - ], - }, - ], - }, - resolve: { - extensions: [".ts", ".tsx", ".js", ".jsx"], - }, -}; From f1571d69ab3ca87d8e90851f64fdb0126d242504 Mon Sep 17 00:00:00 2001 From: cruxcode Date: Tue, 28 Feb 2023 13:35:53 -0800 Subject: [PATCH 26/28] resource layer integrated --- packages/core/src/types.tsx | 10 + packages/pwa-builder-manager/src/api.ts | 15 +- packages/pwa-builder-server/package.json | 4 +- .../src/handle-resources/fetchCSSResource.ts | 189 +++++++++++ .../src/handle-resources/index.ts | 2 + .../handle-resources/manageResourceFiles.ts | 27 ++ packages/pwa-builder-server/src/index.ts | 41 +++ packages/pwa-builder/src/App.tsx | 4 +- .../resource-processor-layer/package.json | 1 + .../src/hooks/useFetchResources.ts | 3 +- .../resource-processor-layer/src/index.tsx | 4 +- yarn.lock | 316 ++---------------- 12 files changed, 318 insertions(+), 298 deletions(-) create mode 100644 packages/pwa-builder-server/src/handle-resources/fetchCSSResource.ts create mode 100644 packages/pwa-builder-server/src/handle-resources/index.ts create mode 100644 packages/pwa-builder-server/src/handle-resources/manageResourceFiles.ts diff --git a/packages/core/src/types.tsx b/packages/core/src/types.tsx index a1ee5bc48..964662676 100644 --- a/packages/core/src/types.tsx +++ b/packages/core/src/types.tsx @@ -389,6 +389,7 @@ export type ManifestIR = { export interface ServerToClientEvents { loadEvent: (forestPkgId: string, urlPath: string, event: AnyEvent) => void; + newResource: (resource: ImportedResource) => void; } export interface ClientToServerEvents { @@ -426,6 +427,15 @@ export interface ClientToServerEvents { [name: string]: { url: string; mime: string }; }) => void ) => void; + /** resource management api */ + importResource: ( + importStatement: { str: string }, + callback: (success: boolean) => void + ) => void; + getResources: (callback: (resources: ImportedResource[]) => void) => void; + subscribeResourceUpdates: ( + callback: (resource: ImportedResource) => void + ) => void; } export interface InterServerEvents {} diff --git a/packages/pwa-builder-manager/src/api.ts b/packages/pwa-builder-manager/src/api.ts index facf08194..c35a9103c 100644 --- a/packages/pwa-builder-manager/src/api.ts +++ b/packages/pwa-builder-manager/src/api.ts @@ -72,11 +72,20 @@ function postNewEvents( function importResource( importStatement: { str: string }, callback: (success: boolean) => void -) {} -function getResources(callback: (resources: ImportedResource[]) => void) {} +) { + socket.emit("importResource", importStatement, callback); +} +function getResources(callback: (resources: ImportedResource[]) => void) { + socket.emit("getResources", callback); +} function subscribeResourceUpdates( callback: (resource: ImportedResource) => void -) {} +) { + socket.on("newResource", callback); + return () => { + socket.off("newResource", callback); + }; +} /** * diff --git a/packages/pwa-builder-server/package.json b/packages/pwa-builder-server/package.json index 8e2cc4c35..482052cb9 100644 --- a/packages/pwa-builder-server/package.json +++ b/packages/pwa-builder-server/package.json @@ -26,9 +26,11 @@ "@atrilabs/atri-app-core": "^0.0.90", "@atrilabs/commands-builder": "^0.0.90", "@atrilabs/core": "^0.0.90", + "css-tree": "^2.3.1", "mime-types": "^2.1.35", "recursive-readdir": "^2.2.3", - "upath": "^2.0.1" + "upath": "^2.0.1", + "uuid": "^9.0.0" }, "devDependencies": { "@types/mime-types": "^2.1.1" diff --git a/packages/pwa-builder-server/src/handle-resources/fetchCSSResource.ts b/packages/pwa-builder-server/src/handle-resources/fetchCSSResource.ts new file mode 100644 index 000000000..d32191fbf --- /dev/null +++ b/packages/pwa-builder-server/src/handle-resources/fetchCSSResource.ts @@ -0,0 +1,189 @@ +import { ImportedResource } from "@atrilabs/core"; +import * as csstree from "css-tree"; +import https from "https"; +import http from "http"; +import fs from "fs"; + +type FontImports = Required["fonts"]; + +function extractFontDetails(cssContent: string) { + return new Promise((res) => { + const fontImports: FontImports = []; + const ast = csstree.parse(cssContent); + csstree.walk(ast, { + enter: (node: csstree.CssNode) => { + if (node.type === "Atrule" && node.name === "font-face") { + const currentFontImport: Partial = {}; + csstree.walk(node, { + enter: () => { + csstree.walk(node, { + enter: (node: csstree.CssNode) => { + if ( + node.type === "Declaration" && + node.property === "font-family" + ) { + const nodeValue = node.value as any; + if ( + nodeValue && + nodeValue.children && + nodeValue.children.head && + nodeValue.children.head.data && + nodeValue.children.head.data.value + ) { + currentFontImport.fontFamily = + nodeValue.children.head.data.value; + } else { + csstree.walk(node, { + visit: "Identifier", + enter: (identifierNode) => { + currentFontImport.fontFamily = identifierNode.name; + }, + }); + } + } + + if ( + node.type === "Declaration" && + node.property === "font-weight" + ) { + const nodeValue = node.value as any; + if ( + nodeValue && + nodeValue.children && + nodeValue.children.head && + nodeValue.children.head.data && + nodeValue.children.head.data.value + ) { + if (nodeValue.children.head.data.type === "String") + currentFontImport.fontWeight = + nodeValue.children.head.data.value; + else if (nodeValue.children.head.data.type === "Number") { + currentFontImport.fontWeight = parseInt( + nodeValue.children.head.data.value + ); + } + } else { + csstree.walk(node, { + visit: "Identifier", + enter: (identifierNode) => { + currentFontImport.fontWeight = identifierNode.name; + }, + }); + } + } + + if ( + node.type === "Declaration" && + node.property === "font-style" + ) { + csstree.walk(node, { + visit: "Identifier", + enter: (identifierNode) => { + currentFontImport.fontStyle = identifierNode.name; + }, + }); + } + }, + }); + }, + }); + fontImports.push(currentFontImport as FontImports[0]); + } + }, + }); + res(fontImports); + }); +} + +function fetchExternalCSS(url: string) { + return new Promise((resolve, reject) => { + const parsedUrl = new URL(url); + let request: http.ClientRequest; + if (parsedUrl.protocol.startsWith("https")) { + request = https.request(parsedUrl); + } else { + request = http.request(url); + } + if (request === undefined) { + reject("unknown protocol"); + return; + } + request.on("response", (resp) => { + let data = ""; + resp.on("data", (chunk) => { + data += chunk; + }); + resp.on("end", () => { + resolve(data); + }); + resp.on("error", (err) => { + console.log("Unable to get response", url); + console.log("Error\n", err); + reject(); + }); + }); + request.on("error", (err) => { + console.log("Unable to send request:", url); + console.log("Error\n", err); + reject(); + }); + request.end(); + }); +} + +export function fetchCSSResource(resourceStr: string) { + // true if found a valid resource + return new Promise((res, rej) => { + const ast = csstree.parse(resourceStr); + let validResource = false; + csstree.walk(ast, { + visit: "Atrule", + enter: (node: csstree.CssNode) => { + if (node.type === "Atrule" && node.name === "import") { + if ( + node.prelude && + "children" in node.prelude && + "head" in node.prelude.children && + "data" in (node.prelude.children as any).head && + "type" in (node.prelude.children as any).head.data && + "value" in (node.prelude.children as any).head.data + ) { + const type = (node.prelude.children as any).head.data.type; + const value = (node.prelude.children as any).head.data + .value as string; + if (type === "Url" && value) { + if (value.startsWith("https://") && value.match(".css")) { + // found a valid css url + validResource = true; + fetchExternalCSS(value) + .then((cssContent) => { + extractFontDetails(cssContent).then((fontImports) => + res({ + str: resourceStr, + method: "css", + imports: { + fonts: fontImports, + }, + }) + ); + }) + .catch(() => { + console.log("Fetching CSS failed"); + rej(); + }); + } + } + } + } + }, + }); + if (!validResource) { + rej(); + } + }); +} + +export async function fetchCSSFromFile(file: string) { + const content = await fs.promises.readFile(file); + return fetchCSSResource(content.toString()); +} diff --git a/packages/pwa-builder-server/src/handle-resources/index.ts b/packages/pwa-builder-server/src/handle-resources/index.ts new file mode 100644 index 000000000..e58b0a28f --- /dev/null +++ b/packages/pwa-builder-server/src/handle-resources/index.ts @@ -0,0 +1,2 @@ +export * from "./fetchCSSResource"; +export * from "./manageResourceFiles"; diff --git a/packages/pwa-builder-server/src/handle-resources/manageResourceFiles.ts b/packages/pwa-builder-server/src/handle-resources/manageResourceFiles.ts new file mode 100644 index 000000000..16b8007f3 --- /dev/null +++ b/packages/pwa-builder-server/src/handle-resources/manageResourceFiles.ts @@ -0,0 +1,27 @@ +import path from "path"; +import fs from "fs"; +import recursive from "recursive-readdir"; +import { v4 as uuidv4 } from "uuid"; + +export const RESOURCES_DIR = path.resolve("public", "resources"); + +function getId() { + return uuidv4(); +} + +function createResourcesDir() { + if (!fs.existsSync(RESOURCES_DIR)) { + fs.mkdirSync(RESOURCES_DIR, { recursive: true }); + } +} + +createResourcesDir(); + +export function createCSSFile(content: string) { + const filename = `${getId()}.css`; + fs.writeFileSync(path.resolve(RESOURCES_DIR, filename), content); +} + +export function getResourceFiles() { + return recursive(RESOURCES_DIR); +} diff --git a/packages/pwa-builder-server/src/index.ts b/packages/pwa-builder-server/src/index.ts index 62c620384..0f91509c0 100644 --- a/packages/pwa-builder-server/src/index.ts +++ b/packages/pwa-builder-server/src/index.ts @@ -6,6 +6,7 @@ import type { ServerToClientEvents, InterServerEvents, SocketData, + ImportedResource, } from "@atrilabs/core"; import { getAppInfo, @@ -17,6 +18,13 @@ import { saveEventsForPage, } from "./utils"; import { saveAssets, getAllAssetsInfo, PUBLIC_DIR } from "./handleAssets"; +import fs from "fs"; +import { + createCSSFile, + fetchCSSFromFile, + fetchCSSResource, + getResourceFiles, +} from "./handle-resources"; const app = express(); const server = http.createServer(app); @@ -77,6 +85,39 @@ io.on("connection", (socket) => { console.log(err); }); }); + socket.on("importResource", (resource, cb) => { + fetchCSSResource(resource.str) + .then((importedResource) => { + try { + createCSSFile(resource.str); + io.sockets.emit("newResource", importedResource); + cb(true); + } catch (err) { + console.log(err); + cb(false); + } + }) + .catch(() => { + console.log("Some error occured while fetching CSS resource."); + cb(false); + }); + }); + socket.on("getResources", (cb) => { + getResourceFiles() + .then((files) => { + const promises = files + .filter((file) => file.endsWith(".css")) + .map((file) => fetchCSSFromFile(file)); + return Promise.all(promises); + }) + .then((resources) => { + cb(resources); + }) + .catch((err) => { + console.log(err); + cb([]); + }); + }); }); app.use(express.static(PUBLIC_DIR)); diff --git a/packages/pwa-builder/src/App.tsx b/packages/pwa-builder/src/App.tsx index cd11c3f57..43e9149d6 100644 --- a/packages/pwa-builder/src/App.tsx +++ b/packages/pwa-builder/src/App.tsx @@ -13,7 +13,7 @@ import CustomPropsLayer from "@atrilabs/custom-props-layer"; import AssetManagerLayer from "@atrilabs/asset-manager-layer"; // import AppTemplateLayer from "@atrilabs/app-template-layer"; import ActionLayer from "@atrilabs/action-layer"; -// import ResourceProcessLayer from "@atrilabs/resource-processor-layer"; +import ResourceProcessLayer from "@atrilabs/resource-processor-layer"; // import UndoRedoLayer from "@atrilabs/undo-redo-layer"; // import ComponentNavigatorLayer from "@atrilabs/component-navigator"; // import ServicesStatusLayer from "@atrilabs/services-status-layer"; @@ -36,7 +36,7 @@ export default function App() { {/* */} - {/* */} + {/* */} {/* */} {/* diff --git a/packages/resource-processor-layer/package.json b/packages/resource-processor-layer/package.json index 3e6982504..05ed481ba 100644 --- a/packages/resource-processor-layer/package.json +++ b/packages/resource-processor-layer/package.json @@ -29,6 +29,7 @@ "@atrilabs/core": "^0.0.90", "@atrilabs/design-system": "^0.0.90", "@atrilabs/layer-types": "^0.0.90", + "@atrilabs/pwa-builder-manager": "^0.0.90", "@atrilabs/scripts": "^0.0.90", "eslint-plugin-atrilabs": "^0.0.90", "typescript": "4.6.3" diff --git a/packages/resource-processor-layer/src/hooks/useFetchResources.ts b/packages/resource-processor-layer/src/hooks/useFetchResources.ts index d7a07fcde..e12d6b340 100644 --- a/packages/resource-processor-layer/src/hooks/useFetchResources.ts +++ b/packages/resource-processor-layer/src/hooks/useFetchResources.ts @@ -1,6 +1,7 @@ -import { api, ImportedResource } from "@atrilabs/core"; +import { ImportedResource } from "@atrilabs/core"; import { useEffect, useState } from "react"; import { addStylesheet } from "@atrilabs/canvas-runtime"; +import { api } from "@atrilabs/pwa-builder-manager"; export const useFetchResources = () => { const [resources, setResources] = useState([]); diff --git a/packages/resource-processor-layer/src/index.tsx b/packages/resource-processor-layer/src/index.tsx index d9793f04a..cf302981c 100644 --- a/packages/resource-processor-layer/src/index.tsx +++ b/packages/resource-processor-layer/src/index.tsx @@ -1,4 +1,4 @@ -import { api, Container, Menu } from "@atrilabs/core"; +import { Container, Menu } from "@atrilabs/core"; import { amber300, gray200, @@ -17,6 +17,8 @@ import { useState, useCallback, useRef } from "react"; import { Cross } from "./assets/Cross"; import { useFetchResources } from "./hooks/useFetchResources"; import "./styles.css"; +import { api } from "@atrilabs/pwa-builder-manager"; + const styles: { [key: string]: React.CSSProperties } = { iconContainer: { borderRight: `1px solid ${gray800}`, diff --git a/yarn.lock b/yarn.lock index 6b8b0c432..83b9d22d1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18,6 +18,13 @@ jsonpointer "^5.0.0" leven "^3.1.0" +"@atrilabs/manifest-shims@^0.0.90": + version "0.0.90" + resolved "https://registry.yarnpkg.com/@atrilabs/manifest-shims/-/manifest-shims-0.0.90.tgz#09add0da8413ceb2313187cea84d3779be6ba7a4" + integrity sha512-VYf+sjtPZswGrP4mCKPsholYa8MlcT2ivGnqLalqqSTWkPuLuInoBz9RP8gBlcpd9ErHmq8t/7NgvTZTjzsIsg== + dependencies: + typescript "^4.6.4" + "@babel/cli@^7.17.10": version "7.17.10" resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.17.10.tgz#5ea0bf6298bb78f3b59c7c06954f9bd1c79d5943" @@ -126,27 +133,6 @@ json5 "^2.2.1" semver "^6.3.0" -"@babel/core@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.17.12.tgz#b4eb2d7ebc3449b062381644c93050db545b70ee" - integrity sha512-44ODe6O1IVz9s2oJE3rZ4trNNKTX9O7KpQpfAP4t8QII/zwrVRHL7i2pxhqtcY7tqMLrrKfMlBKnm1QlrRFs5w== - dependencies: - "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.12" - "@babel/helper-compilation-targets" "^7.17.10" - "@babel/helper-module-transforms" "^7.17.12" - "@babel/helpers" "^7.17.9" - "@babel/parser" "^7.17.12" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.12" - "@babel/types" "^7.17.12" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.1" - semver "^6.3.0" - "@babel/core@^7.18.5": version "7.18.5" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.5.tgz#c597fa680e58d571c28dda9827669c78cdd7f000" @@ -195,15 +181,6 @@ "@jridgewell/gen-mapping" "^0.1.0" jsesc "^2.5.1" -"@babel/generator@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.12.tgz#5970e6160e9be0428e02f4aba62d8551ec366cc8" - integrity sha512-V49KtZiiiLjH/CnIW6OjJdrenrGoyh6AmKQ3k2AZFKozC1h846Q4NYlZ5nqAigPDUXfGzC88+LOUuG8yKd2kCw== - dependencies: - "@babel/types" "^7.17.12" - "@jridgewell/gen-mapping" "^0.3.0" - jsesc "^2.5.1" - "@babel/generator@^7.17.9": version "7.17.9" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.9.tgz#f4af9fd38fa8de143c29fce3f71852406fc1e2fc" @@ -536,20 +513,6 @@ "@babel/traverse" "^7.17.3" "@babel/types" "^7.17.0" -"@babel/helper-module-transforms@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.17.12.tgz#bec00139520cb3feb078ef7a4578562480efb77e" - integrity sha512-t5s2BeSWIghhFRPh9XMn6EIGmvn8Lmw5RVASJzkIx1mSemubQQBNIZiQD7WzaFmaHIrjAec4x8z9Yx8SjJ1/LA== - dependencies: - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-module-imports" "^7.16.7" - "@babel/helper-simple-access" "^7.17.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/helper-validator-identifier" "^7.16.7" - "@babel/template" "^7.16.7" - "@babel/traverse" "^7.17.12" - "@babel/types" "^7.17.12" - "@babel/helper-module-transforms@^7.18.0": version "7.18.0" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.0.tgz#baf05dec7a5875fb9235bd34ca18bad4e21221cd" @@ -815,11 +778,6 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.9.tgz#9c94189a6062f0291418ca021077983058e171ef" integrity sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg== -"@babel/parser@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.12.tgz#36c2ed06944e3691ba82735fc4cf62d12d491a23" - integrity sha512-FLzHmN9V3AJIrWfOpvRlZCeVg/WLdicSnTMsLur6uDj9TT8ymUlG9XxURdW/XvuygK+2CW0poOJABdA4m/YKxA== - "@babel/parser@^7.18.10": version "7.18.10" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.10.tgz#94b5f8522356e69e8277276adf67ed280c90ecc1" @@ -944,15 +902,6 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-class-static-block" "^7.14.5" -"@babel/plugin-proposal-class-static-block@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.17.12.tgz#947f09dd496322c9543ec3b318bf52b4d9833334" - integrity sha512-8ILyDG6eL14F8iub97dVc8q35Md0PJYAnA5Kz9NACFOkt6ffCcr0FISyUPKHsvuAy36fkpIitxZ9bVYPFMGQHA== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - "@babel/plugin-proposal-class-static-block@^7.18.0": version "7.18.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.0.tgz#7d02253156e3c3793bdb9f2faac3a1c05f0ba710" @@ -1133,17 +1082,6 @@ "@babel/plugin-syntax-object-rest-spread" "^7.8.3" "@babel/plugin-transform-parameters" "^7.16.7" -"@babel/plugin-proposal-object-rest-spread@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.12.tgz#f94a91715a7f2f8cfb3c06af820c776440bc0148" - integrity sha512-6l9cO3YXXRh4yPCPRA776ZyJ3RobG4ZKJZhp7NDRbKIOeV3dBPG8FXCF7ZtiO2RTCIOkQOph1xDDcc01iWVNjQ== - dependencies: - "@babel/compat-data" "^7.17.10" - "@babel/helper-compilation-targets" "^7.17.10" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.17.12" - "@babel/plugin-proposal-object-rest-spread@^7.18.0": version "7.18.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.0.tgz#79f2390c892ba2a68ec112eb0d895cfbd11155e8" @@ -1645,13 +1583,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-destructuring@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.12.tgz#0861d61e75e2401aca30f2570d46dfc85caacf35" - integrity sha512-P8pt0YiKtX5UMUL5Xzsc9Oyij+pJE6JuC+F1k0/brq/OOGs5jDa1If3OY0LRWGvJsJhI+8tsiecL3nJLc0WTlg== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-transform-destructuring@^7.18.0": version "7.18.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.0.tgz#dc4f92587e291b4daa78aa20cc2d7a63aa11e858" @@ -1734,13 +1665,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-for-of@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.17.12.tgz#5397c22554ec737a27918e7e7e0e7b679b05f5ec" - integrity sha512-76lTwYaCxw8ldT7tNmye4LLwSoKDbRCBzu6n/DcK/P3FOR29+38CIIaVIZfwol9By8W/QHORYEnYSLuvcQKrsg== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-transform-for-of@^7.18.1": version "7.18.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.1.tgz#ed14b657e162b72afbbb2b4cdad277bf2bb32036" @@ -1817,15 +1741,6 @@ "@babel/helper-plugin-utils" "^7.16.7" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-amd@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.17.12.tgz#08ec1f10f854c15bb3b44952e60f1fc126d7d481" - integrity sha512-p5rt9tB5Ndcc2Za7CeNxVf7YAjRcUMR6yi8o8tKjb9KhRkEvXwa+C0hj6DA5bVDkKRxB0NYhMUGbVKoFu4+zEA== - dependencies: - "@babel/helper-module-transforms" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" - babel-plugin-dynamic-import-node "^2.3.3" - "@babel/plugin-transform-modules-amd@^7.18.0": version "7.18.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.0.tgz#7ef1002e67e36da3155edc8bf1ac9398064c02ed" @@ -1853,16 +1768,6 @@ "@babel/helper-simple-access" "^7.17.7" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-commonjs@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.12.tgz#37691c7404320d007288edd5a2d8600bcef61c34" - integrity sha512-tVPs6MImAJz+DiX8Y1xXEMdTk5Lwxu9jiPjlS+nv5M2A59R7+/d1+9A8C/sbuY0b3QjIxqClkj6KAplEtRvzaA== - dependencies: - "@babel/helper-module-transforms" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-simple-access" "^7.17.7" - babel-plugin-dynamic-import-node "^2.3.3" - "@babel/plugin-transform-modules-commonjs@^7.18.2": version "7.18.2" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.2.tgz#1aa8efa2e2a6e818b6a7f2235fceaf09bdb31e9e" @@ -1893,17 +1798,6 @@ "@babel/helper-validator-identifier" "^7.16.7" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-systemjs@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.17.12.tgz#e631b151b99d25401cd9679476cc35e6e5bbc7d4" - integrity sha512-NVhDb0q00hqZcuLduUf/kMzbOQHiocmPbIxIvk23HLiEqaTKC/l4eRxeC7lO63M72BmACoiKOcb9AkOAJRerpw== - dependencies: - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-module-transforms" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-validator-identifier" "^7.16.7" - babel-plugin-dynamic-import-node "^2.3.3" - "@babel/plugin-transform-modules-systemjs@^7.18.0": version "7.18.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.18.5.tgz#87f11c44fbfd3657be000d4897e192d9cb535996" @@ -1933,14 +1827,6 @@ "@babel/helper-module-transforms" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-modules-umd@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.17.12.tgz#b37be3ecf198c1fea10e6268461729ced05644e1" - integrity sha512-BnsPkrUHsjzZGpnrmJeDFkOMMljWFHPjDc9xDcz71/C+ybF3lfC3V4m3dwXPLZrE5b3bgd4V+3/Pj+3620d7IA== - dependencies: - "@babel/helper-module-transforms" "^7.17.12" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-transform-modules-umd@^7.18.0": version "7.18.0" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.0.tgz#56aac64a2c2a1922341129a4597d1fd5c3ff020f" @@ -2277,13 +2163,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-template-literals@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.17.12.tgz#4aec0a18f39dd86c442e1d077746df003e362c6e" - integrity sha512-kAKJ7DX1dSRa2s7WN1xUAuaQmkTpN+uig4wCKWivVXIObqGbVTUlSavHyfI2iZvz89GFAMGm9p2DBJ4Y1Tp0hw== - dependencies: - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/plugin-transform-template-literals@^7.18.2": version "7.18.2" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.2.tgz#31ed6915721864847c48b656281d0098ea1add28" @@ -2617,86 +2496,6 @@ core-js-compat "^3.20.2" semver "^6.3.0" -"@babel/preset-env@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.17.12.tgz#b81ae0bb762b683d68b07b6d2d4020ccbef8d67a" - integrity sha512-Kke30Rj3Lmcx97bVs71LO0s8M6FmJ7tUAQI9fNId62rf0cYG1UAWwdNO9/sE0/pLEahAw1MqMorymoD12bj5Fg== - dependencies: - "@babel/compat-data" "^7.17.10" - "@babel/helper-compilation-targets" "^7.17.10" - "@babel/helper-plugin-utils" "^7.17.12" - "@babel/helper-validator-option" "^7.16.7" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.17.12" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.17.12" - "@babel/plugin-proposal-async-generator-functions" "^7.17.12" - "@babel/plugin-proposal-class-properties" "^7.17.12" - "@babel/plugin-proposal-class-static-block" "^7.17.12" - "@babel/plugin-proposal-dynamic-import" "^7.16.7" - "@babel/plugin-proposal-export-namespace-from" "^7.17.12" - "@babel/plugin-proposal-json-strings" "^7.17.12" - "@babel/plugin-proposal-logical-assignment-operators" "^7.17.12" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.17.12" - "@babel/plugin-proposal-numeric-separator" "^7.16.7" - "@babel/plugin-proposal-object-rest-spread" "^7.17.12" - "@babel/plugin-proposal-optional-catch-binding" "^7.16.7" - "@babel/plugin-proposal-optional-chaining" "^7.17.12" - "@babel/plugin-proposal-private-methods" "^7.17.12" - "@babel/plugin-proposal-private-property-in-object" "^7.17.12" - "@babel/plugin-proposal-unicode-property-regex" "^7.17.12" - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-class-properties" "^7.12.13" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-transform-arrow-functions" "^7.17.12" - "@babel/plugin-transform-async-to-generator" "^7.17.12" - "@babel/plugin-transform-block-scoped-functions" "^7.16.7" - "@babel/plugin-transform-block-scoping" "^7.17.12" - "@babel/plugin-transform-classes" "^7.17.12" - "@babel/plugin-transform-computed-properties" "^7.17.12" - "@babel/plugin-transform-destructuring" "^7.17.12" - "@babel/plugin-transform-dotall-regex" "^7.16.7" - "@babel/plugin-transform-duplicate-keys" "^7.17.12" - "@babel/plugin-transform-exponentiation-operator" "^7.16.7" - "@babel/plugin-transform-for-of" "^7.17.12" - "@babel/plugin-transform-function-name" "^7.16.7" - "@babel/plugin-transform-literals" "^7.17.12" - "@babel/plugin-transform-member-expression-literals" "^7.16.7" - "@babel/plugin-transform-modules-amd" "^7.17.12" - "@babel/plugin-transform-modules-commonjs" "^7.17.12" - "@babel/plugin-transform-modules-systemjs" "^7.17.12" - "@babel/plugin-transform-modules-umd" "^7.17.12" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.17.12" - "@babel/plugin-transform-new-target" "^7.17.12" - "@babel/plugin-transform-object-super" "^7.16.7" - "@babel/plugin-transform-parameters" "^7.17.12" - "@babel/plugin-transform-property-literals" "^7.16.7" - "@babel/plugin-transform-regenerator" "^7.17.9" - "@babel/plugin-transform-reserved-words" "^7.17.12" - "@babel/plugin-transform-shorthand-properties" "^7.16.7" - "@babel/plugin-transform-spread" "^7.17.12" - "@babel/plugin-transform-sticky-regex" "^7.16.7" - "@babel/plugin-transform-template-literals" "^7.17.12" - "@babel/plugin-transform-typeof-symbol" "^7.17.12" - "@babel/plugin-transform-unicode-escapes" "^7.16.7" - "@babel/plugin-transform-unicode-regex" "^7.16.7" - "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.17.12" - babel-plugin-polyfill-corejs2 "^0.3.0" - babel-plugin-polyfill-corejs3 "^0.5.0" - babel-plugin-polyfill-regenerator "^0.3.0" - core-js-compat "^3.22.1" - semver "^6.3.0" - "@babel/preset-env@^7.18.2": version "7.18.2" resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.18.2.tgz#f47d3000a098617926e674c945d95a28cb90977a" @@ -2947,22 +2746,6 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.17.12": - version "7.17.12" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.12.tgz#011874d2abbca0ccf1adbe38f6f7a4ff1747599c" - integrity sha512-zULPs+TbCvOkIFd4FrG53xrpxvCBwLIgo6tO0tJorY7YV2IWFxUfS/lXDJbGgfyYt9ery/Gxj2niwttNnB0gIw== - dependencies: - "@babel/code-frame" "^7.16.7" - "@babel/generator" "^7.17.12" - "@babel/helper-environment-visitor" "^7.16.7" - "@babel/helper-function-name" "^7.17.9" - "@babel/helper-hoist-variables" "^7.16.7" - "@babel/helper-split-export-declaration" "^7.16.7" - "@babel/parser" "^7.17.12" - "@babel/types" "^7.17.12" - debug "^4.1.0" - globals "^11.1.0" - "@babel/traverse@^7.18.0", "@babel/traverse@^7.18.2", "@babel/traverse@^7.18.5": version "7.18.5" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.5.tgz#94a8195ad9642801837988ab77f36e992d9a20cd" @@ -6651,23 +6434,11 @@ "@webassemblyjs/ast" "1.11.1" "@xtuc/long" "4.2.2" -"@webpack-cli/configtest@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.1.1.tgz#9f53b1b7946a6efc2a749095a4f450e2932e8356" - integrity sha512-1FBc1f9G4P/AxMqIgfZgeOTuRnwZMten8E7zap5zgpPInnCrP8D4Q81+4CWIch8i/Nf7nXjP0v6CjjbHOrXhKg== - "@webpack-cli/configtest@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5" integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg== -"@webpack-cli/info@^1.4.1": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.4.1.tgz#2360ea1710cbbb97ff156a3f0f24556e0fc1ebea" - integrity sha512-PKVGmazEq3oAo46Q63tpMr4HipI3OPfP7LiNOEJg963RMgT0rqheag28NCML0o3GIzA3DmxP1ZIAv9oTX1CUIA== - dependencies: - envinfo "^7.7.3" - "@webpack-cli/info@^1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1" @@ -6675,11 +6446,6 @@ dependencies: envinfo "^7.7.3" -"@webpack-cli/serve@^1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.6.1.tgz#0de2875ac31b46b6c5bb1ae0a7d7f0ba5678dffe" - integrity sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw== - "@webpack-cli/serve@^1.7.0": version "1.7.0" resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1" @@ -8320,7 +8086,7 @@ core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== -cors@^2.8.5, cors@~2.8.5: +cors@~2.8.5: version "2.8.5" resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== @@ -8471,6 +8237,14 @@ css-tree@^2.2.1: mdn-data "2.0.28" source-map-js "^1.0.1" +css-tree@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20" + integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw== + dependencies: + mdn-data "2.0.30" + source-map-js "^1.0.1" + css-unit-converter@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.2.tgz#4c77f5a1954e6dbff60695ecb214e3270436ab21" @@ -13044,6 +12818,11 @@ mdn-data@2.0.28: resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.28.tgz#5ec48e7bef120654539069e1ae4ddc81ca490eba" integrity sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g== +mdn-data@2.0.30: + version "2.0.30" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc" + integrity sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA== + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -17497,6 +17276,11 @@ uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +uuid@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" + integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== + v8-compile-cache-lib@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" @@ -17645,24 +17429,6 @@ webpack-cli@^4.10.0: rechoir "^0.7.0" webpack-merge "^5.7.3" -webpack-cli@^4.9.2: - version "4.9.2" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.9.2.tgz#77c1adaea020c3f9e2db8aad8ea78d235c83659d" - integrity sha512-m3/AACnBBzK/kMTcxWHcZFPrw/eQuY4Df1TxvIWfWM2x7mRqBQCqKEd96oCUa9jkapLBaFfRce33eGDb4Pr7YQ== - dependencies: - "@discoveryjs/json-ext" "^0.5.0" - "@webpack-cli/configtest" "^1.1.1" - "@webpack-cli/info" "^1.4.1" - "@webpack-cli/serve" "^1.6.1" - colorette "^2.0.14" - commander "^7.0.0" - execa "^5.0.0" - fastest-levenshtein "^1.0.12" - import-local "^3.0.2" - interpret "^2.2.0" - rechoir "^0.7.0" - webpack-merge "^5.7.3" - webpack-dev-middleware@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz#aa079a8dedd7e58bfeab358a9af7dab304cee57f" @@ -17816,36 +17582,6 @@ webpack@^5.72.0: watchpack "^2.3.1" webpack-sources "^3.2.3" -webpack@^5.72.1: - version "5.72.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.72.1.tgz#3500fc834b4e9ba573b9f430b2c0a61e1bb57d13" - integrity sha512-dXG5zXCLspQR4krZVR6QgajnZOjW2K/djHvdcRaDQvsjV9z9vaW6+ja5dZOYbqBBjF6kGXka/2ZyxNdc+8Jung== - dependencies: - "@types/eslint-scope" "^3.7.3" - "@types/estree" "^0.0.51" - "@webassemblyjs/ast" "1.11.1" - "@webassemblyjs/wasm-edit" "1.11.1" - "@webassemblyjs/wasm-parser" "1.11.1" - acorn "^8.4.1" - acorn-import-assertions "^1.7.6" - browserslist "^4.14.5" - chrome-trace-event "^1.0.2" - enhanced-resolve "^5.9.3" - es-module-lexer "^0.9.0" - eslint-scope "5.1.1" - events "^3.2.0" - glob-to-regexp "^0.4.1" - graceful-fs "^4.2.9" - json-parse-even-better-errors "^2.3.1" - loader-runner "^4.2.0" - mime-types "^2.1.27" - neo-async "^2.6.2" - schema-utils "^3.1.0" - tapable "^2.1.1" - terser-webpack-plugin "^5.1.3" - watchpack "^2.3.1" - webpack-sources "^3.2.3" - webpack@^5.73.0: version "5.73.0" resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.73.0.tgz#bbd17738f8a53ee5760ea2f59dce7f3431d35d38" From b96d426dbdccef82a1a8ab6e6b63217ed18d0866 Mon Sep 17 00:00:00 2001 From: cruxcode Date: Tue, 28 Feb 2023 15:27:27 -0800 Subject: [PATCH 27/28] imported fonts reflect in editor --- packages/atri-app-core/src/api/canvasApi.ts | 7 ++++++- .../atri-app-core/src/api/handleResources.ts | 11 ++++++++++ packages/atri-app-core/src/types.ts | 12 +++++++++++ packages/core/src/types.tsx | 13 ++---------- .../src/handleResourceTransfer.ts | 20 +++++++++++++++++++ packages/pwa-builder-manager/src/index.ts | 1 + 6 files changed, 52 insertions(+), 12 deletions(-) create mode 100644 packages/atri-app-core/src/api/handleResources.ts create mode 100644 packages/pwa-builder-manager/src/handleResourceTransfer.ts diff --git a/packages/atri-app-core/src/api/canvasApi.ts b/packages/atri-app-core/src/api/canvasApi.ts index 6fb6a8d87..6d6eef872 100644 --- a/packages/atri-app-core/src/api/canvasApi.ts +++ b/packages/atri-app-core/src/api/canvasApi.ts @@ -1,12 +1,13 @@ import { canvasMachineInterpreter, subscribeCanvasMachine } from "./init"; import { componentStoreApi } from "./componentStoreApi"; import { CANVAS_ZONE_ROOT_ID } from "./consts"; -import { CanvasZoneEvent, ComponentEvent } from "../types"; +import { CanvasZoneEvent, ComponentEvent, ImportedResource } from "../types"; import { getComponentIndexInsideCanvasZone, getComponentIndexInsideParentComponent, } from "./utils"; import type { DewireUpdate, RewireUpdate } from "@atrilabs/forest"; +import { handleResources } from "./handleResources"; type ComponentPayload = { id: string; @@ -335,6 +336,10 @@ if (typeof window !== "undefined") { id: ev.data.id, }); } + if (ev.data?.type === "IMPORT_RESOURCES" && ev.data?.resources) { + const resources = ev.data.resources as ImportedResource[]; + handleResources(resources); + } }); window.document.addEventListener( "mouseenter", diff --git a/packages/atri-app-core/src/api/handleResources.ts b/packages/atri-app-core/src/api/handleResources.ts new file mode 100644 index 000000000..895cef15f --- /dev/null +++ b/packages/atri-app-core/src/api/handleResources.ts @@ -0,0 +1,11 @@ +import { ImportedResource } from ".."; + +export function handleResources(resources: ImportedResource[]) { + resources.forEach((resource) => { + if (resource.method === "css") { + const styleTag = document.createElement("style"); + styleTag.innerHTML = resource.str; + document.head.appendChild(styleTag); + } + }); +} diff --git a/packages/atri-app-core/src/types.ts b/packages/atri-app-core/src/types.ts index ba232489a..21ae237ca 100644 --- a/packages/atri-app-core/src/types.ts +++ b/packages/atri-app-core/src/types.ts @@ -60,3 +60,15 @@ export type ComponentCoordsWM = { }; export type Location = { pageX: number; pageY: number }; + +export type ImportedResource = { + str: string; + method: "link" | "css"; + imports: { + fonts?: { + fontFamily: string; + fontWeight: string | number; + fontStyle: string; + }[]; + }; +}; diff --git a/packages/core/src/types.tsx b/packages/core/src/types.tsx index 964662676..95f695d79 100644 --- a/packages/core/src/types.tsx +++ b/packages/core/src/types.tsx @@ -177,17 +177,8 @@ export type EventSubscriber = ( // array of filenames without extension export type TemplateDetail = { relativeDir: string; templateName: string }; -export type ImportedResource = { - str: string; - method: "link" | "css"; - imports: { - fonts?: { - fontFamily: string; - fontWeight: string | number; - fontStyle: string; - }[]; - }; -}; +export type { ImportedResource } from "@atrilabs/atri-app-core"; +import type { ImportedResource } from "@atrilabs/atri-app-core"; export type BrowserClient = { getSocket(): Socket; diff --git a/packages/pwa-builder-manager/src/handleResourceTransfer.ts b/packages/pwa-builder-manager/src/handleResourceTransfer.ts new file mode 100644 index 000000000..5936700ff --- /dev/null +++ b/packages/pwa-builder-manager/src/handleResourceTransfer.ts @@ -0,0 +1,20 @@ +import { editorAppMachineInterpreter, subscribeEditorMachine } from "./init"; +import { api } from "./api"; + +subscribeEditorMachine("after_app_load", () => { + api.getResources((resources) => { + editorAppMachineInterpreter.machine.context.canvasWindow?.postMessage( + { type: "IMPORT_RESOURCES", resources }, + // @ts-ignore + "*" + ); + }); +}); + +api.subscribeResourceUpdates((resource) => { + editorAppMachineInterpreter.machine.context.canvasWindow?.postMessage( + { type: "IMPORT_RESOURCES", resources: [resource] }, + // @ts-ignore + "*" + ); +}); diff --git a/packages/pwa-builder-manager/src/index.ts b/packages/pwa-builder-manager/src/index.ts index 9bc71e0ee..ddf1c531c 100644 --- a/packages/pwa-builder-manager/src/index.ts +++ b/packages/pwa-builder-manager/src/index.ts @@ -7,3 +7,4 @@ import "./handleBrowserForestManagerUpdates"; import "./attachAllKeyboardShortcuts"; import "./handleBreakpointChange"; export * from "./aliasApi"; +import "./handleResourceTransfer"; From b381fbbc6755a5675526ac56def8849c363c08eb Mon Sep 17 00:00:00 2001 From: cruxcode Date: Tue, 28 Feb 2023 16:07:34 -0800 Subject: [PATCH 28/28] bugfix: remove duplicate import --- .../src/editor-components/CanvasOverlay/CanvasOverlay.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/atri-app-core/src/editor-components/CanvasOverlay/CanvasOverlay.tsx b/packages/atri-app-core/src/editor-components/CanvasOverlay/CanvasOverlay.tsx index 778e91cff..d08345366 100644 --- a/packages/atri-app-core/src/editor-components/CanvasOverlay/CanvasOverlay.tsx +++ b/packages/atri-app-core/src/editor-components/CanvasOverlay/CanvasOverlay.tsx @@ -4,8 +4,6 @@ import { useHoverHints, useDraggedOverlay, } from "./hooks"; -import { useHoverHints } from "./hooks/useHoverHints"; - export function CanvasOverlay() { const { dragFC, dragOverlayStyle } = useDragDrop();