@k2b/ui 0.36.0 → 0.38.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/dist/browser/actions/Dropdown.js +132 -116
- package/dist/browser/actions/Dropdown.js.map +1 -1
- package/dist/browser/actions/Tabs.js +18 -15
- package/dist/browser/actions/Tabs.js.map +1 -1
- package/dist/browser/chat/ChatComposer.js +58 -25
- package/dist/browser/chat/ChatComposer.js.map +1 -1
- package/dist/browser/content/MessageRow.js +164 -152
- package/dist/browser/content/MessageRow.js.map +1 -1
- package/dist/browser/feedback/prompts.js +23 -9
- package/dist/browser/feedback/prompts.js.map +1 -1
- package/dist/browser/inputs/Combobox.js +56 -51
- package/dist/browser/inputs/Combobox.js.map +1 -1
- package/dist/browser/inputs/RichSelect.js +86 -67
- package/dist/browser/inputs/RichSelect.js.map +1 -1
- package/dist/browser/internal/keyed-for.js +47 -0
- package/dist/browser/internal/keyed-for.js.map +1 -0
- package/dist/browser/layout/AppWorkspace.js +252 -238
- package/dist/browser/layout/AppWorkspace.js.map +1 -1
- package/dist/browser/text-editor/engine.js +13 -10
- package/dist/browser/text-editor/engine.js.map +1 -1
- package/dist/ssr/actions/Dropdown.js +101 -85
- package/dist/ssr/actions/Dropdown.js.map +1 -1
- package/dist/ssr/actions/Tabs.js +12 -9
- package/dist/ssr/actions/Tabs.js.map +1 -1
- package/dist/ssr/chat/ChatComposer.js +41 -15
- package/dist/ssr/chat/ChatComposer.js.map +1 -1
- package/dist/ssr/content/MessageRow.js +9 -2
- package/dist/ssr/content/MessageRow.js.map +1 -1
- package/dist/ssr/feedback/prompts.js +22 -8
- package/dist/ssr/feedback/prompts.js.map +1 -1
- package/dist/ssr/inputs/Combobox.js +18 -13
- package/dist/ssr/inputs/Combobox.js.map +1 -1
- package/dist/ssr/inputs/RichSelect.js +47 -28
- package/dist/ssr/inputs/RichSelect.js.map +1 -1
- package/dist/ssr/internal/keyed-for.js +47 -0
- package/dist/ssr/internal/keyed-for.js.map +1 -0
- package/dist/ssr/layout/AppWorkspace.js +58 -50
- package/dist/ssr/layout/AppWorkspace.js.map +1 -1
- package/dist/ssr/text-editor/engine.js +13 -10
- package/dist/ssr/text-editor/engine.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types/content/MessageRow.d.ts +1 -1
- package/dist/types/feedback/prompts.d.ts +5 -0
- package/dist/types/internal/keyed-for.d.ts +13 -0
- package/dist/types/layout/AppWorkspace.d.ts +6 -0
- package/dist/types/text-editor/engine.d.ts +1 -1
- package/package.json +3 -2
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { standardKeymap } from "@codemirror/commands";
|
|
2
|
-
import { defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
|
|
2
|
+
import { defaultHighlightStyle, HighlightStyle, syntaxHighlighting } from "@codemirror/language";
|
|
3
3
|
import { Compartment, EditorState, Prec, Text } from "@codemirror/state";
|
|
4
4
|
import { drawSelection, EditorView, keymap, placeholder, runScopeHandlers } from "@codemirror/view";
|
|
5
|
+
import { tags } from "@lezer/highlight";
|
|
5
6
|
import { clipboardLoading } from "./clipboard-loading";
|
|
6
7
|
import { commands } from "./format";
|
|
7
8
|
import { formattedView } from "./formatted";
|
|
8
9
|
import { markdown } from "./language";
|
|
9
10
|
import { cleanPaste } from "./paste";
|
|
10
11
|
import { tableGrid, tableHost } from "./table";
|
|
11
|
-
|
|
12
|
+
// CodeMirror's default style underlines headings; heading lines carry their own size and weight instead.
|
|
13
|
+
const highlightStyle = HighlightStyle.define(defaultHighlightStyle.specs.filter(spec => spec.tag !== tags.heading));
|
|
14
|
+
const builtInPresentation = mode => [markdown(), syntaxHighlighting(highlightStyle, {
|
|
12
15
|
fallback: true
|
|
13
16
|
}), mode === "formatted" ? formattedView : EditorView.contentAttributes.of({
|
|
14
17
|
class: "k2b-ste-source"
|
|
@@ -30,7 +33,8 @@ const builtInPresentation = mode => [markdown(), syntaxHighlighting(defaultHighl
|
|
|
30
33
|
export function createTextEditor(options) {
|
|
31
34
|
const modeSlot = new Compartment();
|
|
32
35
|
const readOnlySlot = new Compartment();
|
|
33
|
-
|
|
36
|
+
// The label is read on every view update; changing it never dispatches, so a host may set it during an update.
|
|
37
|
+
let label = options.label;
|
|
34
38
|
const presentation = options.presentation ?? builtInPresentation;
|
|
35
39
|
const runHistory = (view, direction) => {
|
|
36
40
|
view.state.facet(tableHost)?.[direction]();
|
|
@@ -42,8 +46,8 @@ export function createTextEditor(options) {
|
|
|
42
46
|
parent: options.parent,
|
|
43
47
|
state: EditorState.create({
|
|
44
48
|
doc: Text.of((options.doc ?? "").split("\n")),
|
|
45
|
-
extensions: [drawSelection(), EditorView.lineWrapping, placeholder(options.placeholder ?? ""),
|
|
46
|
-
"aria-label":
|
|
49
|
+
extensions: [drawSelection(), EditorView.lineWrapping, placeholder(options.placeholder ?? ""), EditorView.contentAttributes.of(() => ({
|
|
50
|
+
"aria-label": label
|
|
47
51
|
})), Prec.highest([keymap.of([{
|
|
48
52
|
key: "Mod-z",
|
|
49
53
|
run: view => runHistory(view, "undo"),
|
|
@@ -104,11 +108,10 @@ export function createTextEditor(options) {
|
|
|
104
108
|
setMode: mode => view.dispatch({
|
|
105
109
|
effects: modeSlot.reconfigure(modeExtensions(mode))
|
|
106
110
|
}),
|
|
107
|
-
setLabel:
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}),
|
|
111
|
+
setLabel: next => {
|
|
112
|
+
label = next;
|
|
113
|
+
view.contentDOM.setAttribute("aria-label", next);
|
|
114
|
+
},
|
|
112
115
|
// Hosts re-run this from reactive state that may change inside an editor update; an unchanged value must not dispatch.
|
|
113
116
|
setReadOnly: readOnly => {
|
|
114
117
|
if (view.state.readOnly === readOnly && view.state.facet(EditorView.editable) === !readOnly) return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["text-editor/engine.ts"],"sourcesContent":["import { standardKeymap } from \"@codemirror/commands\";\nimport { defaultHighlightStyle, syntaxHighlighting } from \"@codemirror/language\";\nimport { Compartment, EditorState, type Extension, Prec, Text } from \"@codemirror/state\";\nimport { drawSelection, EditorView, keymap, placeholder, runScopeHandlers } from \"@codemirror/view\";\nimport { clipboardLoading } from \"./clipboard-loading\";\nimport { commands } from \"./format\";\nimport { formattedView } from \"./formatted\";\nimport { markdown } from \"./language\";\nimport { cleanPaste } from \"./paste\";\nimport { type TableHost, tableGrid, tableHost } from \"./table\";\n\nexport type TextEditorMode = \"formatted\" | \"source\" | \"plain\";\nexport type TextEditorOptions = {\n parent: HTMLElement;\n /** Initial Markdown. Subsequent collaborative changes arrive through extensions. */\n doc?: string;\n mode?: TextEditorMode;\n readOnly?: boolean;\n placeholder?: string;\n label: string;\n extensions?: Extension;\n /**\n * Replaces the built-in Markdown language, highlighting, formatted markers and\n * formatting keys of the formatted and source modes. The engine keeps the\n * table grid, clean paste, limits and history routing in every mode.\n */\n presentation?: (mode: Exclude<TextEditorMode, \"plain\">) => Extension;\n /** The host owns history; collaboration bindings supply theirs. */\n host: TableHost;\n onChange?: (text: string) => void;\n onDroppedImages?: (count: number, files: File[]) => void;\n};\n\nconst builtInPresentation = (mode: Exclude<TextEditorMode, \"plain\">): Extension => [\n markdown(),\n syntaxHighlighting(defaultHighlightStyle, { fallback: true }),\n mode === \"formatted\" ? formattedView : EditorView.contentAttributes.of({ class: \"k2b-ste-source\" }),\n keymap.of([\n { key: \"Mod-b\", run: commands.bold },\n { key: \"Mod-i\", run: commands.italic },\n { key: \"Mod-k\", run: commands.link },\n { key: \"Mod-Shift-x\", run: commands.strike },\n ]),\n];\n\n/** Shared engine; application extensions own transport, awareness and domain features. */\nexport function createTextEditor(options: TextEditorOptions) {\n const modeSlot = new Compartment();\n const readOnlySlot = new Compartment();\n const labelSlot = new Compartment();\n const presentation = options.presentation ?? builtInPresentation;\n const runHistory = (view: EditorView, direction: \"undo\" | \"redo\") => {\n view.state.facet(tableHost)?.[direction]();\n // Consume even an empty or read-only history, so another binding cannot handle it again.\n return true;\n };\n const modeExtensions = (mode: TextEditorMode): Extension =>\n mode === \"plain\"\n ? [clipboardLoading(() => options.host), cleanPaste(options.onDroppedImages, true)]\n : [\n presentation(mode),\n mode === \"formatted\" ? tableGrid() : clipboardLoading(() => options.host),\n cleanPaste(options.onDroppedImages),\n ];\n const view = new EditorView({\n parent: options.parent,\n state: EditorState.create({\n doc: Text.of((options.doc ?? \"\").split(\"\\n\")),\n extensions: [\n drawSelection(),\n EditorView.lineWrapping,\n placeholder(options.placeholder ?? \"\"),\n labelSlot.of(EditorView.contentAttributes.of({ \"aria-label\": options.label })),\n Prec.highest([\n keymap.of([\n { key: \"Mod-z\", run: (view) => runHistory(view, \"undo\"), shift: (view) => runHistory(view, \"redo\"), preventDefault: true },\n { key: \"Mod-y\", run: (view) => runHistory(view, \"redo\"), preventDefault: true },\n ]),\n EditorView.domEventHandlers({\n beforeinput(event, view) {\n if (event.inputType === \"historyUndo\") return runHistory(view, \"undo\");\n if (event.inputType === \"historyRedo\") return runHistory(view, \"redo\");\n return false;\n },\n }),\n ]),\n keymap.of(standardKeymap),\n tableHost.of({\n ...options.host,\n onDroppedImages: options.onDroppedImages,\n undo: () => {\n if (!view.state.readOnly) options.host.undo();\n },\n redo: () => {\n if (!view.state.readOnly) options.host.redo();\n },\n }),\n EditorState.transactionFilter.of((tr) => {\n // Only growth is refused: an over-limit text stays editable so it can be shortened.\n if (\n tr.docChanged &&\n tr.isUserEvent(\"input\") &&\n tr.newDoc.length > options.host.maxLength &&\n tr.newDoc.length > tr.startState.doc.length\n ) {\n const inserted: string[] = [];\n tr.changes.iterChanges((_from, _to, _newFrom, _newTo, text) => {\n if (text.length) inserted.push(text.toString());\n });\n queueMicrotask(() => {\n if (inserted.length) options.host.onRecovery(inserted.join(\"\\n\"));\n options.host.onError(\"textTooLarge\");\n });\n return [];\n }\n return tr;\n }),\n EditorView.updateListener.of((update) => {\n if (update.docChanged) options.onChange?.(update.state.doc.toString());\n }),\n modeSlot.of(modeExtensions(options.mode ?? \"formatted\")),\n readOnlySlot.of([EditorState.readOnly.of(options.readOnly ?? false), EditorView.editable.of(!options.readOnly)]),\n options.extensions ?? [],\n ],\n }),\n });\n // CM's Android DOM-input deferral can lose a physical Enter when the next\n // EditContext update arrives before its animation frame. Run the public\n // keymap synchronously only for that reliable, non-composing key event.\n const enter = (event: KeyboardEvent) => {\n if (\n event.target !== view.contentDOM ||\n event.key !== \"Enter\" ||\n event.keyCode !== 13 ||\n event.isComposing ||\n view.composing ||\n event.defaultPrevented ||\n !/Android/.test(view.dom.ownerDocument.defaultView?.navigator.userAgent ?? \"\") ||\n !(\"editContext\" in view.contentDOM && view.contentDOM.editContext)\n )\n return;\n if (runScopeHandlers(view, event, \"editor\")) {\n event.preventDefault();\n event.stopImmediatePropagation();\n }\n };\n view.contentDOM.addEventListener(\"keydown\", enter, true);\n return {\n view,\n /** Also re-reads `presentation`, so a host can refresh it, e.g. after a theme change. */\n setMode: (mode: TextEditorMode) => view.dispatch({ effects: modeSlot.reconfigure(modeExtensions(mode)) }),\n setLabel: (label: string) =>\n view.dispatch({ effects: labelSlot.reconfigure(EditorView.contentAttributes.of({ \"aria-label\": label })) }),\n // Hosts re-run this from reactive state that may change inside an editor update; an unchanged value must not dispatch.\n setReadOnly: (readOnly: boolean) => {\n if (view.state.readOnly === readOnly && view.state.facet(EditorView.editable) === !readOnly) return;\n view.dispatch({ effects: readOnlySlot.reconfigure([EditorState.readOnly.of(readOnly), EditorView.editable.of(!readOnly)]) });\n },\n destroy: () => {\n view.contentDOM.removeEventListener(\"keydown\", enter, true);\n view.destroy();\n },\n };\n}\nexport type TextEditorController = ReturnType<typeof createTextEditor>;\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":[],"sources":["text-editor/engine.ts"],"sourcesContent":["import { standardKeymap } from \"@codemirror/commands\";\nimport { defaultHighlightStyle, HighlightStyle, syntaxHighlighting } from \"@codemirror/language\";\nimport { Compartment, EditorState, type Extension, Prec, Text } from \"@codemirror/state\";\nimport { drawSelection, EditorView, keymap, placeholder, runScopeHandlers } from \"@codemirror/view\";\nimport { tags } from \"@lezer/highlight\";\nimport { clipboardLoading } from \"./clipboard-loading\";\nimport { commands } from \"./format\";\nimport { formattedView } from \"./formatted\";\nimport { markdown } from \"./language\";\nimport { cleanPaste } from \"./paste\";\nimport { type TableHost, tableGrid, tableHost } from \"./table\";\n\nexport type TextEditorMode = \"formatted\" | \"source\" | \"plain\";\nexport type TextEditorOptions = {\n parent: HTMLElement;\n /** Initial Markdown. Subsequent collaborative changes arrive through extensions. */\n doc?: string;\n mode?: TextEditorMode;\n readOnly?: boolean;\n placeholder?: string;\n label: string;\n extensions?: Extension;\n /**\n * Replaces the built-in Markdown language, highlighting, formatted markers and\n * formatting keys of the formatted and source modes. The engine keeps the\n * table grid, clean paste, limits and history routing in every mode.\n */\n presentation?: (mode: Exclude<TextEditorMode, \"plain\">) => Extension;\n /** The host owns history; collaboration bindings supply theirs. */\n host: TableHost;\n onChange?: (text: string) => void;\n onDroppedImages?: (count: number, files: File[]) => void;\n};\n\n// CodeMirror's default style underlines headings; heading lines carry their own size and weight instead.\nconst highlightStyle = HighlightStyle.define(defaultHighlightStyle.specs.filter((spec) => spec.tag !== tags.heading));\n\nconst builtInPresentation = (mode: Exclude<TextEditorMode, \"plain\">): Extension => [\n markdown(),\n syntaxHighlighting(highlightStyle, { fallback: true }),\n mode === \"formatted\" ? formattedView : EditorView.contentAttributes.of({ class: \"k2b-ste-source\" }),\n keymap.of([\n { key: \"Mod-b\", run: commands.bold },\n { key: \"Mod-i\", run: commands.italic },\n { key: \"Mod-k\", run: commands.link },\n { key: \"Mod-Shift-x\", run: commands.strike },\n ]),\n];\n\n/** Shared engine; application extensions own transport, awareness and domain features. */\nexport function createTextEditor(options: TextEditorOptions) {\n const modeSlot = new Compartment();\n const readOnlySlot = new Compartment();\n // The label is read on every view update; changing it never dispatches, so a host may set it during an update.\n let label = options.label;\n const presentation = options.presentation ?? builtInPresentation;\n const runHistory = (view: EditorView, direction: \"undo\" | \"redo\") => {\n view.state.facet(tableHost)?.[direction]();\n // Consume even an empty or read-only history, so another binding cannot handle it again.\n return true;\n };\n const modeExtensions = (mode: TextEditorMode): Extension =>\n mode === \"plain\"\n ? [clipboardLoading(() => options.host), cleanPaste(options.onDroppedImages, true)]\n : [\n presentation(mode),\n mode === \"formatted\" ? tableGrid() : clipboardLoading(() => options.host),\n cleanPaste(options.onDroppedImages),\n ];\n const view = new EditorView({\n parent: options.parent,\n state: EditorState.create({\n doc: Text.of((options.doc ?? \"\").split(\"\\n\")),\n extensions: [\n drawSelection(),\n EditorView.lineWrapping,\n placeholder(options.placeholder ?? \"\"),\n EditorView.contentAttributes.of(() => ({ \"aria-label\": label })),\n Prec.highest([\n keymap.of([\n { key: \"Mod-z\", run: (view) => runHistory(view, \"undo\"), shift: (view) => runHistory(view, \"redo\"), preventDefault: true },\n { key: \"Mod-y\", run: (view) => runHistory(view, \"redo\"), preventDefault: true },\n ]),\n EditorView.domEventHandlers({\n beforeinput(event, view) {\n if (event.inputType === \"historyUndo\") return runHistory(view, \"undo\");\n if (event.inputType === \"historyRedo\") return runHistory(view, \"redo\");\n return false;\n },\n }),\n ]),\n keymap.of(standardKeymap),\n tableHost.of({\n ...options.host,\n onDroppedImages: options.onDroppedImages,\n undo: () => {\n if (!view.state.readOnly) options.host.undo();\n },\n redo: () => {\n if (!view.state.readOnly) options.host.redo();\n },\n }),\n EditorState.transactionFilter.of((tr) => {\n // Only growth is refused: an over-limit text stays editable so it can be shortened.\n if (\n tr.docChanged &&\n tr.isUserEvent(\"input\") &&\n tr.newDoc.length > options.host.maxLength &&\n tr.newDoc.length > tr.startState.doc.length\n ) {\n const inserted: string[] = [];\n tr.changes.iterChanges((_from, _to, _newFrom, _newTo, text) => {\n if (text.length) inserted.push(text.toString());\n });\n queueMicrotask(() => {\n if (inserted.length) options.host.onRecovery(inserted.join(\"\\n\"));\n options.host.onError(\"textTooLarge\");\n });\n return [];\n }\n return tr;\n }),\n EditorView.updateListener.of((update) => {\n if (update.docChanged) options.onChange?.(update.state.doc.toString());\n }),\n modeSlot.of(modeExtensions(options.mode ?? \"formatted\")),\n readOnlySlot.of([EditorState.readOnly.of(options.readOnly ?? false), EditorView.editable.of(!options.readOnly)]),\n options.extensions ?? [],\n ],\n }),\n });\n // CM's Android DOM-input deferral can lose a physical Enter when the next\n // EditContext update arrives before its animation frame. Run the public\n // keymap synchronously only for that reliable, non-composing key event.\n const enter = (event: KeyboardEvent) => {\n if (\n event.target !== view.contentDOM ||\n event.key !== \"Enter\" ||\n event.keyCode !== 13 ||\n event.isComposing ||\n view.composing ||\n event.defaultPrevented ||\n !/Android/.test(view.dom.ownerDocument.defaultView?.navigator.userAgent ?? \"\") ||\n !(\"editContext\" in view.contentDOM && view.contentDOM.editContext)\n )\n return;\n if (runScopeHandlers(view, event, \"editor\")) {\n event.preventDefault();\n event.stopImmediatePropagation();\n }\n };\n view.contentDOM.addEventListener(\"keydown\", enter, true);\n return {\n view,\n /** Also re-reads `presentation`, so a host can refresh it, e.g. after a theme change. */\n setMode: (mode: TextEditorMode) => view.dispatch({ effects: modeSlot.reconfigure(modeExtensions(mode)) }),\n setLabel: (next: string) => {\n label = next;\n view.contentDOM.setAttribute(\"aria-label\", next);\n },\n // Hosts re-run this from reactive state that may change inside an editor update; an unchanged value must not dispatch.\n setReadOnly: (readOnly: boolean) => {\n if (view.state.readOnly === readOnly && view.state.facet(EditorView.editable) === !readOnly) return;\n view.dispatch({ effects: readOnlySlot.reconfigure([EditorState.readOnly.of(readOnly), EditorView.editable.of(!readOnly)]) });\n },\n destroy: () => {\n view.contentDOM.removeEventListener(\"keydown\", enter, true);\n view.destroy();\n },\n };\n}\nexport type TextEditorController = ReturnType<typeof createTextEditor>;\n"],"mappings":"AAAA,SAAS,cAAc,QAAQ,sBAAsB;AACrD,SAAS,qBAAqB,EAAE,cAAc,EAAE,kBAAkB,QAAQ,sBAAsB;AAChG,SAAS,WAAW,EAAE,WAAW,EAAkB,IAAI,EAAE,IAAI,QAAQ,mBAAmB;AACxF,SAAS,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,gBAAgB,QAAQ,kBAAkB;AACnG,SAAS,IAAI,QAAQ,kBAAkB;AACvC,SAAS,gBAAgB,QAAQ,qBAAqB;AACtD,SAAS,QAAQ,QAAQ,UAAU;AACnC,SAAS,aAAa,QAAQ,aAAa;AAC3C,SAAS,QAAQ,QAAQ,YAAY;AACrC,SAAS,UAAU,QAAQ,SAAS;AACpC,SAAyB,SAAS,EAAE,SAAS,QAAQ,SAAS;AAwB9D;AACA,MAAM,cAAc,GAAG,cAAc,CAAC,MAAM,CAAC,qBAAqB,CAAC,KAAK,CAAC,MAAM,CAAE,IAAI,IAAK,IAAI,CAAC,GAAG,KAAK,IAAI,CAAC,OAAO,CAAC,CAAC;AAErH,MAAM,mBAAmB,GAAI,IAAsC,IAAgB,CACjF,QAAQ,CAAC,CAAC,EACV,kBAAkB,CAAC,cAAc,EAAE;EAAE,QAAQ,EAAE;AAAK,CAAC,CAAC,EACtD,IAAI,KAAK,WAAW,GAAG,aAAa,GAAG,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC;EAAE,KAAK,EAAE;AAAiB,CAAC,CAAC,EACnG,MAAM,CAAC,EAAE,CAAC,CACR;EAAE,GAAG,EAAE,OAAO;EAAE,GAAG,EAAE,QAAQ,CAAC;AAAK,CAAC,EACpC;EAAE,GAAG,EAAE,OAAO;EAAE,GAAG,EAAE,QAAQ,CAAC;AAAO,CAAC,EACtC;EAAE,GAAG,EAAE,OAAO;EAAE,GAAG,EAAE,QAAQ,CAAC;AAAK,CAAC,EACpC;EAAE,GAAG,EAAE,aAAa;EAAE,GAAG,EAAE,QAAQ,CAAC;AAAO,CAAC,CAC7C,CAAC,CACH;;AAED;AACA,OAAO,SAAS,gBAAgB,CAAC,OAA0B,EAAE;EAC3D,MAAM,QAAQ,GAAG,IAAI,WAAW,CAAC,CAAC;EAClC,MAAM,YAAY,GAAG,IAAI,WAAW,CAAC,CAAC;EACtC;EACA,IAAI,KAAK,GAAG,OAAO,CAAC,KAAK;EACzB,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,mBAAmB;EAChE,MAAM,UAAU,GAAG,CAAC,IAAgB,EAAE,SAA0B,KAAK;IACnE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC;IAC1C;IACA,OAAO,IAAI;EACb,CAAC;EACD,MAAM,cAAc,GAAI,IAAoB,IAC1C,IAAI,KAAK,OAAO,GACZ,CAAC,gBAAgB,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC,GACjF,CACE,YAAY,CAAC,IAAI,CAAC,EAClB,IAAI,KAAK,WAAW,GAAG,SAAS,CAAC,CAAC,GAAG,gBAAgB,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,EACzE,UAAU,CAAC,OAAO,CAAC,eAAe,CAAC,CACpC;EACP,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC;IAC1B,MAAM,EAAE,OAAO,CAAC,MAAM;IACtB,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC;MACxB,GAAG,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,IAAI,EAAE,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;MAC7C,UAAU,EAAE,CACV,aAAa,CAAC,CAAC,EACf,UAAU,CAAC,YAAY,EACvB,WAAW,CAAC,OAAO,CAAC,WAAW,IAAI,EAAE,CAAC,EACtC,UAAU,CAAC,iBAAiB,CAAC,EAAE,CAAC,OAAO;QAAE,YAAY,EAAE;MAAM,CAAC,CAAC,CAAC,EAChE,IAAI,CAAC,OAAO,CAAC,CACX,MAAM,CAAC,EAAE,CAAC,CACR;QAAE,GAAG,EAAE,OAAO;QAAE,GAAG,EAAG,IAAI,IAAK,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC;QAAE,KAAK,EAAG,IAAI,IAAK,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC;QAAE,cAAc,EAAE;MAAK,CAAC,EAC1H;QAAE,GAAG,EAAE,OAAO;QAAE,GAAG,EAAG,IAAI,IAAK,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC;QAAE,cAAc,EAAE;MAAK,CAAC,CAChF,CAAC,EACF,UAAU,CAAC,gBAAgB,CAAC;QAC1B,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE;UACvB,IAAI,KAAK,CAAC,SAAS,KAAK,aAAa,EAAE,OAAO,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC;UACtE,IAAI,KAAK,CAAC,SAAS,KAAK,aAAa,EAAE,OAAO,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC;UACtE,OAAO,KAAK;QACd;MACF,CAAC,CAAC,CACH,CAAC,EACF,MAAM,CAAC,EAAE,CAAC,cAAc,CAAC,EACzB,SAAS,CAAC,EAAE,CAAC;QACX,GAAG,OAAO,CAAC,IAAI;QACf,eAAe,EAAE,OAAO,CAAC,eAAe;QACxC,IAAI,EAAE,MAAM;UACV,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/C,CAAC;QACD,IAAI,EAAE,MAAM;UACV,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/C;MACF,CAAC,CAAC,EACF,WAAW,CAAC,iBAAiB,CAAC,EAAE,CAAE,EAAE,IAAK;QACvC;QACA,IACE,EAAE,CAAC,UAAU,IACb,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,IACvB,EAAE,CAAC,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,SAAS,IACzC,EAAE,CAAC,MAAM,CAAC,MAAM,GAAG,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,EAC3C;UACA,MAAM,QAAkB,GAAG,EAAE;UAC7B,EAAE,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,KAAK;YAC7D,IAAI,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;UACjD,CAAC,CAAC;UACF,cAAc,CAAC,MAAM;YACnB,IAAI,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjE,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC;UACtC,CAAC,CAAC;UACF,OAAO,EAAE;QACX;QACA,OAAO,EAAE;MACX,CAAC,CAAC,EACF,UAAU,CAAC,cAAc,CAAC,EAAE,CAAE,MAAM,IAAK;QACvC,IAAI,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;MACxE,CAAC,CAAC,EACF,QAAQ,CAAC,EAAE,CAAC,cAAc,CAAC,OAAO,CAAC,IAAI,IAAI,WAAW,CAAC,CAAC,EACxD,YAAY,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAChH,OAAO,CAAC,UAAU,IAAI,EAAE;IAE5B,CAAC;EACH,CAAC,CAAC;EACF;EACA;EACA;EACA,MAAM,KAAK,GAAI,KAAoB,IAAK;IACtC,IACE,KAAK,CAAC,MAAM,KAAK,IAAI,CAAC,UAAU,IAChC,KAAK,CAAC,GAAG,KAAK,OAAO,IACrB,KAAK,CAAC,OAAO,KAAK,EAAE,IACpB,KAAK,CAAC,WAAW,IACjB,IAAI,CAAC,SAAS,IACd,KAAK,CAAC,gBAAgB,IACtB,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,WAAW,EAAE,SAAS,CAAC,SAAS,IAAI,EAAE,CAAC,IAC9E,EAAE,aAAa,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAElE;IACF,IAAI,gBAAgB,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,EAAE;MAC3C,KAAK,CAAC,cAAc,CAAC,CAAC;MACtB,KAAK,CAAC,wBAAwB,CAAC,CAAC;IAClC;EACF,CAAC;EACD,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC;EACxD,OAAO;IACL,IAAI;IACJ;IACA,OAAO,EAAG,IAAoB,IAAK,IAAI,CAAC,QAAQ,CAAC;MAAE,OAAO,EAAE,QAAQ,CAAC,WAAW,CAAC,cAAc,CAAC,IAAI,CAAC;IAAE,CAAC,CAAC;IACzG,QAAQ,EAAG,IAAY,IAAK;MAC1B,KAAK,GAAG,IAAI;MACZ,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,YAAY,EAAE,IAAI,CAAC;IAClD,CAAC;IACD;IACA,WAAW,EAAG,QAAiB,IAAK;MAClC,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE;MAC7F,IAAI,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC;MAAE,CAAC,CAAC;IAC9H,CAAC;IACD,OAAO,EAAE,MAAM;MACb,IAAI,CAAC,UAAU,CAAC,mBAAmB,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC;MAC3D,IAAI,CAAC,OAAO,CAAC,CAAC;IAChB;EACF,CAAC;AACH","ignoreList":[]}
|