@klarkxy/dsh-deep-view 0.1.0 → 0.2.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/lib/answer-text.d.ts +15 -0
- package/lib/answer-text.js +110 -0
- package/lib/answer-text.js.map +1 -0
- package/lib/answers.d.ts +49 -0
- package/lib/answers.js +60 -0
- package/lib/answers.js.map +1 -0
- package/lib/client.inner.cjs +362 -12
- package/lib/client.inner.cjs.map +1 -1
- package/lib/client.js +362 -12
- package/lib/contracts.d.ts +2 -98
- package/lib/contracts.js +2 -1
- package/lib/contracts.js.map +1 -1
- package/lib/contracts2.d.ts +103 -0
- package/lib/copy.d.ts +37 -0
- package/lib/copy.js +69 -0
- package/lib/copy.js.map +1 -0
- package/lib/core.d.ts +57 -0
- package/lib/core.js +2 -0
- package/lib/core2.js +1079 -0
- package/lib/core2.js.map +1 -0
- package/lib/index.d.ts +35 -4
- package/lib/index.js +847 -1130
- package/lib/index.js.map +1 -1
- package/lib/widget.d.ts +71 -0
- package/lib/widget.js +1365 -0
- package/lib/widget.js.map +1 -0
- package/package.json +28 -3
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { g as ViewNode, p as ViewAnswer } from "./contracts2.js";
|
|
2
|
+
//#region src/answer-text.d.ts
|
|
3
|
+
type Card = {
|
|
4
|
+
id: string;
|
|
5
|
+
title: string;
|
|
6
|
+
view: ViewNode;
|
|
7
|
+
};
|
|
8
|
+
declare function answerText(card: Pick<Card, 'title' | 'view'>, action: string, values: Record<string, unknown>): string;
|
|
9
|
+
/** Legacy messages have no metadata. Restore only unambiguous, fully validated
|
|
10
|
+
* answers that reproduce the exact text written by this plugin. Never guess
|
|
11
|
+
* from ordinary prose, and never modify the stored message. */
|
|
12
|
+
declare function legacyAnswer(card: Card, text: string): ViewAnswer | null;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { answerText, legacyAnswer };
|
|
15
|
+
//# sourceMappingURL=answer-text.d.ts.map
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { a as choiceValue, c as inputsSummary, h as viewInputs, p as validateViewAnswer } from "./core2.js";
|
|
2
|
+
import { isRecord } from "./contracts.js";
|
|
3
|
+
//#region src/answer-text.ts
|
|
4
|
+
function controls(view) {
|
|
5
|
+
return [
|
|
6
|
+
"options",
|
|
7
|
+
"confirm",
|
|
8
|
+
"form",
|
|
9
|
+
"submit"
|
|
10
|
+
].includes(view.type) ? [view] : (view.children ?? []).flatMap(controls);
|
|
11
|
+
}
|
|
12
|
+
function answerText(card, action, values) {
|
|
13
|
+
const node = controls(card.view).find((node) => node.id === action);
|
|
14
|
+
const lines = [card.title || "Deep View"];
|
|
15
|
+
if (Array.isArray(values.selected)) lines.push(values.selected.map(String).join(", "));
|
|
16
|
+
if (typeof values.confirmed === "boolean") lines.push(values.confirmed ? node?.confirmLabel ?? "Confirm" : node?.cancelLabel ?? "Cancel");
|
|
17
|
+
if (isRecord(values.fields)) for (const field of node?.fields ?? []) lines.push(`${field.label}: ${String(values.fields[field.id] ?? "")}`);
|
|
18
|
+
const inputs = inputsSummary(card.view, values.inputs);
|
|
19
|
+
if (inputs) lines.push(inputs);
|
|
20
|
+
if (node?.type === "submit") lines.push(node.label ?? "Submit");
|
|
21
|
+
return lines.join("\n");
|
|
22
|
+
}
|
|
23
|
+
/** Legacy messages have no metadata. Restore only unambiguous, fully validated
|
|
24
|
+
* answers that reproduce the exact text written by this plugin. Never guess
|
|
25
|
+
* from ordinary prose, and never modify the stored message. */
|
|
26
|
+
function legacyAnswer(card, text) {
|
|
27
|
+
if (text.startsWith("{")) try {
|
|
28
|
+
const data = JSON.parse(text);
|
|
29
|
+
if (!isRecord(data) || data.kind !== "answer_to_pending_view" || data.tool !== "show_view" || !Array.isArray(data.answers)) return null;
|
|
30
|
+
const answer = data.answers.find((value) => isRecord(value) && value.id === card.id);
|
|
31
|
+
if (!isRecord(answer) || typeof answer.action !== "string") return null;
|
|
32
|
+
const values = validateViewAnswer(card.view, answer.action, answer.values);
|
|
33
|
+
return values.error === void 0 ? {
|
|
34
|
+
id: card.id,
|
|
35
|
+
action: answer.action,
|
|
36
|
+
values
|
|
37
|
+
} : null;
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const lines = text.split("\n");
|
|
42
|
+
if (lines.shift() !== (card.title || "Deep View")) return null;
|
|
43
|
+
const candidates = [];
|
|
44
|
+
for (const node of controls(card.view)) {
|
|
45
|
+
const rest = [...lines];
|
|
46
|
+
const values = {};
|
|
47
|
+
if (node.type === "options") {
|
|
48
|
+
const line = rest.shift();
|
|
49
|
+
if (line === void 0) continue;
|
|
50
|
+
const labels = node.options?.map((option) => option.label) ?? [];
|
|
51
|
+
if (node.multi && labels.some((label) => label.includes(", "))) continue;
|
|
52
|
+
values.selected = node.multi ? line.split(", ") : [line];
|
|
53
|
+
} else if (node.type === "confirm") {
|
|
54
|
+
const line = rest.shift(), yes = node.confirmLabel ?? "Confirm", no = node.cancelLabel ?? "Cancel";
|
|
55
|
+
if (yes === no || line !== yes && line !== no) continue;
|
|
56
|
+
values.confirmed = line === yes;
|
|
57
|
+
} else if (node.type === "form") {
|
|
58
|
+
const fields = {};
|
|
59
|
+
let valid = true;
|
|
60
|
+
for (const field of node.fields ?? []) {
|
|
61
|
+
const line = rest.shift(), prefix = `${field.label}: `;
|
|
62
|
+
if (line === void 0 || !line.startsWith(prefix)) {
|
|
63
|
+
valid = false;
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
fields[field.id] = line.slice(prefix.length);
|
|
67
|
+
}
|
|
68
|
+
if (!valid) continue;
|
|
69
|
+
values.fields = fields;
|
|
70
|
+
}
|
|
71
|
+
const inputs = viewInputs(card.view);
|
|
72
|
+
if (inputs.length) {
|
|
73
|
+
const pairs = rest.shift()?.split("; ");
|
|
74
|
+
if (!pairs || pairs.length !== inputs.length) continue;
|
|
75
|
+
const sent = {};
|
|
76
|
+
let valid = true;
|
|
77
|
+
for (let i = 0; i < inputs.length; i++) {
|
|
78
|
+
const input = inputs[i], prefix = `${input.label || input.id}=`;
|
|
79
|
+
if (!pairs[i].startsWith(prefix)) {
|
|
80
|
+
valid = false;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
const value = pairs[i].slice(prefix.length);
|
|
84
|
+
if (input.type === "toggle") sent[input.id] = value === "on" ? true : value === "off" ? false : void 0;
|
|
85
|
+
else if (input.type === "choice") {
|
|
86
|
+
const choices = input.options?.filter((option) => option.label === value) ?? [];
|
|
87
|
+
sent[input.id] = choices.length === 1 ? choiceValue(choices[0]) : void 0;
|
|
88
|
+
} else {
|
|
89
|
+
const unit = input.unit ? ` ${input.unit}` : "";
|
|
90
|
+
sent[input.id] = !unit || value.endsWith(unit) ? Number(unit ? value.slice(0, -unit.length) : value) : void 0;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (!valid) continue;
|
|
94
|
+
values.inputs = sent;
|
|
95
|
+
}
|
|
96
|
+
if (node.type === "submit" && rest.shift() !== (node.label ?? "Submit")) continue;
|
|
97
|
+
if (rest.length || !node.id) continue;
|
|
98
|
+
const checked = validateViewAnswer(card.view, node.id, values);
|
|
99
|
+
if (checked.error === void 0 && answerText(card, node.id, checked) === text) candidates.push({
|
|
100
|
+
id: card.id,
|
|
101
|
+
action: node.id,
|
|
102
|
+
values: checked
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return candidates.length === 1 ? candidates[0] : null;
|
|
106
|
+
}
|
|
107
|
+
//#endregion
|
|
108
|
+
export { answerText, legacyAnswer };
|
|
109
|
+
|
|
110
|
+
//# sourceMappingURL=answer-text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"answer-text.js","names":[],"sources":["../src/answer-text.ts"],"sourcesContent":["import { choiceValue, inputsSummary, validateViewAnswer, viewInputs } from './core/index.js'\nimport { isRecord, type ViewAnswer, type ViewNode } from './contracts.ts'\n\ntype Card = { id: string; title: string; view: ViewNode }\nfunction controls(view: ViewNode): ViewNode[] {\n return ['options', 'confirm', 'form', 'submit'].includes(view.type)\n ? [view] : (view.children ?? []).flatMap(controls)\n}\n\nexport function answerText(card: Pick<Card, 'title' | 'view'>, action: string, values: Record<string, unknown>): string {\n const node = controls(card.view).find(node => node.id === action)\n const lines = [card.title || 'Deep View']\n if (Array.isArray(values.selected)) lines.push(values.selected.map(String).join(', '))\n if (typeof values.confirmed === 'boolean') lines.push(values.confirmed\n ? node?.confirmLabel ?? 'Confirm' : node?.cancelLabel ?? 'Cancel')\n if (isRecord(values.fields)) {\n for (const field of node?.fields ?? []) lines.push(`${field.label}: ${String(values.fields[field.id] ?? '')}`)\n }\n const inputs = inputsSummary(card.view, values.inputs)\n if (inputs) lines.push(inputs)\n if (node?.type === 'submit') lines.push(node.label ?? 'Submit')\n return lines.join('\\n')\n}\n\n/** Legacy messages have no metadata. Restore only unambiguous, fully validated\n * answers that reproduce the exact text written by this plugin. Never guess\n * from ordinary prose, and never modify the stored message. */\nexport function legacyAnswer(card: Card, text: string): ViewAnswer | null {\n if (text.startsWith('{')) {\n try {\n const data: unknown = JSON.parse(text)\n if (!isRecord(data) || data.kind !== 'answer_to_pending_view' || data.tool !== 'show_view' || !Array.isArray(data.answers)) return null\n const answer = data.answers.find(value => isRecord(value) && value.id === card.id)\n if (!isRecord(answer) || typeof answer.action !== 'string') return null\n const values = validateViewAnswer(card.view, answer.action, answer.values)\n return values.error === undefined ? { id: card.id, action: answer.action, values } : null\n } catch { return null }\n }\n const lines = text.split('\\n')\n if (lines.shift() !== (card.title || 'Deep View')) return null\n const candidates: ViewAnswer[] = []\n for (const node of controls(card.view)) {\n const rest = [...lines]\n const values: Record<string, unknown> = {}\n if (node.type === 'options') {\n const line = rest.shift()\n if (line === undefined) continue\n const labels = node.options?.map(option => option.label) ?? []\n if (node.multi && labels.some(label => label.includes(', '))) continue\n values.selected = node.multi ? line.split(', ') : [line]\n } else if (node.type === 'confirm') {\n const line = rest.shift(), yes = node.confirmLabel ?? 'Confirm', no = node.cancelLabel ?? 'Cancel'\n if (yes === no || line !== yes && line !== no) continue\n values.confirmed = line === yes\n } else if (node.type === 'form') {\n const fields: Record<string, string> = {}\n let valid = true\n for (const field of node.fields ?? []) {\n const line = rest.shift(), prefix = `${field.label}: `\n if (line === undefined || !line.startsWith(prefix)) { valid = false; break }\n fields[field.id] = line.slice(prefix.length)\n }\n if (!valid) continue\n values.fields = fields\n }\n const inputs = viewInputs(card.view) as ViewNode[]\n if (inputs.length) {\n const pairs = rest.shift()?.split('; ')\n if (!pairs || pairs.length !== inputs.length) continue\n const sent: Record<string, unknown> = {}\n let valid = true\n for (let i = 0; i < inputs.length; i++) {\n const input = inputs[i], prefix = `${input.label || input.id}=`\n if (!pairs[i].startsWith(prefix)) { valid = false; break }\n const value = pairs[i].slice(prefix.length)\n if (input.type === 'toggle') sent[input.id!] = value === 'on' ? true : value === 'off' ? false : undefined\n else if (input.type === 'choice') {\n const choices = input.options?.filter(option => option.label === value) ?? []\n sent[input.id!] = choices.length === 1 ? choiceValue(choices[0]) : undefined\n } else {\n const unit = input.unit ? ` ${input.unit}` : ''\n sent[input.id!] = !unit || value.endsWith(unit) ? Number(unit ? value.slice(0, -unit.length) : value) : undefined\n }\n }\n if (!valid) continue\n values.inputs = sent\n }\n if (node.type === 'submit' && rest.shift() !== (node.label ?? 'Submit')) continue\n if (rest.length || !node.id) continue\n const checked = validateViewAnswer(card.view, node.id, values)\n if (checked.error === undefined && answerText(card, node.id, checked) === text) {\n candidates.push({ id: card.id, action: node.id, values: checked })\n }\n }\n return candidates.length === 1 ? candidates[0] : null\n}\n"],"mappings":"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"}
|
package/lib/answers.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { g as ViewNode, p as ViewAnswer } from "./contracts2.js";
|
|
2
|
+
//#region src/runtime.d.ts
|
|
3
|
+
type ToolArgsView = {
|
|
4
|
+
has(key: string): boolean;
|
|
5
|
+
complete(key: string): boolean;
|
|
6
|
+
text(key: string): string | undefined;
|
|
7
|
+
value(key: string): unknown;
|
|
8
|
+
};
|
|
9
|
+
type ToolViewBlock = {
|
|
10
|
+
callId: string;
|
|
11
|
+
name?: string;
|
|
12
|
+
args?: ToolArgsView;
|
|
13
|
+
argsRaw?: string;
|
|
14
|
+
kind?: 'tool-result';
|
|
15
|
+
meta?: unknown;
|
|
16
|
+
isError?: boolean;
|
|
17
|
+
error?: {
|
|
18
|
+
name: string;
|
|
19
|
+
code: string;
|
|
20
|
+
reason?: string;
|
|
21
|
+
};
|
|
22
|
+
content?: readonly unknown[];
|
|
23
|
+
subCalls?: readonly ToolViewBlock[];
|
|
24
|
+
};
|
|
25
|
+
type ReplySnapshot = {
|
|
26
|
+
order: readonly string[];
|
|
27
|
+
locations: {
|
|
28
|
+
getTurn(turn: number): readonly string[];
|
|
29
|
+
};
|
|
30
|
+
nodes: {
|
|
31
|
+
get(key: string): {
|
|
32
|
+
kind: string;
|
|
33
|
+
data?: {
|
|
34
|
+
root?: ToolViewBlock;
|
|
35
|
+
source?: unknown;
|
|
36
|
+
content?: readonly unknown[];
|
|
37
|
+
};
|
|
38
|
+
} | undefined;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/answers.d.ts
|
|
43
|
+
/** Read visible, durable user/steering messages only, including inherited history. */
|
|
44
|
+
declare function replyAnswers(snapshot: ReplySnapshot): ReadonlyMap<string, ViewAnswer>;
|
|
45
|
+
declare function checkedAnswer(answer: ViewAnswer | undefined, id: string | undefined, view: ViewNode): ViewAnswer | null;
|
|
46
|
+
declare function sameAnswers(left: ReadonlyMap<string, ViewAnswer>, right: ReadonlyMap<string, ViewAnswer>): boolean;
|
|
47
|
+
//#endregion
|
|
48
|
+
export { checkedAnswer, replyAnswers, sameAnswers };
|
|
49
|
+
//# sourceMappingURL=answers.d.ts.map
|
package/lib/answers.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { i as checkShownView, p as validateViewAnswer } from "./core2.js";
|
|
2
|
+
import { isRecord } from "./contracts.js";
|
|
3
|
+
import { legacyAnswer } from "./answer-text.js";
|
|
4
|
+
//#region src/answers.ts
|
|
5
|
+
/** Read visible, durable user/steering messages only, including inherited history. */
|
|
6
|
+
function replyAnswers(snapshot) {
|
|
7
|
+
const answers = /* @__PURE__ */ new Map();
|
|
8
|
+
let cards = [];
|
|
9
|
+
const collect = (block) => {
|
|
10
|
+
const meta = block.meta;
|
|
11
|
+
if (block.kind === "tool-result" && !block.isError && isRecord(meta) && meta.owner === "dsh-deep-view" && meta.awaitReply === true && typeof meta.viewId === "string") {
|
|
12
|
+
const shown = checkShownView(meta.title, meta.view);
|
|
13
|
+
if (!("error" in shown)) cards.push({
|
|
14
|
+
id: meta.viewId,
|
|
15
|
+
title: shown.title,
|
|
16
|
+
view: shown.view
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
for (const child of block.subCalls ?? []) collect(child);
|
|
20
|
+
};
|
|
21
|
+
for (const key of snapshot.order) {
|
|
22
|
+
const node = snapshot.nodes.get(key);
|
|
23
|
+
if (node?.kind === "tool-call" && node.data?.root) collect(node.data.root);
|
|
24
|
+
if (node?.kind !== "user" && node?.kind !== "steering") continue;
|
|
25
|
+
const source = node.data?.source;
|
|
26
|
+
if (!isRecord(source) || source.kind !== "user") continue;
|
|
27
|
+
if (source.deepView === void 0) {
|
|
28
|
+
const text = node.data?.content?.filter(isRecord).filter((block) => block.type === "text").map((block) => block.text).join("") ?? "";
|
|
29
|
+
const candidates = cards.map((card) => legacyAnswer(card, text)).filter((answer) => answer !== null);
|
|
30
|
+
if (candidates.length === 1 && !answers.has(candidates[0].id)) answers.set(candidates[0].id, candidates[0]);
|
|
31
|
+
}
|
|
32
|
+
cards = [];
|
|
33
|
+
if (!isRecord(source.deepView)) continue;
|
|
34
|
+
const meta = source.deepView;
|
|
35
|
+
if (meta.owner !== "dsh-deep-view" || meta.version !== 1 || !isRecord(meta.answer)) continue;
|
|
36
|
+
const answer = meta.answer;
|
|
37
|
+
if (typeof answer.id !== "string" || typeof answer.action !== "string" || !isRecord(answer.values)) continue;
|
|
38
|
+
if (!answers.has(answer.id)) answers.set(answer.id, answer);
|
|
39
|
+
}
|
|
40
|
+
return answers;
|
|
41
|
+
}
|
|
42
|
+
function checkedAnswer(answer, id, view) {
|
|
43
|
+
if (!answer || !id || answer.id !== id) return null;
|
|
44
|
+
const values = validateViewAnswer(view, answer.action, answer.values);
|
|
45
|
+
return values.error === void 0 ? {
|
|
46
|
+
id,
|
|
47
|
+
action: answer.action,
|
|
48
|
+
values
|
|
49
|
+
} : null;
|
|
50
|
+
}
|
|
51
|
+
function sameAnswers(left, right) {
|
|
52
|
+
return left.size === right.size && [...left].every(([id, answer]) => {
|
|
53
|
+
const next = right.get(id);
|
|
54
|
+
return next === answer || next?.action === answer.action && JSON.stringify(next.values) === JSON.stringify(answer.values);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
//#endregion
|
|
58
|
+
export { checkedAnswer, replyAnswers, sameAnswers };
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=answers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"answers.js","names":[],"sources":["../src/answers.ts"],"sourcesContent":["import { checkShownView, validateViewAnswer } from './core/index.js'\nimport { isRecord, VIEW_OWNER, type ViewAnswer, type ViewNode } from './contracts.ts'\nimport type { ReplySnapshot, ToolViewBlock } from './runtime.ts'\nimport { legacyAnswer } from './answer-text.ts'\n\n/** Read visible, durable user/steering messages only, including inherited history. */\nexport function replyAnswers(snapshot: ReplySnapshot): ReadonlyMap<string, ViewAnswer> {\n const answers = new Map<string, ViewAnswer>()\n let cards: { id: string; title: string; view: ViewNode }[] = []\n const collect = (block: ToolViewBlock) => {\n const meta = block.meta\n if (block.kind === 'tool-result' && !block.isError && isRecord(meta) && meta.owner === VIEW_OWNER && meta.awaitReply === true && typeof meta.viewId === 'string') {\n const shown = checkShownView(meta.title, meta.view)\n if (!('error' in shown)) cards.push({ id: meta.viewId, title: shown.title, view: shown.view })\n }\n for (const child of block.subCalls ?? []) collect(child)\n }\n for (const key of snapshot.order) {\n const node = snapshot.nodes.get(key)\n if (node?.kind === 'tool-call' && node.data?.root) collect(node.data.root)\n if (node?.kind !== 'user' && node?.kind !== 'steering') continue\n const source = node.data?.source\n if (!isRecord(source) || source.kind !== 'user') continue\n // Only the very next human message can answer an old card, and it must\n // match exactly one card. Ambiguous/edited legacy text remains a preview.\n if (source.deepView === undefined) {\n const text = node.data?.content?.filter(isRecord).filter(block => block.type === 'text').map(block => block.text).join('') ?? ''\n const candidates = cards.map(card => legacyAnswer(card, text)).filter((answer): answer is ViewAnswer => answer !== null)\n if (candidates.length === 1 && !answers.has(candidates[0].id)) answers.set(candidates[0].id, candidates[0])\n }\n cards = []\n if (!isRecord(source.deepView)) continue\n const meta = source.deepView\n if (meta.owner !== VIEW_OWNER || meta.version !== 1 || !isRecord(meta.answer)) continue\n const answer = meta.answer\n if (typeof answer.id !== 'string' || typeof answer.action !== 'string' || !isRecord(answer.values)) continue\n // The first accepted reply settles a card; later inputs cannot change it.\n if (!answers.has(answer.id)) answers.set(answer.id, answer as ViewAnswer)\n }\n return answers\n}\n\nexport function checkedAnswer(answer: ViewAnswer | undefined, id: string | undefined, view: ViewNode): ViewAnswer | null {\n if (!answer || !id || answer.id !== id) return null\n const values = validateViewAnswer(view, answer.action, answer.values)\n return values.error === undefined ? { id, action: answer.action, values } : null\n}\n\nexport function sameAnswers(left: ReadonlyMap<string, ViewAnswer>, right: ReadonlyMap<string, ViewAnswer>): boolean {\n return left.size === right.size && [...left].every(([id, answer]) => {\n const next = right.get(id)\n return next === answer || next?.action === answer.action && JSON.stringify(next.values) === JSON.stringify(answer.values)\n })\n}\n"],"mappings":";;;;;AAMA,SAAgB,aAAa,UAA0D;CACrF,MAAM,0BAAU,IAAI,IAAwB;CAC5C,IAAI,QAAyD,CAAC;CAC9D,MAAM,WAAW,UAAyB;EACxC,MAAM,OAAO,MAAM;EACnB,IAAI,MAAM,SAAS,iBAAiB,CAAC,MAAM,WAAW,SAAS,IAAI,KAAK,KAAK,UAAA,mBAAwB,KAAK,eAAe,QAAQ,OAAO,KAAK,WAAW,UAAU;GAChK,MAAM,QAAQ,eAAe,KAAK,OAAO,KAAK,IAAI;GAClD,IAAI,EAAE,WAAW,QAAQ,MAAM,KAAK;IAAE,IAAI,KAAK;IAAQ,OAAO,MAAM;IAAO,MAAM,MAAM;GAAK,CAAC;EAC/F;EACA,KAAK,MAAM,SAAS,MAAM,YAAY,CAAC,GAAG,QAAQ,KAAK;CACzD;CACA,KAAK,MAAM,OAAO,SAAS,OAAO;EAChC,MAAM,OAAO,SAAS,MAAM,IAAI,GAAG;EACnC,IAAI,MAAM,SAAS,eAAe,KAAK,MAAM,MAAM,QAAQ,KAAK,KAAK,IAAI;EACzE,IAAI,MAAM,SAAS,UAAU,MAAM,SAAS,YAAY;EACxD,MAAM,SAAS,KAAK,MAAM;EAC1B,IAAI,CAAC,SAAS,MAAM,KAAK,OAAO,SAAS,QAAQ;EAGjD,IAAI,OAAO,aAAa,KAAA,GAAW;GACjC,MAAM,OAAO,KAAK,MAAM,SAAS,OAAO,QAAQ,CAAC,CAAC,QAAO,UAAS,MAAM,SAAS,MAAM,CAAC,CAAC,KAAI,UAAS,MAAM,IAAI,CAAC,CAAC,KAAK,EAAE,KAAK;GAC9H,MAAM,aAAa,MAAM,KAAI,SAAQ,aAAa,MAAM,IAAI,CAAC,CAAC,CAAC,QAAQ,WAAiC,WAAW,IAAI;GACvH,IAAI,WAAW,WAAW,KAAK,CAAC,QAAQ,IAAI,WAAW,EAAE,CAAC,EAAE,GAAG,QAAQ,IAAI,WAAW,EAAE,CAAC,IAAI,WAAW,EAAE;EAC5G;EACA,QAAQ,CAAC;EACT,IAAI,CAAC,SAAS,OAAO,QAAQ,GAAG;EAChC,MAAM,OAAO,OAAO;EACpB,IAAI,KAAK,UAAA,mBAAwB,KAAK,YAAY,KAAK,CAAC,SAAS,KAAK,MAAM,GAAG;EAC/E,MAAM,SAAS,KAAK;EACpB,IAAI,OAAO,OAAO,OAAO,YAAY,OAAO,OAAO,WAAW,YAAY,CAAC,SAAS,OAAO,MAAM,GAAG;EAEpG,IAAI,CAAC,QAAQ,IAAI,OAAO,EAAE,GAAG,QAAQ,IAAI,OAAO,IAAI,MAAoB;CAC1E;CACA,OAAO;AACT;AAEA,SAAgB,cAAc,QAAgC,IAAwB,MAAmC;CACvH,IAAI,CAAC,UAAU,CAAC,MAAM,OAAO,OAAO,IAAI,OAAO;CAC/C,MAAM,SAAS,mBAAmB,MAAM,OAAO,QAAQ,OAAO,MAAM;CACpE,OAAO,OAAO,UAAU,KAAA,IAAY;EAAE;EAAI,QAAQ,OAAO;EAAQ;CAAO,IAAI;AAC9E;AAEA,SAAgB,YAAY,MAAuC,OAAiD;CAClH,OAAO,KAAK,SAAS,MAAM,QAAQ,CAAC,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,YAAY;EACnE,MAAM,OAAO,MAAM,IAAI,EAAE;EACzB,OAAO,SAAS,UAAU,MAAM,WAAW,OAAO,UAAU,KAAK,UAAU,KAAK,MAAM,MAAM,KAAK,UAAU,OAAO,MAAM;CAC1H,CAAC;AACH"}
|