@jbpark/live-editor 6.1.0 → 6.1.1
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 +8 -0
- package/README.md +8 -0
- package/dist/{boundary-CySnDUs2.js → boundary-CLMNMZFa.js} +4 -4
- package/dist/boundary-CLMNMZFa.js.map +1 -0
- package/dist/{cn-CAnad2vL.js → compile-BWZ5U59y.js} +135 -2
- package/dist/compile-BWZ5U59y.js.map +1 -0
- package/dist/{context-Bjq0CvBE.js → context-DJxX9epB.js} +2 -3
- package/dist/context-DJxX9epB.js.map +1 -0
- package/dist/{core-BNVuH8-l.js → core-BfJwD8Jx.js} +3 -6
- package/dist/core-BfJwD8Jx.js.map +1 -0
- package/dist/dnd/index.js +2 -2
- package/dist/{dnd-COjjjuD7.js → dnd-DAGjlJ0_.js} +206 -167
- package/dist/dnd-DAGjlJ0_.js.map +1 -0
- package/dist/{document-BkGWDZGc.js → document-MkQGxfry.js} +710 -318
- package/dist/document-MkQGxfry.js.map +1 -0
- package/dist/editor/index.js +2 -2
- package/dist/{editor-CvnzrM9J.js → editor-B_VOz3Q0.js} +2 -2
- package/dist/{editor-CvnzrM9J.js.map → editor-B_VOz3Q0.js.map} +1 -1
- package/dist/editor-caches-DXYXgWeD.js +63 -0
- package/dist/editor-caches-DXYXgWeD.js.map +1 -0
- package/dist/error/index.d.ts +1 -1
- package/dist/error/index.js +2 -2
- package/dist/{error-CFzvnLCf.js → error-E3T5CS3m.js} +13 -12
- package/dist/error-E3T5CS3m.js.map +1 -0
- package/dist/{index-DfIvkht8.d.ts → index-BC9hY7dI.d.ts} +5 -5
- package/dist/{index-CqsS207F.d.ts → index-D-Dfv65w.d.ts} +3 -2
- package/dist/index.d.ts +4 -4
- package/dist/index.js +5 -5
- package/dist/preview/index.d.ts +1 -1
- package/dist/preview/index.js +1 -1
- package/dist/{preview-8ejC5VP2.js → preview-DFb1pOqJ.js} +30 -11
- package/dist/preview-DFb1pOqJ.js.map +1 -0
- package/dist/provider/index.js +1 -1
- package/dist/{sections-rD8NuRI8.js → sections-CnG7OLr8.js} +3 -3
- package/dist/{sections-rD8NuRI8.js.map → sections-CnG7OLr8.js.map} +1 -1
- package/dist/states-Ci1AvoQ9.js.map +1 -1
- package/dist/style.css +74 -74
- package/dist/ui-kit/index.js +2 -2
- package/dist/ui-kit/style.css +22 -6
- package/dist/{update-DyFnX8yE.js → update-DnX2ibYn.js} +162 -43
- package/dist/update-DnX2ibYn.js.map +1 -0
- package/dist/{use-dynamic-tailwind-0nuRN57N.js → use-dynamic-tailwind-CjXeHjsr.js} +107 -25
- package/dist/use-dynamic-tailwind-CjXeHjsr.js.map +1 -0
- package/dist/{use-field-state-iQGW0vc6.js → use-field-state-B1VLhWfr.js} +2 -2
- package/dist/{use-field-state-iQGW0vc6.js.map → use-field-state-B1VLhWfr.js.map} +1 -1
- package/dist/utils/ast/index.js +1 -86
- package/dist/utils/index.js +3 -4
- package/package.json +2 -3
- package/dist/boundary-CySnDUs2.js.map +0 -1
- package/dist/cn-CAnad2vL.js.map +0 -1
- package/dist/compile-my0AjsCw.js +0 -136
- package/dist/compile-my0AjsCw.js.map +0 -1
- package/dist/constants-st9gnJ5g.js +0 -55
- package/dist/constants-st9gnJ5g.js.map +0 -1
- package/dist/context-Bjq0CvBE.js.map +0 -1
- package/dist/core-BNVuH8-l.js.map +0 -1
- package/dist/detect-typescript-CMF0FVKD.js +0 -10
- package/dist/detect-typescript-CMF0FVKD.js.map +0 -1
- package/dist/dnd-COjjjuD7.js.map +0 -1
- package/dist/document-BkGWDZGc.js.map +0 -1
- package/dist/editor-caches-ClMvdcQF.js +0 -23
- package/dist/editor-caches-ClMvdcQF.js.map +0 -1
- package/dist/error-CFzvnLCf.js.map +0 -1
- package/dist/preview-8ejC5VP2.js.map +0 -1
- package/dist/update-DyFnX8yE.js.map +0 -1
- package/dist/use-dynamic-tailwind-0nuRN57N.js.map +0 -1
- package/dist/utils/ast/index.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"constants-st9gnJ5g.js","names":[],"sources":["../src/constants/index.ts"],"sourcesContent":["export 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) => `\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 { PALETTE_SECTIONS } from './palette-sections';\n"],"mappings":";AAEA,MAAa,SAAS;CACpB,aAAa;CAIb,sBAAsB;CAGtB,oBAAoB;AACtB;AAEA,MAAa,YAAY;CACvB,IAAI;CACJ,SAAS;CAGT,aAAa;CACb,MAAM;AACR;AAMA,MAAa,8BAAiD;CAC5D,UAAU;CACV;CACA,UAAU;CACV,UAAU;AACZ;AAEA,MAAa,QAAQ;CACnB,QAAQ;CACR,iBAAiB;CACjB,2BACE;AACJ;AAEA,MAAa,cAAc;CACzB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,MAAa,eAAe;CAC1B,UAAU;CACV,YAAY;CACZ,YAAY;CACZ,OAAO;AACT;AAIA,MAAa,uBAAuB;AAKpC,MAAa,oBAAoB,gBAAwB;;;gBAGzC,YAAY;;;;;;AAO5B,MAAa,mBAAmB,iBAAiB,oBAAoB"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"context-Bjq0CvBE.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 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"core-BNVuH8-l.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\ninterface 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 { useRef } from 'react';\n\nimport { javascript } from '@codemirror/lang-javascript';\nimport { vscodeLight } from '@uiw/codemirror-theme-vscode';\nimport CodeMirror, {\n EditorView,\n type Extension,\n type ReactCodeMirrorProps,\n type ReactCodeMirrorRef,\n} 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?: ReactCodeMirrorProps['onCreateEditor'];\n onUpdate?: ReactCodeMirrorProps['onUpdate'];\n}\n\n// JSX and TypeScript highlighting, with long lines wrapped.\nconst EXTENSIONS = [\n javascript({ jsx: true, typescript: true }),\n EditorView.lineWrapping,\n];\n\n// CodeMirror with this library's options (#346, #309). Cmd/Ctrl+S formats\n// the code with Prettier, shaped by `raw`, `fragment` and\n// `prettierOptions`, then calls `onChange` and `onSave`; a formatter error\n// goes to `onError`.\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 const editorRef = useRef<ReactCodeMirrorRef>(null);\n\n const save = async () => {\n const view = editorRef.current?.view;\n\n if (!view) {\n return;\n }\n\n const current = view.state.doc.toString();\n\n try {\n // `raw` (e.g. innerHTML) skips Prettier.\n const next = raw ? current : await formatCode(current);\n\n // An edit landed while Prettier ran: keep it rather than the result.\n if (view.state.doc.toString() !== current) {\n onError?.(null);\n\n return;\n }\n\n if (next !== current) {\n view.dispatch({\n changes: { from: 0, to: current.length, insert: next },\n selection: {\n anchor: Math.min(view.state.selection.main.head, next.length),\n },\n });\n }\n\n onChange?.(next);\n onSave?.(next);\n onError?.(null);\n } catch (e) {\n onError?.(e instanceof Error ? e.message : String(e));\n }\n };\n\n return (\n <div\n className={cn(className)}\n onKeyDown={e => {\n if ((e.metaKey || e.ctrlKey) && e.key === 's') {\n e.preventDefault();\n void save();\n }\n }}\n >\n <CodeMirror\n ref={editorRef}\n value={value}\n theme={theme ?? vscodeLight}\n height={height || '100%'}\n extensions={EXTENSIONS}\n onChange={onChange}\n {...props}\n />\n </div>\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;;;AClDA,MAAM,aAAa,CACjB,WAAW;CAAE,KAAK;CAAM,YAAY;AAAK,CAAC,GAC1C,WAAW,YACb;AAMA,MAAM,QAAQ,EACZ,OACA,OACA,QACA,WACA,iBACA,UACA,KACA,UACA,QACA,SACA,GAAG,YACQ;CACX,MAAM,aAAa,cAAc;EAAE;EAAU;CAAgB,CAAC;CAE9D,MAAM,YAAY,OAA2B,IAAI;CAEjD,MAAM,OAAO,YAAY;EACvB,MAAM,OAAO,UAAU,SAAS;EAEhC,IAAI,CAAC,MACH;EAGF,MAAM,UAAU,KAAK,MAAM,IAAI,SAAS;EAExC,IAAI;GAEF,MAAM,OAAO,MAAM,UAAU,MAAM,WAAW,OAAO;GAGrD,IAAI,KAAK,MAAM,IAAI,SAAS,MAAM,SAAS;IACzC,UAAU,IAAI;IAEd;GACF;GAEA,IAAI,SAAS,SACX,KAAK,SAAS;IACZ,SAAS;KAAE,MAAM;KAAG,IAAI,QAAQ;KAAQ,QAAQ;IAAK;IACrD,WAAW,EACT,QAAQ,KAAK,IAAI,KAAK,MAAM,UAAU,KAAK,MAAM,KAAK,MAAM,EAC9D;GACF,CAAC;GAGH,WAAW,IAAI;GACf,SAAS,IAAI;GACb,UAAU,IAAI;EAChB,SAAS,GAAG;GACV,UAAU,aAAa,QAAQ,EAAE,UAAU,OAAO,CAAC,CAAC;EACtD;CACF;CAEA,OACE,oBAAC,OAAD;EACE,WAAW,GAAG,SAAS;EACvB,YAAW,MAAK;GACd,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,KAAK;IAC7C,EAAE,eAAe;IACjB,KAAU;GACZ;EACF;EAEA,UAAA,oBAAC,YAAD;GACE,KAAK;GACE;GACP,OAAO,SAAS;GAChB,QAAQ,UAAU;GAClB,YAAY;GACF;GACV,GAAI;EACL,CAAA;CACE,CAAA;AAET"}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { c as TS_PATTERNS, o as REGEX } from "./constants-st9gnJ5g.js";
|
|
2
|
-
//#region src/utils/detect-typescript.ts
|
|
3
|
-
const detectTypeScript = (code) => {
|
|
4
|
-
const withoutModuleLines = code.replace(REGEX.MODULE_IMPORT_EXPORT_LINE, "");
|
|
5
|
-
return TS_PATTERNS.some((pattern) => pattern.test(withoutModuleLines));
|
|
6
|
-
};
|
|
7
|
-
//#endregion
|
|
8
|
-
export { detectTypeScript as t };
|
|
9
|
-
|
|
10
|
-
//# sourceMappingURL=detect-typescript-CMF0FVKD.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"detect-typescript-CMF0FVKD.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"}
|