@jbpark/live-editor 4.4.0 → 4.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +4 -1
- package/README.md +7 -3
- package/dist/{boundary-B17cepvm.js → boundary-BLB80tyw.js} +8 -5
- package/dist/boundary-BLB80tyw.js.map +1 -0
- package/dist/cache-B0-XcIxJ.js.map +1 -1
- package/dist/{compile-DDfk5P25.js → compile-Dxas-D05.js} +3 -3
- package/dist/compile-Dxas-D05.js.map +1 -0
- package/dist/{constants-h1JAgOeW.js → constants-DU17Tdwo.js} +3 -3
- package/dist/constants-DU17Tdwo.js.map +1 -0
- package/dist/{context-DB5kW3qe.js → context-B8MzP7_p.js} +11 -5
- package/dist/context-B8MzP7_p.js.map +1 -0
- package/dist/{core-CRsAGpei.js → core-Bgfek364.js} +2 -2
- package/dist/core-Bgfek364.js.map +1 -0
- package/dist/{detect-typescript-CCj9ALXc.js → detect-typescript-Dr_oLniM.js} +2 -2
- package/dist/detect-typescript-Dr_oLniM.js.map +1 -0
- package/dist/dnd/index.d.ts +1 -1
- package/dist/dnd/index.js +1 -1
- package/dist/{dnd-BE5ZizqX.js → dnd-UoJILA75.js} +973 -885
- package/dist/dnd-UoJILA75.js.map +1 -0
- package/dist/{document-BDn_f08_.js → document-CGbe6bSV.js} +7739 -7736
- package/dist/document-CGbe6bSV.js.map +1 -0
- package/dist/editor/index.js +2 -2
- package/dist/{editor-DQ8Q--qR.js → editor-CMElfHer.js} +2 -2
- package/dist/editor-CMElfHer.js.map +1 -0
- package/dist/editor-caches-ClMvdcQF.js.map +1 -1
- package/dist/error/index.d.ts +1 -1
- package/dist/error/index.js +2 -2
- package/dist/{error-DWhxQEom.js → error-CN_GCnXB.js} +13 -6
- package/dist/error-CN_GCnXB.js.map +1 -0
- package/dist/{index-Dgh1JTX3.d.ts → index-C69Lv-jR.d.ts} +72 -69
- package/dist/{index-C4SGQ3Ci.d.ts → index-CRwEhJ7w.d.ts} +4 -1
- package/dist/{index-f7ijuNTa.d.ts → index-DKYUxi-E.d.ts} +115 -155
- package/dist/index-DrWtSKXu.d.ts +11 -0
- package/dist/index.d.ts +18 -18
- package/dist/index.js +12 -8
- package/dist/index.js.map +1 -1
- package/dist/{ast-dYb78955.js → items-B6OXxcLG.js} +588 -718
- package/dist/items-B6OXxcLG.js.map +1 -0
- package/dist/messages-CSrMI4Rm.js +133 -0
- package/dist/messages-CSrMI4Rm.js.map +1 -0
- package/dist/messages-IBPs4TkZ.d.ts +130 -0
- package/dist/preview/index.js +2 -2
- package/dist/{preview-BnaCEFsF.js → preview-CtyG5UbH.js} +6 -4
- package/dist/preview-CtyG5UbH.js.map +1 -0
- package/dist/provider/index.d.ts +3 -8
- package/dist/provider/index.js +3 -2
- package/dist/{sections-CBVMO8Jl.js → sections-BYWOBISr.js} +9 -4
- package/dist/sections-BYWOBISr.js.map +1 -0
- package/dist/states-Ci1AvoQ9.js.map +1 -1
- package/dist/style.css +505 -21
- package/dist/{use-dynamic-tailwind-Bcipc1hL.js → use-dynamic-tailwind-BP1toJkW.js} +66 -40
- package/dist/use-dynamic-tailwind-BP1toJkW.js.map +1 -0
- package/dist/utils/ast/index.d.ts +3 -2
- package/dist/utils/ast/index.js +150 -3
- package/dist/utils/ast/index.js.map +1 -0
- package/dist/utils/index.d.ts +30 -13
- package/dist/utils/index.js +26 -6
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/tailwind/index.js.map +1 -1
- package/dist/validate-DB59Z_Nx.d.ts +100 -0
- package/dist/validate-iLo8IONY.js +64 -0
- package/dist/validate-iLo8IONY.js.map +1 -0
- package/package.json +3 -3
- package/dist/ast-dYb78955.js.map +0 -1
- package/dist/boundary-B17cepvm.js.map +0 -1
- package/dist/compile-DDfk5P25.js.map +0 -1
- package/dist/constants-h1JAgOeW.js.map +0 -1
- package/dist/context-DB5kW3qe.js.map +0 -1
- package/dist/core-CRsAGpei.js.map +0 -1
- package/dist/detect-typescript-CCj9ALXc.js.map +0 -1
- package/dist/dnd-BE5ZizqX.js.map +0 -1
- package/dist/document-BDn_f08_.js.map +0 -1
- package/dist/editor-DQ8Q--qR.js.map +0 -1
- package/dist/error-DWhxQEom.js.map +0 -1
- package/dist/preview-BnaCEFsF.js.map +0 -1
- package/dist/sections-CBVMO8Jl.js.map +0 -1
- package/dist/use-dynamic-tailwind-Bcipc1hL.js.map +0 -1
package/README.ko.md
CHANGED
|
@@ -34,6 +34,8 @@ live-editor/
|
|
|
34
34
|
└─ package.json
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
각 부분이 어떻게 맞물리는지(읽기·쓰기 경로, commit 단계, 용어집)는 [ARCHITECTURE.ko.md](./ARCHITECTURE.ko.md)에 있습니다.
|
|
38
|
+
|
|
37
39
|
## 🎯 주요 기능
|
|
38
40
|
|
|
39
41
|
- **AST 기반 실시간 코드 반영**: 캔버스 상호작용(추가/이동/삭제, 속성 변경)을 안전하게 소스 코드에 적용합니다.
|
|
@@ -136,7 +138,8 @@ export default function Example() {
|
|
|
136
138
|
## 🚀 개발 (이 저장소)
|
|
137
139
|
|
|
138
140
|
> Live Editor 자체를 개발할 때 참고하세요. 패키지를 앱에서 _사용_ 하려면
|
|
139
|
-
> 위의 [설치](#-설치)를 보세요.
|
|
141
|
+
> 위의 [설치](#-설치)를 보세요. 코드 구조는
|
|
142
|
+
> [ARCHITECTURE.ko.md](./ARCHITECTURE.ko.md)부터 읽기를 권합니다.
|
|
140
143
|
|
|
141
144
|
### pnpm 설정 (권장)
|
|
142
145
|
|
package/README.md
CHANGED
|
@@ -34,6 +34,9 @@ live-editor/
|
|
|
34
34
|
└─ package.json
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
How the pieces fit together — the read and write paths, the commit levels and
|
|
38
|
+
a glossary — is in [ARCHITECTURE.md](./ARCHITECTURE.md).
|
|
39
|
+
|
|
37
40
|
## 🎯 Highlights
|
|
38
41
|
|
|
39
42
|
- **Real-time code updates**: Canvas interactions (add/move/remove, property edits) are propagated back to source code safely.
|
|
@@ -130,13 +133,14 @@ can avoid pulling in the rest (e.g. no CodeMirror in a preview-only build) — s
|
|
|
130
133
|
## ⚙️ Requirements
|
|
131
134
|
|
|
132
135
|
- Peer deps: `react >=19`, `react-dom >=19`
|
|
133
|
-
- Package metadata declares Node.js >=20
|
|
134
|
-
- **pnpm
|
|
136
|
+
- Package metadata declares Node.js >=20. CI checks that the entries meant for Node (`utils`, `utils/ast`, `utils/tailwind`, `provider`, `error`) load on Node 20. Repository development, builds and the rest of CI use the version pinned in `.nvmrc` (currently 24.18.0).
|
|
137
|
+
- **pnpm** (developing this repo only): the version in `packageManager` (currently 12.8.1), managed via [Corepack](https://nodejs.org/api/corepack.html). Installing the package works with any package manager.
|
|
135
138
|
|
|
136
139
|
## 🚀 Development (this repo)
|
|
137
140
|
|
|
138
141
|
> Working on Live Editor itself. To _use_ the package in your app, see
|
|
139
|
-
> [Installation](#-installation) above.
|
|
142
|
+
> [Installation](#-installation) above. Start with
|
|
143
|
+
> [ARCHITECTURE.md](./ARCHITECTURE.md) for how the code is organized.
|
|
140
144
|
|
|
141
145
|
### pnpm Setup (Recommended)
|
|
142
146
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { i as useLiveMessages, t as MessagesContext } from "./messages-CSrMI4Rm.js";
|
|
1
2
|
import { t as cn } from "./cn-CAnad2vL.js";
|
|
2
3
|
import React from "react";
|
|
3
4
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
5
|
//#region src/components/error/error.tsx
|
|
5
|
-
const Error = ({ message, title
|
|
6
|
+
const Error = ({ message, title, className, onReset }) => {
|
|
7
|
+
const messages = useLiveMessages();
|
|
6
8
|
if (!message) return null;
|
|
7
9
|
return /* @__PURE__ */ jsxs("div", {
|
|
8
10
|
className: cn(className),
|
|
@@ -20,7 +22,7 @@ const Error = ({ message, title = "An error occurred", className, onReset }) =>
|
|
|
20
22
|
fontWeight: "bold",
|
|
21
23
|
margin: 0
|
|
22
24
|
},
|
|
23
|
-
children: title
|
|
25
|
+
children: title ?? messages.errors.default
|
|
24
26
|
}),
|
|
25
27
|
/* @__PURE__ */ jsx("pre", {
|
|
26
28
|
style: {
|
|
@@ -43,7 +45,7 @@ const Error = ({ message, title = "An error occurred", className, onReset }) =>
|
|
|
43
45
|
cursor: "pointer"
|
|
44
46
|
},
|
|
45
47
|
onClick: onReset,
|
|
46
|
-
children:
|
|
48
|
+
children: messages.errors.tryAgain
|
|
47
49
|
})
|
|
48
50
|
]
|
|
49
51
|
});
|
|
@@ -52,6 +54,7 @@ const Error = ({ message, title = "An error occurred", className, onReset }) =>
|
|
|
52
54
|
//#region src/components/error/boundary.tsx
|
|
53
55
|
const keysChanged = (prev = [], next = []) => prev.length !== next.length || next.some((key, i) => key !== prev[i]);
|
|
54
56
|
var ErrorBoundary = class extends React.Component {
|
|
57
|
+
static contextType = MessagesContext;
|
|
55
58
|
constructor(props) {
|
|
56
59
|
super(props);
|
|
57
60
|
this.state = {
|
|
@@ -85,7 +88,7 @@ var ErrorBoundary = class extends React.Component {
|
|
|
85
88
|
if (this.state.hasError) return this.props.fallback ? /* @__PURE__ */ jsx(Fragment, { children: this.props.fallback(this.state.error?.message, this.reset) }) : /* @__PURE__ */ jsx(Error, {
|
|
86
89
|
message: this.state.error?.message,
|
|
87
90
|
onReset: this.reset,
|
|
88
|
-
title:
|
|
91
|
+
title: this.context.errors.rendering
|
|
89
92
|
});
|
|
90
93
|
return this.props.children;
|
|
91
94
|
}
|
|
@@ -93,4 +96,4 @@ var ErrorBoundary = class extends React.Component {
|
|
|
93
96
|
//#endregion
|
|
94
97
|
export { Error as n, ErrorBoundary as t };
|
|
95
98
|
|
|
96
|
-
//# sourceMappingURL=boundary-
|
|
99
|
+
//# sourceMappingURL=boundary-BLB80tyw.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boundary-BLB80tyw.js","names":[],"sources":["../src/components/error/error.tsx","../src/components/error/boundary.tsx"],"sourcesContent":["import { useLiveMessages } from '~/components/context/messages';\nimport { cn } from '~/utils/cn';\n\nexport interface Props extends React.ComponentPropsWithRef<'div'> {\n message?: string | null;\n title?: string;\n onReset?: () => void;\n}\n\nconst Error = ({ message, title, className, onReset }: Props) => {\n const messages = useLiveMessages();\n\n if (!message) {\n return null;\n }\n\n return (\n <div\n className={cn(className)}\n style={{\n padding: '16px',\n backgroundColor: '#fee2e2',\n border: '1px solid #fca5a5',\n borderRadius: '6px',\n color: '#7f1d1d',\n }}\n >\n <h3 style={{ fontSize: '14px', fontWeight: 'bold', margin: 0 }}>\n {title ?? messages.errors.default}\n </h3>\n <pre\n style={{\n marginTop: '8px',\n fontSize: '12px',\n whiteSpace: 'pre-wrap',\n margin: '8px 0 0 0',\n }}\n >\n {message}\n </pre>\n {onReset && (\n <button\n style={{\n marginTop: '8px',\n padding: '4px 12px',\n fontSize: '12px',\n color: 'white',\n backgroundColor: '#dc2626',\n border: 'none',\n borderRadius: '4px',\n cursor: 'pointer',\n }}\n onClick={onReset}\n >\n {messages.errors.tryAgain}\n </button>\n )}\n </div>\n );\n};\n\nexport default Error;\n","import React from 'react';\n\nimport {\n type LiveMessages,\n MessagesContext,\n} from '~/components/context/messages';\n\nimport Error from './error';\n\ninterface Props {\n children: React.ReactNode;\n // `reset` clears the caught error and renders `children` again, the same\n // thing the built-in error's reset button does.\n fallback?: (message?: string, reset?: () => void) => React.ReactNode;\n onError?: (e: Error, info: React.ErrorInfo) => void;\n // When any of these values changes (compared one by one, by identity), a\n // caught error resets and `children` render again, such as when the code\n // that failed is edited.\n resetKeys?: readonly unknown[];\n}\n\ninterface State {\n hasError: boolean;\n error?: Error;\n // The resetKeys the last render saw, so getDerivedStateFromProps can tell\n // when they change.\n resetKeys?: readonly unknown[];\n}\n\nconst keysChanged = (\n prev: readonly unknown[] = [],\n next: readonly unknown[] = [],\n) => prev.length !== next.length || next.some((key, i) => key !== prev[i]);\n\nclass ErrorBoundary extends React.Component<Props, State> {\n static contextType = MessagesContext;\n declare context: LiveMessages;\n\n constructor(props: Props) {\n super(props);\n this.state = { hasError: false, resetKeys: props.resetKeys };\n }\n\n static getDerivedStateFromError(error: Error): Partial<State> {\n return { hasError: true, error };\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n console.error('🚨 [Boundary] Rendering error:', error, errorInfo);\n this.props.onError?.(error, errorInfo);\n }\n\n // Clears a caught error in the same render that sees new resetKeys, so\n // `children` get their next chance right away and the fallback doesn't\n // render (and run its effects) once more first (#442). If `children` throw\n // again with the new keys, getDerivedStateFromError catches that in this\n // same render pass.\n static getDerivedStateFromProps(\n props: Props,\n state: State,\n ): Partial<State> | null {\n if (!keysChanged(state.resetKeys, props.resetKeys)) {\n return null;\n }\n\n return state.hasError\n ? { hasError: false, error: undefined, resetKeys: props.resetKeys }\n : { resetKeys: props.resetKeys };\n }\n\n reset = () => this.setState({ hasError: false, error: undefined });\n\n render() {\n if (this.state.hasError) {\n return this.props.fallback ? (\n <>{this.props.fallback(this.state.error?.message, this.reset)}</>\n ) : (\n <Error\n message={this.state.error?.message}\n onReset={this.reset}\n title={this.context.errors.rendering}\n />\n );\n }\n\n return this.props.children;\n }\n}\n\nexport default ErrorBoundary;\n"],"mappings":";;;;;AASA,MAAM,SAAS,EAAE,SAAS,OAAO,WAAW,cAAqB;CAC/D,MAAM,WAAW,gBAAgB;CAEjC,IAAI,CAAC,SACH,OAAO;CAGT,OACE,qBAAC,OAAD;EACE,WAAW,GAAG,SAAS;EACvB,OAAO;GACL,SAAS;GACT,iBAAiB;GACjB,QAAQ;GACR,cAAc;GACd,OAAO;EACT;EARF,UAAA;GAUE,oBAAC,MAAD;IAAI,OAAO;KAAE,UAAU;KAAQ,YAAY;KAAQ,QAAQ;IAAE;IAC1D,UAAA,SAAS,SAAS,OAAO;GACxB,CAAA;GACJ,oBAAC,OAAD;IACE,OAAO;KACL,WAAW;KACX,UAAU;KACV,YAAY;KACZ,QAAQ;IACV;IAEC,UAAA;GACE,CAAA;GACJ,WACC,oBAAC,UAAD;IACE,OAAO;KACL,WAAW;KACX,SAAS;KACT,UAAU;KACV,OAAO;KACP,iBAAiB;KACjB,QAAQ;KACR,cAAc;KACd,QAAQ;IACV;IACA,SAAS;IAER,UAAA,SAAS,OAAO;GACX,CAAA;EAEP;;AAET;;;AC9BA,MAAM,eACJ,OAA2B,CAAC,GAC5B,OAA2B,CAAC,MACzB,KAAK,WAAW,KAAK,UAAU,KAAK,MAAM,KAAK,MAAM,QAAQ,KAAK,EAAE;AAEzE,IAAM,gBAAN,cAA4B,MAAM,UAAwB;CACxD,OAAO,cAAc;CAGrB,YAAY,OAAc;EACxB,MAAM,KAAK;EACX,KAAK,QAAQ;GAAE,UAAU;GAAO,WAAW,MAAM;EAAU;CAC7D;CAEA,OAAO,yBAAyB,OAA8B;EAC5D,OAAO;GAAE,UAAU;GAAM;EAAM;CACjC;CAEA,kBAAkB,OAAc,WAA4B;EAC1D,QAAQ,MAAM,kCAAkC,OAAO,SAAS;EAChE,KAAK,MAAM,UAAU,OAAO,SAAS;CACvC;CAOA,OAAO,yBACL,OACA,OACuB;EACvB,IAAI,CAAC,YAAY,MAAM,WAAW,MAAM,SAAS,GAC/C,OAAO;EAGT,OAAO,MAAM,WACT;GAAE,UAAU;GAAO,OAAO,KAAA;GAAW,WAAW,MAAM;EAAU,IAChE,EAAE,WAAW,MAAM,UAAU;CACnC;CAEA,cAAc,KAAK,SAAS;EAAE,UAAU;EAAO,OAAO,KAAA;CAAU,CAAC;CAEjE,SAAS;EACP,IAAI,KAAK,MAAM,UACb,OAAO,KAAK,MAAM,WAChB,oBAAA,UAAA,EAAA,UAAG,KAAK,MAAM,SAAS,KAAK,MAAM,OAAO,SAAS,KAAK,KAAK,EAAI,CAAA,IAEhE,oBAAC,OAAD;GACE,SAAS,KAAK,MAAM,OAAO;GAC3B,SAAS,KAAK;GACd,OAAO,KAAK,QAAQ,OAAO;EAC5B,CAAA;EAIL,OAAO,KAAK,MAAM;CACpB;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache-B0-XcIxJ.js","names":[],"sources":["../src/utils/cache.ts"],"sourcesContent":["export interface BoundedCache<K, V> {\n has: (key: K) => boolean;\n get: (key: K) => V | undefined;\n set: (key: K, value: V) => void;\n delete: (key: K) => boolean;\n clear: () => void;\n readonly size: number;\n}\n\n//
|
|
1
|
+
{"version":3,"file":"cache-B0-XcIxJ.js","names":[],"sources":["../src/utils/cache.ts"],"sourcesContent":["export interface BoundedCache<K, V> {\n has: (key: K) => boolean;\n get: (key: K) => V | undefined;\n set: (key: K, value: V) => void;\n delete: (key: K) => boolean;\n clear: () => void;\n readonly size: number;\n}\n\n// A fixed-size LRU cache, used by `compile()`, the script cache and\n// `extract()`. A `Map` keeps insertion order, and `get` and `set` move an\n// entry to the end, so the first key is always the least recently used.\n// `onEvict` releases what an evicted value holds, such as a blob URL.\nexport const createBoundedCache = <K, V>(\n limit: number,\n onEvict?: (key: K, value: V) => void,\n): BoundedCache<K, V> => {\n const store = new Map<K, V>();\n\n const get = (key: K): V | undefined => {\n if (!store.has(key)) {\n return undefined;\n }\n\n const value = store.get(key)!;\n\n store.delete(key);\n store.set(key, value);\n\n return value;\n };\n\n const set = (key: K, value: V) => {\n if (store.has(key)) {\n const oldValue = store.get(key)!;\n\n store.delete(key);\n onEvict?.(key, oldValue);\n } else if (store.size >= limit) {\n const oldestKey = store.keys().next().value;\n\n if (oldestKey !== undefined) {\n const oldestValue = store.get(oldestKey)!;\n\n store.delete(oldestKey);\n onEvict?.(oldestKey, oldestValue);\n }\n }\n\n store.set(key, value);\n };\n\n const deleteKey = (key: K): boolean => {\n if (!store.has(key)) {\n return false;\n }\n\n const value = store.get(key)!;\n\n store.delete(key);\n onEvict?.(key, value);\n\n return true;\n };\n\n const clear = () => {\n for (const [key, value] of store) {\n onEvict?.(key, value);\n }\n\n store.clear();\n };\n\n return {\n has: key => store.has(key),\n get,\n set,\n delete: deleteKey,\n clear,\n get size() {\n return store.size;\n },\n };\n};\n"],"mappings":";AAaA,MAAa,sBACX,OACA,YACuB;CACvB,MAAM,wBAAQ,IAAI,IAAU;CAE5B,MAAM,OAAO,QAA0B;EACrC,IAAI,CAAC,MAAM,IAAI,GAAG,GAChB;EAGF,MAAM,QAAQ,MAAM,IAAI,GAAG;EAE3B,MAAM,OAAO,GAAG;EAChB,MAAM,IAAI,KAAK,KAAK;EAEpB,OAAO;CACT;CAEA,MAAM,OAAO,KAAQ,UAAa;EAChC,IAAI,MAAM,IAAI,GAAG,GAAG;GAClB,MAAM,WAAW,MAAM,IAAI,GAAG;GAE9B,MAAM,OAAO,GAAG;GAChB,UAAU,KAAK,QAAQ;EACzB,OAAO,IAAI,MAAM,QAAQ,OAAO;GAC9B,MAAM,YAAY,MAAM,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;GAEtC,IAAI,cAAc,KAAA,GAAW;IAC3B,MAAM,cAAc,MAAM,IAAI,SAAS;IAEvC,MAAM,OAAO,SAAS;IACtB,UAAU,WAAW,WAAW;GAClC;EACF;EAEA,MAAM,IAAI,KAAK,KAAK;CACtB;CAEA,MAAM,aAAa,QAAoB;EACrC,IAAI,CAAC,MAAM,IAAI,GAAG,GAChB,OAAO;EAGT,MAAM,QAAQ,MAAM,IAAI,GAAG;EAE3B,MAAM,OAAO,GAAG;EAChB,UAAU,KAAK,KAAK;EAEpB,OAAO;CACT;CAEA,MAAM,cAAc;EAClB,KAAK,MAAM,CAAC,KAAK,UAAU,OACzB,UAAU,KAAK,KAAK;EAGtB,MAAM,MAAM;CACd;CAEA,OAAO;EACL,MAAK,QAAO,MAAM,IAAI,GAAG;EACzB;EACA;EACA,QAAQ;EACR;EACA,IAAI,OAAO;GACT,OAAO,MAAM;EACf;CACF;AACF"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { n as CONFIG } from "./constants-
|
|
1
|
+
import { n as CONFIG } from "./constants-DU17Tdwo.js";
|
|
2
2
|
import { n as registerEditorCache } from "./editor-caches-ClMvdcQF.js";
|
|
3
3
|
import { t as createBoundedCache } from "./cache-B0-XcIxJ.js";
|
|
4
|
-
import { t as detectTypeScript } from "./detect-typescript-
|
|
4
|
+
import { t as detectTypeScript } from "./detect-typescript-Dr_oLniM.js";
|
|
5
5
|
import React from "react";
|
|
6
6
|
import * as Babel from "@babel/standalone";
|
|
7
7
|
//#region src/utils/scripts.ts
|
|
@@ -132,4 +132,4 @@ registerEditorCache(clearCompilationCache);
|
|
|
132
132
|
//#endregion
|
|
133
133
|
export { getCachedScriptBlob as a, clearScriptCache as i, compile as n, preloadScripts as o, transformCode as r, withScriptBlobs as s, clearCompilationCache as t };
|
|
134
134
|
|
|
135
|
-
//# sourceMappingURL=compile-
|
|
135
|
+
//# sourceMappingURL=compile-Dxas-D05.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile-Dxas-D05.js","names":[],"sources":["../src/utils/scripts.ts","../src/utils/compile.ts"],"sourcesContent":["import { CONFIG } from '../constants';\nimport { createBoundedCache } from './cache';\nimport { registerEditorCache } from './editor-caches';\n\n// How many callers have requested each script and not yet injected it. A\n// blob URL evicted in that time isn't revoked until the last of them is done\n// (`deferredRevocations`), or its `<script>` would fail to load without an\n// error (#443).\nconst pins = new Map<string, number>();\nconst deferredRevocations = new Map<string, string[]>();\n\nconst revoke = (src: string, blobUrl: string) => {\n if (!pins.has(src)) {\n URL.revokeObjectURL(blobUrl);\n return;\n }\n\n deferredRevocations.set(src, [\n ...(deferredRevocations.get(src) ?? []),\n blobUrl,\n ]);\n};\n\nconst pin = (src: string) => {\n pins.set(src, (pins.get(src) ?? 0) + 1);\n};\n\nconst unpin = (src: string) => {\n const count = (pins.get(src) ?? 0) - 1;\n\n if (count > 0) {\n pins.set(src, count);\n return;\n }\n\n pins.delete(src);\n deferredRevocations.get(src)?.forEach(blobUrl => {\n URL.revokeObjectURL(blobUrl);\n });\n deferredRevocations.delete(src);\n};\n\nconst scriptCache = createBoundedCache<string, string>(\n CONFIG.SCRIPT_CACHE_LIMIT,\n revoke,\n);\nconst loadingScriptCache = new Map<string, Promise<string>>();\n\nexport const getCachedScriptBlob = async (src: string): Promise<string> => {\n const cached = scriptCache.get(src);\n\n if (cached) {\n return cached;\n }\n\n const existing = loadingScriptCache.get(src);\n\n if (existing) {\n return existing;\n }\n\n const promise = fetch(src)\n .then(res => res.text())\n .then(text => {\n const blob = new Blob([text], { type: 'application/javascript' });\n const blobUrl = URL.createObjectURL(blob);\n\n scriptCache.set(src, blobUrl);\n\n return blobUrl;\n })\n // Clean up after a failure too: a rejected promise left here would be\n // reused by every later caller, so the script could never load again.\n .finally(() => {\n loadingScriptCache.delete(src);\n });\n\n loadingScriptCache.set(src, promise);\n\n return promise;\n};\n\n// Resolves every script to a blob URL and hands them to `inject` while none\n// of them can be revoked. `inject` must use them at once, as appending\n// `<script>` elements does: the browser resolves a blob URL on insertion.\n// Several scripts, or several frames, can push more scripts through the cache\n// than it holds before the last one arrives (#443).\nexport const withScriptBlobs = async (\n scripts: string[],\n inject: (blobUrls: string[]) => void,\n): Promise<void> => {\n scripts.forEach(pin);\n\n try {\n inject(await Promise.all(scripts.map(getCachedScriptBlob)));\n } finally {\n scripts.forEach(unpin);\n }\n};\n\nexport const preloadScripts = (scripts: string[]): void => {\n scripts.forEach(src => {\n // Not awaited, so the rejection is handled here. The real load reports a\n // failure to whoever awaits it.\n getCachedScriptBlob(src).catch(() => {});\n });\n};\n\n// Revokes every blob URL, through the same `onEvict` the LRU uses, and drops\n// the pending loads, so the next session doesn't get a URL created before\n// the revocation.\nexport const clearScriptCache = () => {\n scriptCache.clear();\n loadingScriptCache.clear();\n};\n\nregisterEditorCache(clearScriptCache);\n","import React from 'react';\n\nimport * as Babel from '@babel/standalone';\n\nimport type { Module } from '~/types';\n\nimport { CONFIG } from '../constants';\nimport { createBoundedCache } from './cache';\nimport { detectTypeScript } from './detect-typescript';\nimport { registerEditorCache } from './editor-caches';\n\ninterface CompilationKey {\n code: string;\n modules: [string, unknown][];\n}\n\n// The keys still in the LRU. Comparing at most `CACHE_LIMIT` of them avoids\n// keeping every module object ever seen, or serializing modules, which can be\n// cyclic or closures.\nconst compilationKeys = new Set<CompilationKey>();\nconst compilationCache = createBoundedCache<CompilationKey, Module>(\n CONFIG.CACHE_LIMIT,\n key => compilationKeys.delete(key),\n);\n\nconst createCacheKey = (\n code: string,\n modules: Record<string, unknown>,\n): CompilationKey => ({\n code,\n modules: Object.keys(modules)\n .sort()\n .map(name => [name, modules[name]]),\n});\n\n// Objects/functions compare by reference; primitives compare by value.\n// Replace a module object when its implementation changes. In-place edits\n// inside an existing module are intentionally not observed by this cache.\nexport const compile = (\n code: string,\n modules: Record<string, unknown>,\n): Module => {\n const candidate = createCacheKey(code, modules);\n\n for (const key of compilationKeys) {\n if (\n key.code === candidate.code &&\n key.modules.length === candidate.modules.length &&\n key.modules.every(\n ([name, value], index) =>\n name === candidate.modules[index]![0] &&\n Object.is(value, candidate.modules[index]![1]),\n )\n ) {\n return compilationCache.get(key)!;\n }\n }\n\n const result = compileModule(code, Object.fromEntries(candidate.modules));\n\n compilationCache.set(candidate, result);\n compilationKeys.add(candidate);\n\n return result;\n};\n\n// `compilationKeys` needs no separate pass: it is drained by the `onEvict`\n// the cache was built with, which `clear()` runs for every entry.\nexport const clearCompilationCache = () => {\n compilationCache.clear();\n};\n\n// Transforms JSX and TypeScript with `@babel/standalone`'s presets, without\n// the `typescript` package (#192). Babel works per file without types, so a\n// `const enum` becomes a real enum and legacy decorators aren't supported.\n// On an error, the input comes back unchanged.\nexport const transformCode = (code: string, isTypeScript = false): string => {\n try {\n const result = Babel.transform(code, {\n filename: isTypeScript ? 'preview.tsx' : 'preview.jsx',\n presets: isTypeScript ? ['typescript', 'env', 'react'] : ['env', 'react'],\n sourceType: 'module',\n plugins: [Babel.availablePlugins['transform-modules-commonjs']],\n }).code;\n\n return result || '';\n } catch (e) {\n console.error('❌ Babel transformation error:', e);\n return code;\n }\n};\n\nconst compileModule = (\n code: string,\n modules: Record<string, unknown>,\n): Module => {\n const isTypeScript = detectTypeScript(code);\n\n type RenderFunction = (\n exports: Module['exports'],\n require: (name: string) => unknown,\n module: Module,\n React: typeof import('react'),\n ) => void;\n\n let render: RenderFunction;\n\n // Runs in this window's own JS realm, not inside the preview iframe — the iframe only\n // renders the resulting React elements via portal. See README \"Security Notes\".\n try {\n render = new Function(\n 'exports',\n 'require',\n 'module',\n 'React',\n transformCode(code, isTypeScript),\n ) as RenderFunction;\n } catch (e) {\n const errorMessage = e instanceof Error ? e.message : 'Syntax error';\n return { exports: {}, error: errorMessage };\n }\n\n const module: Module = { exports: {} };\n\n const customRequire = (name: string) => {\n if (name === 'react') {\n return React;\n }\n\n // Check presence, not truthiness: a module can be a falsy value (`0`, `''`,\n // `false`, `null`).\n if (name in modules) {\n return modules[name];\n }\n\n throw new Error(`Module not found: ${name}`);\n };\n\n try {\n render(module.exports, customRequire, module, React);\n } catch (e) {\n const errorMessage = e instanceof Error ? e.message : 'Runtime error';\n return { exports: {}, error: errorMessage };\n }\n\n return module;\n};\n\nregisterEditorCache(clearCompilationCache);\n"],"mappings":"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"}
|
|
@@ -52,7 +52,7 @@ const App = () => {
|
|
|
52
52
|
export default App;
|
|
53
53
|
`;
|
|
54
54
|
const DEFAULT_TEMPLATE = documentTemplate(DEFAULT_CONTAINER_ID);
|
|
55
|
-
const
|
|
55
|
+
const PALETTE_SECTIONS = [
|
|
56
56
|
{
|
|
57
57
|
id: "hero",
|
|
58
58
|
name: "Hero",
|
|
@@ -1454,6 +1454,6 @@ const DRAGGABLE_ITEMS = [
|
|
|
1454
1454
|
}
|
|
1455
1455
|
];
|
|
1456
1456
|
//#endregion
|
|
1457
|
-
export { DEFAULT_TEMPLATE as a, RESERVED_BINDING_PROPERTIES as c, DEFAULT_CONTAINER_ID as i, TS_PATTERNS as l, CONFIG as n,
|
|
1457
|
+
export { DEFAULT_TEMPLATE as a, RESERVED_BINDING_PROPERTIES as c, DEFAULT_CONTAINER_ID as i, TS_PATTERNS as l, CONFIG as n, PALETTE_SECTIONS as o, DATA_ATTR as r, REGEX as s, BINDING_PROP as t, documentTemplate as u };
|
|
1458
1458
|
|
|
1459
|
-
//# sourceMappingURL=constants-
|
|
1459
|
+
//# sourceMappingURL=constants-DU17Tdwo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants-DU17Tdwo.js","names":[],"sources":["../src/constants/index.ts"],"sourcesContent":["import { type Section } from '../types';\n\nexport const STORAGE_KEY = 'live-editor-code';\n\nexport const CONFIG = {\n CACHE_LIMIT: 50,\n // The document parse cache. Its entries are whole Babel ASTs (about\n // 0.66 MB per document), and only the current and previous version get hits,\n // so it keeps few (#106).\n DOCUMENT_CACHE_LIMIT: 4,\n // The script cache (`utils/scripts.ts`). Its entries are live blob URLs,\n // revoked on eviction, and a preview loads only a few scripts (#104).\n SCRIPT_CACHE_LIMIT: 10,\n} as const;\n\nexport const DATA_ATTR = {\n ID: 'data-id',\n BINDING: 'data-binding',\n // Names an entry of `Live.Dnd`'s `bindingKeys` instead of declaring the\n // bindings inline (#513).\n BINDING_KEY: 'data-binding-key',\n ITEM: 'data-item',\n} as const;\n\n// Attributes the editor owns. `data-id` links the canvas to the source,\n// `data-name` is a section's name, and `data-binding` and `data-binding-key`\n// declare what each edit is checked against. A binding can't target them,\n// since rewriting one would break that link (#429).\nexport const RESERVED_BINDING_PROPERTIES: readonly string[] = [\n DATA_ATTR.ID,\n 'data-name',\n DATA_ATTR.BINDING,\n DATA_ATTR.BINDING_KEY,\n];\n\nexport const REGEX = {\n NUMBER: /^\\d+(\\.\\d+)?$/,\n BOOLEAN_OR_NULL: /^(true|false|null|undefined)$/,\n MODULE_IMPORT_EXPORT_LINE:\n /^\\s*(?:import|export)\\b[^\\n]*\\bfrom\\s+['\"][^'\"]*['\"];?\\s*$/gm,\n} as const;\n\nexport const TS_PATTERNS = [\n /interface\\s+\\w+/,\n /type\\s+\\w+\\s*=/,\n /:\\s*\\w+(\\[\\])?(\\s*\\||\\s*&|\\s*=|\\s*;|\\s*,|\\s*\\))/,\n /as\\s+\\w+/,\n /<[A-Z]\\w*>/,\n /enum\\s+\\w+/,\n /public\\s+|private\\s+|protected\\s+/,\n /readonly\\s+/,\n /\\?\\s*:/,\n] as const;\n\nexport const BINDING_PROP = {\n CHILDREN: 'children',\n INNER_TEXT: 'innerText',\n INNER_HTML: 'innerHTML',\n ITEMS: 'items',\n} as const;\n\n// The element whose `<section>` children are a document's sections, unless\n// `Live.Dnd`'s `containerId` says otherwise (#449).\nexport const DEFAULT_CONTAINER_ID = 'app-container';\n\n// A new, empty document built around `containerId`. `createDocument()` in\n// `@jbpark/live-editor/utils` is the public way in; this is the template\n// behind it and behind `DEFAULT_TEMPLATE`.\nexport const documentTemplate = (containerId: string) => `\nimport * as ui from 'ui-kit';\nimport { cn } from 'ui-kit/utils';\n\nconst App = () => {\n return (\n <main id=\"${containerId}\"></main>\n )\n}\n\nexport default App;\n`;\n\nexport const DEFAULT_TEMPLATE = documentTemplate(DEFAULT_CONTAINER_ID);\n\nexport const PALETTE_SECTIONS: Section[] = [\n {\n id: 'hero',\n name: 'Hero',\n code: `\n <section\n data-name=\"Hero\"\n className={cn(\n 'text-center text-white',\n //\n )}\n >\n <ui.Space\n data-id=\"\"\n data-binding={[\n {\n label: 'Background Style',\n property: 'style',\n type: 'object',\n // Types the keys inside the object value; a leaf takes the same\n // fields as a top-level binding.\n render: {\n backgroundColor: { type: 'color', label: 'Background Color' },\n },\n // Not a library field, so it arrives as meta.group.\n group: 'layout',\n },\n {\n label: 'Content Spacing',\n property: 'size',\n type: 'number',\n // Constraints the library validates with or without a widget.\n min: 0,\n max: 40,\n // Presentation only: the built-in panel ignores it and shows a\n // number input; a custom panel can switch on widget.type.\n widget: { type: 'slider', step: 4, unit: 'px' },\n group: 'layout',\n },\n ]}\n style={{\n backgroundColor: '#6366f1',\n }}\n orientation=\"vertical\"\n size={16}\n align=\"center\"\n className={cn(\n 'px-5 py-15',\n //\n )}\n >\n <h1\n data-id=\"\"\n data-binding={[\n {\n label: 'Title',\n property: 'innerText',\n type: 'string',\n required: true,\n },\n ]}\n className={cn(\n 'text-5xl font-bold',\n //\n )}\n >\n Welcome to Live Editor\n </h1>\n <p\n data-id=\"\"\n data-binding={[\n {\n label: 'Description',\n type: 'richtext',\n },\n ]}\n className={cn(\n 'text-xl',\n //\n )}\n >\n Build <strong>dynamic components</strong> with drag & drop\n </p>\n <ui.Button\n data-id=\"\"\n data-binding={[\n {\n label: 'Button Text',\n property: 'innerText',\n },\n {\n label: 'Button Icon',\n property: 'icon',\n type: 'jsx',\n },\n {\n label: 'Button Size',\n property: 'size',\n options: [\n { label: 'Small', value: 'small' },\n { label: 'Middle', value: 'middle' },\n { label: 'Large', value: 'large' },\n ],\n },\n {\n label: 'Button Variant',\n property: 'variant',\n options: [\n { label: 'Solid', value: 'solid' },\n { label: 'Outlined', value: 'outlined' },\n { label: 'Filled', value: 'filled' },\n ],\n },\n ]}\n icon={<span>🚀</span>}\n size=\"large\"\n variant=\"solid\"\n className={cn(\n 'bg-white text-indigo-500',\n 'hover:bg-gray-50',\n //\n )}\n >\n Get Started\n </ui.Button>\n </ui.Space>\n </section>\n `,\n },\n {\n id: 'features',\n name: 'Features',\n code: `\n <section\n data-name=\"Features\"\n className={cn(\n 'px-5 py-10',\n //\n )}\n >\n <ui.Space\n data-id=\"\"\n data-binding={[\n {\n label: 'Feature Cards',\n property: 'children',\n },\n ]}\n className={cn(\n 'mx-auto max-w-7xl',\n //\n )}\n size=\"large\"\n wrap\n >\n <ui.Card\n className={cn(\n 'min-w-70 flex-1 text-center',\n //\n )}\n >\n <ui.Space orientation=\"vertical\" size=\"middle\" align=\"center\">\n <div\n data-id=\"\"\n data-binding={[\n {\n label: 'Icon',\n property: 'innerText',\n },\n ]}\n className={cn(\n 'text-5xl',\n //\n )}\n >\n 🚀\n </div>\n <h3\n data-id=\"\"\n data-binding={[\n {\n label: 'Title',\n property: 'innerText',\n },\n ]}\n className={cn(\n 'text-2xl text-gray-800',\n //\n )}\n >\n Feature 1\n </h3>\n <p\n data-id=\"\"\n data-binding={[\n {\n label: 'Description',\n property: 'innerText',\n },\n ]}\n className={cn(\n 'text-gray-600',\n //\n )}\n >\n Amazing feature description\n </p>\n </ui.Space>\n </ui.Card>\n <ui.Card\n className={cn(\n 'min-w-70 flex-1 text-center',\n //\n )}\n >\n <ui.Space orientation=\"vertical\" size=\"middle\" align=\"center\">\n <div\n data-id=\"\"\n data-binding={[\n {\n label: 'Icon',\n property: 'innerText',\n },\n ]}\n className={cn(\n 'text-5xl',\n //\n )}\n >\n ⚡\n </div>\n <h3\n data-id=\"\"\n data-binding={[\n {\n label: 'Title',\n property: 'innerText',\n },\n ]}\n className={cn(\n 'text-2xl text-gray-800',\n //\n )}\n >\n Feature 2\n </h3>\n <p\n data-id=\"\"\n data-binding={[\n {\n label: 'Description',\n property: 'innerText',\n },\n ]}\n className={cn(\n 'text-gray-600',\n //\n )}\n >\n Another great feature\n </p>\n </ui.Space>\n </ui.Card>\n <ui.Card\n className={cn(\n 'min-w-70 flex-1 text-center',\n //\n )}\n >\n <ui.Space orientation=\"vertical\" size=\"middle\" align=\"center\">\n <div\n data-id=\"\"\n data-binding={[\n {\n label: 'Icon',\n property: 'innerText',\n },\n ]}\n className={cn(\n 'text-5xl',\n //\n )}\n >\n ✨\n </div>\n <h3\n data-id=\"\"\n data-binding={[\n {\n label: 'Title',\n property: 'innerText',\n },\n ]}\n className={cn(\n 'text-2xl text-gray-800',\n //\n )}\n >\n Feature 3\n </h3>\n <p\n data-id=\"\"\n data-binding={[\n {\n label: 'Description',\n property: 'innerText',\n },\n ]}\n className={cn(\n 'text-gray-600',\n //\n )}\n >\n One more awesome thing\n </p>\n </ui.Space>\n </ui.Card>\n </ui.Space>\n </section>\n `,\n },\n {\n id: 'about',\n name: 'About',\n code: `\n <section\n data-name=\"About\"\n className={cn(\n 'mx-auto max-w-3xl px-5 py-10',\n //\n )}\n >\n <ui.Typography.Title\n data-id=\"\"\n data-binding={[{ label: 'Title', property: 'innerText' }]}\n level={2}\n className={cn(\n 'mb-5 text-4xl text-gray-800',\n //\n )}\n >\n About Live Editor\n </ui.Typography.Title>\n <ui.Typography.Paragraph\n data-id=\"\"\n data-binding={[\n {\n label: 'Description 1',\n type: 'richtext',\n },\n ]}\n className={cn(\n 'mb-4 text-lg leading-relaxed text-gray-600',\n //\n )}\n >\n Live Editor is a powerful visual development tool that enables you to\n create stunning web interfaces through an intuitive drag-and-drop\n experience. Built with modern web technologies, it streamlines your\n workflow and accelerates development cycles.\n </ui.Typography.Paragraph>\n <ui.Typography.Paragraph\n data-id=\"\"\n data-binding={[\n {\n label: 'Description 2',\n type: 'richtext',\n },\n ]}\n className={cn(\n 'text-lg leading-relaxed text-gray-600',\n //\n )}\n >\n Whether you're a seasoned developer or just starting out, Live\n Editor provides the flexibility and control you need to bring your\n creative vision to life. Focus on what matters most - building great\n products.\n </ui.Typography.Paragraph>\n </section>\n `,\n },\n {\n id: 'cta',\n name: 'CTA',\n code: `\n <section\n data-name=\"CTA\"\n className={cn(\n 'bg-gray-50 px-5 py-12 text-center',\n //\n )}\n >\n <div\n className={cn(\n 'mx-auto max-w-xl',\n //\n )}\n >\n <ui.Typography.Title\n data-id=\"\"\n data-binding={[{ label: 'Title', property: 'innerText' }]}\n level={2}\n className={cn(\n 'mb-4 text-3xl text-gray-800',\n //\n )}\n >\n Ready to get started?\n </ui.Typography.Title>\n <ui.Typography.Paragraph\n data-id=\"\"\n data-binding={[\n {\n label: 'Description',\n property: 'innerText',\n },\n ]}\n className={cn(\n 'mb-6 text-lg text-gray-600',\n //\n )}\n >\n Start exploring what Live Editor can do for your projects\n </ui.Typography.Paragraph>\n <ui.Space\n data-id=\"\"\n data-binding={[{ label: 'Buttons', property: 'children' }]}\n className={cn(\n 'flex flex-wrap justify-center gap-4',\n //\n )}\n >\n <ui.Button\n data-id=\"\"\n data-binding={[\n {\n label: 'Button 1 Text',\n property: 'innerText',\n },\n ]}\n size=\"large\"\n className={cn(\n 'bg-indigo-500 px-8 text-base text-white',\n 'hover:bg-indigo-600',\n //\n )}\n >\n View Documentation\n </ui.Button>\n <ui.Button\n data-id=\"\"\n data-binding={[\n {\n label: 'Button 2 Text',\n property: 'innerText',\n },\n ]}\n variant=\"outlined\"\n size=\"large\"\n className={cn(\n 'border-2 border-indigo-500 px-8 text-base text-indigo-500',\n 'hover:bg-indigo-50',\n //\n )}\n >\n View Examples\n </ui.Button>\n </ui.Space>\n </div>\n </section>\n `,\n },\n {\n id: 'getting-started',\n name: 'Getting Started',\n code: `\n <section\n data-name=\"Getting Started\"\n className={cn(\n 'px-5 py-16',\n //\n )}\n >\n <div\n className={cn(\n 'mx-auto max-w-5xl',\n //\n )}\n >\n <ui.Typography.Title\n data-id=\"\"\n data-binding={[{ label: 'Title', property: 'innerText' }]}\n level={2}\n className={cn(\n 'mb-12 text-center text-4xl font-bold text-gray-800',\n //\n )}\n >\n How to Get Started\n </ui.Typography.Title>\n <div\n className={cn(\n 'grid gap-8 md:grid-cols-3',\n //\n )}\n >\n <ui.Card\n data-id=\"\"\n data-binding={[\n {\n label: 'Card 1 Content',\n property: 'children',\n },\n ]}\n className={cn(\n 'rounded-lg border-2 border-gray-200 bg-white p-8',\n //\n )}\n >\n <ui.Typography.Title\n data-id=\"\"\n data-binding={[\n {\n label: 'Card 1 Title',\n property: 'innerText',\n },\n ]}\n level={3}\n className={cn(\n 'mb-2 text-2xl font-bold text-gray-800',\n //\n )}\n >\n Explore\n </ui.Typography.Title>\n <ui.Typography.Paragraph\n data-id=\"\"\n data-binding={[\n {\n label: 'Card 1 Icon',\n property: 'innerText',\n },\n ]}\n className={cn(\n 'mb-6 text-4xl font-bold text-indigo-600',\n //\n )}\n >\n 📚\n </ui.Typography.Paragraph>\n <ul\n className={cn(\n 'mb-8 space-y-3 text-gray-600',\n //\n )}\n >\n <li>✓ Read documentation</li>\n <li>✓ Browse examples</li>\n <li>✓ Try the demo</li>\n </ul>\n <ui.Button\n data-id=\"\"\n data-binding={[\n {\n label: 'Card 1 Button',\n property: 'innerText',\n },\n ]}\n variant=\"outlined\"\n size=\"large\"\n block\n className={cn(\n 'border-2 border-indigo-500 text-indigo-500',\n 'hover:bg-indigo-50',\n //\n )}\n >\n View Docs\n </ui.Button>\n </ui.Card>\n <ui.Card\n data-id=\"\"\n data-binding={[\n {\n label: 'Card 2 Content',\n property: 'children',\n },\n ]}\n className={cn(\n 'relative rounded-lg border-2 border-indigo-500 bg-white p-8',\n //\n )}\n >\n <div\n className={cn(\n 'absolute -top-4 left-1/2 -translate-x-1/2',\n 'rounded-full bg-indigo-500 px-4 py-1 text-sm text-white',\n //\n )}\n >\n Recommended\n </div>\n <ui.Typography.Title\n data-id=\"\"\n data-binding={[\n {\n label: 'Card 2 Title',\n property: 'innerText',\n },\n ]}\n level={3}\n className={cn(\n 'mb-2 text-2xl font-bold text-gray-800',\n //\n )}\n >\n Practice\n </ui.Typography.Title>\n <ui.Typography.Paragraph\n data-id=\"\"\n data-binding={[\n {\n label: 'Card 2 Icon',\n property: 'innerText',\n },\n ]}\n className={cn(\n 'mb-6 text-4xl font-bold text-indigo-600',\n //\n )}\n >\n 💻\n </ui.Typography.Paragraph>\n <ul\n className={cn(\n 'mb-8 space-y-3 text-gray-600',\n //\n )}\n >\n <li>✓ Interactive playground</li>\n <li>✓ Experiment freely</li>\n <li>✓ Build your own</li>\n </ul>\n <ui.Button\n data-id=\"\"\n data-binding={[\n {\n label: 'Card 2 Button',\n property: 'innerText',\n },\n ]}\n size=\"large\"\n block\n className={cn(\n 'bg-indigo-500 text-white',\n 'hover:bg-indigo-600',\n //\n )}\n >\n Start Building\n </ui.Button>\n </ui.Card>\n <ui.Card\n data-id=\"\"\n data-binding={[\n {\n label: 'Card 3 Content',\n property: 'children',\n },\n ]}\n className={cn(\n 'rounded-lg border-2 border-gray-200 bg-white p-8',\n //\n )}\n >\n <ui.Typography.Title\n data-id=\"\"\n data-binding={[\n {\n label: 'Card 3 Title',\n property: 'innerText',\n },\n ]}\n level={3}\n className={cn(\n 'mb-2 text-2xl font-bold text-gray-800',\n //\n )}\n >\n Contribute\n </ui.Typography.Title>\n <ui.Typography.Paragraph\n data-id=\"\"\n data-binding={[\n {\n label: 'Card 3 Icon',\n property: 'innerText',\n },\n ]}\n className={cn(\n 'mb-6 text-4xl font-bold text-indigo-600',\n //\n )}\n >\n 🤝\n </ui.Typography.Paragraph>\n <ul\n className={cn(\n 'mb-8 space-y-3 text-gray-600',\n //\n )}\n >\n <li>✓ Report issues</li>\n <li>✓ Suggest features</li>\n <li>✓ Submit pull requests</li>\n </ul>\n <ui.Button\n data-id=\"\"\n data-binding={[\n {\n label: 'Card 3 Button',\n property: 'innerText',\n },\n ]}\n variant=\"outlined\"\n size=\"large\"\n block\n className={cn(\n 'border-2 border-indigo-500 text-indigo-500',\n 'hover:bg-indigo-50',\n //\n )}\n >\n View on GitHub\n </ui.Button>\n </ui.Card>\n </div>\n </div>\n </section>\n `,\n },\n {\n id: 'key-features',\n name: 'Key Features',\n code: `\n <section\n data-name=\"Key Features\"\n className={cn(\n 'bg-indigo-50 px-5 py-16',\n //\n )}\n >\n <div\n className={cn(\n 'mx-auto max-w-6xl',\n //\n )}\n >\n <h2\n data-id=\"\"\n data-binding={[{ label: 'Title', property: 'innerText' }]}\n className={cn(\n 'mb-12 text-center text-4xl font-bold text-gray-800',\n //\n )}\n >\n Key Features\n </h2>\n <div\n className={cn(\n 'grid gap-8 md:grid-cols-2',\n //\n )}\n >\n <div\n className={cn(\n 'rounded-lg bg-white p-8 shadow-md',\n //\n )}\n >\n <div\n className={cn(\n 'mb-4 text-4xl',\n //\n )}\n >\n 🎨\n </div>\n <h3\n className={cn(\n 'mb-4 text-2xl font-bold text-gray-800',\n //\n )}\n >\n Visual Editing\n </h3>\n <p\n className={cn(\n 'text-gray-600',\n //\n )}\n >\n See your changes in real-time as you build. Live Editor provides\n instant visual feedback, making it easier to understand how code\n affects your interface.\n </p>\n </div>\n <div\n className={cn(\n 'rounded-lg bg-white p-8 shadow-md',\n //\n )}\n >\n <div\n className={cn(\n 'mb-4 text-4xl',\n //\n )}\n >\n 🧩\n </div>\n <h3\n className={cn(\n 'mb-4 text-2xl font-bold text-gray-800',\n //\n )}\n >\n Component-Based\n </h3>\n <p\n className={cn(\n 'text-gray-600',\n //\n )}\n >\n Build with reusable components that you can drag, drop, and\n customize. Learn modern development patterns while creating your\n interface.\n </p>\n </div>\n </div>\n </div>\n </section>\n `,\n },\n {\n id: 'stats',\n name: 'Stats',\n code: `\n <section\n data-name=\"Stats\"\n className={cn(\n 'bg-indigo-600 text-white',\n //\n )}\n >\n <ui.Marquees\n data-id=\"\"\n data-binding={[\n { label: 'Stats Items', property: 'items' },\n {\n label: 'Scroll Speed',\n property: 'speed',\n type: 'number',\n min: 20,\n max: 300,\n // The bare-string form; it arrives as { type: 'slider' }.\n widget: 'slider',\n },\n { label: 'Pause On Hover', property: 'pauseOnHover', type: 'boolean' },\n ]}\n speed={100}\n pauseOnHover={true}\n autoFill={true}\n className=\"px-5 py-16\"\n items={[\n {\n key: 'stats-row',\n children: (\n <div\n data-id=\"\"\n data-binding={[\n {\n label: 'Stats Cards',\n property: 'children',\n },\n ]}\n className={cn('flex gap-20')}\n >\n <div\n className={cn(\n 'min-w-60 text-center',\n //\n )}\n >\n <p\n data-id=\"\"\n data-binding={[\n {\n label: 'Title',\n property: 'innerText',\n },\n ]}\n className={cn(\n 'mb-2 text-5xl font-bold',\n //\n )}\n >\n Open\n </p>\n <p\n data-id=\"\"\n data-binding={[\n {\n label: 'Description',\n property: 'innerText',\n },\n ]}\n className={cn(\n 'text-indigo-200',\n //\n )}\n >\n Source Project\n </p>\n </div>\n <div\n className={cn(\n 'min-w-60 text-center',\n //\n )}\n >\n <p\n data-id=\"\"\n data-binding={[\n {\n label: 'Title',\n property: 'innerText',\n },\n ]}\n className={cn(\n 'mb-2 text-5xl font-bold',\n //\n )}\n >\n Free\n </p>\n <p\n data-id=\"\"\n data-binding={[\n {\n label: 'Description',\n property: 'innerText',\n },\n ]}\n className={cn(\n 'text-indigo-200',\n //\n )}\n >\n For Everyone\n </p>\n </div>\n <div\n className={cn(\n 'min-w-60 text-center',\n //\n )}\n >\n <p\n data-id=\"\"\n data-binding={[\n {\n label: 'Title',\n property: 'innerText',\n },\n ]}\n className={cn(\n 'mb-2 text-5xl font-bold',\n //\n )}\n >\n Active\n </p>\n <p\n data-id=\"\"\n data-binding={[\n {\n label: 'Description',\n property: 'innerText',\n },\n ]}\n className={cn(\n 'text-indigo-200',\n //\n )}\n >\n Community\n </p>\n </div>\n <div\n className={cn(\n 'min-w-60 text-center',\n //\n )}\n >\n <p\n data-id=\"\"\n data-binding={[\n {\n label: 'Title',\n property: 'innerText',\n },\n ]}\n className={cn(\n 'mb-2 text-5xl font-bold',\n //\n )}\n >\n Learn\n </p>\n <p\n data-id=\"\"\n data-binding={[\n {\n label: 'Description',\n property: 'innerText',\n },\n ]}\n className={cn(\n 'text-indigo-200',\n //\n )}\n >\n By Doing\n </p>\n </div>\n </div>\n ),\n },\n ]}\n />\n </section>\n `,\n },\n {\n id: 'roadmap',\n name: 'Roadmap',\n code: `\n <section\n data-name=\"Roadmap\"\n className={cn(\n 'px-5 py-16',\n //\n )}\n >\n <div\n className={cn(\n 'mx-auto max-w-3xl',\n //\n )}\n >\n <h2\n data-id=\"\"\n data-binding={[\n { label: 'Title', property: 'innerText', required: true },\n ]}\n className={cn(\n 'mb-8 text-center text-4xl font-bold text-gray-800',\n //\n )}\n >\n Roadmap\n </h2>\n <ui.List\n data-id=\"\"\n data-binding={[\n {\n label: 'Milestones',\n property: 'data',\n type: 'array',\n // One leaf per item key. Each leaf takes the same fields as a\n // top-level binding, and label falls back to the key.\n render: {\n title: {\n type: 'string',\n label: 'Title',\n required: true,\n },\n date: { type: 'date', label: 'Release Date', required: true },\n status: {\n type: 'string',\n label: 'Status',\n options: [\n { label: 'Planned', value: 'planned' },\n { label: 'In Progress', value: 'in-progress' },\n { label: 'Shipped', value: 'shipped' },\n ],\n },\n progress: {\n type: 'number',\n label: 'Progress',\n min: 0,\n max: 100,\n widget: { type: 'slider', step: 10, unit: '%' },\n },\n color: { type: 'color', label: 'Accent Color' },\n highlight: { type: 'boolean', label: 'Highlight' },\n // An object-valued key: its own render map types the keys\n // inside it, one level further down.\n link: {\n type: 'object',\n label: 'Link',\n render: {\n text: { type: 'string', label: 'Link Text' },\n href: {\n type: 'url',\n label: 'Link URL',\n pattern: '^https://',\n // Not a library field, so it arrives as meta.hint.\n hint: 'Must use https',\n },\n },\n },\n },\n },\n ]}\n data={[\n {\n title: 'Visual editing',\n date: '2026-03-02',\n status: 'shipped',\n progress: 100,\n color: '#22c55e',\n highlight: false,\n link: { text: 'Release notes', href: 'https://github.com' },\n },\n {\n title: 'Custom panels',\n date: '2026-09-26',\n status: 'in-progress',\n progress: 60,\n color: '#6366f1',\n highlight: true,\n link: { text: 'Guide', href: 'https://github.com' },\n },\n {\n title: 'Collaboration',\n date: '2027-01-15',\n status: 'planned',\n progress: 0,\n color: '#f59e0b',\n highlight: false,\n link: { text: 'Discussion', href: 'https://github.com' },\n },\n ]}\n itemKey={item => item.title}\n renderItem={item => (\n <div\n className={cn(\n 'flex items-center gap-4 border-b border-gray-100 py-4',\n item.highlight && 'rounded-lg bg-indigo-50 px-4',\n )}\n >\n <span\n className=\"size-3 shrink-0 rounded-full\"\n style={{ backgroundColor: item.color }}\n />\n <div className=\"min-w-0 flex-1\">\n <p className=\"font-semibold text-gray-800\">{item.title}</p>\n <p className=\"text-sm text-gray-500\">\n {item.date} · {item.status}\n </p>\n <ui.Progress value={item.progress} className=\"mt-2\" />\n </div>\n <a\n href={item.link.href}\n className=\"text-sm text-indigo-500 hover:underline\"\n >\n {item.link.text}\n </a>\n </div>\n )}\n />\n <p\n className={cn(\n 'mt-6 text-center text-sm text-gray-500',\n //\n )}\n >\n Last updated{' '}\n <time\n data-id=\"\"\n data-binding={[\n {\n label: 'Updated On',\n property: 'dateTime',\n type: 'date',\n required: true,\n },\n { label: 'Updated Text', property: 'innerText' },\n ]}\n dateTime=\"2026-09-26\"\n >\n September 26, 2026\n </time>\n {' · '}\n <a\n data-id=\"\"\n data-binding={[\n { label: 'Link Text', property: 'innerText' },\n {\n label: 'Link URL',\n property: 'href',\n type: 'url',\n required: true,\n pattern: '^https://',\n },\n ]}\n href=\"https://github.com/pjb0811/live-editor/releases\"\n className=\"text-indigo-500 hover:underline\"\n >\n All releases\n </a>\n </p>\n </div>\n </section>\n `,\n },\n {\n id: 'faq',\n name: 'FAQ',\n code: `\n <section\n data-name=\"FAQ\"\n className={cn(\n 'px-5 py-16',\n //\n )}\n >\n <div\n className={cn(\n 'mx-auto max-w-3xl',\n //\n )}\n >\n <ui.Typography.Title\n data-id=\"\"\n data-binding={[{ label: 'Title', property: 'innerText' }]}\n level={2}\n className={cn(\n 'mb-12 text-center text-4xl font-bold text-gray-800',\n //\n )}\n >\n Frequently Asked Questions\n </ui.Typography.Title>\n <ui.Collapse\n data-id=\"\"\n data-binding={[{ label: 'FAQ Items', property: 'items' }]}\n items={[\n {\n key: '1',\n label: (\n <ui.Typography.Text\n data-id=\"\"\n data-binding={[\n {\n label: 'Question 1',\n property: 'innerText',\n },\n ]}\n className={cn('text-lg font-semibold text-gray-800')}\n >\n What is Live Editor?\n </ui.Typography.Text>\n ),\n children: (\n <ui.Typography.Paragraph\n data-id=\"\"\n data-binding={[\n {\n label: 'Answer 1',\n type: 'richtext',\n },\n ]}\n className={cn('text-gray-600')}\n >\n Live Editor is an open-source tool for building web\n interfaces visually. It helps you learn web development by\n providing instant feedback as you build components.\n </ui.Typography.Paragraph>\n ),\n },\n {\n key: '2',\n label: (\n <ui.Typography.Text\n data-id=\"\"\n data-binding={[\n {\n label: 'Question 2',\n property: 'innerText',\n },\n ]}\n className={cn('text-lg font-semibold text-gray-800')}\n >\n Do I need coding experience?\n </ui.Typography.Text>\n ),\n children: (\n <ui.Typography.Paragraph\n data-id=\"\"\n data-binding={[\n {\n label: 'Answer 2',\n type: 'richtext',\n },\n ]}\n className={cn('text-gray-600')}\n >\n No! Live Editor is designed for all skill levels. Beginners\n can learn through visual editing, while experienced\n developers can work directly with code.\n </ui.Typography.Paragraph>\n ),\n },\n {\n key: '3',\n label: (\n <ui.Typography.Text\n data-id=\"\"\n data-binding={[\n {\n label: 'Question 3',\n property: 'innerText',\n },\n ]}\n className={cn('text-lg font-semibold text-gray-800')}\n >\n Is it really free?\n </ui.Typography.Text>\n ),\n children: (\n <ui.Typography.Paragraph\n data-id=\"\"\n data-binding={[\n {\n label: 'Answer 3',\n type: 'richtext',\n },\n ]}\n className={cn('text-gray-600')}\n >\n Yes, completely free! Live Editor is an open-source project\n with no hidden costs or premium tiers. All features are\n available to everyone.\n </ui.Typography.Paragraph>\n ),\n },\n {\n key: '4',\n label: (\n <ui.Typography.Text\n data-id=\"\"\n data-binding={[\n {\n label: 'Question 4',\n property: 'innerText',\n },\n ]}\n className={cn('text-lg font-semibold text-gray-800')}\n >\n How can I contribute?\n </ui.Typography.Text>\n ),\n children: (\n <ui.Typography.Paragraph\n data-id=\"\"\n data-binding={[\n {\n label: 'Answer 4',\n type: 'richtext',\n },\n ]}\n className={cn('text-gray-600')}\n >\n You can contribute by reporting bugs, suggesting features,\n sharing your projects, or contributing code. Check our\n GitHub repository for more details.\n </ui.Typography.Paragraph>\n ),\n },\n ]}\n />\n </div>\n </section>\n `,\n },\n];\n"],"mappings":"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"}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { a as DEFAULT_TEMPLATE } from "./constants-
|
|
1
|
+
import { a as DEFAULT_TEMPLATE } from "./constants-DU17Tdwo.js";
|
|
2
2
|
import { r as registerEditorSession } from "./editor-caches-ClMvdcQF.js";
|
|
3
|
+
import { r as mergeMessages, t as MessagesContext } from "./messages-CSrMI4Rm.js";
|
|
3
4
|
import { n as PreviewContext, t as ErrorContext } from "./states-Ci1AvoQ9.js";
|
|
4
|
-
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
5
|
+
import { useCallback, useContext, useEffect, useMemo, useState } from "react";
|
|
5
6
|
import { jsx } from "react/jsx-runtime";
|
|
6
7
|
//#region src/components/context/context.tsx
|
|
7
|
-
const ContextProvider = ({ children }) => {
|
|
8
|
+
const ContextProvider = ({ children, messages }) => {
|
|
8
9
|
const [code, setCodeState] = useState(DEFAULT_TEMPLATE);
|
|
9
10
|
const [error, setError] = useState(null);
|
|
10
11
|
const setCode = useCallback((next) => {
|
|
@@ -20,15 +21,20 @@ const ContextProvider = ({ children }) => {
|
|
|
20
21
|
error,
|
|
21
22
|
setError
|
|
22
23
|
}), [error, setError]);
|
|
24
|
+
const inherited = useContext(MessagesContext);
|
|
25
|
+
const messagesValue = useMemo(() => mergeMessages(messages, inherited), [messages, inherited]);
|
|
23
26
|
return /* @__PURE__ */ jsx(PreviewContext.Provider, {
|
|
24
27
|
value: previewValue,
|
|
25
28
|
children: /* @__PURE__ */ jsx(ErrorContext.Provider, {
|
|
26
29
|
value: errorValue,
|
|
27
|
-
children
|
|
30
|
+
children: /* @__PURE__ */ jsx(MessagesContext.Provider, {
|
|
31
|
+
value: messagesValue,
|
|
32
|
+
children
|
|
33
|
+
})
|
|
28
34
|
})
|
|
29
35
|
});
|
|
30
36
|
};
|
|
31
37
|
//#endregion
|
|
32
38
|
export { ContextProvider as t };
|
|
33
39
|
|
|
34
|
-
//# sourceMappingURL=context-
|
|
40
|
+
//# sourceMappingURL=context-B8MzP7_p.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context-B8MzP7_p.js","names":[],"sources":["../src/components/context/context.tsx"],"sourcesContent":["'use client';\n\nimport React, {\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n} from 'react';\n\nimport { DEFAULT_TEMPLATE } from '~/constants';\nimport { registerEditorSession } from '~/utils/editor-caches';\n\nimport {\n type LiveMessagesInput,\n MessagesContext,\n mergeMessages,\n} from './messages';\nimport type { ErrorContextType, PreviewContextType } from './states';\nimport { ErrorContext, PreviewContext } from './states';\n\nexport interface Props {\n children?: React.ReactNode;\n // Replaces any of the editor's text, group by group; the rest stays\n // English, or the enclosing `Live`'s. Define it outside render or memoize\n // it: a new object re-renders everything that shows text (#524).\n messages?: LiveMessagesInput;\n}\n\nconst ContextProvider = ({ children, messages }: Props) => {\n const [code, setCodeState] = useState(DEFAULT_TEMPLATE);\n const [error, setError] = useState<ErrorContextType['error']>(null);\n\n // Clears the last error in the same update that sets the code. An effect\n // would run after the new code's own error was reported, and wipe it.\n const setCode = useCallback<PreviewContextType['setCode']>(next => {\n setError(null);\n setCodeState(next);\n }, []);\n\n // Counts this editor, so the page-wide caches are released when the last\n // one unmounts (`utils/editor-caches.ts`).\n useEffect(() => registerEditorSession(), []);\n\n // Memoized, so a parent re-render doesn't re-render every `usePreview()`\n // and `useError()` consumer.\n const previewValue = useMemo<PreviewContextType>(\n () => ({ code, setCode }),\n [code, setCode],\n );\n\n const errorValue = useMemo<ErrorContextType>(\n () => ({ error, setError }),\n [error, setError],\n );\n\n // A provider inside another one starts from the outer one's messages, so\n // a page can set them once around several editors.\n const inherited = useContext(MessagesContext);\n const messagesValue = useMemo(\n () => mergeMessages(messages, inherited),\n [messages, inherited],\n );\n\n return (\n <PreviewContext.Provider value={previewValue}>\n <ErrorContext.Provider value={errorValue}>\n <MessagesContext.Provider value={messagesValue}>\n {children}\n </MessagesContext.Provider>\n </ErrorContext.Provider>\n </PreviewContext.Provider>\n );\n};\n\nexport default ContextProvider;\n"],"mappings":";;;;;;;AA6BA,MAAM,mBAAmB,EAAE,UAAU,eAAsB;CACzD,MAAM,CAAC,MAAM,gBAAgB,SAAS,gBAAgB;CACtD,MAAM,CAAC,OAAO,YAAY,SAAoC,IAAI;CAIlE,MAAM,UAAU,aAA2C,SAAQ;EACjE,SAAS,IAAI;EACb,aAAa,IAAI;CACnB,GAAG,CAAC,CAAC;CAIL,gBAAgB,sBAAsB,GAAG,CAAC,CAAC;CAI3C,MAAM,eAAe,eACZ;EAAE;EAAM;CAAQ,IACvB,CAAC,MAAM,OAAO,CAChB;CAEA,MAAM,aAAa,eACV;EAAE;EAAO;CAAS,IACzB,CAAC,OAAO,QAAQ,CAClB;CAIA,MAAM,YAAY,WAAW,eAAe;CAC5C,MAAM,gBAAgB,cACd,cAAc,UAAU,SAAS,GACvC,CAAC,UAAU,SAAS,CACtB;CAEA,OACE,oBAAC,eAAe,UAAhB;EAAyB,OAAO;EAC9B,UAAA,oBAAC,aAAa,UAAd;GAAuB,OAAO;GAC5B,UAAA,oBAAC,gBAAgB,UAAjB;IAA0B,OAAO;IAC9B;GACuB,CAAA;EACL,CAAA;CACA,CAAA;AAE7B"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as cn } from "./cn-CAnad2vL.js";
|
|
2
|
-
import { t as detectTypeScript } from "./detect-typescript-
|
|
2
|
+
import { t as detectTypeScript } from "./detect-typescript-Dr_oLniM.js";
|
|
3
3
|
import { useCallback, useMemo } from "react";
|
|
4
4
|
import { jsx } from "react/jsx-runtime";
|
|
5
5
|
import CodeEditor from "@jbpark/ui-kit/CodeEditor";
|
|
@@ -75,4 +75,4 @@ const Core = ({ value, theme, height, className, prettierOptions, fragment, raw,
|
|
|
75
75
|
//#endregion
|
|
76
76
|
export { useFormatCode as n, Core as t };
|
|
77
77
|
|
|
78
|
-
//# sourceMappingURL=core-
|
|
78
|
+
//# sourceMappingURL=core-Bgfek364.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"core-Bgfek364.js","names":[],"sources":["../src/components/editor/use-format-code.ts","../src/components/editor/core.tsx"],"sourcesContent":["import { useCallback, useMemo } from 'react';\n\nimport { detectTypeScript } from '~/utils/detect-typescript';\n\n// Prettier is an optional peer dependency (#282), loaded on first use.\n// Without it, code comes back unformatted.\nconst loadPrettier = async () => {\n const [prettier, babel, estree, typescript] = await Promise.all([\n import('prettier'),\n import('prettier/plugins/babel'),\n import('prettier/plugins/estree'),\n import('prettier/plugins/typescript'),\n ]);\n\n return {\n format: prettier.format,\n plugins: [babel.default, estree.default, typescript.default],\n };\n};\n\nconst DEFAULT_PRETTIER_OPTIONS: Record<string, unknown> = {\n tabWidth: 2,\n singleQuote: true,\n trailingComma: 'all',\n htmlWhitespaceSensitivity: 'ignore',\n arrowParens: 'avoid',\n printWidth: 60,\n};\n\nexport interface UseFormatCodeOptions {\n fragment?: boolean;\n prettierOptions?: Record<string, unknown>;\n}\n\n// The formatter the built-in editor uses, shared with a custom\n// `renderEditor`.\nexport const useFormatCode = ({\n fragment,\n prettierOptions,\n}: UseFormatCodeOptions = {}) => {\n const prettierConfig = useMemo(\n () => ({\n ...DEFAULT_PRETTIER_OPTIONS,\n ...prettierOptions,\n }),\n [prettierOptions],\n );\n\n return useCallback(\n async (code: string) => {\n const isTypeScript = detectTypeScript(code);\n const source = fragment ? `<>${code}</>` : code;\n\n // Only a missing prettier is swallowed here; a genuine format error\n // (prettier present, but the code is unparseable) still propagates so\n // the editor's Cmd+S path can surface it via onError.\n let prettier: Awaited<ReturnType<typeof loadPrettier>>;\n try {\n prettier = await loadPrettier();\n } catch {\n return code;\n }\n\n const formatted = await prettier.format(source, {\n parser: isTypeScript ? 'typescript' : 'babel',\n plugins: prettier.plugins,\n ...prettierConfig,\n });\n\n if (fragment) {\n return formatted\n .replace(/^<>\\n?/, '')\n .replace(/\\n?<\\/>;?\\s*$/, '')\n .replace(/^ {2}/gm, '')\n .trim();\n }\n\n return formatted;\n },\n [prettierConfig, fragment],\n );\n};\n","import type { ComponentProps } from 'react';\n\nimport CodeEditor from '@jbpark/ui-kit/CodeEditor';\nimport { vscodeLight } from '@uiw/codemirror-theme-vscode';\nimport { type Extension } from '@uiw/react-codemirror';\n\nimport { cn } from '~/utils/cn';\n\nimport { useFormatCode } from './use-format-code';\n\nexport interface Props {\n value: string;\n height?: string;\n theme?: Extension | 'light' | 'dark' | 'none';\n prettierOptions?: Record<string, unknown>;\n fragment?: boolean;\n raw?: boolean;\n className?: string;\n onChange?: (value: string) => void;\n onSave?: (value: string) => void;\n onError?: (error: string | null) => void;\n onCreateEditor?: ComponentProps<typeof CodeEditor>['onCreateEditor'];\n onUpdate?: ComponentProps<typeof CodeEditor>['onUpdate'];\n}\n\n// `@jbpark/ui-kit`'s CodeEditor with this library's options (#346, #309):\n// `raw`, `fragment` and `prettierOptions` shape the Prettier formatter, and\n// `onError` is its `onFormatError`.\nconst Core = ({\n value,\n theme,\n height,\n className,\n prettierOptions,\n fragment,\n raw,\n onChange,\n onSave,\n onError,\n ...props\n}: Props) => {\n const formatCode = useFormatCode({ fragment, prettierOptions });\n\n return (\n <CodeEditor\n value={value}\n theme={theme ?? vscodeLight}\n height={height || '100%'}\n className={cn(className)}\n // `raw` (e.g. innerHTML) skips prettier; otherwise reuse the shared\n // prettier wiring as the Cmd+S formatter rather than reimplementing it.\n formatCode={raw ? undefined : formatCode}\n onChange={onChange}\n onSave={onSave}\n onFormatError={onError}\n {...props}\n />\n );\n};\n\nexport default Core;\n"],"mappings":";;;;;;;;AAMA,MAAM,eAAe,YAAY;CAC/B,MAAM,CAAC,UAAU,OAAO,QAAQ,cAAc,MAAM,QAAQ,IAAI;EAC9D,OAAO;EACP,OAAO;EACP,OAAO;EACP,OAAO;CACT,CAAC;CAED,OAAO;EACL,QAAQ,SAAS;EACjB,SAAS;GAAC,MAAM;GAAS,OAAO;GAAS,WAAW;EAAO;CAC7D;AACF;AAEA,MAAM,2BAAoD;CACxD,UAAU;CACV,aAAa;CACb,eAAe;CACf,2BAA2B;CAC3B,aAAa;CACb,YAAY;AACd;AASA,MAAa,iBAAiB,EAC5B,UACA,oBACwB,CAAC,MAAM;CAC/B,MAAM,iBAAiB,eACd;EACL,GAAG;EACH,GAAG;CACL,IACA,CAAC,eAAe,CAClB;CAEA,OAAO,YACL,OAAO,SAAiB;EACtB,MAAM,eAAe,iBAAiB,IAAI;EAC1C,MAAM,SAAS,WAAW,KAAK,KAAK,OAAO;EAK3C,IAAI;EACJ,IAAI;GACF,WAAW,MAAM,aAAa;EAChC,QAAQ;GACN,OAAO;EACT;EAEA,MAAM,YAAY,MAAM,SAAS,OAAO,QAAQ;GAC9C,QAAQ,eAAe,eAAe;GACtC,SAAS,SAAS;GAClB,GAAG;EACL,CAAC;EAED,IAAI,UACF,OAAO,UACJ,QAAQ,UAAU,EAAE,CAAC,CACrB,QAAQ,iBAAiB,EAAE,CAAC,CAC5B,QAAQ,WAAW,EAAE,CAAC,CACtB,KAAK;EAGV,OAAO;CACT,GACA,CAAC,gBAAgB,QAAQ,CAC3B;AACF;;;ACrDA,MAAM,QAAQ,EACZ,OACA,OACA,QACA,WACA,iBACA,UACA,KACA,UACA,QACA,SACA,GAAG,YACQ;CACX,MAAM,aAAa,cAAc;EAAE;EAAU;CAAgB,CAAC;CAE9D,OACE,oBAAC,YAAD;EACS;EACP,OAAO,SAAS;EAChB,QAAQ,UAAU;EAClB,WAAW,GAAG,SAAS;EAGvB,YAAY,MAAM,KAAA,IAAY;EACpB;EACF;EACR,eAAe;EACf,GAAI;CACL,CAAA;AAEL"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { l as TS_PATTERNS, s as REGEX } from "./constants-
|
|
1
|
+
import { l as TS_PATTERNS, s as REGEX } from "./constants-DU17Tdwo.js";
|
|
2
2
|
//#region src/utils/detect-typescript.ts
|
|
3
3
|
const detectTypeScript = (code) => {
|
|
4
4
|
const withoutModuleLines = code.replace(REGEX.MODULE_IMPORT_EXPORT_LINE, "");
|
|
@@ -7,4 +7,4 @@ const detectTypeScript = (code) => {
|
|
|
7
7
|
//#endregion
|
|
8
8
|
export { detectTypeScript as t };
|
|
9
9
|
|
|
10
|
-
//# sourceMappingURL=detect-typescript-
|
|
10
|
+
//# sourceMappingURL=detect-typescript-Dr_oLniM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect-typescript-Dr_oLniM.js","names":[],"sources":["../src/utils/detect-typescript.ts"],"sourcesContent":["import { REGEX, TS_PATTERNS } from '../constants';\n\nexport const detectTypeScript = (code: string): boolean => {\n // Drop `import` and `export ... from` lines first: their `as`\n // (`import * as ui`) is plain JavaScript but matches the `as\\s+\\w+` check\n // below.\n const withoutModuleLines = code.replace(REGEX.MODULE_IMPORT_EXPORT_LINE, '');\n\n return TS_PATTERNS.some(pattern => pattern.test(withoutModuleLines));\n};\n"],"mappings":";;AAEA,MAAa,oBAAoB,SAA0B;CAIzD,MAAM,qBAAqB,KAAK,QAAQ,MAAM,2BAA2B,EAAE;CAE3E,OAAO,YAAY,MAAK,YAAW,QAAQ,KAAK,kBAAkB,CAAC;AACrE"}
|
package/dist/dnd/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as PanelBindingSource, A as DndLayout, B as DndPalette, C as useDndItems, D as DraggableItem, E as useDndChildren, F as DndLayoutProps, G as useDndInspector, H as Props, I as DndRegionProps, J as Field, K as DndEditError, L as Layout, M as useDndPalette, N as useDndPanel, O as DraggableItemDragState, P as Canvas, Q as PanelBindingElement, R as Palette, S as DndItemsOptions, T as DndChildrenOptions, U as DndInspector, V as DndPanel, W as DndNodePick, X as PanelBinding, Y as FieldProps, Z as PanelBindingData, _ as DndItemsActions, a as ItemsEditorActions, at as DndSectionFallbackArgs, b as DndItemsNestedElement, c as ItemsEditorNestedGroup, d as useItemsEditor, et as PanelNodeChange, f as FieldKind, g as DndItems, h as isStructuralFieldKind, i as ItemsEditor, it as DndRenderSectionFallback, j as useDndLayout, k as DraggableItemProps, l as ItemsEditorOptions, m as getFieldKind, n as ChildrenEditorOptions, nt as resolvePanelBindings, o as ItemsEditorItem, p as FieldKindBinding, q as DndRenderField, r as Dnd, rt as withPanelCommit, s as ItemsEditorNestedElement, t as ChildrenEditor, tt as PanelNodesChange, u as useChildrenEditor, v as DndItemsExpansion, w as DndChildren, x as DndItemsNestedGroup, y as DndItemsItem, z as Panel } from "../index-C69Lv-jR.js";
|
|
2
2
|
export { Canvas, ChildrenEditor, ChildrenEditorOptions, type DndChildren, type DndChildrenOptions, type DndEditError, type DndInspector, type DndItems, type DndItemsActions, type DndItemsExpansion, type DndItemsItem, type DndItemsNestedElement, type DndItemsNestedGroup, type DndItemsOptions, type DndLayout, type DndLayoutProps, type DndNodePick, type DndPalette, type DndPanel, type DndRegionProps, type DndRenderField, type DndRenderSectionFallback, type DndSectionFallbackArgs, DraggableItem, type DraggableItemDragState, type DraggableItemProps, Field, type FieldKind, type FieldKindBinding, type FieldProps, ItemsEditor, ItemsEditorActions, ItemsEditorItem, ItemsEditorNestedElement, ItemsEditorNestedGroup, ItemsEditorOptions, Layout, Palette, Panel, type PanelBinding, type PanelBindingData, type PanelBindingElement, type PanelBindingSource, type PanelNodeChange, type PanelNodesChange, type Props, Dnd as default, getFieldKind, isStructuralFieldKind, resolvePanelBindings, useChildrenEditor, useDndChildren, useDndInspector, useDndItems, useDndLayout, useDndPalette, useDndPanel, useItemsEditor, withPanelCommit };
|
package/dist/dnd/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _ as useDndPanel, a as Layout, c as Field, d as isStructuralFieldKind, f as useDndChildren, g as useDndPalette, h as useDndLayout, i as Canvas, l as useDndItems, m as withPanelCommit, n as useChildrenEditor, o as Palette, p as resolvePanelBindings, r as useItemsEditor, s as Panel, t as Dnd, u as getFieldKind, v as
|
|
1
|
+
import { _ as useDndPanel, a as Layout, c as Field, d as isStructuralFieldKind, f as useDndChildren, g as useDndPalette, h as useDndLayout, i as Canvas, l as useDndItems, m as withPanelCommit, n as useChildrenEditor, o as Palette, p as resolvePanelBindings, r as useItemsEditor, s as Panel, t as Dnd, u as getFieldKind, v as useDndInspector, y as DraggableItem } from "../dnd-UoJILA75.js";
|
|
2
2
|
export { Canvas, DraggableItem, Field, Layout, Palette, Panel, Dnd as default, getFieldKind, isStructuralFieldKind, resolvePanelBindings, useChildrenEditor, useDndChildren, useDndInspector, useDndItems, useDndLayout, useDndPalette, useDndPanel, useItemsEditor, withPanelCommit };
|