Skip to content

Commit

Permalink
feat(insertables): add component thumbnails
Browse files Browse the repository at this point in the history
GitOrigin-RevId: 441098d6f266eeee9ab54f152a9820fe613bd0b8
  • Loading branch information
IcaroG authored and actions-user committed Oct 15, 2024
1 parent e94a946 commit c675ba5
Show file tree
Hide file tree
Showing 16 changed files with 210 additions and 54 deletions.
1 change: 1 addition & 0 deletions platform/canvas-packages/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
"dayjs": "^1.11.10",
"fast-stringify": "^2.0.0",
"framer-motion": "^7.6.1",
"html-to-image": "^1.11.11",
"immer": "^10.0.3",
"internal-react-slick": "link:./internal_pkgs/react-slick",
"isomorphic-fetch": "^3.0.0",
Expand Down
2 changes: 2 additions & 0 deletions platform/canvas-packages/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// evaluate the generated javascript when each frame loads (but we can fetch the
// code only once when the project loads and store it as a string).

import { toPng } from "html-to-image";
import ResizeObserver from "resize-observer-polyfill";
import * as slate from "slate";
import * as slateReact from "slate-react";
Expand All @@ -18,4 +19,5 @@ import { createModal } from "./modals";
slateReact,
localElement: typeof window !== "undefined" ? Element : undefined,
createModal,
createThumbnail: toPng,
};
5 changes: 5 additions & 0 deletions platform/canvas-packages/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8534,6 +8534,11 @@ html-entities@^2.3.6:
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.4.0.tgz#edd0cee70402584c8c76cc2c0556db09d1f45061"
integrity sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==

html-to-image@^1.11.11:
version "1.11.11"
resolved "https://registry.yarnpkg.com/html-to-image/-/html-to-image-1.11.11.tgz#c0f8a34dc9e4b97b93ff7ea286eb8562642ebbea"
integrity sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA==

http-basic@^8.1.1:
version "8.1.3"
resolved "https://registry.yarnpkg.com/http-basic/-/http-basic-8.1.3.tgz#a7cabee7526869b9b710136970805b1004261bbf"
Expand Down
3 changes: 2 additions & 1 deletion platform/wab/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,9 @@
<link
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700&family=Inter:wght@300;400;500;600;700;900&display=swap"
rel="stylesheet"
crossorigin="anonymous"
/>
<link id="normalizeCss" rel="stylesheet" type="text/css" />
<link id="normalizeCss" rel="stylesheet" type="text/css" crossorigin="anonymous" />
<!-- Inject URLs based on origin -->
<script>
document.getElementById("shortcutIcon").href = `${origin}/favicon.ico`;
Expand Down
44 changes: 28 additions & 16 deletions platform/wab/src/wab/client/components/canvas/canvas-ctx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import * as domMod from "@/wab/client/dom";
import { NodeAndOffset } from "@/wab/client/dom";
import { scriptExec, upsertJQSelector } from "@/wab/client/dom-utils";
import { reduceImageSize } from "@/wab/client/image/transform";
import { StudioCtx } from "@/wab/client/studio-ctx/StudioCtx";
import { ViewCtx } from "@/wab/client/studio-ctx/view-ctx";
import {
Expand Down Expand Up @@ -61,6 +62,8 @@ let gCanvasCtxIndex = 0;
*/
const CANVAS_CTX_TIMEOUT_PERIOD = 3 * 60 * 1000; // 3 minutes

const MAX_THUMBNAIL_SIZE = 120;

export class CanvasCtx {
/**
* Simply for debugging purposes - give each a CanvasCtx a number.
Expand Down Expand Up @@ -106,6 +109,27 @@ export class CanvasCtx {
this._keyAdjustment = null;
}

async getThumbnail(): Promise<string> {
const domNode = this.$eltForTplRoot()[0];
if (!domNode) {
return "";
}
const { width, height } = reduceImageSize(
domNode.clientWidth,
domNode.clientHeight,
MAX_THUMBNAIL_SIZE,
MAX_THUMBNAIL_SIZE
);
return this.Sub.createThumbnail(domNode, {
filter: (node) =>
node.tagName !== "SOURCE" &&
!node.classList?.contains("__wab_placeholder"),
includeQueryParams: true,
canvasWidth: width,
canvasHeight: height,
});
}

private _updatingCcRegistryCount = observable.box(0);
set updatingCcRegistryCount(v: number) {
this._updatingCcRegistryCount.set(v);
Expand Down Expand Up @@ -199,6 +223,7 @@ export class CanvasCtx {
rel: "stylesheet",
type: "text/css",
href: `${getPublicUrl()}/static/styles/canvas/canvas.${COMMITHASH}.css`,
crossOrigin: "anonymous",
})
),
this._$head
Expand Down Expand Up @@ -228,6 +253,7 @@ export class CanvasCtx {
rel: "stylesheet",
type: "text/css",
href: `${getPublicUrl()}/static/styles/canvas/canvas.${COMMITHASH}.css`,
crossOrigin: "anonymous",
})
),
this._$head
Expand Down Expand Up @@ -268,6 +294,7 @@ export class CanvasCtx {
sc.refreshFetchedDataFromPlasmicQuery;
(this._win as any).__PLASMIC_GET_ALL_CACHE_KEYS = sc.getAllDataOpCacheKeys;
(this._win as any).__PLASMIC_STUDIO_PATH = sc.getCurrentPathName;

if (this.usedPkgsDispose) {
this.usedPkgsDispose();
}
Expand Down Expand Up @@ -632,22 +659,7 @@ export class CanvasCtx {
children
);

let wrapped: React.ReactElement = node;

if (this.Sub.StudioFetcherContext) {
wrapped = r(
this.Sub.StudioFetcherContext.Provider,
{
value: {
fetchJson: async (url: string) =>
viewCtx.appCtx.api.queryDataSource(url),
},
},
node
);
}

this.Sub.setPlasmicRootNode(wrapped);
this.Sub.setPlasmicRootNode(node);
}
dispose() {
this._resizeObserver?.disconnect();
Expand Down
11 changes: 10 additions & 1 deletion platform/wab/src/wab/client/components/canvas/subdeps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,16 @@ export interface SubDeps {
slate: typeof slate;
slateReact: typeof slateReact;
localElement: typeof Element;
createThumbnail: (
element: HTMLElement,
opts?: {
canvasWidth?: number;
canvasHeight?: number;
quality?: number;
filter?: (elem: HTMLElement) => boolean;
includeQueryParams?: boolean;
}
) => Promise<string>;
setPlasmicRootNode: (node: React.ReactElement | null) => void;
repeatedElement: RepeatedElementFnType;
setRepeatedElementFn?: (fn: RepeatedElementFnType) => void;
Expand All @@ -221,7 +231,6 @@ export interface SubDeps {
createModal: (
props: Pick<ModalProps, InternalModalProps>
) => (restProps: Omit<ModalProps, InternalModalProps>) => JSX.Element;
StudioFetcherContext?: React.Context<any>; // TODO fix the type from @plasmicapp/host
DataCtxReader: typeof DataCtxReader;
reactWeb: typeof ReactWeb;
dataSources?: typeof PlasmicDataSources;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ import {
canInsertHostlessPackage,
InsertPanelConfig,
} from "@/wab/shared/ui-config-utils";
import { placeholderImgUrl } from "@/wab/shared/urls";
import cn from "classnames";
import { UseComboboxGetItemPropsOptions } from "downshift";
import L, { capitalize, groupBy, last, uniq } from "lodash";
Expand Down Expand Up @@ -1215,7 +1216,16 @@ export function buildAddItemGroups({
) &&
!isBuiltinCodeComponent(c)
)
).map((comp) => createAddTplComponent(comp)),
).map((comp) => ({
...createAddTplComponent(
comp,
// TODO: improve placeholder image!
studioCtx.appCtx.appConfig.componentThumbnails
? studioCtx.getCachedThumbnail(comp.uuid) ?? placeholderImgUrl()
: undefined
),
isCompact: studioCtx.appCtx.appConfig.componentThumbnails,
})),
},

// Insertable Templates
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,10 @@ export function createAddInstallable(meta: Installable): AddInstallableItem {
};
}

export function createAddTplComponent(component: Component): AddTplItem {
export function createAddTplComponent(
component: Component,
previewImageUrl?: string
): AddTplItem {
return {
type: AddItemType.tpl as const,
key: `tpl-component-${component.uuid}`,
Expand All @@ -267,6 +270,7 @@ export function createAddTplComponent(component: Component): AddTplItem {
return tpl;
},
component,
previewImageUrl,
};
}

Expand Down
13 changes: 8 additions & 5 deletions platform/wab/src/wab/client/cseval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
getRenderState,
} from "@/wab/client/studio-ctx/renderState";
import { ViewCtx } from "@/wab/client/studio-ctx/view-ctx";
import { ContextFactory } from "@/wab/shared/code-components/context-factory";
import {
ensure,
ensureInstance,
Expand All @@ -23,18 +24,20 @@ import {
undefinedToDefault,
xOmitNils,
} from "@/wab/shared/common";
import { allComponentVariants, isContextCodeComponent } from "@/wab/shared/core/components";
import { getImplicitlyActivatedStyleVariants } from "@/wab/shared/Variants";
import { ContextFactory } from "@/wab/shared/code-components/context-factory";
import {
ComponentVariantFrame,
GlobalVariantFrame,
} from "@/wab/shared/component-frame";
import { wrapWithContext } from "@/wab/shared/contexts";
import { ValState } from "@/wab/shared/eval/val-state";
import { TplNode, Variant } from "@/wab/shared/model/classes";
import {
allComponentVariants,
isContextCodeComponent,
} from "@/wab/shared/core/components";
import { allGlobalVariants } from "@/wab/shared/core/sites";
import { ValComponent, ValNode } from "@/wab/shared/core/val-nodes";
import { ValState } from "@/wab/shared/eval/val-state";
import { TplNode, Variant } from "@/wab/shared/model/classes";
import { getImplicitlyActivatedStyleVariants } from "@/wab/shared/Variants";
import { autorun, observable } from "mobx";
import React from "react";
import defer = setTimeout;
Expand Down
1 change: 1 addition & 0 deletions platform/wab/src/wab/client/definitions/insertables.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ export type AddTplItem<T = any> = AddItemCommon & {
) => Promise<T | false>;
canWrap?: boolean;
component?: Component;
previewImageUrl?: string;
};

export type AddFakeItem<T = any> = AddItemCommon & {
Expand Down
9 changes: 5 additions & 4 deletions platform/wab/src/wab/client/fonts.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { StudioCtx } from "@/wab/client/studio-ctx/StudioCtx";
import { assertNever, spawn } from "@/wab/shared/common";
import { derefTokenRefs } from "@/wab/commons/StyleToken";
import { getGoogFontsMeta, makeGoogleFontApiUrl } from "@/wab/shared/googfonts";
import { walkDependencyTree } from "@/wab/shared/core/project-deps";
import { extractUsedFontsFromComponents } from "@/wab/shared/codegen/fonts";
import { assertNever, spawn } from "@/wab/shared/common";
import { walkDependencyTree } from "@/wab/shared/core/project-deps";
import { allStyleTokens } from "@/wab/shared/core/sites";
import {
FontInstallSpec,
getFontSpec,
toGoogleFontInstallSpec,
} from "@/wab/shared/fonts";
import { getGoogFontsMeta, makeGoogleFontApiUrl } from "@/wab/shared/googfonts";
import { ProjectDependency, Site } from "@/wab/shared/model/classes";
import { allStyleTokens } from "@/wab/shared/core/sites";
import { notification } from "antd";
import $ from "jquery";
import L from "lodash";
Expand Down Expand Up @@ -228,6 +228,7 @@ export class FontManager {
const $link = $("<link />", {
rel: "stylesheet",
href: url,
crossOrigin: "anonymous",
});
$head.append($link);
});
Expand Down
23 changes: 23 additions & 0 deletions platform/wab/src/wab/client/image/transform.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { reduceImageSize } from "@/wab/client/image/transform";

describe("reduceImageSize", () => {
it("should reduce the width to fit within the maxWidth while maintaining aspect ratio", () => {
const result = reduceImageSize(2000, 1000, 1000, 1000);
expect(result).toEqual({ width: 1000, height: 500 });
});

it("should reduce the height to fit within the maxHeight while maintaining aspect ratio", () => {
const result = reduceImageSize(1000, 2000, 1000, 1000);
expect(result).toEqual({ width: 500, height: 1000 });
});

it("should reduce both width and height to fit within maxWidth and maxHeight while maintaining aspect ratio", () => {
const result = reduceImageSize(4000, 2000, 1000, 500);
expect(result).toEqual({ width: 1000, height: 500 });
});

it("should not change the size if it is within the maxWidth and maxHeight", () => {
const result = reduceImageSize(800, 600, 1000, 1000);
expect(result).toEqual({ width: 800, height: 600 });
});
});
27 changes: 27 additions & 0 deletions platform/wab/src/wab/client/image/transform.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Reduces the dimensions of an image to fit within the specified maximum width and height,
* while maintaining the original aspect ratio.
*
* @param width - The original width of the image.
* @param height - The original height of the image.
* @param maxWidth - The maximum allowed width for the image.
* @param maxHeight - The maximum allowed height for the image.
* @returns An object containing the new width and height of the image.
*/
export function reduceImageSize(
width: number,
height: number,
maxWidth: number,
maxHeight: number
) {
const aspectRatio = width / height;
if (width > maxWidth) {
width = maxWidth;
height = width / aspectRatio;
}
if (height > maxHeight) {
height = maxHeight;
width = height * aspectRatio;
}
return { width, height };
}
Loading

0 comments on commit c675ba5

Please sign in to comment.