From 2ab2e6cb050bd629c2f93593d2ad42e0115b709c Mon Sep 17 00:00:00 2001
From: "UZU, J" <woojoo@softmarshmallow.com>
Date: Tue, 19 Apr 2022 04:24:28 +0900
Subject: [PATCH 01/30] use user defined canvas color

---
 .../canvas-event-target.tsx                   |  1 -
 .../editor-canvas/canvas/canvas.tsx           | 19 +++++++++++++++++++
 editor/core/states/editor-state.ts            | 10 ++++++++--
 editor/scaffolds/canvas/canvas.tsx            | 12 +++++++++---
 editor/scaffolds/editor/warmup.ts             |  6 ++++--
 externals/design-sdk                          |  2 +-
 6 files changed, 41 insertions(+), 9 deletions(-)

diff --git a/editor-packages/editor-canvas/canvas-event-target/canvas-event-target.tsx b/editor-packages/editor-canvas/canvas-event-target/canvas-event-target.tsx
index 97c21e56..7aa7ce70 100644
--- a/editor-packages/editor-canvas/canvas-event-target/canvas-event-target.tsx
+++ b/editor-packages/editor-canvas/canvas-event-target/canvas-event-target.tsx
@@ -201,7 +201,6 @@ export function CanvasEventTarget({
       style={{
         position: "absolute",
         inset: 0,
-        background: "transparent",
         overflow: "hidden",
         touchAction: "none",
         cursor: isSpacebarPressed ? "grab" : "default",
diff --git a/editor-packages/editor-canvas/canvas/canvas.tsx b/editor-packages/editor-canvas/canvas/canvas.tsx
index 671d6e14..a85f9e68 100644
--- a/editor-packages/editor-canvas/canvas/canvas.tsx
+++ b/editor-packages/editor-canvas/canvas/canvas.tsx
@@ -30,6 +30,7 @@ const MIN_ZOOM = 0.02;
 interface CanvasState {
   pageid: string;
   filekey: string;
+  backgroundColor?: React.CSSProperties["backgroundColor"];
   nodes: ReflectSceneNode[];
   highlightedLayer?: string;
   selectedNodes: string[];
@@ -91,6 +92,7 @@ export function Canvas({
   selectedNodes,
   readonly = true,
   config = default_canvas_preferences,
+  backgroundColor,
   ...props
 }: {
   viewbound: Box;
@@ -389,6 +391,7 @@ export function Canvas({
           renderFrameTitle={props.renderFrameTitle}
         />
       </CanvasEventTarget>
+      <CanvasBackground backgroundColor={backgroundColor} />
       <CanvasTransformRoot scale={zoom} xy={nonscaled_offset}>
         <DisableBackdropFilter>{items}</DisableBackdropFilter>
       </CanvasTransformRoot>
@@ -440,6 +443,22 @@ function DisableBackdropFilter({ children }: { children: React.ReactNode }) {
   );
 }
 
+function CanvasBackground({ backgroundColor }: { backgroundColor?: string }) {
+  return (
+    <div
+      style={{
+        zIndex: -2,
+        position: "fixed",
+        top: 0,
+        left: 0,
+        width: "100%",
+        height: "100%",
+        backgroundColor,
+      }}
+    />
+  );
+}
+
 function auto_initial_transform(
   viewbound: Box,
   nodes: ReflectSceneNode[]
diff --git a/editor/core/states/editor-state.ts b/editor/core/states/editor-state.ts
index 8bbe99a8..415e3477 100644
--- a/editor/core/states/editor-state.ts
+++ b/editor/core/states/editor-state.ts
@@ -1,6 +1,6 @@
 import type { ReflectSceneNode } from "@design-sdk/figma-node";
 import type { FrameworkConfig } from "@designto/config";
-import type { WidgetKey } from "@reflect-ui/core";
+import type { RGBA, WidgetKey } from "@reflect-ui/core";
 import type { ComponentNode } from "@design-sdk/figma-types";
 import type { DesignInput } from "@designto/config/input";
 
@@ -50,7 +50,13 @@ export interface FigmaReflectRepository {
   key: string;
 
   // TODO:
-  pages: { id: string; name: string; children: ReflectSceneNode[] }[];
+  pages: {
+    id: string;
+    name: string;
+    children: ReflectSceneNode[];
+    backgroundColor: RGBA;
+    flowStartingPoints: any[];
+  }[];
   components: { [key: string]: ComponentNode };
   // styles: { [key: string]: {} };
   input: DesignInput;
diff --git a/editor/scaffolds/canvas/canvas.tsx b/editor/scaffolds/canvas/canvas.tsx
index 2c1739be..07da0430 100644
--- a/editor/scaffolds/canvas/canvas.tsx
+++ b/editor/scaffolds/canvas/canvas.tsx
@@ -31,9 +31,8 @@ export function VisualContentArea() {
     canvasMode_previous,
   } = state;
 
-  const thisPageNodes = selectedPage
-    ? design.pages.find((p) => p.id == selectedPage).children.filter(Boolean)
-    : [];
+  const thisPage = design?.pages?.find((p) => p.id == selectedPage);
+  const thisPageNodes = selectedPage ? thisPage.children.filter(Boolean) : [];
 
   const isEmptyPage = thisPageNodes?.length === 0;
 
@@ -73,6 +72,12 @@ export function VisualContentArea() {
     [dispatch]
   );
 
+  const _bg =
+    thisPage?.backgroundColor &&
+    `rgba(${thisPage.backgroundColor.r * 255}, ${
+      thisPage.backgroundColor.g * 255
+    }, ${thisPage.backgroundColor.b * 255}, ${thisPage.backgroundColor.a})`;
+
   return (
     <CanvasContainer ref={canvasSizingRef} id="canvas">
       {/* <EditorAppbarFragments.Canvas /> */}
@@ -113,6 +118,7 @@ export function VisualContentArea() {
               ]}
               filekey={state.design.key}
               pageid={selectedPage}
+              backgroundColor={_bg}
               selectedNodes={selectedNodes}
               highlightedLayer={highlightedLayer}
               onSelectNode={(...nodes) => {
diff --git a/editor/scaffolds/editor/warmup.ts b/editor/scaffolds/editor/warmup.ts
index 382f9916..7b9f6ecc 100644
--- a/editor/scaffolds/editor/warmup.ts
+++ b/editor/scaffolds/editor/warmup.ts
@@ -8,7 +8,7 @@ import { createInitialWorkspaceState } from "core/states";
 import { workspaceReducer } from "core/reducers";
 import { PendingState } from "core/utility-types";
 import { WorkspaceAction } from "core/actions";
-import { FileResponse } from "@design-sdk/figma-remote-types";
+import type { Canvas, FileResponse } from "@design-sdk/figma-remote-types";
 import { convert } from "@design-sdk/figma-node-conversion";
 import { mapper } from "@design-sdk/figma-remote";
 import { visit } from "tree-visit";
@@ -42,13 +42,15 @@ export function initialReducer(
 }
 
 export function pagesFrom(file: FileResponse): FigmaReflectRepository["pages"] {
-  return file.document.children.map((page) => ({
+  return file.document.children.map((page: Canvas) => ({
     id: page.id,
     name: page.name,
     children: page["children"]?.map((child) => {
       const _mapped = mapper.mapFigmaRemoteToFigma(child);
       return convert.intoReflectNode(_mapped);
     }),
+    flowStartingPoints: page.flowStartingPoints,
+    backgroundColor: page.backgroundColor,
     type: "design",
   }));
 }
diff --git a/externals/design-sdk b/externals/design-sdk
index 50c261ac..fb847ef7 160000
--- a/externals/design-sdk
+++ b/externals/design-sdk
@@ -1 +1 @@
-Subproject commit 50c261ac07ecce5a0550c0fd3cd81cf2c2c86fcc
+Subproject commit fb847ef7cbf1671ec0e3ceb011bf79e2c3cec4d8

From 599698c673362c14791e2b12980a8d56ac9e0f6c Mon Sep 17 00:00:00 2001
From: "UZU, J" <woojoo@softmarshmallow.com>
Date: Tue, 19 Apr 2022 05:29:51 +0900
Subject: [PATCH 02/30] add transform outline

---
 .../editor-canvas/hud/hud-surface.tsx         | 20 ++++-
 .../editor-canvas/overlay/handle.tsx          | 67 +++++++++++++++
 .../overlay/hover-outline-hightlight.tsx      |  8 +-
 .../editor-canvas/overlay/index.ts            |  3 +-
 .../editor-canvas/overlay/outline-side.tsx    | 22 +++--
 ...ght.tsx => select-hightlight-readonly.tsx} | 72 +++++-----------
 .../overlay/select-hightlight.tsx             | 84 +++++++++++++++++++
 editor/scaffolds/canvas/canvas.tsx            |  1 +
 8 files changed, 213 insertions(+), 64 deletions(-)
 create mode 100644 editor-packages/editor-canvas/overlay/handle.tsx
 rename editor-packages/editor-canvas/overlay/{readonly-select-hightlight.tsx => select-hightlight-readonly.tsx} (75%)
 create mode 100644 editor-packages/editor-canvas/overlay/select-hightlight.tsx

diff --git a/editor-packages/editor-canvas/hud/hud-surface.tsx b/editor-packages/editor-canvas/hud/hud-surface.tsx
index 193414c9..4ff4f899 100644
--- a/editor-packages/editor-canvas/hud/hud-surface.tsx
+++ b/editor-packages/editor-canvas/hud/hud-surface.tsx
@@ -1,5 +1,9 @@
 import React from "react";
-import { HoverOutlineHighlight, ReadonlySelectHightlight } from "../overlay";
+import {
+  HoverOutlineHighlight,
+  ReadonlySelectHightlight,
+  SelectHightlight,
+} from "../overlay";
 import { FrameTitle, FrameTitleProps } from "../frame-title";
 import type { XY, XYWH } from "../types";
 import { Marquee } from "../marquee";
@@ -129,7 +133,19 @@ export function HudSurface({
                   />
                 );
               } else {
-                // TODO: support non readonly canvas
+                return (
+                  <SelectHightlight
+                    onResize={(anchor, e) => {
+                      // console.log("resize", anchor, e);
+                    }}
+                    key={s.id}
+                    type="xywhr"
+                    xywh={xywh}
+                    rotation={s.rotation}
+                    zoom={zoom}
+                    width={1}
+                  />
+                );
               }
             })}
         </>
diff --git a/editor-packages/editor-canvas/overlay/handle.tsx b/editor-packages/editor-canvas/overlay/handle.tsx
new file mode 100644
index 00000000..22617cf4
--- /dev/null
+++ b/editor-packages/editor-canvas/overlay/handle.tsx
@@ -0,0 +1,67 @@
+import React from "react";
+export function Handle({
+  color,
+  anchor,
+  box,
+  outlineWidth = 1,
+  outlineColor,
+  size = 4,
+  borderRadius = 0,
+  cursor,
+  readonly,
+}: {
+  color: string;
+  /**
+   * the width of the outline
+   */
+  outlineWidth: number;
+  outlineColor: string;
+  size: number;
+  anchor: "nw" | "ne" | "sw" | "se";
+  box: [number, number, number, number];
+  borderRadius?: React.CSSProperties["borderRadius"];
+  cursor?: React.CSSProperties["cursor"];
+  readonly?: boolean;
+}) {
+  let dx = 0;
+  let dy = 0;
+  switch (anchor) {
+    case "nw":
+      dx = box[0];
+      dy = box[1];
+      break;
+    case "ne":
+      dx = box[2];
+      dy = box[1];
+      break;
+    case "sw":
+      dx = box[0];
+      dy = box[3];
+      break;
+    case "se":
+      dx = box[2];
+      dy = box[3];
+      break;
+  }
+
+  // translate x, y
+  const [tx, ty] = [dx - size / 2 - outlineWidth, dy - size / 2 - outlineWidth];
+
+  return (
+    <div
+      style={{
+        position: "absolute",
+        borderRadius: borderRadius,
+        width: size,
+        height: size,
+        border: `${outlineColor} solid ${outlineWidth}px`,
+        willChange: "transform",
+        transform: `translate3d(${tx}px, ${ty}px, 0)`,
+        backgroundColor: color,
+        cursor: cursor,
+        zIndex: 1,
+        pointerEvents: readonly ? "none" : "all",
+      }}
+    />
+  );
+}
diff --git a/editor-packages/editor-canvas/overlay/hover-outline-hightlight.tsx b/editor-packages/editor-canvas/overlay/hover-outline-hightlight.tsx
index b5b9b6db..0a878bcf 100644
--- a/editor-packages/editor-canvas/overlay/hover-outline-hightlight.tsx
+++ b/editor-packages/editor-canvas/overlay/hover-outline-hightlight.tsx
@@ -19,10 +19,10 @@ export function HoverOutlineHighlight({ width = 1, ...props }: OutlineProps) {
 
   return (
     <OverlayContainer xywh={bbox} rotation={rotation}>
-      <OulineSide orientation="l" {...vprops} />
-      <OulineSide orientation="t" {...vprops} />
-      <OulineSide orientation="b" {...vprops} />
-      <OulineSide orientation="r" {...vprops} />
+      <OulineSide orientation="w" {...vprops} />
+      <OulineSide orientation="n" {...vprops} />
+      <OulineSide orientation="s" {...vprops} />
+      <OulineSide orientation="e" {...vprops} />
     </OverlayContainer>
   );
 }
diff --git a/editor-packages/editor-canvas/overlay/index.ts b/editor-packages/editor-canvas/overlay/index.ts
index 576ab0ac..738ec8e9 100644
--- a/editor-packages/editor-canvas/overlay/index.ts
+++ b/editor-packages/editor-canvas/overlay/index.ts
@@ -1,2 +1,3 @@
 export * from "./hover-outline-hightlight";
-export * from "./readonly-select-hightlight";
+export * from "./select-hightlight";
+export * from "./select-hightlight-readonly";
diff --git a/editor-packages/editor-canvas/overlay/outline-side.tsx b/editor-packages/editor-canvas/overlay/outline-side.tsx
index e93476b3..2bfc4054 100644
--- a/editor-packages/editor-canvas/overlay/outline-side.tsx
+++ b/editor-packages/editor-canvas/overlay/outline-side.tsx
@@ -1,3 +1,4 @@
+import React from "react";
 import { color_layer_highlight } from "../theme";
 
 export function OulineSide({
@@ -7,46 +8,50 @@ export function OulineSide({
   width = 1,
   box,
   color = color_layer_highlight,
+  readonly = true,
+  cursor,
 }: {
   wh: [number, number];
   box: [number, number, number, number];
   zoom: number;
-  orientation: "l" | "t" | "r" | "b";
+  orientation: "w" | "n" | "e" | "s";
   width?: number;
   color?: string;
+  readonly?: boolean;
+  cursor?: React.CSSProperties["cursor"];
 }) {
   const d = 100;
   const [w, h] = wh;
 
   // is vertical line
-  const isvert = orientation === "l" || orientation === "r";
+  const isvert = orientation === "w" || orientation === "e";
   const l_scalex = isvert ? width / d : (w / d) * zoom;
   const l_scaley = isvert ? (h / d) * zoom : width / d;
 
   let trans = { x: 0, y: 0 };
   switch (orientation) {
-    case "l": {
+    case "w": {
       trans = {
         x: box[0] - d / 2,
         y: box[1] + (d * l_scaley - d) / 2,
       };
       break;
     }
-    case "r": {
+    case "e": {
       trans = {
         x: box[2] - d / 2,
         y: box[1] + (d * l_scaley - d) / 2,
       };
       break;
     }
-    case "t": {
+    case "n": {
       trans = {
         x: box[0] + (d * l_scalex - d) / 2,
         y: box[1] - d / 2,
       };
       break;
     }
-    case "b": {
+    case "s": {
       trans = {
         x: box[0] + (d * l_scalex - d) / 2,
         y: box[3] - d / 2,
@@ -62,10 +67,11 @@ export function OulineSide({
         width: d,
         height: d,
         opacity: 1,
-        pointerEvents: "none",
+        pointerEvents: readonly ? "none" : "all",
+        cursor: cursor,
         willChange: "transform",
         transformOrigin: "0px, 0px",
-        transform: `translateX(${trans.x}px) translateY(${trans.y}px) translateZ(0px) scaleX(${l_scalex}) scaleY(${l_scaley})`,
+        transform: `translate3d(${trans.x}px, ${trans.y}px, 0) scaleX(${l_scalex}) scaleY(${l_scaley})`,
         backgroundColor: color,
       }}
     />
diff --git a/editor-packages/editor-canvas/overlay/readonly-select-hightlight.tsx b/editor-packages/editor-canvas/overlay/select-hightlight-readonly.tsx
similarity index 75%
rename from editor-packages/editor-canvas/overlay/readonly-select-hightlight.tsx
rename to editor-packages/editor-canvas/overlay/select-hightlight-readonly.tsx
index 27ab07db..d2b26686 100644
--- a/editor-packages/editor-canvas/overlay/readonly-select-hightlight.tsx
+++ b/editor-packages/editor-canvas/overlay/select-hightlight-readonly.tsx
@@ -4,6 +4,7 @@ import { color_layer_readonly_highlight } from "../theme";
 import { get_boinding_box } from "./math";
 import { OulineSide } from "./outline-side";
 import { OverlayContainer } from "./overlay-container";
+import { Handle } from "./handle";
 
 export function ReadonlySelectHightlight({
   width = 1,
@@ -64,34 +65,34 @@ export function ReadonlySelectHightlight({
       <>
         <SideCenterEmp
           box={bbox}
-          side="l"
+          side="w"
           size={dot_size}
           color={color_layer_readonly_highlight}
         />
         <SideCenterEmp
           box={bbox}
-          side="r"
+          side="e"
           size={dot_size}
           color={color_layer_readonly_highlight}
         />
         <SideCenterEmp
           box={bbox}
-          side="t"
+          side="n"
           size={dot_size}
           color={color_layer_readonly_highlight}
         />
         <SideCenterEmp
           box={bbox}
-          side="b"
+          side="s"
           size={dot_size}
           color={color_layer_readonly_highlight}
         />
       </>
       <>
-        <OulineSide orientation="l" {...sideprops} />
-        <OulineSide orientation="t" {...sideprops} />
-        <OulineSide orientation="b" {...sideprops} />
-        <OulineSide orientation="r" {...sideprops} />
+        <OulineSide orientation="w" {...sideprops} />
+        <OulineSide orientation="n" {...sideprops} />
+        <OulineSide orientation="s" {...sideprops} />
+        <OulineSide orientation="e" {...sideprops} />
       </>
     </OverlayContainer>
   );
@@ -103,7 +104,7 @@ function SideCenterEmp({
   box,
   size = 3,
 }: {
-  side: "l" | "r" | "t" | "b";
+  side: "w" | "e" | "n" | "s";
   color: string;
   box: [number, number, number, number];
   size?: number;
@@ -111,19 +112,19 @@ function SideCenterEmp({
   let dx = 0;
   let dy = 0;
   switch (side) {
-    case "l":
+    case "w":
       dx = box[0];
       dy = box[1] + (box[3] - box[1]) / 2;
       break;
-    case "r":
+    case "e":
       dx = box[2];
       dy = box[1] + (box[3] - box[1]) / 2;
       break;
-    case "t":
+    case "n":
       dx = box[0] + (box[2] - box[0]) / 2;
       dy = box[1];
       break;
-    case "b":
+    case "s":
       dx = box[0] + (box[2] - box[0]) / 2;
       dy = box[3];
       break;
@@ -166,43 +167,16 @@ function ReadonlyHandle({
   anchor: "nw" | "ne" | "sw" | "se";
   box: [number, number, number, number];
 }) {
-  let dx = 0;
-  let dy = 0;
-  switch (anchor) {
-    case "nw":
-      dx = box[0];
-      dy = box[1];
-      break;
-    case "ne":
-      dx = box[2];
-      dy = box[1];
-      break;
-    case "sw":
-      dx = box[0];
-      dy = box[3];
-      break;
-    case "se":
-      dx = box[2];
-      dy = box[3];
-      break;
-  }
-
-  // translate x, y
-  const [tx, ty] = [dx - size / 2 - outlineWidth, dy - size / 2 - outlineWidth];
-
   return (
-    <div
-      style={{
-        position: "absolute",
-        borderRadius: "50%",
-        width: size,
-        height: size,
-        border: `${outlineColor} solid ${outlineWidth}px`,
-        willChange: "transform",
-        transform: `translateX(${tx}px) translateY(${ty}px)`,
-        backgroundColor: color,
-        zIndex: 1,
-      }}
+    <Handle
+      borderRadius={"50%"}
+      readonly
+      color={color}
+      anchor={anchor}
+      box={box}
+      outlineWidth={outlineWidth}
+      outlineColor={outlineColor}
+      size={size}
     />
   );
 }
diff --git a/editor-packages/editor-canvas/overlay/select-hightlight.tsx b/editor-packages/editor-canvas/overlay/select-hightlight.tsx
new file mode 100644
index 00000000..1362ca6e
--- /dev/null
+++ b/editor-packages/editor-canvas/overlay/select-hightlight.tsx
@@ -0,0 +1,84 @@
+import React, { useRef } from "react";
+import type { OutlineProps } from "./types";
+import { color_layer_highlight } from "../theme";
+import { get_boinding_box } from "./math";
+import { OulineSide } from "./outline-side";
+import { OverlayContainer } from "./overlay-container";
+import { Handle } from "./handle";
+import { useDrag } from "@use-gesture/react";
+import type { OnDragHandler } from "../canvas-event-target";
+
+export function SelectHightlight({
+  width = 1,
+  onResize,
+  ...props
+}: OutlineProps & {
+  onResize?: (anchor: "nw" | "ne" | "sw" | "se", e) => void;
+}) {
+  const { xywh, zoom, rotation } = props;
+  const bbox = get_boinding_box({ xywh, scale: zoom });
+  const wh: [number, number] = [xywh[2], xywh[3]];
+
+  const sideprops = {
+    wh: wh,
+    zoom: props.zoom,
+    width: width,
+    readonly: false,
+    box: bbox,
+    color: color_layer_highlight,
+  };
+
+  const onresizecb = (anchor: "nw" | "ne" | "sw" | "se") => (e) => {
+    if (onResize) {
+      onResize(anchor, e);
+    }
+  };
+
+  return (
+    <OverlayContainer xywh={bbox} rotation={rotation}>
+      <>
+        <ResizeHandle box={bbox} anchor="ne" onDrag={onresizecb("ne")} />
+        <ResizeHandle box={bbox} anchor="nw" onDrag={onresizecb("nw")} />
+        <ResizeHandle box={bbox} anchor="se" onDrag={onresizecb("se")} />
+        <ResizeHandle box={bbox} anchor="sw" onDrag={onresizecb("sw")} />
+      </>
+      <>
+        <OulineSide orientation="w" {...sideprops} cursor="w-resize" />
+        <OulineSide orientation="n" {...sideprops} cursor="n-resize" />
+        <OulineSide orientation="s" {...sideprops} cursor="s-resize" />
+        <OulineSide orientation="e" {...sideprops} cursor="e-resize" />
+      </>
+    </OverlayContainer>
+  );
+}
+
+function ResizeHandle({
+  anchor,
+  box,
+  onDrag,
+}: {
+  anchor: "nw" | "ne" | "sw" | "se";
+  box: [number, number, number, number];
+  onDrag: OnDragHandler;
+}) {
+  const ref = useRef();
+  useDrag(onDrag, {
+    target: ref,
+  });
+
+  return (
+    <div ref={ref}>
+      <Handle
+        cursor={`${anchor}-resize`}
+        readonly={false}
+        color={"white"}
+        anchor={anchor}
+        box={box}
+        borderRadius={1}
+        outlineWidth={1}
+        outlineColor={color_layer_highlight}
+        size={6}
+      />
+    </div>
+  );
+}
diff --git a/editor/scaffolds/canvas/canvas.tsx b/editor/scaffolds/canvas/canvas.tsx
index 07da0430..9ce586db 100644
--- a/editor/scaffolds/canvas/canvas.tsx
+++ b/editor/scaffolds/canvas/canvas.tsx
@@ -139,6 +139,7 @@ export function VisualContentArea() {
                   <D2CVanillaPreview key={p.node.id} target={p.node} {...p} />
                 );
               }}
+              readonly={false}
               config={{
                 can_highlight_selected_layer: true,
                 marquee: {

From ff6dbfcde736f4dd2f20e0411ccdd7c73785f169 Mon Sep 17 00:00:00 2001
From: "UZU, J" <woojoo@softmarshmallow.com>
Date: Tue, 19 Apr 2022 07:00:09 +0900
Subject: [PATCH 03/30] update transform highlight

---
 .../editor-canvas/hud/hud-surface.tsx         |   4 -
 .../editor-canvas/overlay/handle.tsx          |  18 +--
 .../overlay/select-hightlight.tsx             | 118 ++++++++++++------
 3 files changed, 90 insertions(+), 50 deletions(-)

diff --git a/editor-packages/editor-canvas/hud/hud-surface.tsx b/editor-packages/editor-canvas/hud/hud-surface.tsx
index 4ff4f899..67c30d2e 100644
--- a/editor-packages/editor-canvas/hud/hud-surface.tsx
+++ b/editor-packages/editor-canvas/hud/hud-surface.tsx
@@ -135,15 +135,11 @@ export function HudSurface({
               } else {
                 return (
                   <SelectHightlight
-                    onResize={(anchor, e) => {
-                      // console.log("resize", anchor, e);
-                    }}
                     key={s.id}
                     type="xywhr"
                     xywh={xywh}
                     rotation={s.rotation}
                     zoom={zoom}
-                    width={1}
                   />
                 );
               }
diff --git a/editor-packages/editor-canvas/overlay/handle.tsx b/editor-packages/editor-canvas/overlay/handle.tsx
index 22617cf4..b5c93c95 100644
--- a/editor-packages/editor-canvas/overlay/handle.tsx
+++ b/editor-packages/editor-canvas/overlay/handle.tsx
@@ -1,10 +1,10 @@
-import React from "react";
-export function Handle({
+import React, { forwardRef } from "react";
+export const Handle = forwardRef(function ({
   color,
   anchor,
   box,
   outlineWidth = 1,
-  outlineColor,
+  outlineColor = "transparent",
   size = 4,
   borderRadius = 0,
   cursor,
@@ -14,8 +14,8 @@ export function Handle({
   /**
    * the width of the outline
    */
-  outlineWidth: number;
-  outlineColor: string;
+  outlineWidth?: number;
+  outlineColor?: string;
   size: number;
   anchor: "nw" | "ne" | "sw" | "se";
   box: [number, number, number, number];
@@ -54,14 +54,14 @@ export function Handle({
         borderRadius: borderRadius,
         width: size,
         height: size,
-        border: `${outlineColor} solid ${outlineWidth}px`,
+        border: outlineWidth && `${outlineColor} solid ${outlineWidth}px`,
         willChange: "transform",
         transform: `translate3d(${tx}px, ${ty}px, 0)`,
         backgroundColor: color,
         cursor: cursor,
-        zIndex: 1,
-        pointerEvents: readonly ? "none" : "all",
+        zIndex: 2,
+        pointerEvents: readonly ? "none" : "auto",
       }}
     />
   );
-}
+});
diff --git a/editor-packages/editor-canvas/overlay/select-hightlight.tsx b/editor-packages/editor-canvas/overlay/select-hightlight.tsx
index 1362ca6e..e4752bd6 100644
--- a/editor-packages/editor-canvas/overlay/select-hightlight.tsx
+++ b/editor-packages/editor-canvas/overlay/select-hightlight.tsx
@@ -5,16 +5,12 @@ import { get_boinding_box } from "./math";
 import { OulineSide } from "./outline-side";
 import { OverlayContainer } from "./overlay-container";
 import { Handle } from "./handle";
-import { useDrag } from "@use-gesture/react";
+import { useGesture } from "@use-gesture/react";
 import type { OnDragHandler } from "../canvas-event-target";
 
 export function SelectHightlight({
-  width = 1,
-  onResize,
   ...props
-}: OutlineProps & {
-  onResize?: (anchor: "nw" | "ne" | "sw" | "se", e) => void;
-}) {
+}: Omit<OutlineProps, "width"> & {}) {
   const { xywh, zoom, rotation } = props;
   const bbox = get_boinding_box({ xywh, scale: zoom });
   const wh: [number, number] = [xywh[2], xywh[3]];
@@ -22,39 +18,72 @@ export function SelectHightlight({
   const sideprops = {
     wh: wh,
     zoom: props.zoom,
-    width: width,
+    width: 1,
     readonly: false,
     box: bbox,
     color: color_layer_highlight,
   };
 
-  const onresizecb = (anchor: "nw" | "ne" | "sw" | "se") => (e) => {
-    if (onResize) {
-      onResize(anchor, e);
-    }
-  };
-
   return (
     <OverlayContainer xywh={bbox} rotation={rotation}>
+      {/* TODO: add rotation knob */}
+      {/* <>
+        <RotateHandle box={bbox} anchor="ne" onDrag={onrotatecb("ne")} />
+        <RotateHandle box={bbox} anchor="nw" onDrag={onrotatecb("nw")} />
+        <RotateHandle box={bbox} anchor="se" onDrag={onrotatecb("se")} />
+        <RotateHandle box={bbox} anchor="sw" onDrag={onrotatecb("sw")} />
+      </> */}
       <>
-        <ResizeHandle box={bbox} anchor="ne" onDrag={onresizecb("ne")} />
-        <ResizeHandle box={bbox} anchor="nw" onDrag={onresizecb("nw")} />
-        <ResizeHandle box={bbox} anchor="se" onDrag={onresizecb("se")} />
-        <ResizeHandle box={bbox} anchor="sw" onDrag={onresizecb("sw")} />
+        <ResizeHandle box={bbox} anchor="ne" />
+        <ResizeHandle box={bbox} anchor="nw" />
+        <ResizeHandle box={bbox} anchor="se" />
+        <ResizeHandle box={bbox} anchor="sw" />
       </>
       <>
-        <OulineSide orientation="w" {...sideprops} cursor="w-resize" />
-        <OulineSide orientation="n" {...sideprops} cursor="n-resize" />
-        <OulineSide orientation="s" {...sideprops} cursor="s-resize" />
-        <OulineSide orientation="e" {...sideprops} cursor="e-resize" />
+        <OulineSide orientation="w" {...sideprops} cursor="ew-resize" />
+        <OulineSide orientation="n" {...sideprops} cursor="ns-resize" />
+        <OulineSide orientation="s" {...sideprops} cursor="ns-resize" />
+        <OulineSide orientation="e" {...sideprops} cursor="ew-resize" />
       </>
     </OverlayContainer>
   );
 }
 
+const resize_cursor_map = {
+  nw: "nwse-resize",
+  ne: "nesw-resize",
+  sw: "nesw-resize",
+  se: "nwse-resize",
+  w: "ew-resize",
+  n: "ns-resize",
+  s: "ns-resize",
+  e: "ew-resize",
+};
+
 function ResizeHandle({
   anchor,
   box,
+}: {
+  anchor: "nw" | "ne" | "sw" | "se";
+  box: [number, number, number, number];
+}) {
+  return (
+    <Handle
+      cursor={resize_cursor_map[anchor]}
+      readonly={false}
+      color={"white"}
+      anchor={anchor}
+      box={box}
+      outlineWidth={1}
+      outlineColor={color_layer_highlight}
+      size={8}
+    />
+  );
+}
+
+function RotateHandle({
+  anchor,
+  box,
   onDrag,
 }: {
   anchor: "nw" | "ne" | "sw" | "se";
@@ -62,23 +91,38 @@ function ResizeHandle({
   onDrag: OnDragHandler;
 }) {
   const ref = useRef();
-  useDrag(onDrag, {
-    target: ref,
-  });
+  useGesture(
+    {
+      onDragStart: (e) => {
+        e.event.stopPropagation();
+      },
+      onDragEnd: (e) => {
+        e.event.stopPropagation();
+      },
+      onDrag: (e) => {
+        onDrag(e);
+        e.event.stopPropagation();
+      },
+    },
+    {
+      target: ref,
+      eventOptions: {
+        capture: false,
+      },
+    }
+  );
 
   return (
-    <div ref={ref}>
-      <Handle
-        cursor={`${anchor}-resize`}
-        readonly={false}
-        color={"white"}
-        anchor={anchor}
-        box={box}
-        borderRadius={1}
-        outlineWidth={1}
-        outlineColor={color_layer_highlight}
-        size={6}
-      />
-    </div>
+    <Handle
+      ref={ref}
+      cursor={"crosshair"} // FIXME:
+      readonly={false}
+      // center={false}
+      color={"transparent"}
+      anchor={anchor}
+      box={box}
+      outlineWidth={1}
+      size={18}
+    />
   );
 }

From bd1233b1632ca5b2e7c970cf9e2a631cd31223aa Mon Sep 17 00:00:00 2001
From: "UZU, J" <woojoo@softmarshmallow.com>
Date: Tue, 19 Apr 2022 08:06:53 +0900
Subject: [PATCH 04/30] add grouped highlight

---
 .../editor-canvas/canvas/canvas.tsx           |  13 ++
 .../editor-canvas/hud/hud-surface.tsx         | 111 +++++++++++++-----
 .../editor-canvas/math/bounding-box.ts        |  90 ++++++++++++++
 .../editor-canvas/math/center-of.ts           |  24 +---
 editor-packages/editor-canvas/math/index.ts   |   1 +
 .../overlay/hover-outline-hightlight.tsx      |   4 +-
 .../editor-canvas/overlay/index.ts            |   1 +
 editor-packages/editor-canvas/overlay/math.ts |  18 ---
 .../select-highlight-in-selection-group.tsx   |  34 ++++++
 .../overlay/select-hightlight-readonly.tsx    |   4 +-
 .../overlay/select-hightlight.tsx             |   4 +-
 editor-packages/editor-canvas/types/index.ts  |  25 +++-
 editor/pages/canvas-server/index.tsx          |   5 +-
 editor/scaffolds/canvas/canvas.tsx            |   3 +
 14 files changed, 262 insertions(+), 75 deletions(-)
 create mode 100644 editor-packages/editor-canvas/math/bounding-box.ts
 delete mode 100644 editor-packages/editor-canvas/overlay/math.ts
 create mode 100644 editor-packages/editor-canvas/overlay/select-highlight-in-selection-group.tsx

diff --git a/editor-packages/editor-canvas/canvas/canvas.tsx b/editor-packages/editor-canvas/canvas/canvas.tsx
index a85f9e68..27796777 100644
--- a/editor-packages/editor-canvas/canvas/canvas.tsx
+++ b/editor-packages/editor-canvas/canvas/canvas.tsx
@@ -56,6 +56,7 @@ type CanvasCustomRenderers = HudCustomRenderers & {
 interface CanvsPreferences {
   can_highlight_selected_layer?: boolean;
   marquee: MarqueeOprions;
+  grouping: GroupingOptions;
 }
 
 interface MarqueeOprions {
@@ -67,11 +68,22 @@ interface MarqueeOprions {
   disabled?: boolean;
 }
 
+interface GroupingOptions {
+  /**
+   * disable grouping - multiple selections will not be grouped.
+   * @default false
+   **/
+  disabled?: boolean;
+}
+
 const default_canvas_preferences: CanvsPreferences = {
   can_highlight_selected_layer: false,
   marquee: {
     disabled: false,
   },
+  grouping: {
+    disabled: false,
+  },
 };
 
 interface HovringNode {
@@ -367,6 +379,7 @@ export function Canvas({
           hide={is_canvas_transforming}
           readonly={readonly}
           disableMarquee={config.marquee.disabled}
+          disableGrouping={config.grouping.disabled}
           marquee={marquee}
           labelDisplayNodes={nodes}
           selectedNodes={selected_nodes}
diff --git a/editor-packages/editor-canvas/hud/hud-surface.tsx b/editor-packages/editor-canvas/hud/hud-surface.tsx
index 67c30d2e..d95a8c91 100644
--- a/editor-packages/editor-canvas/hud/hud-surface.tsx
+++ b/editor-packages/editor-canvas/hud/hud-surface.tsx
@@ -2,11 +2,13 @@ import React from "react";
 import {
   HoverOutlineHighlight,
   ReadonlySelectHightlight,
+  InSelectionGroupSelectHighlight,
   SelectHightlight,
 } from "../overlay";
 import { FrameTitle, FrameTitleProps } from "../frame-title";
 import type { XY, XYWH } from "../types";
 import { Marquee } from "../marquee";
+import { boundingbox, box_to_xywh } from "../math";
 interface HudControls {
   onSelectNode: (node: string) => void;
   onHoverNode: (node: string) => void;
@@ -37,6 +39,7 @@ export function HudSurface({
   labelDisplayNodes,
   selectedNodes,
   readonly,
+  disableGrouping = false,
   onSelectNode,
   onHoverNode,
   marquee,
@@ -52,6 +55,7 @@ export function HudSurface({
   hide: boolean;
   marquee?: XYWH;
   disableMarquee?: boolean;
+  disableGrouping?: boolean;
   readonly: boolean;
 } & HudControls &
   HudCustomRenderers) {
@@ -113,43 +117,88 @@ export function HudSurface({
                 />
               );
             })}
-          {selectedNodes &&
-            selectedNodes.map((s) => {
-              const xywh: [number, number, number, number] = [
-                s.absoluteX,
-                s.absoluteY,
-                s.width,
-                s.height,
-              ];
-              if (readonly) {
-                return (
-                  <ReadonlySelectHightlight
-                    key={s.id}
-                    type="xywhr"
-                    xywh={xywh}
-                    rotation={s.rotation}
-                    zoom={zoom}
-                    width={1}
-                  />
-                );
-              } else {
-                return (
-                  <SelectHightlight
-                    key={s.id}
-                    type="xywhr"
-                    xywh={xywh}
-                    rotation={s.rotation}
-                    zoom={zoom}
-                  />
-                );
-              }
-            })}
+          {selectedNodes?.length &&
+            (disableGrouping ? (
+              selectedNodes.map((s) => {
+                const xywh: [number, number, number, number] = [
+                  s.absoluteX,
+                  s.absoluteY,
+                  s.width,
+                  s.height,
+                ];
+                if (readonly) {
+                  return (
+                    <ReadonlySelectHightlight
+                      key={s.id}
+                      type="xywhr"
+                      xywh={xywh}
+                      rotation={s.rotation}
+                      zoom={zoom}
+                      width={1}
+                    />
+                  );
+                } else {
+                  return (
+                    <SelectHightlight
+                      key={s.id}
+                      type="xywhr"
+                      xywh={xywh}
+                      rotation={s.rotation}
+                      zoom={zoom}
+                    />
+                  );
+                }
+              })
+            ) : (
+              <SelectionGroupHighlight selections={selectedNodes} zoom={zoom} />
+            ))}
         </>
       )}
     </div>
   );
 }
 
+function SelectionGroupHighlight({
+  selections,
+  zoom,
+}: {
+  selections: DisplayNodeMeta[];
+  zoom: number;
+}) {
+  const box = boundingbox(
+    selections.map((d) => {
+      return [d.absoluteX, d.absoluteY, d.width, d.height, d.rotation];
+    }),
+    2
+  );
+
+  const xywh = box_to_xywh(box);
+
+  return (
+    <>
+      <>
+        {selections.map((s) => {
+          return (
+            <InSelectionGroupSelectHighlight
+              key={s.id}
+              type={"xywhr"}
+              xywh={[s.absoluteX, s.absoluteY, s.width, s.height]}
+              zoom={zoom}
+            />
+          );
+        })}
+      </>
+      <SelectHightlight
+        key={"selections-highlight"}
+        type="xywhr"
+        xywh={xywh}
+        rotation={0}
+        zoom={zoom}
+      />
+    </>
+  );
+}
+
 const frame_title_default_renderer = (p: FrameTitleProps) => (
   <FrameTitle key={p.id} {...p} />
 );
diff --git a/editor-packages/editor-canvas/math/bounding-box.ts b/editor-packages/editor-canvas/math/bounding-box.ts
new file mode 100644
index 00000000..a6edc10d
--- /dev/null
+++ b/editor-packages/editor-canvas/math/bounding-box.ts
@@ -0,0 +1,90 @@
+import type { XYWH, X1Y1X2Y2, Box, XYWHR } from "../types";
+
+export function xywh_to_bounding_box({
+  xywh,
+  scale,
+}: {
+  xywh: XYWH;
+  scale: number;
+}): Box {
+  const [x, y, w, h] = xywh;
+
+  // return the bounding box in [number, number, number, number] form with givven x, y, w, h, rotation and scale.
+  const [x1, y1, x2, y2] = [
+    x * scale,
+    y * scale,
+    x * scale + w * scale,
+    y * scale + h * scale,
+  ];
+  return [x1, y1, x2, y2];
+}
+
+type BoundingBoxInput =
+  | (Box & { type?: 0 })
+  | (XYWH & { type?: 1 })
+  | (XYWHR & { type?: 2 });
+
+export function boundingbox(
+  rects: BoundingBoxInput[],
+  t?: BoundingBoxInput["type"]
+): Box {
+  let x1 = Infinity;
+  let y1 = Infinity;
+  let x2 = -Infinity;
+  let y2 = -Infinity;
+  for (const rect of rects) {
+    const [_x1, _y1, _x2, _y2] = to_box(rect, t);
+    x1 = Math.min(x1, _x1);
+    y1 = Math.min(y1, _y1);
+    x2 = Math.max(x2, _x2);
+    y2 = Math.max(y2, _y2);
+  }
+  return [x1, y1, x2, y2];
+}
+
+/**
+ // TODO: handle rotation. (no rotation for now)
+ * 
+ * @param rect 
+ * @returns 
+ */
+const to_box = (rect: BoundingBoxInput, t?: BoundingBoxInput["type"]): Box => {
+  let _x1,
+    _y1,
+    _x2,
+    _y2,
+    _r = 0;
+
+  switch (rect.type ?? t) {
+    case 0: {
+      [_x1, _y1, _x2, _y2] = rect;
+    }
+    case 1: {
+      const [x, y, w, h] = rect;
+      _x1 = x;
+      _y1 = y;
+      _x2 = x + w;
+      _y2 = y + h;
+    }
+    case 2: {
+      const [x, y, w, h, r] = rect;
+      _x1 = x;
+      _y1 = y;
+      _x2 = x + w;
+      _y2 = y + h;
+      _r = r;
+    }
+  }
+
+  return [_x1, _y1, _x2, _y2];
+};
+
+export function box_to_xywh(box: Box): XYWH {
+  const [x1, y1, x2, y2] = box;
+  return [x1, y1, x2 - x1, y2 - y1];
+}
+
+export function box_to_xywhr(box: Box): XYWHR {
+  const [x1, y1, x2, y2] = box;
+  return [x1, y1, x2 - x1, y2 - y1, 0];
+}
diff --git a/editor-packages/editor-canvas/math/center-of.ts b/editor-packages/editor-canvas/math/center-of.ts
index 8b23ee4c..9c9e46e3 100644
--- a/editor-packages/editor-canvas/math/center-of.ts
+++ b/editor-packages/editor-canvas/math/center-of.ts
@@ -1,4 +1,5 @@
-import type { Box, XY } from "../types";
+import type { Box, XY, XYWHR } from "../types";
+import { boundingbox } from "./bounding-box";
 
 type Rect = {
   x: number;
@@ -24,6 +25,9 @@ export function centerOf(
   translate: XY;
   scale: number;
 } {
+  const xywhrs = rects.map((r) => {
+    return [r.x, r.y, r.width, r.height, r.rotation] as XYWHR;
+  });
   if (!rects || rects.length === 0) {
     return {
       box: viewbound,
@@ -36,7 +40,7 @@ export function centerOf(
     };
   }
 
-  const [x1, y1, x2, y2] = bound(...rects);
+  const [x1, y1, x2, y2] = boundingbox(xywhrs, 2);
   // box containing the rects.
   const box: Box = [x1, y1, x2, y2];
   // center of the box, viewbound not considered.
@@ -63,22 +67,6 @@ export function centerOf(
   };
 }
 
-function bound(...rects: Rect[]): Box {
-  let x1 = Infinity;
-  let y1 = Infinity;
-  let x2 = -Infinity;
-  let y2 = -Infinity;
-  for (const rect of rects) {
-    const { x, y, width: w, height: h } = rect;
-    // TODO: handle rotation. (no rotation for now)
-    x1 = Math.min(x1, x);
-    y1 = Math.min(y1, y);
-    x2 = Math.max(x2, x + w);
-    y2 = Math.max(y2, y + h);
-  }
-  return [x1, y1, x2, y2];
-}
-
 function rotate(x: number, y: number, r: number): [number, number] {
   const cos = Math.cos(r);
   const sin = Math.sin(r);
diff --git a/editor-packages/editor-canvas/math/index.ts b/editor-packages/editor-canvas/math/index.ts
index 70d1cd75..5cf8cf7e 100644
--- a/editor-packages/editor-canvas/math/index.ts
+++ b/editor-packages/editor-canvas/math/index.ts
@@ -1,3 +1,4 @@
+export * from "./bounding-box";
 export * from "./target-of-area";
 export * from "./target-of-point";
 export * from "./center-of";
diff --git a/editor-packages/editor-canvas/overlay/hover-outline-hightlight.tsx b/editor-packages/editor-canvas/overlay/hover-outline-hightlight.tsx
index 0a878bcf..91fae0c9 100644
--- a/editor-packages/editor-canvas/overlay/hover-outline-hightlight.tsx
+++ b/editor-packages/editor-canvas/overlay/hover-outline-hightlight.tsx
@@ -1,13 +1,13 @@
 import React from "react";
 import { color_layer_highlight } from "../theme";
-import { get_boinding_box } from "./math";
+import { xywh_to_bounding_box } from "../math";
 import { OulineSide } from "./outline-side";
 import { OverlayContainer } from "./overlay-container";
 import type { OutlineProps } from "./types";
 
 export function HoverOutlineHighlight({ width = 1, ...props }: OutlineProps) {
   const { xywh, zoom, rotation } = props;
-  const bbox = get_boinding_box({ xywh, scale: zoom });
+  const bbox = xywh_to_bounding_box({ xywh, scale: zoom });
   const wh: [number, number] = [xywh[2], xywh[3]];
   const vprops = {
     wh: wh,
diff --git a/editor-packages/editor-canvas/overlay/index.ts b/editor-packages/editor-canvas/overlay/index.ts
index 738ec8e9..0edc8078 100644
--- a/editor-packages/editor-canvas/overlay/index.ts
+++ b/editor-packages/editor-canvas/overlay/index.ts
@@ -1,3 +1,4 @@
 export * from "./hover-outline-hightlight";
+export * from "./select-highlight-in-selection-group";
 export * from "./select-hightlight";
 export * from "./select-hightlight-readonly";
diff --git a/editor-packages/editor-canvas/overlay/math.ts b/editor-packages/editor-canvas/overlay/math.ts
deleted file mode 100644
index 9c391b42..00000000
--- a/editor-packages/editor-canvas/overlay/math.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-export function get_boinding_box({
-  xywh,
-  scale,
-}: {
-  xywh: [number, number, number, number];
-  scale: number;
-}): [number, number, number, number] {
-  const [x, y, w, h] = xywh;
-
-  // return the bounding box in [number, number, number, number] form with givven x, y, w, h, rotation and scale.
-  const [x1, y1, x2, y2] = [
-    x * scale,
-    y * scale,
-    x * scale + w * scale,
-    y * scale + h * scale,
-  ];
-  return [x1, y1, x2, y2];
-}
diff --git a/editor-packages/editor-canvas/overlay/select-highlight-in-selection-group.tsx b/editor-packages/editor-canvas/overlay/select-highlight-in-selection-group.tsx
new file mode 100644
index 00000000..4facd9d2
--- /dev/null
+++ b/editor-packages/editor-canvas/overlay/select-highlight-in-selection-group.tsx
@@ -0,0 +1,34 @@
+import React from "react";
+import type { OutlineProps } from "./types";
+import { color_layer_readonly_highlight } from "../theme";
+import { xywh_to_bounding_box } from "../math";
+import { OulineSide } from "./outline-side";
+import { OverlayContainer } from "./overlay-container";
+
+export function InSelectionGroupSelectHighlight({
+  width = 0.3,
+  ...props
+}: OutlineProps) {
+  const { xywh, zoom, rotation } = props;
+  const bbox = xywh_to_bounding_box({ xywh, scale: zoom });
+  const wh: [number, number] = [xywh[2], xywh[3]];
+
+  const sideprops = {
+    wh: wh,
+    zoom: props.zoom,
+    width: width,
+    box: bbox,
+    color: color_layer_readonly_highlight,
+  };
+
+  return (
+    <OverlayContainer xywh={bbox} rotation={rotation}>
+      <>
+        <OulineSide orientation="w" {...sideprops} />
+        <OulineSide orientation="n" {...sideprops} />
+        <OulineSide orientation="s" {...sideprops} />
+        <OulineSide orientation="e" {...sideprops} />
+      </>
+    </OverlayContainer>
+  );
+}
diff --git a/editor-packages/editor-canvas/overlay/select-hightlight-readonly.tsx b/editor-packages/editor-canvas/overlay/select-hightlight-readonly.tsx
index d2b26686..656eaf1a 100644
--- a/editor-packages/editor-canvas/overlay/select-hightlight-readonly.tsx
+++ b/editor-packages/editor-canvas/overlay/select-hightlight-readonly.tsx
@@ -1,7 +1,7 @@
 import React from "react";
 import type { OutlineProps } from "./types";
 import { color_layer_readonly_highlight } from "../theme";
-import { get_boinding_box } from "./math";
+import { xywh_to_bounding_box } from "../math";
 import { OulineSide } from "./outline-side";
 import { OverlayContainer } from "./overlay-container";
 import { Handle } from "./handle";
@@ -11,7 +11,7 @@ export function ReadonlySelectHightlight({
   ...props
 }: OutlineProps) {
   const { xywh, zoom, rotation } = props;
-  const bbox = get_boinding_box({ xywh, scale: zoom });
+  const bbox = xywh_to_bounding_box({ xywh, scale: zoom });
   const wh: [number, number] = [xywh[2], xywh[3]];
 
   const handle_outline_width = width;
diff --git a/editor-packages/editor-canvas/overlay/select-hightlight.tsx b/editor-packages/editor-canvas/overlay/select-hightlight.tsx
index e4752bd6..8bd4669b 100644
--- a/editor-packages/editor-canvas/overlay/select-hightlight.tsx
+++ b/editor-packages/editor-canvas/overlay/select-hightlight.tsx
@@ -1,7 +1,7 @@
 import React, { useRef } from "react";
 import type { OutlineProps } from "./types";
 import { color_layer_highlight } from "../theme";
-import { get_boinding_box } from "./math";
+import { xywh_to_bounding_box } from "../math";
 import { OulineSide } from "./outline-side";
 import { OverlayContainer } from "./overlay-container";
 import { Handle } from "./handle";
@@ -12,7 +12,7 @@ export function SelectHightlight({
   ...props
 }: Omit<OutlineProps, "width"> & {}) {
   const { xywh, zoom, rotation } = props;
-  const bbox = get_boinding_box({ xywh, scale: zoom });
+  const bbox = xywh_to_bounding_box({ xywh, scale: zoom });
   const wh: [number, number] = [xywh[2], xywh[3]];
 
   const sideprops = {
diff --git a/editor-packages/editor-canvas/types/index.ts b/editor-packages/editor-canvas/types/index.ts
index 907c5e37..edc030df 100644
--- a/editor-packages/editor-canvas/types/index.ts
+++ b/editor-packages/editor-canvas/types/index.ts
@@ -1,10 +1,33 @@
+/**
+ * represents a point with x and y
+ */
 export type XY = [number, number];
+
+/**
+ * represents a rectangle with x, y, width, height
+ */
 export type XYWH = [number, number, number, number];
+
+/**
+ * represents a rectangle with x, y, width, height, rotation
+ */
+export type XYWHR = [number, number, number, number, number];
+
+/**
+ * represents a rectangle with x1, y1, x2, y2
+ */
+export type X1Y1X2Y2 = [number, number, number, number];
+
 export type CanvasTransform = {
   scale: number;
   xy: XY;
 };
-export type Box = [number, number, number, number];
+
+/**
+ * a bounding box with x1, y1, x2, y2
+ */
+export type Box = X1Y1X2Y2;
+
 export interface Tree {
   id: string;
   /**
diff --git a/editor/pages/canvas-server/index.tsx b/editor/pages/canvas-server/index.tsx
index a7bd1f8b..25deb849 100644
--- a/editor/pages/canvas-server/index.tsx
+++ b/editor/pages/canvas-server/index.tsx
@@ -69,7 +69,10 @@ export default function CanvasServerPage() {
         config={{
           can_highlight_selected_layer: true,
           marquee: {
-            disabled: true,
+            disabled: false,
+          },
+          grouping: {
+            disabled: false,
           },
         }}
         renderFrameTitle={(p) => (
diff --git a/editor/scaffolds/canvas/canvas.tsx b/editor/scaffolds/canvas/canvas.tsx
index 9ce586db..1eaec972 100644
--- a/editor/scaffolds/canvas/canvas.tsx
+++ b/editor/scaffolds/canvas/canvas.tsx
@@ -145,6 +145,9 @@ export function VisualContentArea() {
                 marquee: {
                   disabled: false,
                 },
+                grouping: {
+                  disabled: false,
+                },
               }}
               renderFrameTitle={(p) => (
                 <FrameTitleRenderer

From c9341330f75d58e25d6aa19d85e59eec020fe485 Mon Sep 17 00:00:00 2001
From: "UZU, J" <woojoo@softmarshmallow.com>
Date: Tue, 19 Apr 2022 08:18:26 +0900
Subject: [PATCH 05/30] ignore hover on selected nodes

---
 editor-packages/editor-canvas/canvas/canvas.tsx   | 1 +
 editor-packages/editor-canvas/hud/hud-surface.tsx | 9 ++++++---
 2 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/editor-packages/editor-canvas/canvas/canvas.tsx b/editor-packages/editor-canvas/canvas/canvas.tsx
index 27796777..140fc932 100644
--- a/editor-packages/editor-canvas/canvas/canvas.tsx
+++ b/editor-packages/editor-canvas/canvas/canvas.tsx
@@ -211,6 +211,7 @@ export function Canvas({
       offset: nonscaled_offset,
       margin: LAYER_HOVER_HIT_MARGIN,
       reverse: true,
+      ignore: (n) => selectedNodes.includes(n.id),
     });
 
     if (!hovering) {
diff --git a/editor-packages/editor-canvas/hud/hud-surface.tsx b/editor-packages/editor-canvas/hud/hud-surface.tsx
index d95a8c91..6c1000f6 100644
--- a/editor-packages/editor-canvas/hud/hud-surface.tsx
+++ b/editor-packages/editor-canvas/hud/hud-surface.tsx
@@ -117,8 +117,8 @@ export function HudSurface({
                 />
               );
             })}
-          {selectedNodes?.length &&
-            (disableGrouping ? (
+          {selectedNodes?.length ? (
+            disableGrouping ? (
               selectedNodes.map((s) => {
                 const xywh: [number, number, number, number] = [
                   s.absoluteX,
@@ -151,7 +151,10 @@ export function HudSurface({
               })
             ) : (
               <SelectionGroupHighlight selections={selectedNodes} zoom={zoom} />
-            ))}
+            )
+          ) : (
+            <></>
+          )}
         </>
       )}
     </div>

From dd713de2adc9ecce163cd711d57acbd2480a4a7a Mon Sep 17 00:00:00 2001
From: "UZU, J" <woojoo@softmarshmallow.com>
Date: Tue, 19 Apr 2022 17:07:00 +0900
Subject: [PATCH 06/30] add move node

---
 .../editor-canvas/canvas/canvas.tsx           | 64 ++++++++++++++++++-
 .../editor-canvas/core/actions/index.ts       |  0
 .../editor-canvas/core/dispatch/index.ts      |  0
 .../editor-canvas/core/reducer/index.ts       |  0
 .../editor-canvas/core/states/index.ts        |  0
 .../editor-canvas/math/bounding-box.ts        |  8 ++-
 editor/core/actions/index.ts                  |  9 +++
 editor/core/reducers/editor-reducer.ts        | 28 +++++++-
 editor/scaffolds/canvas/canvas.tsx            |  8 +++
 9 files changed, 112 insertions(+), 5 deletions(-)
 create mode 100644 editor-packages/editor-canvas/core/actions/index.ts
 create mode 100644 editor-packages/editor-canvas/core/dispatch/index.ts
 create mode 100644 editor-packages/editor-canvas/core/reducer/index.ts
 create mode 100644 editor-packages/editor-canvas/core/states/index.ts

diff --git a/editor-packages/editor-canvas/canvas/canvas.tsx b/editor-packages/editor-canvas/canvas/canvas.tsx
index 140fc932..aedbe3ed 100644
--- a/editor-packages/editor-canvas/canvas/canvas.tsx
+++ b/editor-packages/editor-canvas/canvas/canvas.tsx
@@ -14,6 +14,8 @@ import {
   centerOf,
   edge_scrolling,
   target_of_area,
+  boundingbox,
+  is_point_inside_box,
 } from "../math";
 import { utils } from "@design-sdk/core";
 import { LazyFrame } from "@code-editor/canvas/lazy-frame";
@@ -95,6 +97,9 @@ export function Canvas({
   viewbound,
   renderItem,
   onSelectNode,
+  onMoveNodeStart,
+  onMoveNode,
+  onMoveNodeEnd,
   onClearSelection,
   filekey,
   pageid,
@@ -109,6 +114,9 @@ export function Canvas({
 }: {
   viewbound: Box;
   onSelectNode?: (...node: ReflectSceneNode[]) => void;
+  onMoveNodeStart?: (...node: string[]) => void;
+  onMoveNode?: (delta: XY, ...node: string[]) => void;
+  onMoveNodeEnd?: (delta: XY, ...node: string[]) => void;
   onClearSelection?: () => void;
 } & CanvasCustomRenderers &
   CanvasState & {
@@ -150,7 +158,8 @@ export function Canvas({
     ? [offset[0] / zoom, offset[1] / zoom]
     : [0, 0];
   const [isPanning, setIsPanning] = useState(false);
-  const [isDraggomg, setIsDragging] = useState(false);
+  const [isDraggimg, setIsDragging] = useState(false);
+  const [isMovingSelections, setIsMovingSelections] = useState(false);
   const [marquee, setMarquee] = useState<XYWH>(null);
 
   const cvtransform: CanvasTransform = {
@@ -200,7 +209,7 @@ export function Canvas({
   }, [marquee]);
 
   const onPointerMove: OnPointerMoveHandler = (state) => {
-    if (isPanning || isZooming || isDraggomg) {
+    if (isPanning || isZooming || isDraggimg) {
       // don't perform hover calculation while transforming.
       return;
     }
@@ -230,9 +239,16 @@ export function Canvas({
   };
 
   const onPointerDown: OnPointerDownHandler = (state) => {
+    const [x, y] = [state.event.clientX, state.event.clientY];
+
     if (isPanning || isZooming) {
       return;
     }
+
+    if (shouldStartMoveSelections([x, y])) {
+      return; // don't do anything. onDrag will handle this. only block the event.
+    }
+
     if (hoveringLayer) {
       switch (hoveringLayer.reason) {
         case "frame-title":
@@ -274,7 +290,6 @@ export function Canvas({
   };
 
   const onDragStart: OnDragHandler = (s) => {
-    onClearSelection();
     setIsDragging(true);
     setHoveringLayer(null);
 
@@ -282,9 +297,35 @@ export function Canvas({
     const [x, y] = s.initial;
     const [ox, oy] = offset;
     const [x1, y1] = [x - ox, y - oy];
+
+    // if dragging a selection group bounding box, move the selected items.
+    if (shouldStartMoveSelections([x, y])) {
+      setIsMovingSelections(true);
+      onMoveNodeStart?.(...selectedNodes);
+      return;
+    }
+
+    // else, clear and start a marquee
+    onClearSelection();
     setMarquee([x1, y1, 0, 0]);
   };
 
+  const shouldStartMoveSelections = ([cx, cy]) => {
+    // x, y is a client x, y.
+    const [ox, oy] = offset;
+    [cx, cy] = [cx - ox, cy - oy];
+    const [x, y] = [cx / zoom, cy / zoom];
+
+    const box = boundingbox(
+      selected_nodes.map((d) => {
+        return [d.absoluteX, d.absoluteY, d.width, d.height, d.rotation];
+      }),
+      2
+    );
+
+    return is_point_inside_box([x, y], box);
+  };
+
   const onDrag: OnDragHandler = (s) => {
     const [ox, oy] = offset;
     const [x, y] = [
@@ -296,6 +337,11 @@ export function Canvas({
 
     const [x1, y1] = [x - ox, y - oy];
 
+    if (isMovingSelections) {
+      const [dx, dy] = s.delta;
+      onMoveNode?.([dx / zoom, dy / zoom], ...selectedNodes);
+    }
+
     if (marquee) {
       const [w, h] = [
         x1 - marquee[0], // w
@@ -315,6 +361,18 @@ export function Canvas({
   const onDragEnd: OnDragHandler = (s) => {
     setMarquee(null);
     setIsDragging(false);
+    if (isMovingSelections) {
+      const [ix, iy] = s.initial;
+      const [fx, fy] = [
+        //@ts-ignore
+        s.event.clientX,
+        //@ts-ignore
+        s.event.clientY,
+      ];
+
+      onMoveNodeEnd?.([(fx - ix) / zoom, (fy - iy) / zoom], ...selectedNodes);
+      setIsMovingSelections(false);
+    }
   };
 
   const is_canvas_transforming = isPanning || isZooming;
diff --git a/editor-packages/editor-canvas/core/actions/index.ts b/editor-packages/editor-canvas/core/actions/index.ts
new file mode 100644
index 00000000..e69de29b
diff --git a/editor-packages/editor-canvas/core/dispatch/index.ts b/editor-packages/editor-canvas/core/dispatch/index.ts
new file mode 100644
index 00000000..e69de29b
diff --git a/editor-packages/editor-canvas/core/reducer/index.ts b/editor-packages/editor-canvas/core/reducer/index.ts
new file mode 100644
index 00000000..e69de29b
diff --git a/editor-packages/editor-canvas/core/states/index.ts b/editor-packages/editor-canvas/core/states/index.ts
new file mode 100644
index 00000000..e69de29b
diff --git a/editor-packages/editor-canvas/math/bounding-box.ts b/editor-packages/editor-canvas/math/bounding-box.ts
index a6edc10d..2a852d1e 100644
--- a/editor-packages/editor-canvas/math/bounding-box.ts
+++ b/editor-packages/editor-canvas/math/bounding-box.ts
@@ -1,4 +1,4 @@
-import type { XYWH, X1Y1X2Y2, Box, XYWHR } from "../types";
+import type { XYWH, Box, XYWHR, XY } from "../types";
 
 export function xywh_to_bounding_box({
   xywh,
@@ -42,6 +42,12 @@ export function boundingbox(
   return [x1, y1, x2, y2];
 }
 
+export function is_point_inside_box(point: XY, box: Box) {
+  const [x, y] = point;
+  const [x1, y1, x2, y2] = box;
+  return x >= x1 && x <= x2 && y >= y1 && y <= y2;
+}
+
 /**
  // TODO: handle rotation. (no rotation for now)
  * 
diff --git a/editor/core/actions/index.ts b/editor/core/actions/index.ts
index 4dc3a79e..d521a13d 100644
--- a/editor/core/actions/index.ts
+++ b/editor/core/actions/index.ts
@@ -18,6 +18,7 @@ export type Action =
   | PageAction
   | SelectNodeAction
   | HighlightLayerAction
+  | CanvasEditAction
   | CanvasModeAction
   | PreviewAction
   | CodeEditorAction;
@@ -30,6 +31,14 @@ export interface SelectNodeAction {
   node: string | string[];
 }
 
+export type CanvasEditAction = TranslateNodeAction;
+
+export interface TranslateNodeAction {
+  type: "node-transform-translate";
+  translate: [number, number];
+  node: string[];
+}
+
 export type PageAction = SelectPageAction;
 
 export interface SelectPageAction {
diff --git a/editor/core/reducers/editor-reducer.ts b/editor/core/reducers/editor-reducer.ts
index 91ddaa7c..4a49436d 100644
--- a/editor/core/reducers/editor-reducer.ts
+++ b/editor/core/reducers/editor-reducer.ts
@@ -6,6 +6,7 @@ import type {
   CodeEditorEditComponentCodeAction,
   CanvasModeSwitchAction,
   CanvasModeGobackAction,
+  TranslateNodeAction,
   PreviewBuildingStateUpdateAction,
   PreviewSetAction,
 } from "core/actions";
@@ -13,6 +14,7 @@ import { EditorState } from "core/states";
 import { useRouter } from "next/router";
 import { CanvasStateStore } from "@code-editor/canvas/stores";
 import assert from "assert";
+import { find_node_by_id_under_inpage_nodes } from "@design-sdk/core/utils/query";
 
 const _editor_path_name = "/files/[key]/";
 
@@ -23,10 +25,18 @@ export function editorReducer(state: EditorState, action: Action): EditorState {
   switch (action.type) {
     case "select-node": {
       const { node } = <SelectNodeAction>action;
+      const ids = Array.isArray(node) ? node : [node];
+
+      const current_node = state.selectedNodes;
+
+      if (ids.length > 1 && ids.length === current_node.length) {
+        // the selection event is always triggered by user, which means selecting same amount of nodes (greater thatn 1, and having a different node array is impossible.)
+        return;
+      }
+
       console.clear();
       console.info("cleard console by editorReducer#select-node");
 
-      const ids = Array.isArray(node) ? node : [node];
       const primary = ids?.[0];
 
       // update router
@@ -84,6 +94,22 @@ export function editorReducer(state: EditorState, action: Action): EditorState {
         draft.selectedNodes = last_known_selections_of_this_page;
       });
     }
+    case "node-transform-translate": {
+      const { translate, node } = <TranslateNodeAction>action;
+
+      return produce(state, (draft) => {
+        const page = draft.design.pages.find(
+          (p) => p.id === state.selectedPage
+        );
+
+        node
+          .map((n) => find_node_by_id_under_inpage_nodes(n, page.children))
+          .map((n) => {
+            n.x += translate[0];
+            n.y += translate[1];
+          });
+      });
+    }
     case "code-editor-edit-component-code": {
       const { ...rest } = <CodeEditorEditComponentCodeAction>action;
       return produce(state, (draft) => {
diff --git a/editor/scaffolds/canvas/canvas.tsx b/editor/scaffolds/canvas/canvas.tsx
index 1eaec972..afa4c219 100644
--- a/editor/scaffolds/canvas/canvas.tsx
+++ b/editor/scaffolds/canvas/canvas.tsx
@@ -124,6 +124,14 @@ export function VisualContentArea() {
               onSelectNode={(...nodes) => {
                 dispatch({ type: "select-node", node: nodes.map((n) => n.id) });
               }}
+              onMoveNodeEnd={([x, y], ...nodes) => {
+                dispatch({
+                  type: "node-transform-translate",
+                  node: nodes,
+                  translate: [x, y],
+                });
+              }}
+              onMoveNode={() => {}}
               onClearSelection={() => {
                 dispatch({ type: "select-node", node: null });
               }}

From d56eddd950bd3d3b09cd868bb176a30ad47ec6b4 Mon Sep 17 00:00:00 2001
From: "UZU, J" <woojoo@softmarshmallow.com>
Date: Tue, 19 Apr 2022 18:29:52 +0900
Subject: [PATCH 07/30] qf

---
 .../editor-services-prettier/index.ts         | 52 +++++++++++--------
 editor/core/reducers/editor-reducer.ts        |  2 +-
 2 files changed, 31 insertions(+), 23 deletions(-)

diff --git a/editor-packages/editor-services-prettier/index.ts b/editor-packages/editor-services-prettier/index.ts
index 847f96d5..d1e4acd0 100644
--- a/editor-packages/editor-services-prettier/index.ts
+++ b/editor-packages/editor-services-prettier/index.ts
@@ -10,16 +10,20 @@ export function registerDocumentPrettier(editor, monaco) {
 
   const dartFormattingEditProvider = {
     provideDocumentFormattingEdits: (model, options, token) => {
-      const raw = model.getValue();
-      const { code, error } = formatDartCode(raw);
-      if (error) return [];
-      __dangerous__lastFormattedValue__global = code;
-      return [
-        {
-          range: model.getFullModelRange(),
-          text: code,
-        },
-      ];
+      try {
+        const raw = model.getValue();
+        const { code, error } = formatDartCode(raw);
+        if (error) return [];
+        __dangerous__lastFormattedValue__global = code;
+        return [
+          {
+            range: model.getFullModelRange(),
+            text: code,
+          },
+        ];
+      } catch (_) {
+        // ignore. this is caused by disposed model
+      }
     },
   };
 
@@ -31,19 +35,23 @@ export function registerDocumentPrettier(editor, monaco) {
         );
       }
 
-      const { canceled, error, pretty } = await prettierWorker?.emit({
-        text: model.getValue(),
-        language: model._languageId,
-      });
+      try {
+        const { canceled, error, pretty } = await prettierWorker?.emit({
+          text: model.getValue(),
+          language: model._languageId,
+        });
 
-      if (canceled || error) return [];
-      __dangerous__lastFormattedValue__global = pretty;
-      return [
-        {
-          range: model.getFullModelRange(),
-          text: pretty,
-        },
-      ];
+        if (canceled || error) return [];
+        __dangerous__lastFormattedValue__global = pretty;
+        return [
+          {
+            range: model.getFullModelRange(),
+            text: pretty,
+          },
+        ];
+      } catch (_) {
+        // ignore. this is caused by disposed model
+      }
     },
   };
 
diff --git a/editor/core/reducers/editor-reducer.ts b/editor/core/reducers/editor-reducer.ts
index 4a49436d..9bf831c0 100644
--- a/editor/core/reducers/editor-reducer.ts
+++ b/editor/core/reducers/editor-reducer.ts
@@ -31,7 +31,7 @@ export function editorReducer(state: EditorState, action: Action): EditorState {
 
       if (ids.length > 1 && ids.length === current_node.length) {
         // the selection event is always triggered by user, which means selecting same amount of nodes (greater thatn 1, and having a different node array is impossible.)
-        return;
+        return produce(state, (draft) => {});
       }
 
       console.clear();

From 6f0bdebed8c17323275b73e297ff9920d6f665ac Mon Sep 17 00:00:00 2001
From: "UZU, J" <woojoo@softmarshmallow.com>
Date: Wed, 20 Apr 2022 16:11:51 +0900
Subject: [PATCH 08/30] add size meter label

---
 .../editor-canvas/hud/hud-surface.tsx         | 20 ++++++
 .../editor-canvas/math/bounding-box.ts        | 13 ++++
 .../editor-canvas/overlay/index.ts            |  1 +
 .../overlay/size-meter-label-box.tsx          | 70 +++++++++++++++++++
 4 files changed, 104 insertions(+)
 create mode 100644 editor-packages/editor-canvas/overlay/size-meter-label-box.tsx

diff --git a/editor-packages/editor-canvas/hud/hud-surface.tsx b/editor-packages/editor-canvas/hud/hud-surface.tsx
index 6c1000f6..bcb47e13 100644
--- a/editor-packages/editor-canvas/hud/hud-surface.tsx
+++ b/editor-packages/editor-canvas/hud/hud-surface.tsx
@@ -4,6 +4,7 @@ import {
   ReadonlySelectHightlight,
   InSelectionGroupSelectHighlight,
   SelectHightlight,
+  SizeMeterLabelBox,
 } from "../overlay";
 import { FrameTitle, FrameTitleProps } from "../frame-title";
 import type { XY, XYWH } from "../types";
@@ -164,9 +165,11 @@ export function HudSurface({
 function SelectionGroupHighlight({
   selections,
   zoom,
+  disableSizeDisplay = false,
 }: {
   selections: DisplayNodeMeta[];
   zoom: number;
+  disableSizeDisplay?: boolean;
 }) {
   const box = boundingbox(
     selections.map((d) => {
@@ -176,6 +179,7 @@ function SelectionGroupHighlight({
   );
 
   const xywh = box_to_xywh(box);
+  const [x, y, w, h] = xywh;
 
   return (
     <>
@@ -191,6 +195,22 @@ function SelectionGroupHighlight({
           );
         })}
       </>
+      <>
+        {!disableSizeDisplay ? (
+          <SizeMeterLabelBox
+            xywh={xywh}
+            zoom={zoom}
+            anchor="s"
+            margin={8}
+            size={{
+              width: w,
+              height: h,
+            }}
+          />
+        ) : (
+          <></>
+        )}
+      </>
       <SelectHightlight
         key={"selections-highlight"}
         type="xywhr"
diff --git a/editor-packages/editor-canvas/math/bounding-box.ts b/editor-packages/editor-canvas/math/bounding-box.ts
index 2a852d1e..da3f427c 100644
--- a/editor-packages/editor-canvas/math/bounding-box.ts
+++ b/editor-packages/editor-canvas/math/bounding-box.ts
@@ -19,6 +19,19 @@ export function xywh_to_bounding_box({
   return [x1, y1, x2, y2];
 }
 
+/**
+ * @deprecated - not tested
+ * @param box
+ * @param zoom
+ * @returns
+ */
+export function zoom_box(box: Box, zoom: number): Box {
+  const [x1, y1, x2, y2] = box;
+  const [w, h] = [x2 - x1, y2 - y1];
+  const [dw, dh] = [w * zoom, h * zoom];
+  return [x1 * zoom, y1 * zoom, x1 + dw, y1 + dh];
+}
+
 type BoundingBoxInput =
   | (Box & { type?: 0 })
   | (XYWH & { type?: 1 })
diff --git a/editor-packages/editor-canvas/overlay/index.ts b/editor-packages/editor-canvas/overlay/index.ts
index 0edc8078..80998c4e 100644
--- a/editor-packages/editor-canvas/overlay/index.ts
+++ b/editor-packages/editor-canvas/overlay/index.ts
@@ -2,3 +2,4 @@ export * from "./hover-outline-hightlight";
 export * from "./select-highlight-in-selection-group";
 export * from "./select-hightlight";
 export * from "./select-hightlight-readonly";
+export * from "./size-meter-label-box";
diff --git a/editor-packages/editor-canvas/overlay/size-meter-label-box.tsx b/editor-packages/editor-canvas/overlay/size-meter-label-box.tsx
new file mode 100644
index 00000000..4373674f
--- /dev/null
+++ b/editor-packages/editor-canvas/overlay/size-meter-label-box.tsx
@@ -0,0 +1,70 @@
+import React, { useMemo } from "react";
+import styled from "@emotion/styled";
+import type { XYWH } from "../types";
+import { xywh_to_bounding_box } from "../math";
+
+export function SizeMeterLabelBox({
+  size,
+  anchor = "s",
+  margin = 0,
+  xywh,
+  zoom,
+}: {
+  size: { width: number; height: number };
+  anchor?: "w" | "n" | "s" | "e";
+  margin?: number;
+} & {
+  xywh: XYWH;
+  zoom: number;
+}) {
+  // TODO: add anchor handling
+
+  const bbox = useMemo(
+    () => xywh_to_bounding_box({ xywh, scale: zoom }),
+    [xywh, zoom]
+  );
+
+  const [x1, y1, x2, y2] = bbox;
+  const bottomY = y2;
+  const boxWidth = x2 - x1; // use this to center position the label
+
+  const text = `${+size.width.toFixed(2)} x ${+size.height.toFixed()}`;
+
+  return (
+    <div
+      id="size-meter"
+      style={{
+        display: "flex",
+        minWidth: size.width * zoom,
+        justifyContent: "center",
+        position: "absolute",
+        pointerEvents: "none",
+        transform: `translate3d(${x1}px, ${y2 + margin}px, 0)`,
+        willChange: "transform, opacity",
+      }}
+    >
+      <Container>
+        <Label>{text}</Label>
+      </Container>
+    </div>
+  );
+}
+
+const Container = styled.div`
+  display: flex;
+  border-radius: 4px;
+  background-color: rgb(0, 87, 255);
+  box-sizing: border-box;
+  overflow: auto;
+  padding: 2px 4px;
+`;
+
+const Label = styled.span`
+  width: max-content;
+  color: white;
+  text-overflow: ellipsis;
+  font-size: 10px;
+  font-family: Inter, sans-serif;
+  font-weight: 500;
+  text-align: center;
+`;

From bac24d327f92ba27a715e18a3ed531d10e76c8cf Mon Sep 17 00:00:00 2001
From: "UZU, J" <woojoo@softmarshmallow.com>
Date: Wed, 20 Apr 2022 17:00:18 +0900
Subject: [PATCH 09/30] add shift+0 - zoom to fit command on canvas

---
 .../canvas-event-target/canvas-event-target.tsx             | 6 ++++++
 editor-packages/editor-canvas/canvas/canvas.tsx             | 4 ++++
 2 files changed, 10 insertions(+)

diff --git a/editor-packages/editor-canvas/canvas-event-target/canvas-event-target.tsx b/editor-packages/editor-canvas/canvas-event-target/canvas-event-target.tsx
index 7aa7ce70..a7116eee 100644
--- a/editor-packages/editor-canvas/canvas-event-target/canvas-event-target.tsx
+++ b/editor-packages/editor-canvas/canvas-event-target/canvas-event-target.tsx
@@ -25,6 +25,7 @@ export type OnPointerDownHandler = (
 const ZOOM_WITH_SCROLL_SENSITIVITY = 0.001;
 
 export function CanvasEventTarget({
+  onZoomToFit,
   onPanning,
   onPanningStart,
   onPanningEnd,
@@ -40,6 +41,7 @@ export function CanvasEventTarget({
   onDragEnd,
   children,
 }: {
+  onZoomToFit?: () => void;
   onPanning: OnPanningHandler;
   onPanningStart: OnPanningHandler;
   onPanningEnd: OnPanningHandler;
@@ -69,6 +71,10 @@ export function CanvasEventTarget({
       if (e.code === "Space") {
         setIsSpacebarPressed(true);
       }
+      // if shift + 0
+      else if (e.code === "Digit0" && e.shiftKey) {
+        onZoomToFit?.();
+      }
     };
     const ku = (e) => {
       if (e.code === "Space") {
diff --git a/editor-packages/editor-canvas/canvas/canvas.tsx b/editor-packages/editor-canvas/canvas/canvas.tsx
index aedbe3ed..9ef02099 100644
--- a/editor-packages/editor-canvas/canvas/canvas.tsx
+++ b/editor-packages/editor-canvas/canvas/canvas.tsx
@@ -416,6 +416,10 @@ export function Canvas({
           setIsPanning(false);
           _canvas_state_store.saveLastTransform(cvtransform);
         }}
+        onZoomToFit={() => {
+          setZoom(1);
+          _canvas_state_store.saveLastTransform(cvtransform);
+        }}
         onZooming={onZooming}
         onZoomingStart={() => {
           setIsZooming(true);

From 38805bf3143172035099e9fb74442022e32a39e3 Mon Sep 17 00:00:00 2001
From: "UZU, J" <woojoo@softmarshmallow.com>
Date: Wed, 20 Apr 2022 17:07:06 +0900
Subject: [PATCH 10/30] optimized size meter view w/o flex box

---
 .../overlay/size-meter-label-box.tsx          | 45 ++++++++-----------
 1 file changed, 19 insertions(+), 26 deletions(-)

diff --git a/editor-packages/editor-canvas/overlay/size-meter-label-box.tsx b/editor-packages/editor-canvas/overlay/size-meter-label-box.tsx
index 4373674f..8713ab30 100644
--- a/editor-packages/editor-canvas/overlay/size-meter-label-box.tsx
+++ b/editor-packages/editor-canvas/overlay/size-meter-label-box.tsx
@@ -3,6 +3,8 @@ import styled from "@emotion/styled";
 import type { XYWH } from "../types";
 import { xywh_to_bounding_box } from "../math";
 
+const font_size = 10;
+
 export function SizeMeterLabelBox({
   size,
   anchor = "s",
@@ -29,42 +31,33 @@ export function SizeMeterLabelBox({
   const boxWidth = x2 - x1; // use this to center position the label
 
   const text = `${+size.width.toFixed(2)} x ${+size.height.toFixed()}`;
+  const labelwidth = (text.length * font_size) / 1.8; // a view width assumption (we will not use flex box for faster painting)
+  const viewwidth = labelwidth + 4; // 4 is for horizontal padding
+
+  const [tx, ty] = [x1 + boxWidth / 2 - viewwidth / 2, y2 + margin];
 
   return (
     <div
       id="size-meter"
       style={{
-        display: "flex",
-        minWidth: size.width * zoom,
-        justifyContent: "center",
+        minWidth: viewwidth,
         position: "absolute",
         pointerEvents: "none",
-        transform: `translate3d(${x1}px, ${y2 + margin}px, 0)`,
+        transform: `translate3d(${tx}px, ${ty}px, 0)`,
         willChange: "transform, opacity",
+        boxSizing: "border-box",
+        whiteSpace: "nowrap",
+        borderRadius: 4,
+        background: "rgb(0, 87, 255)",
+        padding: "2px 4px",
+        color: "white",
+        fontSize: font_size,
+        fontFamily: "Inter, sans-serif",
+        fontWeight: 500,
+        textAlign: "center",
       }}
     >
-      <Container>
-        <Label>{text}</Label>
-      </Container>
+      {text}
     </div>
   );
 }
-
-const Container = styled.div`
-  display: flex;
-  border-radius: 4px;
-  background-color: rgb(0, 87, 255);
-  box-sizing: border-box;
-  overflow: auto;
-  padding: 2px 4px;
-`;
-
-const Label = styled.span`
-  width: max-content;
-  color: white;
-  text-overflow: ellipsis;
-  font-size: 10px;
-  font-family: Inter, sans-serif;
-  font-weight: 500;
-  text-align: center;
-`;

From 1cfd49e623fea2a4af31fc3bd64ad1102f278d28 Mon Sep 17 00:00:00 2001
From: "UZU, J" <woojoo@softmarshmallow.com>
Date: Wed, 20 Apr 2022 17:20:08 +0900
Subject: [PATCH 11/30] init core-drafting docs

---
 .../editor-canvas/core/drafting/README.md     |  0
 .../editor-canvas/core/drafting/index.ts      |  0
 .../core/drafting/transform-drafting.ts       |  0
 .../editor-canvas/docs/core-drafting.md       | 20 +++++++++++++++++++
 4 files changed, 20 insertions(+)
 create mode 100644 editor-packages/editor-canvas/core/drafting/README.md
 create mode 100644 editor-packages/editor-canvas/core/drafting/index.ts
 create mode 100644 editor-packages/editor-canvas/core/drafting/transform-drafting.ts
 create mode 100644 editor-packages/editor-canvas/docs/core-drafting.md

diff --git a/editor-packages/editor-canvas/core/drafting/README.md b/editor-packages/editor-canvas/core/drafting/README.md
new file mode 100644
index 00000000..e69de29b
diff --git a/editor-packages/editor-canvas/core/drafting/index.ts b/editor-packages/editor-canvas/core/drafting/index.ts
new file mode 100644
index 00000000..e69de29b
diff --git a/editor-packages/editor-canvas/core/drafting/transform-drafting.ts b/editor-packages/editor-canvas/core/drafting/transform-drafting.ts
new file mode 100644
index 00000000..e69de29b
diff --git a/editor-packages/editor-canvas/docs/core-drafting.md b/editor-packages/editor-canvas/docs/core-drafting.md
new file mode 100644
index 00000000..65d6b4d3
--- /dev/null
+++ b/editor-packages/editor-canvas/docs/core-drafting.md
@@ -0,0 +1,20 @@
+# State (property drafting)
+
+There are properties that are highly likely to be updated every frame (e.g. position, rotation, scale, etc.).
+This are performed by drag-related user input, and this properties will not be modified direcly to the design model.
+
+The final callback to the higher state holder will be called once after this operation is complete.
+
+## The properties are..
+
+**transform**
+
+- x
+- y
+- width
+- height
+- rotation
+
+**style**
+
+- color

From e5efe761613e4e1531966661c02777897c35f0bf Mon Sep 17 00:00:00 2001
From: "UZU, J" <woojoo@softmarshmallow.com>
Date: Thu, 21 Apr 2022 12:22:50 +0900
Subject: [PATCH 12/30] wip

---
 .../editor-canvas/canvas/canvas.tsx           |  1 +
 .../editor-canvas/core/drafting/README.md     |  1 +
 .../core/drafting/transform-drafting.ts       | 41 +++++++++++++++++++
 3 files changed, 43 insertions(+)

diff --git a/editor-packages/editor-canvas/canvas/canvas.tsx b/editor-packages/editor-canvas/canvas/canvas.tsx
index 9ef02099..a947caf2 100644
--- a/editor-packages/editor-canvas/canvas/canvas.tsx
+++ b/editor-packages/editor-canvas/canvas/canvas.tsx
@@ -418,6 +418,7 @@ export function Canvas({
         }}
         onZoomToFit={() => {
           setZoom(1);
+          // setOffset([newx, newy]); // TODO: set offset to center of the viewport
           _canvas_state_store.saveLastTransform(cvtransform);
         }}
         onZooming={onZooming}
diff --git a/editor-packages/editor-canvas/core/drafting/README.md b/editor-packages/editor-canvas/core/drafting/README.md
index e69de29b..31b8de9a 100644
--- a/editor-packages/editor-canvas/core/drafting/README.md
+++ b/editor-packages/editor-canvas/core/drafting/README.md
@@ -0,0 +1 @@
+# Properties drafting
diff --git a/editor-packages/editor-canvas/core/drafting/transform-drafting.ts b/editor-packages/editor-canvas/core/drafting/transform-drafting.ts
index e69de29b..f3e0204e 100644
--- a/editor-packages/editor-canvas/core/drafting/transform-drafting.ts
+++ b/editor-packages/editor-canvas/core/drafting/transform-drafting.ts
@@ -0,0 +1,41 @@
+// move
+// resize
+
+// width
+// height
+// x
+// y
+// rotation
+
+abstract class DraftingStore<T> {
+  readonly store = new Map<string, T>();
+
+  abstract update(id: string, draft: T);
+
+  abstract get(id: string): T;
+}
+
+interface Transform {
+  x: number;
+  y: number;
+  width: number;
+  height: number;
+  rotation: number;
+}
+
+class TransformDraftingStore extends DraftingStore<Transform> {
+  constructor(draftingStore: DraftingStore<Transform>) {
+    super();
+  }
+
+  update(id: string, transform: Transform) {
+    this.store.set(id, transform);
+  }
+
+  get(id: string): Transform {
+    return this.store.get(id);
+  }
+}
+
+// const store = new TransformDraftingStore();
+// export function

From d1099b18cf2e01ea059873868f20c8b71a34a4cf Mon Sep 17 00:00:00 2001
From: "UZU, J" <woojoo@softmarshmallow.com>
Date: Fri, 22 Apr 2022 11:22:32 +0900
Subject: [PATCH 13/30] distroy web workers on editor dispose

---
 .../index.ts                                  |  8 ++++++++
 .../code-editor/monaco-utils/register.ts      | 20 +++++++++++++++----
 editor/components/code-editor/monaco.tsx      |  6 +++++-
 3 files changed, 29 insertions(+), 5 deletions(-)

diff --git a/editor-packages/editor-services-jsx-syntax-highlight/index.ts b/editor-packages/editor-services-jsx-syntax-highlight/index.ts
index 6f8b0a0f..df30d42e 100644
--- a/editor-packages/editor-services-jsx-syntax-highlight/index.ts
+++ b/editor-packages/editor-services-jsx-syntax-highlight/index.ts
@@ -54,4 +54,12 @@ export function registerJsxHighlighter(
       oldDecor = editor.deltaDecorations(oldDecor, decorations);
     });
   });
+
+  return {
+    dispose() {
+      if (syntaxWorker) {
+        syntaxWorker.terminate();
+      }
+    },
+  };
 }
diff --git a/editor/components/code-editor/monaco-utils/register.ts b/editor/components/code-editor/monaco-utils/register.ts
index b56784c7..561c3454 100644
--- a/editor/components/code-editor/monaco-utils/register.ts
+++ b/editor/components/code-editor/monaco-utils/register.ts
@@ -1,12 +1,24 @@
 import * as monaco from "monaco-editor";
-import { Monaco, OnMount } from "@monaco-editor/react";
+import { Monaco } from "@monaco-editor/react";
 import { registerDocumentPrettier } from "@code-editor/prettier-services";
 import { registerJsxHighlighter } from "@code-editor/jsx-syntax-highlight-services";
 type CompilerOptions = monaco.languages.typescript.CompilerOptions;
 
-export const initEditor: OnMount = (editor, monaco) => {
-  registerJsxHighlighter(editor, monaco);
-  registerDocumentPrettier(editor, monaco);
+export const initEditor = (
+  editor: monaco.editor.IStandaloneCodeEditor,
+  monaco: Monaco
+) => {
+  const { dispose: disposeJsxHighlighter } = registerJsxHighlighter(
+    editor,
+    monaco
+  );
+
+  const { dispose: disposePrettier } = registerDocumentPrettier(editor, monaco);
+
+  return () => {
+    disposeJsxHighlighter();
+    disposePrettier();
+  };
 };
 
 export const initMonaco = (monaco: Monaco) => {
diff --git a/editor/components/code-editor/monaco.tsx b/editor/components/code-editor/monaco.tsx
index 919011a0..ea199449 100644
--- a/editor/components/code-editor/monaco.tsx
+++ b/editor/components/code-editor/monaco.tsx
@@ -29,7 +29,7 @@ export function MonacoEditor(props: MonacoEditorProps) {
 
     instance.current = { editor, format };
 
-    register.initEditor(editor, monaco);
+    const dispose = register.initEditor(editor, monaco);
 
     editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, function () {
       format.run();
@@ -65,6 +65,10 @@ export function MonacoEditor(props: MonacoEditorProps) {
     editor.onDidChangeModelContent(() =>
       debounce(() => editor.saveViewState(), 200)
     );
+
+    editor.onDidDispose(() => {
+      dispose();
+    });
   };
 
   return (

From 43fc7fb52f0769ad6f3eae85c4b910cf27e93caf Mon Sep 17 00:00:00 2001
From: "UZU, J" <woojoo@softmarshmallow.com>
Date: Fri, 22 Apr 2022 11:22:38 +0900
Subject: [PATCH 14/30] safety

---
 editor/scaffolds/editor/editor-preview-provider.tsx | 1 +
 1 file changed, 1 insertion(+)

diff --git a/editor/scaffolds/editor/editor-preview-provider.tsx b/editor/scaffolds/editor/editor-preview-provider.tsx
index 93aee854..c36237f9 100644
--- a/editor/scaffolds/editor/editor-preview-provider.tsx
+++ b/editor/scaffolds/editor/editor-preview-provider.tsx
@@ -173,6 +173,7 @@ export function EditorPreviewDataProvider({
   //   // ------ for esbuild -----
   useEffect(() => {
     if (
+      !target ||
       !state.editingModule ||
       // now only react is supported.
       state.editingModule.framework !== "react"

From 4e183698cffbbe278a623c301b0a35eb970621f7 Mon Sep 17 00:00:00 2001
From: "UZU, J" <woojoo@softmarshmallow.com>
Date: Fri, 22 Apr 2022 12:40:21 +0900
Subject: [PATCH 15/30] add dummy auth state to workspace state

---
 editor/core/states/workspace-state.ts | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/editor/core/states/workspace-state.ts b/editor/core/states/workspace-state.ts
index 21ca4492..d62cd1cc 100644
--- a/editor/core/states/workspace-state.ts
+++ b/editor/core/states/workspace-state.ts
@@ -8,6 +8,16 @@ export interface WorkspaceState {
    */
   highlightedLayer?: string;
   preferences: WorkspacePreferences;
+
+  /**
+   * figma authentication data store state
+   * @deprecated - not implemented
+   */
+  authenticationFigma?: {
+    name?: string;
+    accessToken?: string;
+    personalAccessToken?: string;
+  };
 }
 
 export interface WorkspacePreferences {

From 0ed282d17bf55a73d5e3a28ef265ece9e2ec9ee9 Mon Sep 17 00:00:00 2001
From: "UZU, J" <woojoo@softmarshmallow.com>
Date: Fri, 22 Apr 2022 12:40:50 +0900
Subject: [PATCH 16/30] split & cleanup

---
 editor/pages/canvas-server/index.tsx          |  13 +-
 editor/scaffolds/canvas/canvas.tsx            |  30 +-
 .../{preview => preview-canvas}/README.md     |   0
 editor/scaffolds/preview-canvas/cache.ts      |  16 +
 .../canvas-preview-worker-messenger.ts        |  49 +++
 .../editor-canvas-preview-provider.tsx        |  24 ++
 .../preview-canvas/image-preview.tsx          |  70 ++++
 editor/scaffolds/preview-canvas/index.ts      |   2 +
 .../preview-canvas/preview-content.tsx        |  44 +++
 editor/scaffolds/preview-canvas/prop-type.ts  |   8 +
 editor/scaffolds/preview-canvas/util.ts       |   8 +
 .../preview-canvas/vanilla-preview-async.tsx  | 151 ++++++++
 .../vanilla-preview-webworker.tsx             |  33 ++
 .../workers/canvas-preview.worker.js          | 103 ++++++
 editor/scaffolds/preview/index.tsx            | 347 ------------------
 .../preview/workers/vanilla.worker.js         |  78 ----
 16 files changed, 524 insertions(+), 452 deletions(-)
 rename editor/scaffolds/{preview => preview-canvas}/README.md (100%)
 create mode 100644 editor/scaffolds/preview-canvas/cache.ts
 create mode 100644 editor/scaffolds/preview-canvas/canvas-preview-worker-messenger.ts
 create mode 100644 editor/scaffolds/preview-canvas/editor-canvas-preview-provider.tsx
 create mode 100644 editor/scaffolds/preview-canvas/image-preview.tsx
 create mode 100644 editor/scaffolds/preview-canvas/index.ts
 create mode 100644 editor/scaffolds/preview-canvas/preview-content.tsx
 create mode 100644 editor/scaffolds/preview-canvas/prop-type.ts
 create mode 100644 editor/scaffolds/preview-canvas/util.ts
 create mode 100644 editor/scaffolds/preview-canvas/vanilla-preview-async.tsx
 create mode 100644 editor/scaffolds/preview-canvas/vanilla-preview-webworker.tsx
 create mode 100644 editor/scaffolds/preview-canvas/workers/canvas-preview.worker.js
 delete mode 100644 editor/scaffolds/preview/index.tsx
 delete mode 100644 editor/scaffolds/preview/workers/vanilla.worker.js

diff --git a/editor/pages/canvas-server/index.tsx b/editor/pages/canvas-server/index.tsx
index 25deb849..4197a46a 100644
--- a/editor/pages/canvas-server/index.tsx
+++ b/editor/pages/canvas-server/index.tsx
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
 import {
   D2CVanillaPreview,
   WebWorkerD2CVanillaPreview,
-} from "scaffolds/preview";
+} from "scaffolds/preview-canvas";
 import { Canvas } from "@code-editor/canvas";
 import useMeasure from "react-use-measure";
 import { FrameTitleRenderer } from "scaffolds/canvas/render/frame-title";
@@ -17,17 +17,6 @@ export default function CanvasServerPage() {
   const [canvasSizingRef, canvasBounds] = useMeasure();
   const [selectedPage, setSelectedPage] = useState<string | null>(null);
 
-  // useEffect(() => {
-  //   const handler = (e) => {
-  //     //
-  //   };
-
-  //   window.addEventListener("message", handler);
-  //   return () => {
-  //     window.removeEventListener("message", handler);
-  //   };
-  // }, []);
-
   // const thisPageNodes = selectedPage
   //   ? design.pages.find((p) => p.id == selectedPage).children.filter(Boolean)
   //   : [];
diff --git a/editor/scaffolds/canvas/canvas.tsx b/editor/scaffolds/canvas/canvas.tsx
index afa4c219..d6bd4b90 100644
--- a/editor/scaffolds/canvas/canvas.tsx
+++ b/editor/scaffolds/canvas/canvas.tsx
@@ -5,7 +5,7 @@ import { useEditorState, useWorkspace } from "core/states";
 import {
   D2CVanillaPreview,
   WebWorkerD2CVanillaPreview,
-} from "scaffolds/preview";
+} from "scaffolds/preview-canvas";
 import useMeasure from "react-use-measure";
 import { useDispatch } from "core/dispatch";
 import { FrameTitleRenderer } from "./render/frame-title";
@@ -124,14 +124,14 @@ export function VisualContentArea() {
               onSelectNode={(...nodes) => {
                 dispatch({ type: "select-node", node: nodes.map((n) => n.id) });
               }}
-              onMoveNodeEnd={([x, y], ...nodes) => {
-                dispatch({
-                  type: "node-transform-translate",
-                  node: nodes,
-                  translate: [x, y],
-                });
-              }}
-              onMoveNode={() => {}}
+              // onMoveNodeEnd={([x, y], ...nodes) => {
+              //   dispatch({
+              //     type: "node-transform-translate",
+              //     node: nodes,
+              //     translate: [x, y],
+              //   });
+              // }}
+              // onMoveNode={() => {}}
               onClearSelection={() => {
                 dispatch({ type: "select-node", node: null });
               }}
@@ -139,12 +139,12 @@ export function VisualContentArea() {
               // initialTransform={ } // TODO: if the initial selection is provided from first load, from the query param, we have to focus to fit that node.
               renderItem={(p) => {
                 return (
-                  // <WebWorkerD2CVanillaPreview
-                  //   key={p.node.id}
-                  //   target={p.node}
-                  //   {...p}
-                  // />
-                  <D2CVanillaPreview key={p.node.id} target={p.node} {...p} />
+                  <WebWorkerD2CVanillaPreview
+                    key={p.node.id}
+                    target={p.node}
+                    {...p}
+                  />
+                  // <D2CVanillaPreview key={p.node.id} target={p.node} {...p} />
                 );
               }}
               readonly={false}
diff --git a/editor/scaffolds/preview/README.md b/editor/scaffolds/preview-canvas/README.md
similarity index 100%
rename from editor/scaffolds/preview/README.md
rename to editor/scaffolds/preview-canvas/README.md
diff --git a/editor/scaffolds/preview-canvas/cache.ts b/editor/scaffolds/preview-canvas/cache.ts
new file mode 100644
index 00000000..b0b6170a
--- /dev/null
+++ b/editor/scaffolds/preview-canvas/cache.ts
@@ -0,0 +1,16 @@
+import type { Result } from "@designto/code";
+
+type TResultCache = Result & { __image: boolean };
+
+export const cache = {
+  set: (key: string, value: TResultCache) => {
+    sessionStorage.setItem(key, JSON.stringify(value));
+  },
+  get: (key: string): TResultCache => {
+    const value = sessionStorage.getItem(key);
+    return value ? JSON.parse(value) : null;
+  },
+};
+
+export const cachekey = (target: { filekey; id }) =>
+  target ? `${target.filekey}-${target.id}-${new Date().getMinutes()}` : null;
diff --git a/editor/scaffolds/preview-canvas/canvas-preview-worker-messenger.ts b/editor/scaffolds/preview-canvas/canvas-preview-worker-messenger.ts
new file mode 100644
index 00000000..48e4de2e
--- /dev/null
+++ b/editor/scaffolds/preview-canvas/canvas-preview-worker-messenger.ts
@@ -0,0 +1,49 @@
+import { createWorkerQueue } from "@code-editor/webworker-services-core";
+import type { Result } from "@designto/code";
+
+let previewworker: Worker;
+export function initialize(filekey: string, authentication) {
+  // initialize the worker and set the preferences.
+  if (!previewworker) {
+    const { worker } = createWorkerQueue(
+      new Worker(new URL("./workers/vanilla.worker.js", import.meta.url))
+    );
+
+    previewworker = worker;
+  }
+
+  previewworker.postMessage({
+    $type: "init",
+    filekey,
+    authentication,
+  });
+
+  return () => {
+    if (previewworker) {
+      previewworker.terminate();
+    }
+  };
+}
+
+export function preview(
+  node: string,
+  onResult: (result: Result) => void,
+  onError?: (error: Error) => void
+) {
+  previewworker.postMessage({
+    $type: "preview",
+    node,
+  });
+
+  previewworker.addEventListener("message", (e) => {
+    // TODO: add id matcher (?)
+    switch (e.data.$type) {
+      case "result":
+        onResult(e.data);
+        break;
+      case "error":
+        onError(new Error(e.data.message));
+        break;
+    }
+  });
+}
diff --git a/editor/scaffolds/preview-canvas/editor-canvas-preview-provider.tsx b/editor/scaffolds/preview-canvas/editor-canvas-preview-provider.tsx
new file mode 100644
index 00000000..8b3194f7
--- /dev/null
+++ b/editor/scaffolds/preview-canvas/editor-canvas-preview-provider.tsx
@@ -0,0 +1,24 @@
+import React, { useEffect } from "react";
+import { useFigmaAccessToken } from "hooks/use-figma-access-token";
+import { initialize } from "scaffolds/preview-canvas/canvas-preview-worker-messenger";
+import { useEditorState } from "core/states";
+
+export function EditorCanvasPreviewProvider({
+  children,
+}: {
+  children?: React.ReactNode;
+}) {
+  const [state] = useEditorState();
+  const fat = useFigmaAccessToken();
+
+  useEffect(() => {
+    if (
+      state.design.key &&
+      (fat.personalAccessToken || !fat.accessToken.loading)
+    ) {
+      initialize(state.design.key, fat);
+    }
+  }, [fat, state.design.key]);
+
+  return <>{children}</>;
+}
diff --git a/editor/scaffolds/preview-canvas/image-preview.tsx b/editor/scaffolds/preview-canvas/image-preview.tsx
new file mode 100644
index 00000000..14fa30cf
--- /dev/null
+++ b/editor/scaffolds/preview-canvas/image-preview.tsx
@@ -0,0 +1,70 @@
+import React, { useState, useEffect } from "react";
+import { remote } from "@design-sdk/figma";
+
+const DEV_ONLY_FIGMA_PAT =
+  process.env.NEXT_PUBLIC_DEVELOPER_FIGMA_PERSONAL_ACCESS_TOKEN;
+
+export function FigmaFrameImageView({
+  filekey,
+  nodeid,
+  zoom,
+}: {
+  filekey: string;
+  nodeid: string;
+  zoom: number;
+}) {
+  // fetch image
+  const [image_1, setImage_1] = useState<string>();
+  const [image_s, setImage_s] = useState<string>();
+
+  useEffect(() => {
+    // fetch image from figma
+    // fetch smaller one first, then fatch the full scaled.
+    remote
+      .fetchNodeAsImage(
+        filekey,
+        { personalAccessToken: DEV_ONLY_FIGMA_PAT },
+        nodeid
+        // scale = 1
+      )
+      .then((r) => {
+        console.log("fetched image from figma", r);
+        setImage_1(r.__default);
+        setImage_s(r.__default);
+      });
+  }, [filekey, nodeid]);
+
+  let imgscale: 1 | 0.2 = 1;
+  if (zoom > 1) {
+    return null;
+  } else if (zoom <= 1 && zoom > 0.3) {
+    imgscale = 1;
+    // display 1 scaled image
+  } else {
+    // display 0.2 scaled image
+    imgscale = 0.2;
+  }
+
+  return (
+    <div
+      style={{
+        top: 0,
+        left: 0,
+        position: "fixed",
+        width: "100%",
+        height: "100%",
+      }}
+    >
+      <img
+        style={{
+          width: "100%",
+          height: "100%",
+          objectFit: "fill",
+          border: 0,
+        }}
+        src={imgscale === 1 ? image_1 : image_s}
+        alt=""
+      />
+    </div>
+  );
+}
diff --git a/editor/scaffolds/preview-canvas/index.ts b/editor/scaffolds/preview-canvas/index.ts
new file mode 100644
index 00000000..042ca293
--- /dev/null
+++ b/editor/scaffolds/preview-canvas/index.ts
@@ -0,0 +1,2 @@
+export { D2CVanillaPreview } from "./vanilla-preview-async";
+export { WebWorkerD2CVanillaPreview } from "./vanilla-preview-webworker";
diff --git a/editor/scaffolds/preview-canvas/preview-content.tsx b/editor/scaffolds/preview-canvas/preview-content.tsx
new file mode 100644
index 00000000..fb7a8b0e
--- /dev/null
+++ b/editor/scaffolds/preview-canvas/preview-content.tsx
@@ -0,0 +1,44 @@
+import React from "react";
+import { VanillaRunner } from "components/app-runner/vanilla-app-runner";
+
+export function PreviewContent({
+  width,
+  height,
+  backgroundColor,
+  id,
+  source,
+  name,
+}: {
+  width: number;
+  height: number;
+  backgroundColor: string;
+  id: string;
+  source: string;
+  name: string;
+}) {
+  return (
+    <div
+      style={{
+        width: width,
+        height: height,
+        borderRadius: 1,
+        backgroundColor: backgroundColor,
+        contain: "layout style paint",
+      }}
+    >
+      {source && (
+        <VanillaRunner
+          key={id}
+          style={{
+            borderRadius: 1,
+            contain: "layout style paint",
+          }}
+          source={source}
+          width="100%"
+          height="100%"
+          componentName={name}
+        />
+      )}
+    </div>
+  );
+}
diff --git a/editor/scaffolds/preview-canvas/prop-type.ts b/editor/scaffolds/preview-canvas/prop-type.ts
new file mode 100644
index 00000000..7e856a35
--- /dev/null
+++ b/editor/scaffolds/preview-canvas/prop-type.ts
@@ -0,0 +1,8 @@
+import type { ReflectSceneNode } from "@design-sdk/figma-node";
+import type { FrameOptimizationFactors } from "@code-editor/canvas/frame";
+
+export type VanillaPreviewProps = {
+  target: ReflectSceneNode & {
+    filekey: string;
+  };
+} & FrameOptimizationFactors;
diff --git a/editor/scaffolds/preview-canvas/util.ts b/editor/scaffolds/preview-canvas/util.ts
new file mode 100644
index 00000000..70ea6024
--- /dev/null
+++ b/editor/scaffolds/preview-canvas/util.ts
@@ -0,0 +1,8 @@
+import { colorFromFills } from "@design-sdk/core/utils/colors";
+import type { ReflectSceneNode } from "@design-sdk/figma-node";
+
+export const blurred_bg_fill = (target: ReflectSceneNode) => {
+  const __bg = colorFromFills(target.fills);
+  const bg_color_str = __bg ? "#" + __bg.hex : "transparent";
+  return bg_color_str;
+};
diff --git a/editor/scaffolds/preview-canvas/vanilla-preview-async.tsx b/editor/scaffolds/preview-canvas/vanilla-preview-async.tsx
new file mode 100644
index 00000000..d7e99b22
--- /dev/null
+++ b/editor/scaffolds/preview-canvas/vanilla-preview-async.tsx
@@ -0,0 +1,151 @@
+import React, { useEffect, useState } from "react";
+import { config } from "@designto/config";
+import { preview_presets } from "@grida/builder-config-preset";
+import { designToCode, Result } from "@designto/code";
+import { MainImageRepository } from "@design-sdk/core/assets-repository";
+import { cachekey, cache } from "./cache";
+import { blurred_bg_fill } from "./util";
+import { PreviewContent } from "./preview-content";
+import type { VanillaPreviewProps } from "./prop-type";
+
+const placeholderimg =
+  "https://bridged-service-static.s3.us-west-1.amazonaws.com/placeholder-images/image-placeholder-bw-tile-100.png";
+
+const build_config: config.BuildConfiguration = {
+  ...config.default_build_configuration,
+  disable_components: true,
+  disable_detection: true,
+  disable_flags_support: true,
+};
+
+const framework_config: config.VanillaPreviewFrameworkConfig = {
+  ...preview_presets.default,
+  additional_css_declaration: {
+    declarations: [
+      {
+        key: {
+          name: "body",
+          selector: "tag",
+        },
+        style: {
+          contain: "layout style paint",
+        },
+      },
+    ],
+  },
+};
+
+export function D2CVanillaPreview({
+  target,
+  isZooming,
+  isPanning,
+}: VanillaPreviewProps) {
+  const [preview, setPreview] = useState<Result>();
+  const key = cachekey(target);
+
+  const on_preview_result = (result: Result, __image: boolean) => {
+    if (preview) {
+      if (preview.code === result.code) {
+        return;
+      }
+    }
+    setPreview(result);
+    cache.set(target.filekey, { ...result, __image });
+  };
+
+  const hide_preview = isZooming || isPanning;
+
+  useEffect(() => {
+    if (hide_preview) {
+      // don't make preview if zooming.
+      return;
+    }
+
+    if (preview) {
+      return;
+    }
+
+    const d2c_firstload = () => {
+      return designToCode({
+        input: _input,
+        build_config: build_config,
+        framework: framework_config,
+        asset_config: {
+          skip_asset_replacement: false,
+          asset_repository: MainImageRepository.instance,
+          custom_asset_replacement: {
+            type: "static",
+            resource: placeholderimg,
+          },
+        },
+      });
+    };
+
+    const d2c_imageload = () => {
+      if (!MainImageRepository.instance.empty) {
+        designToCode({
+          input: _input,
+          build_config: build_config,
+          framework: framework_config,
+          asset_config: { asset_repository: MainImageRepository.instance },
+        })
+          .then((r) => {
+            on_preview_result(r, true);
+          })
+          .catch((e) => {
+            console.error(
+              "error while making preview with image repo provided.",
+              e
+            );
+          });
+      }
+    };
+
+    const _input = target
+      ? {
+          id: target.id,
+          name: target.name,
+          entry: target,
+        }
+      : null;
+
+    const cached = cache.get(key);
+    if (cached) {
+      setPreview(cached);
+      if (cached.__image) {
+        return;
+      }
+      if (_input) {
+        d2c_imageload();
+      }
+    } else {
+      if (_input) {
+        d2c_firstload()
+          .then((r) => {
+            on_preview_result(r, false);
+            // if the result contains a image and needs to be fetched,
+            if (r.code.raw.includes(placeholderimg)) {
+              // TODO: we don't yet have other way to know if image is used, other than checking if placeholder image is used. - this needs to be updated in d2c module to include used images meta in the result.
+              d2c_imageload();
+            }
+          })
+          .catch(console.error);
+      }
+    }
+  }, [target?.id, isZooming, isPanning]);
+
+  const bg_color_str = blurred_bg_fill(target);
+
+  return (
+    <PreviewContent
+      id={target.id}
+      name={target.name}
+      source={preview?.scaffold?.raw}
+      width={target.width}
+      height={target.height}
+      backgroundColor={
+        !preview && bg_color_str // clear bg after preview is rendered.
+      }
+    />
+  );
+}
diff --git a/editor/scaffolds/preview-canvas/vanilla-preview-webworker.tsx b/editor/scaffolds/preview-canvas/vanilla-preview-webworker.tsx
new file mode 100644
index 00000000..d0496fde
--- /dev/null
+++ b/editor/scaffolds/preview-canvas/vanilla-preview-webworker.tsx
@@ -0,0 +1,33 @@
+import React, { useEffect, useState } from "react";
+import type { Result } from "@designto/code";
+import { PreviewContent } from "./preview-content";
+import type { VanillaPreviewProps } from "./prop-type";
+import { blurred_bg_fill } from "./util";
+import { cachekey, cache } from "./cache";
+import { preview as wwpreview } from "./canvas-preview-worker-messenger";
+
+export function WebWorkerD2CVanillaPreview({ target }: VanillaPreviewProps) {
+  const [preview, setPreview] = useState<Result>();
+  const bg_color_str = blurred_bg_fill(target);
+
+  useEffect(() => {
+    if (preview) {
+      return;
+    }
+
+    wwpreview(target.id, setPreview);
+  }, [target?.id]);
+
+  return (
+    <PreviewContent
+      id={target.id}
+      name={target.name}
+      source={preview?.scaffold?.raw}
+      width={target.width}
+      height={target.height}
+      backgroundColor={
+        !preview && bg_color_str // clear bg after preview is rendered.
+      }
+    />
+  );
+}
diff --git a/editor/scaffolds/preview-canvas/workers/canvas-preview.worker.js b/editor/scaffolds/preview-canvas/workers/canvas-preview.worker.js
new file mode 100644
index 00000000..7022a7ba
--- /dev/null
+++ b/editor/scaffolds/preview-canvas/workers/canvas-preview.worker.js
@@ -0,0 +1,103 @@
+import { designToCode } from "@designto/code";
+import {
+  ImageRepository,
+  MainImageRepository,
+} from "@design-sdk/core/assets-repository";
+import { RemoteImageRepositories } from "@design-sdk/figma-remote/lib/asset-repository/image-repository";
+import { config } from "@designto/config";
+import { preview_presets } from "@grida/builder-config-preset";
+
+const placeholderimg =
+  "https://bridged-service-static.s3.us-west-1.amazonaws.com/placeholder-images/image-placeholder-bw-tile-100.png";
+
+// : config.BuildConfiguration
+const build_config = {
+  ...config.default_build_configuration,
+  disable_components: true,
+  disable_detection: true,
+  disable_flags_support: true,
+};
+
+// : config.VanillaPreviewFrameworkConfig
+const framework_config = {
+  ...preview_presets.default,
+  additional_css_declaration: {
+    declarations: [
+      {
+        key: {
+          name: "body",
+          selector: "tag",
+        },
+        style: {
+          contain: "layout style paint",
+        },
+      },
+    ],
+  },
+};
+
+function initialize({ filekey, authentication }) {
+  // ------- setup image repo with auth + filekey -------
+  MainImageRepository.instance = new RemoteImageRepositories(filekey, {
+    authentication: authentication,
+  });
+
+  MainImageRepository.instance.register(
+    new ImageRepository(
+      "fill-later-assets",
+      "grida://assets-reservation/images/"
+    )
+  );
+  // ----------------------------------------------------
+
+  // setup indexed db connection for reading the file.
+  // ⛔️ the assumbtion is that file does not change during the app is running.
+
+  // 1. read the raw data from indexed db
+  // 2. format the data to reflect
+  // 3. set the data status as 'ready'
+  // (the below requests can be operated after when this processes are complete)
+}
+
+addEventListener("message", async (event) => {
+  function respond(data) {
+    setTimeout(() => {
+      postMessage({ $type: "result", ...data });
+    }, 0);
+  }
+
+  switch (event.data.$type) {
+    case "initialize": {
+      initialize({
+        filekey: event.data.filekey,
+        authentication: event.data.authentication,
+      });
+      return;
+    }
+    case "preview": {
+      try {
+        const { node } = event.data;
+
+        const result = await designToCode({
+          input: input,
+          build_config: build_config,
+          framework: framework_config,
+          asset_config: {
+            skip_asset_replacement: false,
+            asset_repository: MainImageRepository.instance,
+            custom_asset_replacement: {
+              type: "static",
+              resource: placeholderimg,
+            },
+          },
+        });
+
+        respond(result);
+      } catch (error) {
+        respond({ error });
+      }
+
+      break;
+    }
+  }
+});
diff --git a/editor/scaffolds/preview/index.tsx b/editor/scaffolds/preview/index.tsx
deleted file mode 100644
index cb8a51a8..00000000
--- a/editor/scaffolds/preview/index.tsx
+++ /dev/null
@@ -1,347 +0,0 @@
-import React, { useEffect, useState } from "react";
-import { preview_presets } from "@grida/builder-config-preset";
-import { designToCode, Result } from "@designto/code";
-import { config } from "@designto/config";
-import { MainImageRepository } from "@design-sdk/core/assets-repository";
-import type { ReflectSceneNode } from "@design-sdk/figma-node";
-import { VanillaRunner } from "components/app-runner/vanilla-app-runner";
-import { colorFromFills } from "@design-sdk/core/utils/colors";
-import type { FrameOptimizationFactors } from "@code-editor/canvas/frame";
-import { remote } from "@design-sdk/figma";
-
-const DEV_ONLY_FIGMA_PAT =
-  process.env.NEXT_PUBLIC_DEVELOPER_FIGMA_PERSONAL_ACCESS_TOKEN;
-
-const placeholderimg =
-  "https://bridged-service-static.s3.us-west-1.amazonaws.com/placeholder-images/image-placeholder-bw-tile-100.png";
-
-const build_config: config.BuildConfiguration = {
-  ...config.default_build_configuration,
-  disable_components: true,
-  disable_detection: true,
-  disable_flags_support: true,
-};
-
-const framework_config: config.VanillaPreviewFrameworkConfig = {
-  ...preview_presets.default,
-  additional_css_declaration: {
-    declarations: [
-      {
-        key: {
-          name: "body",
-          selector: "tag",
-        },
-        style: {
-          contain: "layout style paint",
-        },
-      },
-    ],
-  },
-};
-
-type TResultCache = Result & { __image: boolean };
-const cache = {
-  set: (key: string, value: TResultCache) => {
-    sessionStorage.setItem(key, JSON.stringify(value));
-  },
-  get: (key: string): TResultCache => {
-    const value = sessionStorage.getItem(key);
-    return value ? JSON.parse(value) : null;
-  },
-};
-
-const cachekey = (target: { filekey; id }) =>
-  target ? `${target.filekey}-${target.id}-${new Date().getMinutes()}` : null;
-
-const blurred_bg_fill = (target: ReflectSceneNode) => {
-  const __bg = colorFromFills(target.fills);
-  const bg_color_str = __bg ? "#" + __bg.hex : "transparent";
-  return bg_color_str;
-};
-
-type VanillaPreviewProps = {
-  target: ReflectSceneNode & {
-    filekey: string;
-  };
-} & FrameOptimizationFactors;
-
-export function D2CVanillaPreview({
-  target,
-  isZooming,
-  isPanning,
-}: VanillaPreviewProps) {
-  const [preview, setPreview] = useState<Result>();
-  const key = cachekey(target);
-
-  const on_preview_result = (result: Result, __image: boolean) => {
-    if (preview) {
-      if (preview.code === result.code) {
-        return;
-      }
-    }
-    setPreview(result);
-    cache.set(target.filekey, { ...result, __image });
-  };
-
-  const hide_preview = isZooming || isPanning;
-
-  useEffect(() => {
-    if (hide_preview) {
-      // don't make preview if zooming.
-      return;
-    }
-
-    if (preview) {
-      return;
-    }
-
-    const d2c_firstload = () => {
-      return designToCode({
-        input: _input,
-        build_config: build_config,
-        framework: framework_config,
-        asset_config: {
-          skip_asset_replacement: false,
-          asset_repository: MainImageRepository.instance,
-          custom_asset_replacement: {
-            type: "static",
-            resource: placeholderimg,
-          },
-        },
-      });
-    };
-
-    const d2c_imageload = () => {
-      if (!MainImageRepository.instance.empty) {
-        designToCode({
-          input: _input,
-          build_config: build_config,
-          framework: framework_config,
-          asset_config: { asset_repository: MainImageRepository.instance },
-        })
-          .then((r) => {
-            on_preview_result(r, true);
-          })
-          .catch((e) => {
-            console.error(
-              "error while making preview with image repo provided.",
-              e
-            );
-          });
-      }
-    };
-
-    const _input = target
-      ? {
-          id: target.id,
-          name: target.name,
-          entry: target,
-        }
-      : null;
-
-    const cached = cache.get(key);
-    if (cached) {
-      setPreview(cached);
-      if (cached.__image) {
-        return;
-      }
-      if (_input) {
-        d2c_imageload();
-      }
-    } else {
-      if (_input) {
-        d2c_firstload()
-          .then((r) => {
-            on_preview_result(r, false);
-            // if the result contains a image and needs to be fetched,
-            if (r.code.raw.includes(placeholderimg)) {
-              // TODO: we don't yet have other way to know if image is used, other than checking if placeholder image is used. - this needs to be updated in d2c module to include used images meta in the result.
-              d2c_imageload();
-            }
-          })
-          .catch(console.error);
-      }
-    }
-  }, [target?.id, isZooming, isPanning]);
-
-  const bg_color_str = blurred_bg_fill(target);
-
-  return (
-    <PreviewContent
-      id={target.id}
-      name={target.name}
-      source={preview?.scaffold?.raw}
-      width={target.width}
-      height={target.height}
-      backgroundColor={
-        !preview && bg_color_str // clear bg after preview is rendered.
-      }
-    />
-  );
-}
-
-import { createWorkerQueue } from "@code-editor/webworker-services-core";
-import { useFigmaAccessToken } from "hooks/use-figma-access-token";
-
-export function WebWorkerD2CVanillaPreview({ target }: VanillaPreviewProps) {
-  const [preview, setPreview] = useState<Result>();
-  const bg_color_str = blurred_bg_fill(target);
-
-  const fat = useFigmaAccessToken();
-
-  useEffect(() => {
-    if (preview) {
-      return;
-    }
-
-    const { worker, terminate } = createWorkerQueue(
-      new Worker(new URL("./workers/vanilla.worker.js", import.meta.url))
-    );
-
-    const input = target
-      ? {
-          id: target.id,
-          name: target.name,
-          entry: target,
-        }
-      : null;
-
-    // TODO: this is not production ready
-    worker.postMessage({
-      input,
-      authentication: fat,
-      filekey: target.filekey,
-    });
-
-    worker.addEventListener("message", (e) => {
-      // console.log(target.id, e.data.id);
-      // if (((e.data as Result).id = target.id)) {
-      setPreview(e.data as Result);
-      // }
-    });
-
-    () => {
-      terminate();
-    };
-  }, [target?.id]);
-
-  return (
-    <PreviewContent
-      id={target.id}
-      name={target.name}
-      source={preview?.scaffold?.raw}
-      width={target.width}
-      height={target.height}
-      backgroundColor={
-        !preview && bg_color_str // clear bg after preview is rendered.
-      }
-    />
-  );
-}
-
-function PreviewContent({
-  width,
-  height,
-  backgroundColor,
-  id,
-  source,
-  name,
-}: {
-  width: number;
-  height: number;
-  backgroundColor: string;
-  id: string;
-  source: string;
-  name: string;
-}) {
-  return (
-    <div
-      style={{
-        width: width,
-        height: height,
-        borderRadius: 1,
-        backgroundColor: backgroundColor,
-        contain: "layout style paint",
-      }}
-    >
-      {source && (
-        <VanillaRunner
-          key={id}
-          style={{
-            borderRadius: 1,
-            contain: "layout style paint",
-          }}
-          source={source}
-          width="100%"
-          height="100%"
-          componentName={name}
-        />
-      )}
-    </div>
-  );
-}
-
-function FigmaFrameImageView({
-  filekey,
-  nodeid,
-  zoom,
-}: {
-  filekey: string;
-  nodeid: string;
-  zoom: number;
-}) {
-  // fetch image
-  const [image_1, setImage_1] = useState<string>();
-  const [image_s, setImage_s] = useState<string>();
-
-  useEffect(() => {
-    // fetch image from figma
-    // fetch smaller one first, then fatch the full scaled.
-    remote
-      .fetchNodeAsImage(
-        filekey,
-        { personalAccessToken: DEV_ONLY_FIGMA_PAT },
-        nodeid
-        // scale = 1
-      )
-      .then((r) => {
-        console.log("fetched image from figma", r);
-        setImage_1(r.__default);
-        setImage_s(r.__default);
-      });
-  }, [filekey, nodeid]);
-
-  let imgscale: 1 | 0.2 = 1;
-  if (zoom > 1) {
-    return null;
-  } else if (zoom <= 1 && zoom > 0.3) {
-    imgscale = 1;
-    // display 1 scaled image
-  } else {
-    // display 0.2 scaled image
-    imgscale = 0.2;
-  }
-
-  return (
-    <div
-      style={{
-        top: 0,
-        left: 0,
-        position: "fixed",
-        width: "100%",
-        height: "100%",
-      }}
-    >
-      <img
-        style={{
-          width: "100%",
-          height: "100%",
-          objectFit: "fill",
-          border: 0,
-        }}
-        src={imgscale === 1 ? image_1 : image_s}
-        alt=""
-      />
-    </div>
-  );
-}
diff --git a/editor/scaffolds/preview/workers/vanilla.worker.js b/editor/scaffolds/preview/workers/vanilla.worker.js
deleted file mode 100644
index 9ddfef0b..00000000
--- a/editor/scaffolds/preview/workers/vanilla.worker.js
+++ /dev/null
@@ -1,78 +0,0 @@
-import { designToCode } from "@designto/code";
-import {
-  ImageRepository,
-  MainImageRepository,
-} from "@design-sdk/core/assets-repository";
-import { RemoteImageRepositories } from "@design-sdk/figma-remote/lib/asset-repository/image-repository";
-import { config } from "@designto/config";
-import { preview_presets } from "@grida/builder-config-preset";
-
-const placeholderimg =
-  "https://bridged-service-static.s3.us-west-1.amazonaws.com/placeholder-images/image-placeholder-bw-tile-100.png";
-
-// : config.BuildConfiguration
-const build_config = {
-  ...config.default_build_configuration,
-  disable_components: true,
-  disable_detection: true,
-  disable_flags_support: true,
-};
-
-// : config.VanillaPreviewFrameworkConfig
-const framework_config = {
-  ...preview_presets.default,
-  additional_css_declaration: {
-    declarations: [
-      {
-        key: {
-          name: "body",
-          selector: "tag",
-        },
-        style: {
-          contain: "layout style paint",
-        },
-      },
-    ],
-  },
-};
-
-addEventListener("message", async (event) => {
-  function respond(data) {
-    setTimeout(() => {
-      postMessage({ type: "response", ...data });
-    }, 0);
-  }
-
-  try {
-    const { input, authentication, filekey } = event.data;
-
-    MainImageRepository.instance = new RemoteImageRepositories(filekey, {
-      authentication: authentication,
-    });
-
-    MainImageRepository.instance.register(
-      new ImageRepository(
-        "fill-later-assets",
-        "grida://assets-reservation/images/"
-      )
-    );
-
-    const result = await designToCode({
-      input: input,
-      build_config: build_config,
-      framework: framework_config,
-      asset_config: {
-        skip_asset_replacement: false,
-        asset_repository: MainImageRepository.instance,
-        custom_asset_replacement: {
-          type: "static",
-          resource: placeholderimg,
-        },
-      },
-    });
-
-    respond(result);
-  } catch (error) {
-    respond({ error });
-  }
-});

From da552cbc07e9cddb8c5e92250542ce6efbb30484 Mon Sep 17 00:00:00 2001
From: "UZU, J" <woojoo@softmarshmallow.com>
Date: Fri, 22 Apr 2022 12:56:15 +0900
Subject: [PATCH 17/30] add wwpreview provider

---
 editor/scaffolds/canvas/canvas.tsx            | 128 +++++++++---------
 .../canvas-preview-worker-messenger.ts        |   2 +-
 .../workers/canvas-preview.worker.js          |   5 +
 3 files changed, 73 insertions(+), 62 deletions(-)

diff --git a/editor/scaffolds/canvas/canvas.tsx b/editor/scaffolds/canvas/canvas.tsx
index d6bd4b90..5202406f 100644
--- a/editor/scaffolds/canvas/canvas.tsx
+++ b/editor/scaffolds/canvas/canvas.tsx
@@ -12,6 +12,7 @@ import { FrameTitleRenderer } from "./render/frame-title";
 import { IsolateModeCanvas } from "./isolate-mode";
 import { Dialog } from "@material-ui/core";
 import { FullScreenPreview } from "scaffolds/preview-full-screen";
+import { EditorCanvasPreviewProvider } from "scaffolds/preview-canvas/editor-canvas-preview-provider";
 
 /**
  * Statefull canvas segment that contains canvas as a child, with state-data connected.
@@ -103,70 +104,75 @@ export function VisualContentArea() {
               onEnterFullscreen={startFullscreenPreviewMode}
             />
           )}
-          <div
-            style={{
-              display: canvasMode !== "free" && "none",
-            }}
-          >
-            <Canvas
-              key={selectedPage}
-              viewbound={[
-                canvasBounds.left,
-                canvasBounds.top,
-                canvasBounds.bottom,
-                canvasBounds.right,
-              ]}
-              filekey={state.design.key}
-              pageid={selectedPage}
-              backgroundColor={_bg}
-              selectedNodes={selectedNodes}
-              highlightedLayer={highlightedLayer}
-              onSelectNode={(...nodes) => {
-                dispatch({ type: "select-node", node: nodes.map((n) => n.id) });
+          <EditorCanvasPreviewProvider>
+            <div
+              style={{
+                display: canvasMode !== "free" && "none",
               }}
-              // onMoveNodeEnd={([x, y], ...nodes) => {
-              //   dispatch({
-              //     type: "node-transform-translate",
-              //     node: nodes,
-              //     translate: [x, y],
-              //   });
-              // }}
-              // onMoveNode={() => {}}
-              onClearSelection={() => {
-                dispatch({ type: "select-node", node: null });
-              }}
-              nodes={thisPageNodes}
-              // initialTransform={ } // TODO: if the initial selection is provided from first load, from the query param, we have to focus to fit that node.
-              renderItem={(p) => {
-                return (
-                  <WebWorkerD2CVanillaPreview
-                    key={p.node.id}
-                    target={p.node}
+            >
+              <Canvas
+                key={selectedPage}
+                viewbound={[
+                  canvasBounds.left,
+                  canvasBounds.top,
+                  canvasBounds.bottom,
+                  canvasBounds.right,
+                ]}
+                filekey={state.design.key}
+                pageid={selectedPage}
+                backgroundColor={_bg}
+                selectedNodes={selectedNodes}
+                highlightedLayer={highlightedLayer}
+                onSelectNode={(...nodes) => {
+                  dispatch({
+                    type: "select-node",
+                    node: nodes.map((n) => n.id),
+                  });
+                }}
+                // onMoveNodeEnd={([x, y], ...nodes) => {
+                //   dispatch({
+                //     type: "node-transform-translate",
+                //     node: nodes,
+                //     translate: [x, y],
+                //   });
+                // }}
+                // onMoveNode={() => {}}
+                onClearSelection={() => {
+                  dispatch({ type: "select-node", node: null });
+                }}
+                nodes={thisPageNodes}
+                // initialTransform={ } // TODO: if the initial selection is provided from first load, from the query param, we have to focus to fit that node.
+                renderItem={(p) => {
+                  return (
+                    <WebWorkerD2CVanillaPreview
+                      key={p.node.id}
+                      target={p.node}
+                      {...p}
+                    />
+                    // <D2CVanillaPreview key={p.node.id} target={p.node} {...p} />
+                  );
+                }}
+                readonly={false}
+                config={{
+                  can_highlight_selected_layer: true,
+                  marquee: {
+                    disabled: false,
+                  },
+                  grouping: {
+                    disabled: false,
+                  },
+                }}
+                renderFrameTitle={(p) => (
+                  <FrameTitleRenderer
+                    key={p.id}
                     {...p}
+                    runnable={selectedNodes.length === 1}
+                    onRunClick={startIsolatedViewMode}
                   />
-                  // <D2CVanillaPreview key={p.node.id} target={p.node} {...p} />
-                );
-              }}
-              readonly={false}
-              config={{
-                can_highlight_selected_layer: true,
-                marquee: {
-                  disabled: false,
-                },
-                grouping: {
-                  disabled: false,
-                },
-              }}
-              renderFrameTitle={(p) => (
-                <FrameTitleRenderer
-                  key={p.id}
-                  {...p}
-                  runnable={selectedNodes.length === 1}
-                  onRunClick={startIsolatedViewMode}
-                />
-              )}
-            />
-          </div>
+                )}
+              />
+            </div>
+          </EditorCanvasPreviewProvider>
         </>
       )}
     </CanvasContainer>
diff --git a/editor/scaffolds/preview-canvas/canvas-preview-worker-messenger.ts b/editor/scaffolds/preview-canvas/canvas-preview-worker-messenger.ts
index 48e4de2e..96f6a6e3 100644
--- a/editor/scaffolds/preview-canvas/canvas-preview-worker-messenger.ts
+++ b/editor/scaffolds/preview-canvas/canvas-preview-worker-messenger.ts
@@ -6,7 +6,7 @@ export function initialize(filekey: string, authentication) {
   // initialize the worker and set the preferences.
   if (!previewworker) {
     const { worker } = createWorkerQueue(
-      new Worker(new URL("./workers/vanilla.worker.js", import.meta.url))
+      new Worker(new URL("./workers/canvas-preview.worker.js", import.meta.url))
     );
 
     previewworker = worker;
diff --git a/editor/scaffolds/preview-canvas/workers/canvas-preview.worker.js b/editor/scaffolds/preview-canvas/workers/canvas-preview.worker.js
index 7022a7ba..aec8702d 100644
--- a/editor/scaffolds/preview-canvas/workers/canvas-preview.worker.js
+++ b/editor/scaffolds/preview-canvas/workers/canvas-preview.worker.js
@@ -37,6 +37,7 @@ const framework_config = {
 };
 
 function initialize({ filekey, authentication }) {
+  console.info("initializing.. wwpreview");
   // ------- setup image repo with auth + filekey -------
   MainImageRepository.instance = new RemoteImageRepositories(filekey, {
     authentication: authentication,
@@ -57,6 +58,9 @@ function initialize({ filekey, authentication }) {
   // 2. format the data to reflect
   // 3. set the data status as 'ready'
   // (the below requests can be operated after when this processes are complete)
+  // indexedDB.open("").onsuccess = (event) => {
+  //   //
+  // };
 }
 
 addEventListener("message", async (event) => {
@@ -78,6 +82,7 @@ addEventListener("message", async (event) => {
       try {
         const { node } = event.data;
 
+        throw "not ready";
         const result = await designToCode({
           input: input,
           build_config: build_config,

From 0b2119728d22919e2d120ba78b1b6ab06797ddde Mon Sep 17 00:00:00 2001
From: "UZU, J" <woojoo@softmarshmallow.com>
Date: Fri, 22 Apr 2022 13:01:06 +0900
Subject: [PATCH 18/30] match interface

---
 .../code-editor/monaco-utils/register-preset-types.ts           | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/editor/components/code-editor/monaco-utils/register-preset-types.ts b/editor/components/code-editor/monaco-utils/register-preset-types.ts
index e9c61041..3e29f689 100644
--- a/editor/components/code-editor/monaco-utils/register-preset-types.ts
+++ b/editor/components/code-editor/monaco-utils/register-preset-types.ts
@@ -17,5 +17,5 @@ const react_preset_dependencies = [
  */
 export function registerPresetTypes() {
   // load the react presets
-  loadTypes(react_preset_dependencies);
+  return loadTypes(react_preset_dependencies);
 }

From 40476ee3a81b764e375990ffa4d6a44be6a20226 Mon Sep 17 00:00:00 2001
From: "UZU, J" <woojoo@softmarshmallow.com>
Date: Fri, 22 Apr 2022 13:50:21 +0900
Subject: [PATCH 19/30] prevent same selection triggering state update

---
 editor/core/reducers/editor-reducer.ts | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/editor/core/reducers/editor-reducer.ts b/editor/core/reducers/editor-reducer.ts
index d2fcc6fe..0f75afef 100644
--- a/editor/core/reducers/editor-reducer.ts
+++ b/editor/core/reducers/editor-reducer.ts
@@ -31,6 +31,15 @@ export function editorReducer(state: EditorState, action: Action): EditorState {
 
       const current_node = state.selectedNodes;
 
+      if (
+        ids.length <= 1 &&
+        current_node.length <= 1 &&
+        ids[0] === current_node[0]
+      ) {
+        // same selection (no selection or same 1 selection)
+        return produce(state, (draft) => {});
+      }
+
       if (ids.length > 1 && ids.length === current_node.length) {
         // the selection event is always triggered by user, which means selecting same amount of nodes (greater thatn 1, and having a different node array is impossible.)
         return produce(state, (draft) => {});

From bbfc2aa569a3ddbd8051ac39688df46d360247e6 Mon Sep 17 00:00:00 2001
From: "UZU, J" <woojoo@softmarshmallow.com>
Date: Fri, 22 Apr 2022 15:02:59 +0900
Subject: [PATCH 20/30] made figma file store as a package

---
 .../editor-store-figma-file}/figma-file-store.ts             | 2 +-
 .../editor-store-figma-file}/index.ts                        | 0
 editor-packages/editor-store-figma-file/package.json         | 4 ++++
 editor/pages/files/index.tsx                                 | 2 +-
 editor/repository/figma-design-repository/index.ts           | 5 +----
 editor/repository/workspace-repository/index.ts              | 5 +----
 editor/store/index.ts                                        | 1 +
 7 files changed, 9 insertions(+), 10 deletions(-)
 rename {editor/store/fimga-file-store => editor-packages/editor-store-figma-file}/figma-file-store.ts (97%)
 rename {editor/store/fimga-file-store => editor-packages/editor-store-figma-file}/index.ts (100%)
 create mode 100644 editor-packages/editor-store-figma-file/package.json

diff --git a/editor/store/fimga-file-store/figma-file-store.ts b/editor-packages/editor-store-figma-file/figma-file-store.ts
similarity index 97%
rename from editor/store/fimga-file-store/figma-file-store.ts
rename to editor-packages/editor-store-figma-file/figma-file-store.ts
index f4dbbca2..ce603552 100644
--- a/editor/store/fimga-file-store/figma-file-store.ts
+++ b/editor-packages/editor-store-figma-file/figma-file-store.ts
@@ -1,5 +1,5 @@
 import { openDB, IDBPDatabase } from "idb";
-import { FileResponse } from "@design-sdk/figma-remote-api";
+import type { FileResponse } from "@design-sdk/figma-remote-api";
 
 // #region global db initialization
 const __db_pref = { name: "fimga-file-store", version: 1 };
diff --git a/editor/store/fimga-file-store/index.ts b/editor-packages/editor-store-figma-file/index.ts
similarity index 100%
rename from editor/store/fimga-file-store/index.ts
rename to editor-packages/editor-store-figma-file/index.ts
diff --git a/editor-packages/editor-store-figma-file/package.json b/editor-packages/editor-store-figma-file/package.json
new file mode 100644
index 00000000..299c259c
--- /dev/null
+++ b/editor-packages/editor-store-figma-file/package.json
@@ -0,0 +1,4 @@
+{
+  "name": "@editor/figma-file-store",
+  "version": "0.0.0"
+}
\ No newline at end of file
diff --git a/editor/pages/files/index.tsx b/editor/pages/files/index.tsx
index 8ec7c948..4ccae5ee 100644
--- a/editor/pages/files/index.tsx
+++ b/editor/pages/files/index.tsx
@@ -8,7 +8,7 @@ import {
   HomeSidebar,
 } from "components/home";
 import { WorkspaceRepository } from "repository";
-import { FileResponseRecord } from "store/fimga-file-store/figma-file-store";
+import { FileResponseRecord } from "@editor/figma-file-store";
 import { colors } from "theme";
 
 export default function FilesPage() {
diff --git a/editor/repository/figma-design-repository/index.ts b/editor/repository/figma-design-repository/index.ts
index 27695a6c..ead1055a 100644
--- a/editor/repository/figma-design-repository/index.ts
+++ b/editor/repository/figma-design-repository/index.ts
@@ -1,9 +1,6 @@
 import { fetch } from "@design-sdk/figma-remote";
 import { FileResponse } from "@design-sdk/figma-remote-types";
-import {
-  FigmaFileStore,
-  FileResponseRecord,
-} from "store/fimga-file-store/figma-file-store";
+import { FigmaFileStore, FileResponseRecord } from "@editor/figma-file-store";
 
 export type TFetchFileForApp = (
   | fetch.FetchFileGeneratorReturnType
diff --git a/editor/repository/workspace-repository/index.ts b/editor/repository/workspace-repository/index.ts
index c03388d5..98bb2503 100644
--- a/editor/repository/workspace-repository/index.ts
+++ b/editor/repository/workspace-repository/index.ts
@@ -1,7 +1,4 @@
-import {
-  FigmaFilesStore,
-  FileResponseRecord,
-} from "store/fimga-file-store/figma-file-store";
+import { FigmaFilesStore, FileResponseRecord } from "@editor/figma-file-store";
 
 export type LastUsedFileDisplay = FileResponseRecord & { type: "file" } & {
   lastUsed: Date;
diff --git a/editor/store/index.ts b/editor/store/index.ts
index c5235dc3..9b865148 100644
--- a/editor/store/index.ts
+++ b/editor/store/index.ts
@@ -1 +1,2 @@
+export * from "@editor/figma-file-store";
 export * from "./remote-design-session-cache-store";

From 7006705a98640403cfa046a5d29d3275409b2b13 Mon Sep 17 00:00:00 2001
From: "UZU, J" <woojoo@softmarshmallow.com>
Date: Fri, 22 Apr 2022 15:03:59 +0900
Subject: [PATCH 21/30] wip: wwpreview without queue, w 50ms delay

---
 .../editor-canvas/canvas/canvas.tsx           |   6 +-
 editor/scaffolds/canvas/canvas.tsx            | 131 +++++++++---------
 editor/scaffolds/editor/_providers.tsx        |   7 +-
 .../canvas-preview-worker-messenger.ts        |  46 ++++--
 .../editor-canvas-preview-provider.tsx        |  14 +-
 editor/scaffolds/preview-canvas/prop-type.ts  |   1 +
 .../vanilla-preview-webworker.tsx             |  16 ++-
 .../workers/canvas-preview.worker.js          |  81 +++++++++--
 8 files changed, 199 insertions(+), 103 deletions(-)

diff --git a/editor-packages/editor-canvas/canvas/canvas.tsx b/editor-packages/editor-canvas/canvas/canvas.tsx
index a947caf2..75fd3150 100644
--- a/editor-packages/editor-canvas/canvas/canvas.tsx
+++ b/editor-packages/editor-canvas/canvas/canvas.tsx
@@ -50,7 +50,7 @@ interface CanvasState {
 type CanvasCustomRenderers = HudCustomRenderers & {
   renderItem: (
     p: {
-      node: ReflectSceneNode & { filekey: string };
+      node: ReflectSceneNode & { filekey: string; page: string };
     } & FrameOptimizationFactors
   ) => React.ReactNode;
 };
@@ -383,13 +383,15 @@ export function Canvas({
   const items = useMemo(() => {
     return nodes?.map((node) => {
       node["filekey"] = filekey;
+      node["page"] = pageid;
+
       return (
         <LazyFrame key={node.id} xy={[node.x, node.y]} size={node}>
           {/* 👇 dev only (for performance tracking) 👇 */}
           {/* <div style={{ width: "100%", height: "100%", background: "grey" }} /> */}
           {/* 👆 ----------------------------------- 👆 */}
           {renderItem({
-            node: node as ReflectSceneNode & { filekey: string },
+            node: node as ReflectSceneNode & { filekey: string; page: string },
             zoom, // ? use scaled_zoom ?
             inViewport: true, // TODO:
             isZooming: isZooming,
diff --git a/editor/scaffolds/canvas/canvas.tsx b/editor/scaffolds/canvas/canvas.tsx
index 796f5b90..63a7b848 100644
--- a/editor/scaffolds/canvas/canvas.tsx
+++ b/editor/scaffolds/canvas/canvas.tsx
@@ -12,7 +12,6 @@ import { FrameTitleRenderer } from "./render/frame-title";
 import { IsolateModeCanvas } from "./isolate-mode";
 import { Dialog } from "@mui/material";
 import { FullScreenPreview } from "scaffolds/preview-full-screen";
-import { EditorCanvasPreviewProvider } from "scaffolds/preview-canvas/editor-canvas-preview-provider";
 
 /**
  * Statefull canvas segment that contains canvas as a child, with state-data connected.
@@ -104,75 +103,73 @@ export function VisualContentArea() {
               onEnterFullscreen={startFullscreenPreviewMode}
             />
           )}
-          <EditorCanvasPreviewProvider>
-            <div
-              style={{
-                display: canvasMode !== "free" && "none",
+          <div
+            style={{
+              display: canvasMode !== "free" && "none",
+            }}
+          >
+            <Canvas
+              key={selectedPage}
+              viewbound={[
+                canvasBounds.left,
+                canvasBounds.top,
+                canvasBounds.bottom,
+                canvasBounds.right,
+              ]}
+              filekey={state.design.key}
+              pageid={selectedPage}
+              backgroundColor={_bg}
+              selectedNodes={selectedNodes}
+              highlightedLayer={highlightedLayer}
+              onSelectNode={(...nodes) => {
+                dispatch({
+                  type: "select-node",
+                  node: nodes.map((n) => n.id),
+                });
               }}
-            >
-              <Canvas
-                key={selectedPage}
-                viewbound={[
-                  canvasBounds.left,
-                  canvasBounds.top,
-                  canvasBounds.bottom,
-                  canvasBounds.right,
-                ]}
-                filekey={state.design.key}
-                pageid={selectedPage}
-                backgroundColor={_bg}
-                selectedNodes={selectedNodes}
-                highlightedLayer={highlightedLayer}
-                onSelectNode={(...nodes) => {
-                  dispatch({
-                    type: "select-node",
-                    node: nodes.map((n) => n.id),
-                  });
-                }}
-                // onMoveNodeEnd={([x, y], ...nodes) => {
-                //   dispatch({
-                //     type: "node-transform-translate",
-                //     node: nodes,
-                //     translate: [x, y],
-                //   });
-                // }}
-                // onMoveNode={() => {}}
-                onClearSelection={() => {
-                  dispatch({ type: "select-node", node: null });
-                }}
-                nodes={thisPageNodes}
-                // initialTransform={ } // TODO: if the initial selection is provided from first load, from the query param, we have to focus to fit that node.
-                renderItem={(p) => {
-                  return (
-                    <WebWorkerD2CVanillaPreview
-                      key={p.node.id}
-                      target={p.node}
-                      {...p}
-                    />
-                    // <D2CVanillaPreview key={p.node.id} target={p.node} {...p} />
-                  );
-                }}
-                readonly={false}
-                config={{
-                  can_highlight_selected_layer: true,
-                  marquee: {
-                    disabled: false,
-                  },
-                  grouping: {
-                    disabled: false,
-                  },
-                }}
-                renderFrameTitle={(p) => (
-                  <FrameTitleRenderer
-                    key={p.id}
+              // onMoveNodeEnd={([x, y], ...nodes) => {
+              //   dispatch({
+              //     type: "node-transform-translate",
+              //     node: nodes,
+              //     translate: [x, y],
+              //   });
+              // }}
+              // onMoveNode={() => {}}
+              onClearSelection={() => {
+                dispatch({ type: "select-node", node: [] });
+              }}
+              nodes={thisPageNodes}
+              // initialTransform={ } // TODO: if the initial selection is provided from first load, from the query param, we have to focus to fit that node.
+              renderItem={(p) => {
+                return (
+                  <WebWorkerD2CVanillaPreview
+                    key={p.node.id}
+                    target={p.node}
                     {...p}
-                    runnable={selectedNodes.length === 1}
-                    onRunClick={startIsolatedViewMode}
                   />
-                )}
-              />
-            </div>
-          </EditorCanvasPreviewProvider>
+                  // <D2CVanillaPreview key={p.node.id} target={p.node} {...p} />
+                );
+              }}
+              readonly={false}
+              config={{
+                can_highlight_selected_layer: true,
+                marquee: {
+                  disabled: false,
+                },
+                grouping: {
+                  disabled: false,
+                },
+              }}
+              renderFrameTitle={(p) => (
+                <FrameTitleRenderer
+                  key={p.id}
+                  {...p}
+                  runnable={selectedNodes.length === 1}
+                  onRunClick={startIsolatedViewMode}
+                />
+              )}
+            />
+          </div>
         </>
       )}
     </CanvasContainer>
diff --git a/editor/scaffolds/editor/_providers.tsx b/editor/scaffolds/editor/_providers.tsx
index 7ad76825..1fa593c0 100644
--- a/editor/scaffolds/editor/_providers.tsx
+++ b/editor/scaffolds/editor/_providers.tsx
@@ -2,12 +2,17 @@ import React from "react";
 import { useHotkeys } from "react-hotkeys-hook";
 import { EditorImageRepositoryProvider } from "./editor-image-repository-provider";
 import { EditorPreviewDataProvider } from "./editor-preview-provider";
+import { EditorCanvasPreviewProvider } from "scaffolds/preview-canvas/editor-canvas-preview-provider";
 
 export function EditorDefaultProviders(props: { children: React.ReactNode }) {
   return (
     <ShortcutsProvider>
       <EditorImageRepositoryProvider>
-        <EditorPreviewDataProvider>{props.children}</EditorPreviewDataProvider>
+        <EditorCanvasPreviewProvider>
+          <EditorPreviewDataProvider>
+            {props.children}
+          </EditorPreviewDataProvider>
+        </EditorCanvasPreviewProvider>
       </EditorImageRepositoryProvider>
     </ShortcutsProvider>
   );
diff --git a/editor/scaffolds/preview-canvas/canvas-preview-worker-messenger.ts b/editor/scaffolds/preview-canvas/canvas-preview-worker-messenger.ts
index 96f6a6e3..dfc98577 100644
--- a/editor/scaffolds/preview-canvas/canvas-preview-worker-messenger.ts
+++ b/editor/scaffolds/preview-canvas/canvas-preview-worker-messenger.ts
@@ -2,7 +2,10 @@ import { createWorkerQueue } from "@code-editor/webworker-services-core";
 import type { Result } from "@designto/code";
 
 let previewworker: Worker;
-export function initialize(filekey: string, authentication) {
+export function initialize(
+  { filekey, authentication }: { filekey: string; authentication },
+  onReady: () => void
+) {
   // initialize the worker and set the preferences.
   if (!previewworker) {
     const { worker } = createWorkerQueue(
@@ -13,11 +16,17 @@ export function initialize(filekey: string, authentication) {
   }
 
   previewworker.postMessage({
-    $type: "init",
+    $type: "initialize",
     filekey,
     authentication,
   });
 
+  previewworker.addEventListener("message", (e) => {
+    if (e.data.$type === "data-readt") {
+      onReady();
+    }
+  });
+
   return () => {
     if (previewworker) {
       previewworker.terminate();
@@ -26,24 +35,33 @@ export function initialize(filekey: string, authentication) {
 }
 
 export function preview(
-  node: string,
+  { target, page }: { target: string; page: string },
   onResult: (result: Result) => void,
   onError?: (error: Error) => void
 ) {
   previewworker.postMessage({
     $type: "preview",
-    node,
+    page,
+    target,
   });
 
-  previewworker.addEventListener("message", (e) => {
-    // TODO: add id matcher (?)
-    switch (e.data.$type) {
-      case "result":
-        onResult(e.data);
-        break;
-      case "error":
-        onError(new Error(e.data.message));
-        break;
+  const handler = (e) => {
+    const id = e.data.id;
+    if (target === id) {
+      switch (e.data.$type) {
+        case "result":
+          onResult(e.data);
+          break;
+        case "error":
+          onError(new Error(e.data.message));
+          break;
+      }
     }
-  });
+  };
+
+  previewworker.addEventListener("message", handler);
+
+  return () => {
+    previewworker.removeEventListener("message", handler);
+  };
 }
diff --git a/editor/scaffolds/preview-canvas/editor-canvas-preview-provider.tsx b/editor/scaffolds/preview-canvas/editor-canvas-preview-provider.tsx
index 8b3194f7..7c49031e 100644
--- a/editor/scaffolds/preview-canvas/editor-canvas-preview-provider.tsx
+++ b/editor/scaffolds/preview-canvas/editor-canvas-preview-provider.tsx
@@ -1,7 +1,7 @@
 import React, { useEffect } from "react";
 import { useFigmaAccessToken } from "hooks/use-figma-access-token";
-import { initialize } from "scaffolds/preview-canvas/canvas-preview-worker-messenger";
 import { useEditorState } from "core/states";
+import { initialize } from "./canvas-preview-worker-messenger";
 
 export function EditorCanvasPreviewProvider({
   children,
@@ -13,12 +13,18 @@ export function EditorCanvasPreviewProvider({
 
   useEffect(() => {
     if (
-      state.design.key &&
+      state.design?.key &&
       (fat.personalAccessToken || !fat.accessToken.loading)
     ) {
-      initialize(state.design.key, fat);
+      const authentication = {
+        personalAccessToken: fat.personalAccessToken,
+        accessToken: fat.accessToken.token,
+      };
+      initialize({ filekey: state.design.key, authentication }, () => {
+        //
+      });
     }
-  }, [fat, state.design.key]);
+  }, [fat.personalAccessToken, fat.accessToken.token, state.design?.key]);
 
   return <>{children}</>;
 }
diff --git a/editor/scaffolds/preview-canvas/prop-type.ts b/editor/scaffolds/preview-canvas/prop-type.ts
index 7e856a35..0d39baa7 100644
--- a/editor/scaffolds/preview-canvas/prop-type.ts
+++ b/editor/scaffolds/preview-canvas/prop-type.ts
@@ -3,6 +3,7 @@ import type { FrameOptimizationFactors } from "@code-editor/canvas/frame";
 
 export type VanillaPreviewProps = {
   target: ReflectSceneNode & {
+    page: string;
     filekey: string;
   };
 } & FrameOptimizationFactors;
diff --git a/editor/scaffolds/preview-canvas/vanilla-preview-webworker.tsx b/editor/scaffolds/preview-canvas/vanilla-preview-webworker.tsx
index d0496fde..2cef7a22 100644
--- a/editor/scaffolds/preview-canvas/vanilla-preview-webworker.tsx
+++ b/editor/scaffolds/preview-canvas/vanilla-preview-webworker.tsx
@@ -15,7 +15,21 @@ export function WebWorkerD2CVanillaPreview({ target }: VanillaPreviewProps) {
       return;
     }
 
-    wwpreview(target.id, setPreview);
+    let dispose;
+
+    setTimeout(() => {
+      dispose = wwpreview(
+        {
+          page: target.page,
+          target: target.id,
+        },
+        setPreview
+      );
+    }, 50);
+
+    return () => {
+      dispose?.();
+    };
   }, [target?.id]);
 
   return (
diff --git a/editor/scaffolds/preview-canvas/workers/canvas-preview.worker.js b/editor/scaffolds/preview-canvas/workers/canvas-preview.worker.js
index aec8702d..2025cc75 100644
--- a/editor/scaffolds/preview-canvas/workers/canvas-preview.worker.js
+++ b/editor/scaffolds/preview-canvas/workers/canvas-preview.worker.js
@@ -6,6 +6,9 @@ import {
 import { RemoteImageRepositories } from "@design-sdk/figma-remote/lib/asset-repository/image-repository";
 import { config } from "@designto/config";
 import { preview_presets } from "@grida/builder-config-preset";
+import { FigmaFileStore } from "@editor/figma-file-store";
+import { convert } from "@design-sdk/figma-node-conversion";
+import { mapper } from "@design-sdk/figma-remote";
 
 const placeholderimg =
   "https://bridged-service-static.s3.us-west-1.amazonaws.com/placeholder-images/image-placeholder-bw-tile-100.png";
@@ -36,6 +39,9 @@ const framework_config = {
   },
 };
 
+let initialized = false;
+let pages = [];
+
 function initialize({ filekey, authentication }) {
   console.info("initializing.. wwpreview");
   // ------- setup image repo with auth + filekey -------
@@ -54,13 +60,31 @@ function initialize({ filekey, authentication }) {
   // setup indexed db connection for reading the file.
   // ⛔️ the assumbtion is that file does not change during the app is running.
 
+  const store = new FigmaFileStore(filekey);
   // 1. read the raw data from indexed db
-  // 2. format the data to reflect
-  // 3. set the data status as 'ready'
+  store.get().then((raw) => {
+    // 2. format the data to reflect
+    pages = pagesFrom(raw);
+
+    // 3. set the data status as 'ready'
+    initialized = true;
+    postMessage({ $type: "data-ready" });
+  });
   // (the below requests can be operated after when this processes are complete)
-  // indexedDB.open("").onsuccess = (event) => {
-  //   //
-  // };
+}
+
+function pagesFrom(file) {
+  return file.document.children.map((page) => ({
+    id: page.id,
+    name: page.name,
+    children: page["children"]?.map((child) => {
+      const _mapped = mapper.mapFigmaRemoteToFigma(child);
+      return convert.intoReflectNode(_mapped);
+    }),
+    flowStartingPoints: page.flowStartingPoints,
+    backgroundColor: page.backgroundColor,
+    type: "design",
+  }));
 }
 
 addEventListener("message", async (event) => {
@@ -70,21 +94,39 @@ addEventListener("message", async (event) => {
     }, 0);
   }
 
+  if (event.data.$type === "initialize") {
+    initialize({
+      filekey: event.data.filekey,
+      authentication: event.data.authentication,
+    });
+    return;
+  }
+
+  if (!initialized) {
+    return;
+  }
+
   switch (event.data.$type) {
     case "initialize": {
-      initialize({
-        filekey: event.data.filekey,
-        authentication: event.data.authentication,
-      });
-      return;
+      // unreachable
+      break;
     }
     case "preview": {
       try {
-        const { node } = event.data;
+        // requires all 2 data for faster query
+        const { page, target } = event.data;
+        const node = pages
+          .find((p) => p.id === page)
+          .children.find((c) => c.id === target);
+
+        const _input = {
+          id: node.id,
+          name: node.name,
+          entry: node,
+        };
 
-        throw "not ready";
         const result = await designToCode({
-          input: input,
+          input: _input,
           build_config: build_config,
           framework: framework_config,
           asset_config: {
@@ -98,8 +140,19 @@ addEventListener("message", async (event) => {
         });
 
         respond(result);
+
+        const result_w_img = await designToCode({
+          input: _input,
+          build_config: build_config,
+          framework: framework_config,
+          asset_config: {
+            asset_repository: MainImageRepository.instance,
+          },
+        });
+
+        respond(result_w_img);
       } catch (error) {
-        respond({ error });
+        // respond({ error });
       }
 
       break;

From 028c42dd0dc3d9ddfa9b9355009b256f8a2fe348 Mon Sep 17 00:00:00 2001
From: softmarshmallow <woojoo@softmarshmallow.com>
Date: Fri, 22 Apr 2022 15:09:52 +0900
Subject: [PATCH 22/30] extract constants

---
 .../editor-canvas/canvas/canvas.tsx           | 41 ++++++++++---------
 editor-packages/editor-canvas/k/index.ts      |  6 +++
 2 files changed, 28 insertions(+), 19 deletions(-)
 create mode 100644 editor-packages/editor-canvas/k/index.ts

diff --git a/editor-packages/editor-canvas/canvas/canvas.tsx b/editor-packages/editor-canvas/canvas/canvas.tsx
index a947caf2..3fe24331 100644
--- a/editor-packages/editor-canvas/canvas/canvas.tsx
+++ b/editor-packages/editor-canvas/canvas/canvas.tsx
@@ -22,12 +22,15 @@ import { LazyFrame } from "@code-editor/canvas/lazy-frame";
 import { HudCustomRenderers, HudSurface } from "../hud";
 import type { Box, XY, CanvasTransform, XYWH } from "../types";
 import type { FrameOptimizationFactors } from "../frame";
-const designq = utils.query;
+import { TransformDraftingStore } from "../drafting";
+import {
+  CANVAS_LAYER_HOVER_HIT_MARGIN,
+  CANVAS_INITIAL_XY,
+  CANVAS_INITIAL_SCALE,
+  CANVAS_MIN_ZOOM,
+} from "../k";
 
-const INITIAL_SCALE = 0.5;
-const INITIAL_XY: XY = [0, 0];
-const LAYER_HOVER_HIT_MARGIN = 3.5;
-const MIN_ZOOM = 0.02;
+const designq = utils.query;
 
 interface CanvasState {
   pageid: string;
@@ -122,11 +125,6 @@ export function Canvas({
   CanvasState & {
     config?: CanvsPreferences;
   }) {
-  const _canvas_state_store = useMemo(
-    () => new CanvasStateStore(filekey, pageid),
-    [filekey, pageid]
-  );
-
   useEffect(() => {
     if (transformIntitialized) {
       return;
@@ -162,6 +160,16 @@ export function Canvas({
   const [isMovingSelections, setIsMovingSelections] = useState(false);
   const [marquee, setMarquee] = useState<XYWH>(null);
 
+  const is_canvas_transforming = isPanning || isZooming;
+  const selected_nodes = selectedNodes
+    ?.map((id) => designq.find_node_by_id_under_inpage_nodes(id, nodes))
+    .filter(Boolean);
+
+  const _canvas_state_store = useMemo(
+    () => new CanvasStateStore(filekey, pageid),
+    [filekey, pageid]
+  );
+
   const cvtransform: CanvasTransform = {
     scale: zoom,
     xy: offset,
@@ -218,7 +226,7 @@ export function Canvas({
       tree: nodes,
       zoom: zoom,
       offset: nonscaled_offset,
-      margin: LAYER_HOVER_HIT_MARGIN,
+      margin: CANVAS_LAYER_HOVER_HIT_MARGIN,
       reverse: true,
       ignore: (n) => selectedNodes.includes(n.id),
     });
@@ -277,7 +285,7 @@ export function Canvas({
     // the origin point of the zooming point in x, y
     const [ox, oy]: XY = state.origin;
 
-    const newzoom = Math.max(zoom + zoomdelta, MIN_ZOOM);
+    const newzoom = Math.max(zoom + zoomdelta, CANVAS_MIN_ZOOM);
 
     // calculate the offset that should be applied with scale with css transform.
     const [newx, newy] = [
@@ -375,11 +383,6 @@ export function Canvas({
     }
   };
 
-  const is_canvas_transforming = isPanning || isZooming;
-  const selected_nodes = selectedNodes
-    ?.map((id) => designq.find_node_by_id_under_inpage_nodes(id, nodes))
-    .filter(Boolean);
-
   const items = useMemo(() => {
     return nodes?.map((node) => {
       node["filekey"] = filekey;
@@ -541,8 +544,8 @@ function auto_initial_transform(
   nodes: ReflectSceneNode[]
 ): CanvasTransform {
   const _default = {
-    scale: INITIAL_SCALE,
-    xy: INITIAL_XY,
+    scale: CANVAS_INITIAL_SCALE,
+    xy: CANVAS_INITIAL_XY,
   };
 
   if (!nodes || viewbound_not_measured(viewbound)) {
diff --git a/editor-packages/editor-canvas/k/index.ts b/editor-packages/editor-canvas/k/index.ts
new file mode 100644
index 00000000..45a7c562
--- /dev/null
+++ b/editor-packages/editor-canvas/k/index.ts
@@ -0,0 +1,6 @@
+import type { XY } from "../types";
+
+export const CANVAS_INITIAL_SCALE = 0.5;
+export const CANVAS_INITIAL_XY: XY = [0, 0];
+export const CANVAS_LAYER_HOVER_HIT_MARGIN = 3.5;
+export const CANVAS_MIN_ZOOM = 0.02;

From 7601d80e44b2c5940352783933712503cfc7decd Mon Sep 17 00:00:00 2001
From: "UZU, J" <woojoo@softmarshmallow.com>
Date: Fri, 22 Apr 2022 16:46:03 +0900
Subject: [PATCH 23/30] qf

---
 editor-packages/editor-canvas/canvas/canvas.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/editor-packages/editor-canvas/canvas/canvas.tsx b/editor-packages/editor-canvas/canvas/canvas.tsx
index a3f75adf..930e96c2 100644
--- a/editor-packages/editor-canvas/canvas/canvas.tsx
+++ b/editor-packages/editor-canvas/canvas/canvas.tsx
@@ -22,7 +22,7 @@ import { LazyFrame } from "@code-editor/canvas/lazy-frame";
 import { HudCustomRenderers, HudSurface } from "../hud";
 import type { Box, XY, CanvasTransform, XYWH } from "../types";
 import type { FrameOptimizationFactors } from "../frame";
-import { TransformDraftingStore } from "../drafting";
+// import { TransformDraftingStore } from "../drafting";
 import {
   CANVAS_LAYER_HOVER_HIT_MARGIN,
   CANVAS_INITIAL_XY,

From 03196c709fc6bc8a1e11a837240afd2098a41a44 Mon Sep 17 00:00:00 2001
From: softmarshmallow <woojoo@softmarshmallow.com>
Date: Mon, 25 Apr 2022 20:13:59 +0900
Subject: [PATCH 24/30] update drafting

---
 .../editor-canvas/core/drafting/index.ts      |  0
 .../core/drafting/transform-drafting.ts       | 41 -------------
 .../{core => }/drafting/README.md             |  0
 editor-packages/editor-canvas/drafting/_.ts   | 16 ++++++
 .../editor-canvas/drafting/index.ts           |  1 +
 .../drafting/transform-drafting.ts            | 57 +++++++++++++++++++
 6 files changed, 74 insertions(+), 41 deletions(-)
 delete mode 100644 editor-packages/editor-canvas/core/drafting/index.ts
 delete mode 100644 editor-packages/editor-canvas/core/drafting/transform-drafting.ts
 rename editor-packages/editor-canvas/{core => }/drafting/README.md (100%)
 create mode 100644 editor-packages/editor-canvas/drafting/_.ts
 create mode 100644 editor-packages/editor-canvas/drafting/index.ts
 create mode 100644 editor-packages/editor-canvas/drafting/transform-drafting.ts

diff --git a/editor-packages/editor-canvas/core/drafting/index.ts b/editor-packages/editor-canvas/core/drafting/index.ts
deleted file mode 100644
index e69de29b..00000000
diff --git a/editor-packages/editor-canvas/core/drafting/transform-drafting.ts b/editor-packages/editor-canvas/core/drafting/transform-drafting.ts
deleted file mode 100644
index f3e0204e..00000000
--- a/editor-packages/editor-canvas/core/drafting/transform-drafting.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-// move
-// resize
-
-// width
-// height
-// x
-// y
-// rotation
-
-abstract class DraftingStore<T> {
-  readonly store = new Map<string, T>();
-
-  abstract update(id: string, draft: T);
-
-  abstract get(id: string): T;
-}
-
-interface Transform {
-  x: number;
-  y: number;
-  width: number;
-  height: number;
-  rotation: number;
-}
-
-class TransformDraftingStore extends DraftingStore<Transform> {
-  constructor(draftingStore: DraftingStore<Transform>) {
-    super();
-  }
-
-  update(id: string, transform: Transform) {
-    this.store.set(id, transform);
-  }
-
-  get(id: string): Transform {
-    return this.store.get(id);
-  }
-}
-
-// const store = new TransformDraftingStore();
-// export function
diff --git a/editor-packages/editor-canvas/core/drafting/README.md b/editor-packages/editor-canvas/drafting/README.md
similarity index 100%
rename from editor-packages/editor-canvas/core/drafting/README.md
rename to editor-packages/editor-canvas/drafting/README.md
diff --git a/editor-packages/editor-canvas/drafting/_.ts b/editor-packages/editor-canvas/drafting/_.ts
new file mode 100644
index 00000000..d32c40a7
--- /dev/null
+++ b/editor-packages/editor-canvas/drafting/_.ts
@@ -0,0 +1,16 @@
+export abstract class DraftingStore<T> {
+  readonly store = new Map<string, T>();
+  lastUpdated: number;
+
+  constructor() {
+    this.lastUpdated = Date.now();
+  }
+
+  abstract update(id: string, draft: T);
+
+  abstract get(id: string): T;
+
+  updated() {
+    this.lastUpdated = Date.now();
+  }
+}
diff --git a/editor-packages/editor-canvas/drafting/index.ts b/editor-packages/editor-canvas/drafting/index.ts
new file mode 100644
index 00000000..ab76e45e
--- /dev/null
+++ b/editor-packages/editor-canvas/drafting/index.ts
@@ -0,0 +1 @@
+export * from "./transform-drafting";
diff --git a/editor-packages/editor-canvas/drafting/transform-drafting.ts b/editor-packages/editor-canvas/drafting/transform-drafting.ts
new file mode 100644
index 00000000..3521a27d
--- /dev/null
+++ b/editor-packages/editor-canvas/drafting/transform-drafting.ts
@@ -0,0 +1,57 @@
+import type { XY } from "../types";
+import { DraftingStore } from "./_";
+// move
+// resize
+
+// width
+// height
+// x
+// y
+// rotation
+
+interface Transform {
+  x: number;
+  y: number;
+  width: number;
+  height: number;
+  rotation: number;
+}
+
+export class TransformDraftingStore extends DraftingStore<Transform> {
+  constructor(transforrms: (Transform & { id: string })[]) {
+    super();
+
+    transforrms.forEach((transform) => {
+      this.store.set(transform.id, TransformDraftingStore.flat(transform));
+    });
+  }
+
+  static flat(t: Transform): Transform {
+    return {
+      x: t.x,
+      y: t.y,
+      width: t.width,
+      height: t.height,
+      rotation: t.rotation,
+    };
+  }
+
+  moveBy(delta: XY) {
+    Object.keys(this.store).forEach((id) => {
+      this.store[id].x += delta[0];
+      this.store[id].y += delta[1];
+    });
+    this.updated();
+  }
+
+  update(id: string, transform: Transform) {
+    this.store.set(id, TransformDraftingStore.flat(transform));
+    this.updated();
+  }
+
+  get(id: string, fallback?: Transform): Transform {
+    return this.store.get(id) ?? fallback;
+  }
+}
+
+// export function

From 251ba504a7adae9f31f273fde8cac96d7954baa7 Mon Sep 17 00:00:00 2001
From: softmarshmallow <woojoo@softmarshmallow.com>
Date: Mon, 25 Apr 2022 20:14:04 +0900
Subject: [PATCH 25/30] add types

---
 editor-packages/editor-canvas/types/index.ts | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/editor-packages/editor-canvas/types/index.ts b/editor-packages/editor-canvas/types/index.ts
index edc030df..793db67e 100644
--- a/editor-packages/editor-canvas/types/index.ts
+++ b/editor-packages/editor-canvas/types/index.ts
@@ -42,3 +42,13 @@ export interface Tree {
   height: number;
   children?: Tree[] | undefined;
 }
+
+export const directions_cardinal = ["n", "e", "s", "w"] as const;
+export type CardinalDirection = typeof directions_cardinal[number];
+export const directions_ordinal = ["ne", "se", "sw", "nw"] as const;
+export type OrdinalDirection = typeof directions_ordinal[number];
+export const directions_compass: CompassDirection[] = [
+  ...directions_cardinal,
+  ...directions_cardinal,
+];
+export type CompassDirection = CardinalDirection | OrdinalDirection;

From 4674a768ede92a9f0d015fdf9776a69a8e05d9b8 Mon Sep 17 00:00:00 2001
From: softmarshmallow <woojoo@softmarshmallow.com>
Date: Mon, 25 Apr 2022 20:14:13 +0900
Subject: [PATCH 26/30] add resize docs

---
 .../editor-canvas/docs/feature-resize.md           | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
 create mode 100644 editor-packages/editor-canvas/docs/feature-resize.md

diff --git a/editor-packages/editor-canvas/docs/feature-resize.md b/editor-packages/editor-canvas/docs/feature-resize.md
new file mode 100644
index 00000000..a1ac9d26
--- /dev/null
+++ b/editor-packages/editor-canvas/docs/feature-resize.md
@@ -0,0 +1,14 @@
+# Resizing selection (s)
+
+Group resizing alg
+
+```
+| a ---- b ---- c |
+```
+
+In above scenario, width of a as x, is scaled in
+
+- origin width of selection as w1
+- new width of selection as w2
+
+x = x \* w2 / w1

From a53e6cbcdabb069a9b84c67b8d089dff1e06dc6e Mon Sep 17 00:00:00 2001
From: softmarshmallow <woojoo@softmarshmallow.com>
Date: Mon, 25 Apr 2022 20:15:02 +0900
Subject: [PATCH 27/30] update reducer placeholding

---
 .../{core/actions/index.ts => reducer/history-reducer.ts}         | 0
 editor-packages/editor-canvas/{core/dispatch => reducer}/index.ts | 0
 .../{core/reducer/index.ts => reducer/interaction-reducer.ts}     | 0
 .../{core/states/index.ts => reducer/node-property-reducer.ts}    | 0
 editor-packages/editor-canvas/reducer/node-reducer.ts             | 0
 editor-packages/editor-canvas/reducer/node-style-reducer.ts       | 0
 6 files changed, 0 insertions(+), 0 deletions(-)
 rename editor-packages/editor-canvas/{core/actions/index.ts => reducer/history-reducer.ts} (100%)
 rename editor-packages/editor-canvas/{core/dispatch => reducer}/index.ts (100%)
 rename editor-packages/editor-canvas/{core/reducer/index.ts => reducer/interaction-reducer.ts} (100%)
 rename editor-packages/editor-canvas/{core/states/index.ts => reducer/node-property-reducer.ts} (100%)
 create mode 100644 editor-packages/editor-canvas/reducer/node-reducer.ts
 create mode 100644 editor-packages/editor-canvas/reducer/node-style-reducer.ts

diff --git a/editor-packages/editor-canvas/core/actions/index.ts b/editor-packages/editor-canvas/reducer/history-reducer.ts
similarity index 100%
rename from editor-packages/editor-canvas/core/actions/index.ts
rename to editor-packages/editor-canvas/reducer/history-reducer.ts
diff --git a/editor-packages/editor-canvas/core/dispatch/index.ts b/editor-packages/editor-canvas/reducer/index.ts
similarity index 100%
rename from editor-packages/editor-canvas/core/dispatch/index.ts
rename to editor-packages/editor-canvas/reducer/index.ts
diff --git a/editor-packages/editor-canvas/core/reducer/index.ts b/editor-packages/editor-canvas/reducer/interaction-reducer.ts
similarity index 100%
rename from editor-packages/editor-canvas/core/reducer/index.ts
rename to editor-packages/editor-canvas/reducer/interaction-reducer.ts
diff --git a/editor-packages/editor-canvas/core/states/index.ts b/editor-packages/editor-canvas/reducer/node-property-reducer.ts
similarity index 100%
rename from editor-packages/editor-canvas/core/states/index.ts
rename to editor-packages/editor-canvas/reducer/node-property-reducer.ts
diff --git a/editor-packages/editor-canvas/reducer/node-reducer.ts b/editor-packages/editor-canvas/reducer/node-reducer.ts
new file mode 100644
index 00000000..e69de29b
diff --git a/editor-packages/editor-canvas/reducer/node-style-reducer.ts b/editor-packages/editor-canvas/reducer/node-style-reducer.ts
new file mode 100644
index 00000000..e69de29b

From 892aca47611ae12f2de2bbca40da551435d9309a Mon Sep 17 00:00:00 2001
From: softmarshmallow <woojoo@softmarshmallow.com>
Date: Tue, 25 Oct 2022 04:02:13 +0900
Subject: [PATCH 28/30] fix build

---
 editor/core/reducers/editor-reducer.ts        |  4 ++--
 editor/scaffolds/editor/warmup.ts             |  2 +-
 .../preview-canvas/image-preview.tsx          | 24 +++++++++----------
 editor/scaffolds/preview-canvas/prop-type.ts  |  5 +---
 editor/scaffolds/preview-canvas/util.ts       |  2 +-
 .../preview-canvas/vanilla-preview-async.tsx  |  6 ++---
 .../vanilla-preview-webworker.tsx             |  3 ++-
 .../workers/canvas-preview.worker.js          |  6 ++---
 8 files changed, 24 insertions(+), 28 deletions(-)

diff --git a/editor/core/reducers/editor-reducer.ts b/editor/core/reducers/editor-reducer.ts
index a1d310ad..78bb3752 100644
--- a/editor/core/reducers/editor-reducer.ts
+++ b/editor/core/reducers/editor-reducer.ts
@@ -18,8 +18,8 @@ import type {
 import { EditorState } from "core/states";
 import { useRouter } from "next/router";
 import { CanvasStateStore } from "@code-editor/canvas/stores";
+import q from "@design-sdk/query";
 import assert from "assert";
-import { find_node_by_id_under_inpage_nodes } from "@design-sdk/core/utils/query";
 
 const _editor_path_name = "/files/[key]/";
 
@@ -117,7 +117,7 @@ export function editorReducer(state: EditorState, action: Action): EditorState {
         );
 
         node
-          .map((n) => find_node_by_id_under_inpage_nodes(n, page.children))
+          .map((n) => q.getNodeByIdFrom(n, page.children))
           .map((n) => {
             n.x += translate[0];
             n.y += translate[1];
diff --git a/editor/scaffolds/editor/warmup.ts b/editor/scaffolds/editor/warmup.ts
index 60871d4d..e7a18631 100644
--- a/editor/scaffolds/editor/warmup.ts
+++ b/editor/scaffolds/editor/warmup.ts
@@ -45,7 +45,7 @@ export function pagesFrom(
   filekey: string,
   file: FileResponse
 ): FigmaReflectRepository["pages"] {
-  return file.document.children.map((page) => ({
+  return (file.document.children as Array<Canvas>).map((page) => ({
     id: page.id,
     name: page.name,
     children: page["children"]?.map((child) => {
diff --git a/editor/scaffolds/preview-canvas/image-preview.tsx b/editor/scaffolds/preview-canvas/image-preview.tsx
index 14fa30cf..bf7a14c2 100644
--- a/editor/scaffolds/preview-canvas/image-preview.tsx
+++ b/editor/scaffolds/preview-canvas/image-preview.tsx
@@ -1,5 +1,5 @@
 import React, { useState, useEffect } from "react";
-import { remote } from "@design-sdk/figma";
+import { fetchNodeAsImage } from "@design-sdk/figma-remote";
 
 const DEV_ONLY_FIGMA_PAT =
   process.env.NEXT_PUBLIC_DEVELOPER_FIGMA_PERSONAL_ACCESS_TOKEN;
@@ -20,18 +20,16 @@ export function FigmaFrameImageView({
   useEffect(() => {
     // fetch image from figma
     // fetch smaller one first, then fatch the full scaled.
-    remote
-      .fetchNodeAsImage(
-        filekey,
-        { personalAccessToken: DEV_ONLY_FIGMA_PAT },
-        nodeid
-        // scale = 1
-      )
-      .then((r) => {
-        console.log("fetched image from figma", r);
-        setImage_1(r.__default);
-        setImage_s(r.__default);
-      });
+    fetchNodeAsImage(
+      filekey,
+      { personalAccessToken: DEV_ONLY_FIGMA_PAT },
+      nodeid
+      // scale = 1
+    ).then((r) => {
+      console.log("fetched image from figma", r);
+      setImage_1(r.__default);
+      setImage_s(r.__default);
+    });
   }, [filekey, nodeid]);
 
   let imgscale: 1 | 0.2 = 1;
diff --git a/editor/scaffolds/preview-canvas/prop-type.ts b/editor/scaffolds/preview-canvas/prop-type.ts
index 0d39baa7..c02c8b82 100644
--- a/editor/scaffolds/preview-canvas/prop-type.ts
+++ b/editor/scaffolds/preview-canvas/prop-type.ts
@@ -2,8 +2,5 @@ import type { ReflectSceneNode } from "@design-sdk/figma-node";
 import type { FrameOptimizationFactors } from "@code-editor/canvas/frame";
 
 export type VanillaPreviewProps = {
-  target: ReflectSceneNode & {
-    page: string;
-    filekey: string;
-  };
+  target: ReflectSceneNode;
 } & FrameOptimizationFactors;
diff --git a/editor/scaffolds/preview-canvas/util.ts b/editor/scaffolds/preview-canvas/util.ts
index 70ea6024..7df37353 100644
--- a/editor/scaffolds/preview-canvas/util.ts
+++ b/editor/scaffolds/preview-canvas/util.ts
@@ -1,4 +1,4 @@
-import { colorFromFills } from "@design-sdk/core/utils/colors";
+import { colorFromFills } from "@design-sdk/core/utils";
 import type { ReflectSceneNode } from "@design-sdk/figma-node";
 
 export const blurred_bg_fill = (target: ReflectSceneNode) => {
diff --git a/editor/scaffolds/preview-canvas/vanilla-preview-async.tsx b/editor/scaffolds/preview-canvas/vanilla-preview-async.tsx
index d7e99b22..4d58c01f 100644
--- a/editor/scaffolds/preview-canvas/vanilla-preview-async.tsx
+++ b/editor/scaffolds/preview-canvas/vanilla-preview-async.tsx
@@ -1,8 +1,8 @@
 import React, { useEffect, useState } from "react";
-import { config } from "@designto/config";
+import { config } from "@grida/builder-config";
 import { preview_presets } from "@grida/builder-config-preset";
 import { designToCode, Result } from "@designto/code";
-import { MainImageRepository } from "@design-sdk/core/assets-repository";
+import { MainImageRepository } from "@design-sdk/asset-repository";
 import { cachekey, cache } from "./cache";
 import { blurred_bg_fill } from "./util";
 import { PreviewContent } from "./preview-content";
@@ -50,7 +50,7 @@ export function D2CVanillaPreview({
       }
     }
     setPreview(result);
-    cache.set(target.filekey, { ...result, __image });
+    cache.set(target.filekey as string, { ...result, __image });
   };
 
   const hide_preview = isZooming || isPanning;
diff --git a/editor/scaffolds/preview-canvas/vanilla-preview-webworker.tsx b/editor/scaffolds/preview-canvas/vanilla-preview-webworker.tsx
index 2cef7a22..362546bd 100644
--- a/editor/scaffolds/preview-canvas/vanilla-preview-webworker.tsx
+++ b/editor/scaffolds/preview-canvas/vanilla-preview-webworker.tsx
@@ -20,7 +20,8 @@ export function WebWorkerD2CVanillaPreview({ target }: VanillaPreviewProps) {
     setTimeout(() => {
       dispose = wwpreview(
         {
-          page: target.page,
+          page: "", // TODO:
+          // page: target.page,
           target: target.id,
         },
         setPreview
diff --git a/editor/scaffolds/preview-canvas/workers/canvas-preview.worker.js b/editor/scaffolds/preview-canvas/workers/canvas-preview.worker.js
index 2025cc75..46157bc1 100644
--- a/editor/scaffolds/preview-canvas/workers/canvas-preview.worker.js
+++ b/editor/scaffolds/preview-canvas/workers/canvas-preview.worker.js
@@ -2,9 +2,9 @@ import { designToCode } from "@designto/code";
 import {
   ImageRepository,
   MainImageRepository,
-} from "@design-sdk/core/assets-repository";
-import { RemoteImageRepositories } from "@design-sdk/figma-remote/lib/asset-repository/image-repository";
-import { config } from "@designto/config";
+} from "@design-sdk/asset-repository";
+import { RemoteImageRepositories } from "@design-sdk/figma-remote/asset-repository";
+import { config } from "@grida/builder-config";
 import { preview_presets } from "@grida/builder-config-preset";
 import { FigmaFileStore } from "@editor/figma-file-store";
 import { convert } from "@design-sdk/figma-node-conversion";

From f07afd60675eb1c67d941901d68b373abcbdbe28 Mon Sep 17 00:00:00 2001
From: softmarshmallow <woojoo@softmarshmallow.com>
Date: Tue, 25 Oct 2022 04:26:31 +0900
Subject: [PATCH 29/30] disable chrome default gesture

---
 editor/styles/global.css | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/editor/styles/global.css b/editor/styles/global.css
index 4a78ecbb..e1991479 100644
--- a/editor/styles/global.css
+++ b/editor/styles/global.css
@@ -2,11 +2,17 @@ body {
   margin: 0px;
   padding: 0;
   font-family: "Helvetica Nueue", "Roboto", sans-serif;
+}
 
-  /* for editor canvas */
+/* for editor canvas */
+html {
+  overscroll-behavior-x: none;
+}
+body {
   overscroll-behavior-x: none;
-  overscroll-behavior-y: none;
+  touch-action: none;
 }
+/* ----------------- */
 
 iframe {
   border: none;

From 2360dbbf1acca256de33d03e77e490645d7f4366 Mon Sep 17 00:00:00 2001
From: softmarshmallow <woojoo@softmarshmallow.com>
Date: Tue, 25 Oct 2022 04:26:46 +0900
Subject: [PATCH 30/30] update readonly canvas

---
 .../editor-canvas/canvas/canvas.tsx           |  2 --
 .../editor-canvas/hud/hud-surface.tsx         | 32 ++++++++++++-----
 editor/scaffolds/canvas/canvas.tsx            | 35 ++++++++++---------
 3 files changed, 42 insertions(+), 27 deletions(-)

diff --git a/editor-packages/editor-canvas/canvas/canvas.tsx b/editor-packages/editor-canvas/canvas/canvas.tsx
index 5471898e..4fd713a2 100644
--- a/editor-packages/editor-canvas/canvas/canvas.tsx
+++ b/editor-packages/editor-canvas/canvas/canvas.tsx
@@ -392,8 +392,6 @@ export function Canvas({
     ?.map((id) => qdoc.getNodeById(id))
     .filter(Boolean);
 
-  console.log({ selectedNodes, highlightedLayer, selected_nodes });
-
   const items = useMemo(() => {
     return nodes?.map((node) => {
       return (
diff --git a/editor-packages/editor-canvas/hud/hud-surface.tsx b/editor-packages/editor-canvas/hud/hud-surface.tsx
index 17342972..1c8524fd 100644
--- a/editor-packages/editor-canvas/hud/hud-surface.tsx
+++ b/editor-packages/editor-canvas/hud/hud-surface.tsx
@@ -151,7 +151,11 @@ export function HudSurface({
                 }
               })
             ) : (
-              <SelectionGroupHighlight selections={selectedNodes} zoom={zoom} />
+              <SelectionGroupHighlight
+                selections={selectedNodes}
+                zoom={zoom}
+                readonly={readonly}
+              />
             )
           ) : (
             <></>
@@ -166,7 +170,9 @@ function SelectionGroupHighlight({
   selections,
   zoom,
   disableSizeDisplay = false,
+  readonly,
 }: {
+  readonly: boolean;
   selections: DisplayNodeMeta[];
   zoom: number;
   disableSizeDisplay?: boolean;
@@ -211,13 +217,23 @@ function SelectionGroupHighlight({
           <></>
         )}
       </>
-      <SelectHightlight
-        key={"selections-highlight"}
-        type="xywhr"
-        xywh={xywh}
-        rotation={0}
-        zoom={zoom}
-      />
+      {readonly ? (
+        <ReadonlySelectHightlight
+          key={"selections-highlight"}
+          type="xywhr"
+          xywh={xywh}
+          rotation={0}
+          zoom={zoom}
+        />
+      ) : (
+        <SelectHightlight
+          key={"selections-highlight"}
+          type="xywhr"
+          xywh={xywh}
+          rotation={0}
+          zoom={zoom}
+        />
+      )}
     </>
   );
 }
diff --git a/editor/scaffolds/canvas/canvas.tsx b/editor/scaffolds/canvas/canvas.tsx
index 10a6ef84..dbe0cf29 100644
--- a/editor/scaffolds/canvas/canvas.tsx
+++ b/editor/scaffolds/canvas/canvas.tsx
@@ -2,7 +2,10 @@ import React, { useCallback } from "react";
 import styled from "@emotion/styled";
 import { Canvas } from "@code-editor/canvas";
 import { useEditorState, useWorkspace } from "core/states";
-import { WebWorkerD2CVanillaPreview } from "scaffolds/preview-canvas";
+import {
+  WebWorkerD2CVanillaPreview,
+  D2CVanillaPreview,
+} from "scaffolds/preview-canvas";
 import useMeasure from "react-use-measure";
 import { useDispatch } from "core/dispatch";
 import { FrameTitleRenderer } from "./render/frame-title";
@@ -119,18 +122,15 @@ export function VisualContentArea() {
               selectedNodes={selectedNodes}
               highlightedLayer={highlightedLayer}
               onSelectNode={(...nodes) => {
+                dispatch({ type: "select-node", node: nodes.map((n) => n.id) });
+              }}
+              onMoveNodeEnd={([x, y], ...nodes) => {
                 dispatch({
-                  type: "select-node",
-                  node: nodes.map((n) => n?.id),
+                  type: "node-transform-translate",
+                  node: nodes,
+                  translate: [x, y],
                 });
               }}
-              // onMoveNodeEnd={([x, y], ...nodes) => {
-              //   dispatch({
-              //     type: "node-transform-translate",
-              //     node: nodes,
-              //     translate: [x, y],
-              //   });
-              // }}
               // onMoveNode={() => {}}
               onClearSelection={() => {
                 dispatch({ type: "select-node", node: [] });
@@ -139,15 +139,16 @@ export function VisualContentArea() {
               // initialTransform={ } // TODO: if the initial selection is provided from first load, from the query param, we have to focus to fit that node.
               renderItem={(p) => {
                 return (
-                  <WebWorkerD2CVanillaPreview
-                    key={p.node.id}
-                    target={p.node}
-                    {...p}
-                  />
-                  // <D2CVanillaPreview key={p.node.id} target={p.node} {...p} />
+                  // <WebWorkerD2CVanillaPreview
+                  //   key={p.node.id}
+                  //   target={p.node}
+                  //   {...p}
+                  // />
+                  <D2CVanillaPreview key={p.node.id} target={p.node} {...p} />
                 );
               }}
-              readonly={false}
+              // readonly={false}
+              readonly
               config={{
                 can_highlight_selected_layer: true,
                 marquee: {
