diff --git a/bun.lockb b/bun.lockb index de784a9..7512e08 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 78043d3..b392e7e 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,10 @@ "preview": "vite preview" }, "dependencies": { + "@uiw/codemirror-extensions-color": "^4.23.0", + "@uiw/codemirror-extensions-langs": "^4.23.0", + "@uiw/react-codemirror": "^4.23.0", + "@uiw/react-json-view": "1.12.0", "localforage": "^1.10.0", "match-sorter": "^6.3.4", "react": "^18.2.0", @@ -18,9 +22,12 @@ "react-transition-group": "^4.4.5", "sort-by": "^1.2.0", "sucrase": "^3.31.0", + "terser": "^5.31.3", "use-editable": "^2.3.3" }, "devDependencies": { + "@codemirror/autocomplete": "^6.17.0", + "@codemirror/state": "^6.4.1", "@types/react": "^18.2.0", "@types/react-dom": "^18.2.0", "@types/react-transition-group": "^4.4.10", diff --git a/src/components/Live/style.css b/src/components/Live/style.css index cdda334..cab373c 100644 --- a/src/components/Live/style.css +++ b/src/components/Live/style.css @@ -5,5 +5,5 @@ .preview-box > div *:not(div), .preview-box > div div *:not(div) { - cursor: default; + /* cursor: initial; */ } \ No newline at end of file diff --git a/src/pages/app/index.tsx b/src/pages/app/index.tsx index c0bc58e..f47a3f3 100644 --- a/src/pages/app/index.tsx +++ b/src/pages/app/index.tsx @@ -5,32 +5,42 @@ import evalCode from "../../utils/transpile/evalCode"; import transform from "../../utils/transpile/transform"; import { ThemeProvider } from "../../components/Theme/ThemePrivider"; import errorBoundary from "../../utils/transpile/errorBoundary"; -import "./style.css" -import { CSSTransition, TransitionGroup } from 'react-transition-group'; +import "./style.css"; +import { CSSTransition, TransitionGroup } from "react-transition-group"; +import minifyCode from "../../utils/minify"; +// import ReactDOM from "react-dom/client"; export const App = () => { - // 定义成一个数组便于下面操作,实际上只有一个元素 - const [results, setResults] = useState([]); + // 定义成一个数组便于下面操作,实际上只有一个元素 + const [results, setResults] = useState([]); - const errorCallBack = (error: Error) => { - console.error(error); - }; - - const transforms: Transform[] = ["jsx", "typescript", "imports"]; - - const render = (element: ComponentType) => { - if (typeof element === "undefined") { - errorCallBack(new SyntaxError("`render` must be called with valid JSX.")); - } else { - setResults([errorBoundary(element, errorCallBack)]); - } - }; - - // 开发环境下,useEffect会执行两次,模拟装载和卸载组件,生产环境没事。 - useEffect(() => { - const allCpns = functionBlockList.map(item => item.code).join("\n"); - const allPage = functionBlockList.map((item,index) => `<${item.cpnName} { ...props[${index}] }/>`).join("\n"); - const allCode = ` + const errorCallBack = (error: Error) => { + console.error(error); + }; + + const transforms: Transform[] = ["jsx", "typescript", "imports"]; + + const render = (element: ComponentType) => { + const Component = errorBoundary(element, errorCallBack); + if (typeof element === "undefined") { + errorCallBack(new SyntaxError("`render` must be called with valid JSX.")); + } else { + setResults([Component]); + } + // ReactDOM.createRoot(document.querySelector(".test-app") as HTMLElement).render( + // + // < Component /> + // + // ); + }; + + // 开发环境下,useEffect会执行两次,模拟装载和卸载组件,生产环境没事。 + useEffect(() => { + const allCpns = functionBlockList.map((item) => item.code).join("\n"); + const allPage = functionBlockList + .map((item, index) => `<${item.cpnName} { ...props[${index}] }/>`) + .join("\n"); + const allCode = ` ${allCpns} const ApplicationContext = () => { @@ -43,29 +53,30 @@ export const App = () => { render(ApplicationContext) `; - - const propsList = functionBlockList.map(item => item.props); - const allTransformed = transform({ transforms })(allCode); + const propsList = functionBlockList.map((item) => item.props); - setTimeout(() => { - evalCode(allTransformed, { React, render, props: propsList }); - },1000) - }, []); + const allTransformed = transform({ transforms })(allCode); - return ( - -
- - {results.map((Cpn, index) => ( - - - - ))} - - {results.length === 0 &&
loading...
} -
-
- ); + setTimeout(async () => { + const minifiedCode = await minifyCode(allTransformed); + + evalCode(minifiedCode, { React, render, props: propsList }); + }, 1000); + }, []); + + return ( + +
+ + {results.map((Cpn, index) => ( + + + + ))} + + {results.length === 0 &&
loading...
} +
+
+ ); }; diff --git a/src/pages/app/transpiled.ts b/src/pages/app/transpiled.ts index 907b4fa..4dc84c6 100644 --- a/src/pages/app/transpiled.ts +++ b/src/pages/app/transpiled.ts @@ -1,250 +1 @@ -export const remotecode = `"use strict";const _jsxFileName = ""; - - const Banner = (props - - - -) => { - function handleClick() { - console.log("Hi there!"); - } - - return ( - React.createElement('div', { className: "bg-primary-1 py-16 px-8" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 13}} - , React.createElement('div', { className: "max-w-7xl mx-auto text-center" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 14}} - , React.createElement('h1', { className: "text-primary text-5xl md:text-7xl font-extrabold mb-4" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 15}} - , props.title - ) - , React.createElement('p', { className: "text-primary text-xl md:text-2xl mb-8" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 18}} - , props.subTitle - ) - , React.createElement('button', { - onClick: handleClick, - className: "font-bold py-3 px-6 rounded-full shadow-lg transition duration-300 ease-in-out" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 21}} - - , props.btnText - ) - ) - ) - ); - }; - - - const Feature = (props - - - - - - - - -) => { - return ( - React.createElement('div', { className: "bg-background-2 py-16 px-8" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 44}} - , React.createElement('div', { className: "max-w-7xl mx-auto" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 45}} - , React.createElement('h2', { className: "text-text-1 text-4xl font-bold mb-4 text-center" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 46}} - , props.title - ) - , React.createElement('p', { className: "text-text-2 text-lg mb-8 text-center" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 49}} - , props.subTitle - ) - , React.createElement('div', { className: "grid grid-cols-1 md:grid-cols-3 gap-8" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 52}} - , React.createElement('div', { className: "bg-data-2 p-6 rounded-lg shadow-lg text-center" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 53}} - , React.createElement('h3', { className: "text-primary text-2xl font-semibold mb-4" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 54}} - , props.featureTitle1 - ) - , React.createElement('p', { className: "text-text-1", __self: this, __source: {fileName: _jsxFileName, lineNumber: 57}} - , props.featureDesc1 - ) - ) - , React.createElement('div', { className: "bg-data-8 p-6 rounded-lg shadow-lg text-center" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 61}} - , React.createElement('h3', { className: "text-primary text-2xl font-semibold mb-4" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 62}} - , props.featureTitle2 - ) - , React.createElement('p', { className: "text-text-1", __self: this, __source: {fileName: _jsxFileName, lineNumber: 65}} - , props.featureDesc2 - ) - ) - , React.createElement('div', { className: "bg-data-10 p-6 rounded-lg shadow-lg text-center" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 69}} - , React.createElement('h3', { className: "text-primary text-2xl font-semibold mb-4" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 70}} - , props.featureTitle3 - ) - , React.createElement('p', { className: "text-text-1", __self: this, __source: {fileName: _jsxFileName, lineNumber: 73}} - , props.featureDesc3 - ) - ) - ) - ) - ) - ); - }; - - - const Case = (props - - - - - - -) => { - return ( - React.createElement('div', { className: "bg-background-1 py-16 px-8" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 93}} - , React.createElement('div', { className: "max-w-7xl mx-auto text-center" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 94}} - , React.createElement('h2', { className: "text-text-1 text-4xl font-bold mb-4" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 95}} - , props.title - ) - , React.createElement('p', { className: "text-text-2 text-lg mb-8" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 98}} - , props.subTitle - ) - , React.createElement('div', { className: "grid grid-cols-1 md:grid-cols-2 gap-8" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 101}} - , React.createElement('div', { className: "bg-data-18 p-6 rounded-lg shadow-lg" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 102}} - , React.createElement('h3', { className: "text-primary text-2xl font-semibold mb-4" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 103}} - , props.caseTitle1 - ) - , React.createElement('p', { className: "text-text-1", __self: this, __source: {fileName: _jsxFileName, lineNumber: 106}} - , props.caseDesc1 - ) - ) - , React.createElement('div', { className: "bg-data-16 p-6 rounded-lg shadow-lg" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 110}} - , React.createElement('h3', { className: "text-primary text-2xl font-semibold mb-4" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 111}} - , props.caseTitle2 - ) - , React.createElement('p', { className: "text-text-1", __self: this, __source: {fileName: _jsxFileName, lineNumber: 114}} - , props.caseDesc2 - ) - ) - ) - ) - ) - ); - }; - - - const Review = (props - - - - - - -) => { - return ( - React.createElement('div', { className: "bg-background-2 py-16 px-8" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 134}} - , React.createElement('div', { className: "max-w-7xl mx-auto text-center" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 135}} - , React.createElement('h2', { className: "text-text-1 text-4xl font-bold mb-4" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 136}} - , props.title - ) - , React.createElement('p', { className: "text-text-2 text-lg mb-8" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 139}} - , props.subTitle - ) - , React.createElement('div', { className: "grid grid-cols-1 md:grid-cols-2 gap-8" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 142}} - , React.createElement('div', { className: "bg-data-4 p-6 rounded-lg shadow-lg" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 143}} - , React.createElement('h3', { className: "text-primary text-2xl font-semibold mb-4" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 144}} - , props.reviewUser1 - ) - , React.createElement('p', { className: "text-text-1", __self: this, __source: {fileName: _jsxFileName, lineNumber: 147}} - , props.reviewDesc1 - ) - ) - , React.createElement('div', { className: "bg-data-8 p-6 rounded-lg shadow-lg" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 151}} - , React.createElement('h3', { className: "text-primary text-2xl font-semibold mb-4" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 152}} - , props.reviewUser2 - ) - , React.createElement('p', { className: "text-text-1", __self: this, __source: {fileName: _jsxFileName, lineNumber: 155}} - , props.reviewDesc2 - ) - ) - ) - ) - ) - ); - }; - - - const Faq = (props - - - - - - - - -) => { - return ( - React.createElement('div', { className: "bg-background-1 py-16 px-8" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 177}} - , React.createElement('div', { className: "max-w-7xl mx-auto text-center" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 178}} - , React.createElement('h2', { className: "text-text-1 text-4xl font-bold mb-4" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 179}} - , props.title - ) - , React.createElement('p', { className: "text-text-2 text-lg mb-8" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 182}} - , props.subTitle - ) - , React.createElement('div', { className: "text-left max-w-3xl mx-auto" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 185}} - , React.createElement('div', { className: "mb-8", __self: this, __source: {fileName: _jsxFileName, lineNumber: 186}} - , React.createElement('h3', { className: "text-primary text-2xl font-semibold mb-2" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 187}} - , props.faq1 - ) - , React.createElement('p', { className: "text-text-3", __self: this, __source: {fileName: _jsxFileName, lineNumber: 190}} - , props.faqAnswer1 - ) - ) - , React.createElement('div', { className: "mb-8", __self: this, __source: {fileName: _jsxFileName, lineNumber: 194}} - , React.createElement('h3', { className: "text-primary text-2xl font-semibold mb-2" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 195}} - , props.faq2 - ) - , React.createElement('p', { className: "text-text-3", __self: this, __source: {fileName: _jsxFileName, lineNumber: 198}} - , props.faqAnswer2 - ) - ) - , React.createElement('div', { className: "mb-8", __self: this, __source: {fileName: _jsxFileName, lineNumber: 202}} - , React.createElement('h3', { className: "text-primary text-2xl font-semibold mb-2" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 203}} - , props.faq3 - ) - , React.createElement('p', { className: "text-text-3", __self: this, __source: {fileName: _jsxFileName, lineNumber: 206}} - , props.faqAnswer3 - ) - ) - ) - ) - ) - ); - }; - - - const Contant = (props - - -) => { - return ( - React.createElement('div', { className: "bg-primary-1 py-16 px-8" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 222}} - , React.createElement('div', { className: "max-w-7xl mx-auto text-center" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 223}} - , React.createElement('h2', { className: "text-text-1 text-4xl font-bold mb-4" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 224}} - , props.title - ) - , React.createElement('p', { className: "text-text-2 text-lg mb-8" , __self: this, __source: {fileName: _jsxFileName, lineNumber: 227}} - , props.subTitle - ) - ) - ) - ); - }; - - - const ApplicationContext = () => { - return ( - React.createElement('div', {__self: this, __source: {fileName: _jsxFileName, lineNumber: 238}} - , React.createElement(Banner, { ...props[0] , __self: this, __source: {fileName: _jsxFileName, lineNumber: 239}}) -, React.createElement(Feature, { ...props[1] , __self: this, __source: {fileName: _jsxFileName, lineNumber: 240}}) -, React.createElement(Case, { ...props[2] , __self: this, __source: {fileName: _jsxFileName, lineNumber: 241}}) -, React.createElement(Review, { ...props[3] , __self: this, __source: {fileName: _jsxFileName, lineNumber: 242}}) -, React.createElement(Faq, { ...props[4] , __self: this, __source: {fileName: _jsxFileName, lineNumber: 243}}) -, React.createElement(Contant, { ...props[5] , __self: this, __source: {fileName: _jsxFileName, lineNumber: 244}}) - ) - ); - }; - - render(ApplicationContext) - ` \ No newline at end of file +export const remotecode = `"use strict";const e=React.createElement,s=React.Fragment,l=React.useState,t=React.useEffect,i=React.Component,a=React.createContext,m=React.useContext,_=React.useReducer,r=React.memo,N="",u=s=>e("div",{className:"bg-primary-1 py-16 px-8",__self:this,__source:{fileName:N,lineNumber:13}},e("div",{className:"max-w-7xl mx-auto text-center",__self:this,__source:{fileName:N,lineNumber:14}},e("h1",{className:"text-primary text-5xl md:text-7xl font-extrabold mb-4",__self:this,__source:{fileName:N,lineNumber:15}},s.title),e("p",{className:"text-primary text-xl md:text-2xl mb-8",__self:this,__source:{fileName:N,lineNumber:18}},s.subTitle),e("button",{onClick:function(){},className:"font-bold py-3 px-6 rounded-full shadow-lg transition duration-300 ease-in-out",__self:this,__source:{fileName:N,lineNumber:21}},s.btnText))),c=s=>e("div",{className:"bg-background-2 py-16 px-8",__self:this,__source:{fileName:N,lineNumber:44}},e("div",{className:"max-w-7xl mx-auto",__self:this,__source:{fileName:N,lineNumber:45}},e("h2",{className:"text-text-1 text-4xl font-bold mb-4 text-center",__self:this,__source:{fileName:N,lineNumber:46}},s.title),e("p",{className:"text-text-2 text-lg mb-8 text-center",__self:this,__source:{fileName:N,lineNumber:49}},s.subTitle),e("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-8",__self:this,__source:{fileName:N,lineNumber:52}},e("div",{className:"bg-data-2 p-6 rounded-lg shadow-lg text-center",__self:this,__source:{fileName:N,lineNumber:53}},e("h3",{className:"text-primary text-2xl font-semibold mb-4",__self:this,__source:{fileName:N,lineNumber:54}},s.featureTitle1),e("p",{className:"text-text-1",__self:this,__source:{fileName:N,lineNumber:57}},s.featureDesc1)),e("div",{className:"bg-data-8 p-6 rounded-lg shadow-lg text-center",__self:this,__source:{fileName:N,lineNumber:61}},e("h3",{className:"text-primary text-2xl font-semibold mb-4",__self:this,__source:{fileName:N,lineNumber:62}},s.featureTitle2),e("p",{className:"text-text-1",__self:this,__source:{fileName:N,lineNumber:65}},s.featureDesc2)),e("div",{className:"bg-data-10 p-6 rounded-lg shadow-lg text-center",__self:this,__source:{fileName:N,lineNumber:69}},e("h3",{className:"text-primary text-2xl font-semibold mb-4",__self:this,__source:{fileName:N,lineNumber:70}},s.featureTitle3),e("p",{className:"text-text-1",__self:this,__source:{fileName:N,lineNumber:73}},s.featureDesc3))))),f=s=>e("div",{className:"bg-background-1 py-16 px-8",__self:this,__source:{fileName:N,lineNumber:93}},e("div",{className:"max-w-7xl mx-auto text-center",__self:this,__source:{fileName:N,lineNumber:94}},e("h2",{className:"text-text-1 text-4xl font-bold mb-4",__self:this,__source:{fileName:N,lineNumber:95}},s.title),e("p",{className:"text-text-2 text-lg mb-8",__self:this,__source:{fileName:N,lineNumber:98}},s.subTitle),e("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-8",__self:this,__source:{fileName:N,lineNumber:101}},e("div",{className:"bg-data-18 p-6 rounded-lg shadow-lg",__self:this,__source:{fileName:N,lineNumber:102}},e("h3",{className:"text-primary text-2xl font-semibold mb-4",__self:this,__source:{fileName:N,lineNumber:103}},s.caseTitle1),e("p",{className:"text-text-1",__self:this,__source:{fileName:N,lineNumber:106}},s.caseDesc1)),e("div",{className:"bg-data-16 p-6 rounded-lg shadow-lg",__self:this,__source:{fileName:N,lineNumber:110}},e("h3",{className:"text-primary text-2xl font-semibold mb-4",__self:this,__source:{fileName:N,lineNumber:111}},s.caseTitle2),e("p",{className:"text-text-1",__self:this,__source:{fileName:N,lineNumber:114}},s.caseDesc2))))),o=s=>e("div",{className:"bg-background-2 py-16 px-8",__self:this,__source:{fileName:N,lineNumber:134}},e("div",{className:"max-w-7xl mx-auto text-center",__self:this,__source:{fileName:N,lineNumber:135}},e("h2",{className:"text-text-1 text-4xl font-bold mb-4",__self:this,__source:{fileName:N,lineNumber:136}},s.title),e("p",{className:"text-text-2 text-lg mb-8",__self:this,__source:{fileName:N,lineNumber:139}},s.subTitle),e("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-8",__self:this,__source:{fileName:N,lineNumber:142}},e("div",{className:"bg-data-4 p-6 rounded-lg shadow-lg",__self:this,__source:{fileName:N,lineNumber:143}},e("h3",{className:"text-primary text-2xl font-semibold mb-4",__self:this,__source:{fileName:N,lineNumber:144}},s.reviewUser1),e("p",{className:"text-text-1",__self:this,__source:{fileName:N,lineNumber:147}},s.reviewDesc1)),e("div",{className:"bg-data-8 p-6 rounded-lg shadow-lg",__self:this,__source:{fileName:N,lineNumber:151}},e("h3",{className:"text-primary text-2xl font-semibold mb-4",__self:this,__source:{fileName:N,lineNumber:152}},s.reviewUser2),e("p",{className:"text-text-1",__self:this,__source:{fileName:N,lineNumber:155}},s.reviewDesc2))))),x=s=>e("div",{className:"bg-background-1 py-16 px-8",__self:this,__source:{fileName:N,lineNumber:177}},e("div",{className:"max-w-7xl mx-auto text-center",__self:this,__source:{fileName:N,lineNumber:178}},e("h2",{className:"text-text-1 text-4xl font-bold mb-4",__self:this,__source:{fileName:N,lineNumber:179}},s.title),e("p",{className:"text-text-2 text-lg mb-8",__self:this,__source:{fileName:N,lineNumber:182}},s.subTitle),e("div",{className:"text-left max-w-3xl mx-auto",__self:this,__source:{fileName:N,lineNumber:185}},e("div",{className:"mb-8",__self:this,__source:{fileName:N,lineNumber:186}},e("h3",{className:"text-primary text-2xl font-semibold mb-2",__self:this,__source:{fileName:N,lineNumber:187}},s.faq1),e("p",{className:"text-text-3",__self:this,__source:{fileName:N,lineNumber:190}},s.faqAnswer1)),e("div",{className:"mb-8",__self:this,__source:{fileName:N,lineNumber:194}},e("h3",{className:"text-primary text-2xl font-semibold mb-2",__self:this,__source:{fileName:N,lineNumber:195}},s.faq2),e("p",{className:"text-text-3",__self:this,__source:{fileName:N,lineNumber:198}},s.faqAnswer2)),e("div",{className:"mb-8",__self:this,__source:{fileName:N,lineNumber:202}},e("h3",{className:"text-primary text-2xl font-semibold mb-2",__self:this,__source:{fileName:N,lineNumber:203}},s.faq3),e("p",{className:"text-text-3",__self:this,__source:{fileName:N,lineNumber:206}},s.faqAnswer3))))),b=s=>e("div",{className:"bg-primary-1 py-16 px-8",__self:this,__source:{fileName:N,lineNumber:222}},e("div",{className:"max-w-7xl mx-auto text-center",__self:this,__source:{fileName:N,lineNumber:223}},e("h2",{className:"text-text-1 text-4xl font-bold mb-4",__self:this,__source:{fileName:N,lineNumber:224}},s.title),e("p",{className:"text-text-2 text-lg mb-8",__self:this,__source:{fileName:N,lineNumber:227}},s.subTitle))),n=()=>e("div",{__self:this,__source:{fileName:N,lineNumber:238}},e(u,{...props[0],__self:this,__source:{fileName:N,lineNumber:239}}),e(c,{...props[1],__self:this,__source:{fileName:N,lineNumber:240}}),e(f,{...props[2],__self:this,__source:{fileName:N,lineNumber:241}}),e(o,{...props[3],__self:this,__source:{fileName:N,lineNumber:242}}),e(x,{...props[4],__self:this,__source:{fileName:N,lineNumber:243}}),e(b,{...props[5],__self:this,__source:{fileName:N,lineNumber:244}}));render(n);`; diff --git a/src/pages/creation/completions.ts b/src/pages/creation/completions.ts new file mode 100644 index 0000000..4b8700b --- /dev/null +++ b/src/pages/creation/completions.ts @@ -0,0 +1,98 @@ +import { Extension } from '@codemirror/state'; +import { CompletionContext, autocompletion, Completion } from '@codemirror/autocomplete'; + +const colorMap = [ + { label: "--primary-7", type: "variable", info: "(点击(click))" }, + { label: "--primary-6", type: "variable", info: "(常规)" }, + { label: "--primary-5", type: "variable", info: "(悬浮(hover))" }, + { label: "--primary-4", type: "variable", info: "(特殊场景)" }, + { label: "--primary-3", type: "variable", info: "(一般禁用)" }, + { label: "--primary-2", type: "variable", info: "(文字禁用)" }, + { label: "--primary-1", type: "variable", info: "(浅色/白底悬浮)" }, + { label: "--success-7", type: "variable", info: "(点击(click))" }, + { label: "--success-6", type: "variable", info: "(常规)" }, + { label: "--success-5", type: "variable", info: "(悬浮(hover))" }, + { label: "--success-4", type: "variable", info: "(特殊场景)" }, + { label: "--success-3", type: "variable", info: "(一般禁用)" }, + { label: "--success-2", type: "variable", info: "(文字禁用)" }, + { label: "--success-1", type: "variable", info: "(浅色/白底悬浮)" }, + { label: "--warning-7", type: "variable", info: "(点击(click))" }, + { label: "--warning-6", type: "variable", info: "(常规)" }, + { label: "--warning-5", type: "variable", info: "(悬浮(hover))" }, + { label: "--warning-4", type: "variable", info: "(特殊场景)" }, + { label: "--warning-3", type: "variable", info: "(一般禁用)" }, + { label: "--warning-2", type: "variable", info: "(文字禁用)" }, + { label: "--warning-1", type: "variable", info: "(浅色/白底悬浮)" }, + { label: "--danger-7", type: "variable", info: "(点击(click))" }, + { label: "--danger-6", type: "variable", info: "(常规)" }, + { label: "--danger-5", type: "variable", info: "(悬浮(hover))" }, + { label: "--danger-4", type: "variable", info: "(特殊场景)" }, + { label: "--danger-3", type: "variable", info: "(一般禁用)" }, + { label: "--danger-2", type: "variable", info: "(文字禁用)" }, + { label: "--danger-1", type: "variable", info: "(浅色/白底悬浮)" }, + { label: "--link-7", type: "variable", info: "(点击(click))" }, + { label: "--link-6", type: "variable", info: "(常规)" }, + { label: "--link-5", type: "variable", info: "(悬浮(hover))" }, + { label: "--link-4", type: "variable", info: "(特殊场景)" }, + { label: "--link-3", type: "variable", info: "(一般禁用)" }, + { label: "--link-2", type: "variable", info: "(文字禁用)" }, + { label: "--link-1", type: "variable", info: "(浅色/白底悬浮)" }, + { label: "--data-1", type: "variable", info: "" }, + { label: "--data-2", type: "variable", info: "" }, + { label: "--data-3", type: "variable", info: "" }, + { label: "--data-4", type: "variable", info: "" }, + { label: "--data-5", type: "variable", info: "" }, + { label: "--data-6", type: "variable", info: "" }, + { label: "--data-7", type: "variable", info: "" }, + { label: "--data-8", type: "variable", info: "" }, + { label: "--data-9", type: "variable", info: "" }, + { label: "--data-10", type: "variable", info: "" }, + { label: "--data-11", type: "variable", info: "" }, + { label: "--data-12", type: "variable", info: "" }, + { label: "--data-13", type: "variable", info: "" }, + { label: "--data-14", type: "variable", info: "" }, + { label: "--data-15", type: "variable", info: "" }, + { label: "--data-16", type: "variable", info: "" }, + { label: "--data-17", type: "variable", info: "" }, + { label: "--data-18", type: "variable", info: "" }, + { label: "--data-19", type: "variable", info: "" }, + { label: "--data-20", type: "variable", info: "" }, + { label: "--color-border-1", type: "variable", info: "(浅色)" }, + { label: "--color-border-2", type: "variable", info: "(一般)" }, + { label: "--color-border-3", type: "variable", info: "(深/悬浮)" }, + { label: "--color-border-4", type: "variable", info: "(重/按钮描边)" }, + { label: "--color-fill-1", type: "variable", info: "(浅/禁用)" }, + { label: "--color-fill-2", type: "variable", info: "(常规/白底悬浮)" }, + { label: "--color-fill-3", type: "variable", info: "(深/灰底悬浮)" }, + { label: "--color-fill-4", type: "variable", info: "(重/特殊场景)" }, + { label: "--color-text-1", type: "variable", info: "(强调/正文标题)" }, + { label: "--color-text-2", type: "variable", info: "(次强调/正文标题)" }, + { label: "--color-text-3", type: "variable", info: "(次要信息)" }, + { label: "--color-text-4", type: "variable", info: "(置灰信息)" }, + { label: "--color-bg-1", type: "variable", info: "(整体背景色)" }, + { label: "--color-bg-2", type: "variable", info: "(一级容器背景)" }, + { label: "--color-bg-3", type: "variable", info: "(二级容器背景)" }, + { label: "--color-bg-4", type: "variable", info: "(三级容器背景)" }, + { label: "--color-bg-5", type: "variable", info: "(下拉弹出框、Tooltip 背景颜色)" }, + { label: "--color-bg-white", type: "variable", info: "(白色背景)" } +]; + +export function colorCompletions(data: Completion[] = []): Extension { + return autocompletion({ + override: [ + (context: CompletionContext) => { + let word = context.matchBefore(/--\w+/g); + if (!word) return null; + if (word && word.from == word.to && !context.explicit) { + return null; + } + return { + from: word?.from!, + options: [...data], + }; + }, + ], + }); +} + +export const plugin = colorCompletions(colorMap); \ No newline at end of file diff --git a/src/pages/creation/index.tsx b/src/pages/creation/index.tsx new file mode 100644 index 0000000..537531d --- /dev/null +++ b/src/pages/creation/index.tsx @@ -0,0 +1,191 @@ +import React from "react"; +import CodeMirror, { basicSetup } from "@uiw/react-codemirror"; +import { loadLanguage, langs } from "@uiw/codemirror-extensions-langs"; +import LiveProvider from "../../components/Live/LiveProvider"; +import LivePreview from "../../components/Live/LivePreview"; +import LiveError from "../../components/Live/LiveError"; +import { color, colorView, colorTheme } from "@uiw/codemirror-extensions-color"; +import JsonViewEditor from "@uiw/react-json-view/editor"; +import { ThemeProvider } from "../../components/Theme/ThemePrivider"; +import { plugin as colorCom } from "./completions"; + +loadLanguage("tsx"); +const extensions = [color, langs.tsx(), colorView(false), colorTheme, colorCom]; + +export const Creation = () => { + // useEffect 拦截ctrls等操作,避免浏览器默认行为 + React.useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + if (event.ctrlKey && event.key === "s") { + event.preventDefault(); + // 弹出已经保存的提示 + } + }; + document.addEventListener("keydown", handleKeyDown); + return () => { + document.removeEventListener("keydown", handleKeyDown); + }; + }, []); + + const [componentName, setComponentName] = React.useState("Banner"); + + const [componentsProps, setComponentsProps] = React.useState({ + title: "AI智能建站助手", + subTitle: "让我们帮助您快速构建和优化您的网站", + btnText: "立即开始", + }); + + const [newPropKey, setNewPropKey] = React.useState(""); + + const handleAddProp = () => { + if (newPropKey.trim() !== "") { + setComponentsProps((prevProps) => ({ + ...prevProps, + [newPropKey]: "", + })); + setNewPropKey(""); + } + }; + + const [code, setCode] = React.useState(` + const Banner = (props: { + title: string + subTitle: string + btnText: string + }) => { + function handleClick() { + console.log("Hi there!"); + } + return ( +
+
+

+ {props.title} +

+

+ {props.subTitle} +

+ +
+
+ ); + }; +`); + + const renderCode = (code: string) => { + return ` + ${code} + render(${componentName}); + `; + }; + + const onChange = React.useCallback((value: string) => { + setCode(value); + }, []); + + const handleComponentNameChange = ( + event: React.ChangeEvent + ) => { + setComponentName(event.target.value); + }; + + return ( + +
+
+ + +
+ +
+
+ +
+
+ +
+
+ +
+
+

Props 编辑器

+
+
+ +
+ setNewPropKey(e.target.value)} + className="border border-text-1 rounded p-2 flex-grow" + /> + +
+
+ { + const updateNestedProp = ( + obj: Record, + path: string[], + value: unknown + ) => { + if (path.length === 1) { + obj[path[0]] = value; + } else { + if (!obj[path[0]]) { + obj[path[0]] = {}; + } + updateNestedProp(obj[path[0]], path.slice(1), value); + } + }; + const updatedProps = { ...componentsProps }; + updateNestedProp( + updatedProps, + (opts as any).namespace, + opts.value + ); + setComponentsProps(updatedProps); + return true; + }} + /> +
+
+
+
+ ); +}; diff --git a/src/pages/editor/index.tsx b/src/pages/editor/index.tsx index ec16b8b..ce69644 100644 --- a/src/pages/editor/index.tsx +++ b/src/pages/editor/index.tsx @@ -7,7 +7,7 @@ import { useState } from "react"; import { functionBlockList } from "../../data/functionBlocks"; import ClickContextProvider from "../../components/Selected/ClickContext"; -const code = (func: string,cpnName:string) => ` +const code = (func: string, cpnName: string) => ` ${func} render(${cpnName}) `; @@ -112,7 +112,11 @@ export const Editor = () => { )} {functionBlockList.map((func, index) => { return ( - + Hello world!, - }, - { - path: "/editor", - element: , - }, - { - path: "/app", - element: , - } + { + path: "/", + element:
Hello world!
, + }, + { + path: "/editor", + element: , + }, + { + path: "/app", + element: , + }, + { + path: "/create", + element: , + }, ]); diff --git a/src/utils/minify/index.ts b/src/utils/minify/index.ts new file mode 100644 index 0000000..d246c0f --- /dev/null +++ b/src/utils/minify/index.ts @@ -0,0 +1,31 @@ +import { minify } from "terser"; + +export default async function minifyCode(code: string) { + const replaced = code + .replace(RegExp(`React.createElement`, "g"), `rc`) + .replace(RegExp(`React.Fragment`, "g"), `rf`) + .replace(RegExp(`React.useState`, "g"), `rs`) + .replace(RegExp(`React.useEffect`, "g"), `re`) + .replace(RegExp(`React.Component`, "g"), `rcp`) + .replace(RegExp(`React.createContext`, "g"), `rcc`) + .replace(RegExp(`React.useContext`, "g"), `rcu`) + .replace(RegExp(`React.useReducer`, "g"), `rd`) + .replace(RegExp(`React.memo`, "g"), `rm`) + .replace( + `"use strict";`, + `"use strict"; const rc=React.createElement,rf=React.Fragment,rs=React.useState,re=React.useEffect,rcp=React.Component,rcc=React.createContext,rcu=React.useContext,rd=React.useReducer,rm=React.memo;` + ); + + const { code: minifiedCode } = await minify(replaced, { + compress: { + passes: 2, + drop_console: true, + drop_debugger: true, + arguments: true, + }, + mangle: { + toplevel: true, + }, + }); + return minifiedCode || ""; +}