= new Map([
[WorkScreen.layout, "beta"],
[WorkScreen.icon, "release"],
[WorkScreen.lint, "release"],
+ [WorkScreen.code_mdx, "beta"],
[WorkScreen.g11n, "beta"],
[WorkScreen.exporter, "beta"],
[WorkScreen.dev, "beta"],
diff --git a/app/lib/navigation/work-screen.ts b/app/lib/navigation/work-screen.ts
index 67a61cc3..282400ed 100644
--- a/app/lib/navigation/work-screen.ts
+++ b/app/lib/navigation/work-screen.ts
@@ -6,6 +6,7 @@ export enum WorkScreen {
code_flutter = "code/flutter",
/** @deprecated only used for sendig event */
code_react = "code/react",
+ code_mdx = "code_mdx",
component = "component",
layout = "layout",
icon = "icon",
diff --git a/figma-core/code-thread/runon.ts b/figma-core/code-thread/runon.ts
index 8f359dde..216ff29b 100644
--- a/figma-core/code-thread/runon.ts
+++ b/figma-core/code-thread/runon.ts
@@ -3,7 +3,7 @@ import {
EK_IMAGE_ASSET_REPOSITORY_MAP,
EK_VANILLA_TRANSPORT,
} from "@core/constant/ek.constant";
-import { vanilla } from "@design-sdk/core";
+import * as vanilla from "@design-sdk/vanilla";
import { ReflectFrameNode, ReflectSceneNode } from "@design-sdk/core/nodes";
import { user_interest } from "./user-interest";
import { broadcastSelectionPreview } from "./broadcast-selection-preview";
@@ -37,8 +37,7 @@ export async function runon(rnode: ReflectSceneNode) {
// region make vanilla
if (user_interest == "g11n" || user_interest == "exporter") {
const globalizatoinScreen = vanilla.makeVanilla(rnode as ReflectFrameNode);
- const vanillaTransportableImageRepository =
- await globalizatoinScreen.repository.makeTransportable();
+ const vanillaTransportableImageRepository = await globalizatoinScreen.repository.makeTransportable();
figma.ui.postMessage({
type: EK_IMAGE_ASSET_REPOSITORY_MAP,
data: vanillaTransportableImageRepository,
diff --git a/package.json b/package.json
index 4222d964..b3a9d409 100644
--- a/package.json
+++ b/package.json
@@ -21,7 +21,7 @@
"packages/base-sdk/_firstparty/*",
"packages/reflect-core/packages/*",
"packages/design-to-code/packages/designto-*",
- "packages/design-to-code/packages/builder-config",
+ "packages/design-to-code/packages/builder-*",
"packages/design-to-code/packages/coli/packages/*",
"packages/design-to-code/packages/coli-web-builder/*",
"packages/design-to-code/packages/reflect-detection"
diff --git a/packages/app-data-mapper/__plugin/index.ts b/packages/app-data-mapper/__plugin/index.ts
index b34403ba..3bc793b8 100644
--- a/packages/app-data-mapper/__plugin/index.ts
+++ b/packages/app-data-mapper/__plugin/index.ts
@@ -4,6 +4,7 @@ import { Figma } from "@design-sdk/figma";
import { PluginSdk } from "@plugin-sdk/app";
import { extractDataFromDataSourceNode } from "../data-source-node";
import { onService, _Event_DataMapper_GoodUserInputTransfer } from "./events";
+import { FigmaEnum } from "@design-sdk/figma/features/variant";
export const TEMPLATE_NODE_PATTERN = "@//template-for-manipulation/*";
@@ -183,18 +184,18 @@ function mapVariant_try(
const set = variant.extractTypeFromVariantNames_Figma(_names);
for (const s of set) {
- const value = data[s.name];
+ const value = data[s.key];
const _isConpat =
value && typeof s.type == "string"
? s.type == value
- : s.type.includes(value);
+ : (s.type as FigmaEnum).values.includes(value);
if (_isConpat) {
// 4. map the variant
const swappingName = variant.buildVariantNameIncluding_Figma({
including: {
- swapPropertyName: s.name,
+ swapPropertyName: s.key,
swapPropertyValue: value,
thisOriginName: thisVariantName,
},
diff --git a/packages/app-mdx-processor/__plugn/event.ts b/packages/app-mdx-processor/__plugn/event.ts
new file mode 100644
index 00000000..ce90c41f
--- /dev/null
+++ b/packages/app-mdx-processor/__plugn/event.ts
@@ -0,0 +1,39 @@
+import { PluginSdk } from "@plugin-sdk/app";
+import { PluginSdkService } from "@plugin-sdk/service";
+
+const _KEY = "@app/mdx-processor";
+
+export type Requests = ParseMdxRequest;
+export interface ParseMdxRequest {
+ type: "request-parse-mdx-from-frame";
+ /**
+ * target frame id to parse
+ */
+ frame: string;
+ /**
+ * @deprecated - wip
+ */
+ options?: {
+ customComponents?: {}[];
+ };
+}
+
+export interface MdxParsedResponse {
+ /**
+ * full mdx code
+ */
+ mdx: string;
+ /**
+ * @deprecated - not implemented
+ * mdx value mapped by node id
+ */
+ map?: { [node: string]: string };
+}
+
+export function fromApp(req: Requests) {
+ PluginSdk.appEvent(_KEY, req);
+}
+
+export function onService(cb: (data: Requests) => void) {
+ PluginSdkService.onAppReqquest(_KEY, cb);
+}
diff --git a/packages/app-mdx-processor/__plugn/index.ts b/packages/app-mdx-processor/__plugn/index.ts
new file mode 100644
index 00000000..7b10c5d9
--- /dev/null
+++ b/packages/app-mdx-processor/__plugn/index.ts
@@ -0,0 +1,158 @@
+import { mapGrandchildren } from "@design-sdk/core/utils";
+import { getTextStyleById } from "@design-sdk/figma";
+import { Minimatch } from "minimatch";
+import {
+ MdxParsedResponse,
+ onService,
+ ParseMdxRequest,
+ Requests,
+} from "./event";
+import * as make from "./make";
+
+///
+/// register
+onService(main_cb);
+/// register
+///
+
+function main_cb(data: Requests) {
+ switch (data.type) {
+ case "request-parse-mdx-from-frame": {
+ const frame = figma.getNodeById(data.frame);
+ const res = frameToMdx(frame as FrameNode);
+ console.log("handling with res - ", res);
+ if (res) {
+ figma.ui.postMessage({
+ type: "parse-mdx-from-frame-result", // TODO: make this constant shared key
+ data: res,
+ });
+ } else {
+ console.log(
+ `tried to make mdx from frame ${frame.name}, but failed. no parsable content.`
+ );
+ }
+ break;
+ }
+ }
+}
+
+function frameToMdx(frame: FrameNode): MdxParsedResponse | false {
+ if (!isMdxFrame(frame)) {
+ console.log(`${frame.name} is not a mdx frame. skipping.`);
+ return false;
+ }
+
+ // currently we only loop trhough 1 depth under frame. TODO: this needs to be fixed.
+ const lines = Array.from(frame.children)
+ .sort(sort_by_x)
+ .map((child) => {
+ switch (child.type) {
+ case "TEXT": {
+ const text = child as TextNode;
+ if (isTextMixed(text)) {
+ // TODO: additional mixed style handling is required.
+ return text.characters;
+ } else {
+ // since no style is mixed, we can return the value as is.
+ const mdx_textstyle = isMdxTextStyle(
+ text.textStyleId
+ ? getTextStyleById(text.textStyleId as string).name
+ : ""
+ );
+ switch (mdx_textstyle) {
+ case false:
+ return make.paragraph(text.characters);
+ case "h1":
+ return make.h1(text.characters);
+ case "h2":
+ return make.h2(text.characters);
+ case "h3":
+ return make.h3(text.characters);
+ case "h4":
+ return make.h4(text.characters);
+ case "h5":
+ return make.h5(text.characters);
+ case "h6":
+ return make.h6(text.characters);
+ }
+ return make.unknown(text.characters);
+ }
+ break;
+ }
+ }
+ });
+
+ return {
+ mdx: lines.join("\n"),
+ };
+}
+
+const sort_by_x = (a: { x: number }, b: { x: number }) => {
+ return a.x - b.x;
+};
+
+/**
+ * returns if text contains mixed content. e.g.
+ *
+ * ```mdx
+ * this is a __mixed__ content example. also with **bold** texts.
+ * ```
+ * @returns
+ */
+function isTextMixed(text: TextNode): boolean {
+ if (
+ text.textStyleId === figma.mixed ||
+ text.fontName === figma.mixed ||
+ text.fontSize === figma.mixed ||
+ text.fills === figma.mixed
+ // add more validation here
+ ) {
+ return true;
+ }
+ return false;
+}
+
+/**
+ * returns all text style that is included under target frame. (including grandchilds)
+ * @param frame
+ * @returns
+ */
+function readTextStyles(frame: FrameNode): string[] {
+ const all_text: TextNode[] = mapGrandchildren(frame, null, undefined, (d) => {
+ return d.type == "TEXT";
+ });
+ return [...new Set(all_text.map((t) => t.textStyleId))].map((id) => {
+ return figma.getLocalTextStyles().find((ts) => ts.id === id).name;
+ });
+}
+
+/**
+ * validates if acceptable mdx textsyle by its name
+ * @param textstyle
+ */
+function isMdxTextStyle(
+ textstyle: string
+): "h1" | "h2" | "h3" | "h4" | "h5" | "h6" | false {
+ const heading_pattern = `**/!(mdx-*, *-mdx, mdx)/heading[1-6]`;
+ const mm = new Minimatch(heading_pattern);
+ if (mm.match(textstyle)) {
+ // assuming heading textstyle always ends with `heading[1-6]` based on above pattern.
+ const level = textstyle.match(/[1-6]/g).pop();
+ return (`h` + level) as "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
+ }
+ return false;
+}
+
+function isMdxFrame(frame: FrameNode): boolean {
+ // 1. must be frame
+ if (frame.type == "FRAME") {
+ // must match path
+ // const glob_pattern = `(+(document|doc|docs|mdx|md|content))*/**/*.+(md|mdx)`;
+ const glob_pattern = `*/**/*.+(md|mdx)`;
+ const mm = new Minimatch(glob_pattern);
+ if (mm.match(frame.name)) {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/packages/app-mdx-processor/__plugn/make.ts b/packages/app-mdx-processor/__plugn/make.ts
new file mode 100644
index 00000000..ee79401d
--- /dev/null
+++ b/packages/app-mdx-processor/__plugn/make.ts
@@ -0,0 +1,43 @@
+export function paragraph(t: string) {
+ return t;
+}
+
+export function quote(t: string) {
+ return `${t}
`;
+}
+
+export function inlineCode(t: string) {
+ return `${t}`;
+}
+
+export function h1(t: string) {
+ return `${t}
`;
+}
+
+export function h2(t: string) {
+ return `${t}
`;
+}
+
+export function h3(t: string) {
+ return `${t}
`;
+}
+
+export function h4(t: string) {
+ return `${t}
`;
+}
+
+export function h5(t: string) {
+ return `${t}
`;
+}
+
+export function h6(t: string) {
+ return `${t}
`;
+}
+
+export function unknown(t: string) {
+ return comment(`unknown text provided >>> ` + t);
+}
+
+export function comment(t: string) {
+ return ``;
+}
diff --git a/packages/app-mdx-processor/__plugn/tokens.ts b/packages/app-mdx-processor/__plugn/tokens.ts
new file mode 100644
index 00000000..ee7b889a
--- /dev/null
+++ b/packages/app-mdx-processor/__plugn/tokens.ts
@@ -0,0 +1,15 @@
+abstract class MdxToken {}
+abstract class TextToken extends MdxToken {}
+abstract class HeadingToken extends TextToken {}
+
+export class Heading1Token extends HeadingToken {}
+export class Heading2Token extends HeadingToken {}
+export class Heading3Token extends HeadingToken {}
+export class Heading4Token extends HeadingToken {}
+export class Heading5Token extends HeadingToken {}
+export class Heading6Token extends HeadingToken {}
+
+export class ParagraphToken extends MdxToken {}
+export class CodeToken extends MdxToken {}
+export class LineBreakToken extends MdxToken {}
+export class CustomTagToken extends MdxToken {}
diff --git a/packages/app-mdx-processor/index.ts b/packages/app-mdx-processor/index.ts
new file mode 100644
index 00000000..ac572c89
--- /dev/null
+++ b/packages/app-mdx-processor/index.ts
@@ -0,0 +1 @@
+export { MdxProcessorScreen } from "./mdx-processor-screen";
diff --git a/packages/app-mdx-processor/lint/README.md b/packages/app-mdx-processor/lint/README.md
new file mode 100644
index 00000000..8374a7cf
--- /dev/null
+++ b/packages/app-mdx-processor/lint/README.md
@@ -0,0 +1,3 @@
+# Custom linter for MDX on Design
+
+> This is a custom linter extending `@reflect-ui/lint` designed specifically only for mdx document frames.
diff --git a/packages/app-mdx-processor/mdx-processor-screen.tsx b/packages/app-mdx-processor/mdx-processor-screen.tsx
new file mode 100644
index 00000000..0ddb7521
--- /dev/null
+++ b/packages/app-mdx-processor/mdx-processor-screen.tsx
@@ -0,0 +1,41 @@
+import React, { useEffect, useState } from "react";
+import { CodeBox } from "@ui/codebox";
+import { useSingleSelection } from "plugin-app";
+import { fromApp, MdxParsedResponse, ParseMdxRequest } from "./__plugn/event";
+
+export function MdxProcessorScreen() {
+ const [mdx, setMdx] = useState("");
+ const selection = useSingleSelection();
+
+ const onMessage = (ev) => {
+ const msg = ev.data.pluginMessage;
+ switch (msg.type) {
+ case "parse-mdx-from-frame-result":
+ (msg.data as MdxParsedResponse) && setMdx(msg.data.mdx);
+ }
+ };
+
+ useEffect(() => {
+ window.addEventListener("message", onMessage);
+ return function cleaup() {
+ window.removeEventListener("message", onMessage);
+ };
+ }, []);
+
+ useEffect(() => {
+ if (selection) {
+ fromApp({
+ type: "request-parse-mdx-from-frame",
+ frame: selection.id,
+ });
+ }
+ }, [selection]);
+
+ return (
+ <>
+ select a frame that contains mdx content.
+
+
+ >
+ );
+}
diff --git a/packages/app-mdx-processor/package.json b/packages/app-mdx-processor/package.json
new file mode 100644
index 00000000..914615df
--- /dev/null
+++ b/packages/app-mdx-processor/package.json
@@ -0,0 +1,7 @@
+{
+ "name": "@app/mdx-processor",
+ "version": "0.0.0",
+ "private": false,
+ "license": "Apache 2.0",
+ "author": "Grida.co"
+}
\ No newline at end of file
diff --git a/packages/app-schema-editor/schema-editor.tsx b/packages/app-schema-editor/schema-editor.tsx
index 761cdda8..dba72eda 100644
--- a/packages/app-schema-editor/schema-editor.tsx
+++ b/packages/app-schema-editor/schema-editor.tsx
@@ -174,10 +174,10 @@ function _Mode_Component(props: { node: nodes.light.IReflectNodeReference }) {
// 0. check if variant compat component (if it's parent is variant-set then it is.)
const isVariantCompat =
- node.parentReference.origin == nodes.ReflectSceneNodeType.variant_set;
+ node.parent.origin == nodes.ReflectSceneNodeType.variant_set;
// if variant, load default property set by variant namings.
- let variantProperties: variant.FimaVariantPropertyData[];
+ let variantProperties: variant.VariantProperty[];
if (isVariantCompat) {
const names = variant.getVariantNamesSetFromReference_Figma(node);
variantProperties = variant.extractTypeFromVariantNames_Figma(names);
@@ -213,7 +213,7 @@ function _Mode_Component(props: { node: nodes.light.IReflectNodeReference }) {
{variantProperties.map((n) => {
return (
- name:{n.name}, type:{n.type}
+ name:{n.key}, type:{n.type}
);
})}
diff --git a/packages/design-sdk b/packages/design-sdk
index 8d0368c9..620d2209 160000
--- a/packages/design-sdk
+++ b/packages/design-sdk
@@ -1 +1 @@
-Subproject commit 8d0368c90ff7cfbb05bb21d32c99cad56758977a
+Subproject commit 620d220970d0136be4db9e88338ce57d540b1c28
diff --git a/packages/design-to-code b/packages/design-to-code
index 9826a17b..21fc05c9 160000
--- a/packages/design-to-code
+++ b/packages/design-to-code
@@ -1 +1 @@
-Subproject commit 9826a17b02f8d000fe09a3f7c054286ff723dfe9
+Subproject commit 21fc05c90793a1bb24c9ebe46836e12779fe157e
diff --git a/web/next.config.js b/web/next.config.js
index 190e59be..54333948 100644
--- a/web/next.config.js
+++ b/web/next.config.js
@@ -15,6 +15,7 @@ const withTM = require("next-transpile-modules")([
"@app/auth",
"@app/i18n",
"@app/component-manage",
+ "@app/mdx-processor",
"@app/button-maker",
"@app/data-mapper",
"@app/design-lint",
diff --git a/yarn.lock b/yarn.lock
index c3e6bd30..ac26932e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1582,6 +1582,11 @@
"@tippyjs/react" "^4.2.5"
handlebars "^4.7.7"
+"@design-sdk/figma-remote-api@0.0.0":
+ version "0.0.0"
+ resolved "https://registry.yarnpkg.com/@design-sdk/figma-remote-api/-/figma-remote-api-0.0.0.tgz#c1937575bbf824ca8fc45141db5327dd98d789ff"
+ integrity sha512-AA+LelJSudE2fEIvrecgcc7nmXTeRZy8WEdqhqZV4SuvgqZUH8i+t2m3dw/L/NmbxxdduRmltzTuX78MxwGntA==
+
"@design-sdk/figma-url@0.0.2":
version "0.0.2"
resolved "https://registry.yarnpkg.com/@design-sdk/figma-url/-/figma-url-0.0.2.tgz#6cdac717440def04f419efea080f95dae23e9d6f"
@@ -2624,6 +2629,11 @@
resolved "https://registry.yarnpkg.com/@reflect-ui/uiutils/-/uiutils-0.1.2.tgz#1020e557d2fdbf512a10b97b0c424c430ff7ad38"
integrity sha512-uhE5Btrzud86MuNykB23EfEHn/jBJ8BicYjOkq7ZFgh38dnvF54vqZV0/LNYF77Z+3jTgQMwZR++e4gL8zCyHw==
+"@reflect-ui/uiutils@0.1.2-1":
+ version "0.1.2-1"
+ resolved "https://registry.yarnpkg.com/@reflect-ui/uiutils/-/uiutils-0.1.2-1.tgz#b88d08223eb19ab5e6dd5ecf4a03075a41bb89c1"
+ integrity sha512-fv3mITNqM6U7DEpOr5B6c9lmmkj22r3114aR8Tvp2izMIndLbFJBzLnYEVA07bHiglic1POWCePwYVhB+bAF1Q==
+
"@sinonjs/commons@^1.7.0":
version "1.8.3"
resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d"
@@ -3600,6 +3610,14 @@
jest-diff "^26.0.0"
pretty-format "^26.0.0"
+"@types/jest@^27.0.1":
+ version "27.0.1"
+ resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.0.1.tgz#fafcc997da0135865311bb1215ba16dba6bdf4ca"
+ integrity sha512-HTLpVXHrY69556ozYkcq47TtQJXpcWAWfkoqz+ZGz2JnmZhzlRjprCIyFnetSy8gpDWwTTGBcRVv1J1I1vBrHw==
+ dependencies:
+ jest-diff "^27.0.0"
+ pretty-format "^27.0.0"
+
"@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8":
version "7.0.9"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
@@ -8635,7 +8653,7 @@ jest-diff@^26.0.0:
jest-get-type "^26.3.0"
pretty-format "^26.6.2"
-jest-diff@^27.1.0:
+jest-diff@^27.0.0, jest-diff@^27.1.0:
version "27.1.0"
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.1.0.tgz#c7033f25add95e2218f3c7f4c3d7b634ab6b3cd2"
integrity sha512-rjfopEYl58g/SZTsQFmspBODvMSytL16I+cirnScWTLkQVXYVZfxm78DFfdIIXc05RCYuGjxJqrdyG4PIFzcJg==
@@ -9039,7 +9057,7 @@ jest-worker@^27.1.0:
merge-stream "^2.0.0"
supports-color "^8.0.0"
-jest@^27.0.3, jest@^27.0.4, jest@^27.0.6:
+jest@^27.0.3, jest@^27.0.4, jest@^27.0.6, jest@^27.1.0:
version "27.1.0"
resolved "https://registry.yarnpkg.com/jest/-/jest-27.1.0.tgz#eaab62dfdc02d8b7c814cd27b8d2d92bc46d3d69"
integrity sha512-pSQDVwRSwb109Ss13lcMtdfS9r8/w2Zz8+mTUA9VORD66GflCdl8nUFCqM96geOD2EBwWCNURrNAfQsLIDNBdg==
@@ -10846,7 +10864,7 @@ pretty-format@^26.0.0, pretty-format@^26.6.2:
ansi-styles "^4.0.0"
react-is "^17.0.1"
-pretty-format@^27.1.0:
+pretty-format@^27.0.0, pretty-format@^27.1.0:
version "27.1.0"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.1.0.tgz#022f3fdb19121e0a2612f3cff8d724431461b9ca"
integrity sha512-4aGaud3w3rxAO6OXmK3fwBFQ0bctIOG3/if+jYEFGNGIs0EvuidQm3bZ9mlP2/t9epLNC/12czabfy7TZNSwVA==
@@ -12869,7 +12887,7 @@ ts-essentials@^2.0.3:
resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-2.0.12.tgz#c9303f3d74f75fa7528c3d49b80e089ab09d8745"
integrity sha512-3IVX4nI6B5cc31/GFFE+i8ey/N2eA0CZDbo6n0yrz0zDX8ZJ8djmU1p+XRz7G3is0F3bB3pu2pAroFdAWQKU3w==
-ts-jest@^27.0.2, ts-jest@^27.0.3:
+ts-jest@^27.0.2, ts-jest@^27.0.3, ts-jest@^27.0.5:
version "27.0.5"
resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-27.0.5.tgz#0b0604e2271167ec43c12a69770f0bb65ad1b750"
integrity sha512-lIJApzfTaSSbtlksfFNHkWOzLJuuSm4faFAfo5kvzOiRAuoN4/eKxVJ2zEAho8aecE04qX6K1pAzfH5QHL1/8w==
@@ -12988,7 +13006,7 @@ typescript@4.3.5:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4"
integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==
-typescript@^4.0.5, typescript@^4.1.2, typescript@^4.2.3, typescript@^4.2.4, typescript@^4.3.2, typescript@^4.3.5:
+typescript@^4.0.5, typescript@^4.1.2, typescript@^4.2.3, typescript@^4.2.4, typescript@^4.3.2, typescript@^4.3.5, typescript@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.2.tgz#6d618640d430e3569a1dfb44f7d7e600ced3ee86"
integrity sha512-gzP+t5W4hdy4c+68bfcv0t400HVJMMd2+H9B7gae1nQlBzCqvrXX+6GL/b3GAgyTH966pzrZ70/fRjwAtZksSQ==