@dmrlzz/davecode 0.0.0-stage → 0.4.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/LICENSE +21 -0
- package/README.md +34 -2
- package/bin/davecode.js +2 -0
- package/dist/ChatApp-BO7QMBWD.js +1045 -0
- package/dist/ChatApp-BO7QMBWD.js.map +1 -0
- package/dist/RunnerView-A7JSPUW5.js +18 -0
- package/dist/RunnerView-A7JSPUW5.js.map +1 -0
- package/dist/account-UGNLJPQ4.js +23 -0
- package/dist/account-UGNLJPQ4.js.map +1 -0
- package/dist/accounts-NM7IWP53.js +37 -0
- package/dist/accounts-NM7IWP53.js.map +1 -0
- package/dist/budget-INAW6FXD.js +115 -0
- package/dist/budget-INAW6FXD.js.map +1 -0
- package/dist/chat-U4QJQ7M6.js +359 -0
- package/dist/chat-U4QJQ7M6.js.map +1 -0
- package/dist/chunk-3Q2KCXLN.js +9608 -0
- package/dist/chunk-3Q2KCXLN.js.map +1 -0
- package/dist/chunk-5LKOJBUL.js +20 -0
- package/dist/chunk-5LKOJBUL.js.map +1 -0
- package/dist/chunk-7XVXEO7U.js +23 -0
- package/dist/chunk-7XVXEO7U.js.map +1 -0
- package/dist/chunk-CWBVNS3L.js +731 -0
- package/dist/chunk-CWBVNS3L.js.map +1 -0
- package/dist/chunk-DZ2FDABF.js +1614 -0
- package/dist/chunk-DZ2FDABF.js.map +1 -0
- package/dist/chunk-FRFSJMN4.js +79 -0
- package/dist/chunk-FRFSJMN4.js.map +1 -0
- package/dist/chunk-GE22VBCY.js +200 -0
- package/dist/chunk-GE22VBCY.js.map +1 -0
- package/dist/chunk-GHDOUII5.js +144 -0
- package/dist/chunk-GHDOUII5.js.map +1 -0
- package/dist/chunk-GZJ6JSO7.js +213 -0
- package/dist/chunk-GZJ6JSO7.js.map +1 -0
- package/dist/chunk-IC3TDONV.js +12 -0
- package/dist/chunk-IC3TDONV.js.map +1 -0
- package/dist/chunk-MC7RIPL7.js +116 -0
- package/dist/chunk-MC7RIPL7.js.map +1 -0
- package/dist/chunk-MOK5D3XJ.js +114 -0
- package/dist/chunk-MOK5D3XJ.js.map +1 -0
- package/dist/chunk-PGPXE6LF.js +20 -0
- package/dist/chunk-PGPXE6LF.js.map +1 -0
- package/dist/chunk-QA3O6RA2.js +81 -0
- package/dist/chunk-QA3O6RA2.js.map +1 -0
- package/dist/chunk-ROIUTNQV.js +20 -0
- package/dist/chunk-ROIUTNQV.js.map +1 -0
- package/dist/chunk-SF5ZEAP4.js +105 -0
- package/dist/chunk-SF5ZEAP4.js.map +1 -0
- package/dist/chunk-Z6NQDJEN.js +180 -0
- package/dist/chunk-Z6NQDJEN.js.map +1 -0
- package/dist/config-YEAPBMBX.js +158 -0
- package/dist/config-YEAPBMBX.js.map +1 -0
- package/dist/dashboard/assets/Accounts-CeVyX4a3.js +1 -0
- package/dist/dashboard/assets/Badge-CGY6DgGx.js +55 -0
- package/dist/dashboard/assets/Callout-DauCk_cW.js +1 -0
- package/dist/dashboard/assets/Field-OXMnT9Tb.js +1 -0
- package/dist/dashboard/assets/Goals-DH-KDh8M.js +1 -0
- package/dist/dashboard/assets/Library-C15szLjZ.js +1 -0
- package/dist/dashboard/assets/Markdown-Xhao3sgL.js +69 -0
- package/dist/dashboard/assets/Panel-Du40pM7n.js +1 -0
- package/dist/dashboard/assets/RoutesPage-Cj1OelGO.js +4 -0
- package/dist/dashboard/assets/Runner-BK1-ZF7h.js +1 -0
- package/dist/dashboard/assets/Settings-C1nzQQhe.js +1 -0
- package/dist/dashboard/assets/Switch-bwtIJ00H.js +1 -0
- package/dist/dashboard/assets/Tasks-x4s8IiPG.js +2 -0
- package/dist/dashboard/assets/Traffic-CV7TaTid.js +1 -0
- package/dist/dashboard/assets/Workspace-DAL-MBc0.js +1 -0
- package/dist/dashboard/assets/arrow-up-goM9r-I9.js +1 -0
- package/dist/dashboard/assets/forms-DtpfI65-.js +1 -0
- package/dist/dashboard/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
- package/dist/dashboard/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
- package/dist/dashboard/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dist/dashboard/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/dashboard/assets/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
- package/dist/dashboard/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
- package/dist/dashboard/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
- package/dist/dashboard/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
- package/dist/dashboard/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
- package/dist/dashboard/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
- package/dist/dashboard/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
- package/dist/dashboard/assets/index-BCi-Vuvh.js +104 -0
- package/dist/dashboard/assets/index-CcJFhc98.css +2 -0
- package/dist/dashboard/assets/task-errors-B2pgjUGN.js +1 -0
- package/dist/dashboard/assets/trash-BVojETtF.js +1 -0
- package/dist/dashboard/favicon.svg +1 -0
- package/dist/dashboard/github.svg +1 -0
- package/dist/dashboard/index.html +31 -0
- package/dist/dist-RRQGUBL3.js +379 -0
- package/dist/dist-RRQGUBL3.js.map +1 -0
- package/dist/doctor-MQYK44S2.js +279 -0
- package/dist/doctor-MQYK44S2.js.map +1 -0
- package/dist/goal-6NUX4AIT.js +302 -0
- package/dist/goal-6NUX4AIT.js.map +1 -0
- package/dist/index.js +399 -0
- package/dist/index.js.map +1 -0
- package/dist/init-AXYIWAB3.js +194 -0
- package/dist/init-AXYIWAB3.js.map +1 -0
- package/dist/prompts-TZ7UC42M.js +219 -0
- package/dist/prompts-TZ7UC42M.js.map +1 -0
- package/dist/run-QRTLHFB3.js +376 -0
- package/dist/run-QRTLHFB3.js.map +1 -0
- package/dist/runner-state-QFU3LJVJ.js +17 -0
- package/dist/runner-state-QFU3LJVJ.js.map +1 -0
- package/dist/runtime.js +18 -0
- package/dist/runtime.js.map +1 -0
- package/dist/start-O5I5TSO2.js +285 -0
- package/dist/start-O5I5TSO2.js.map +1 -0
- package/dist/status-LEA3RVUX.js +25 -0
- package/dist/status-LEA3RVUX.js.map +1 -0
- package/dist/tasks-XOTVICVZ.js +184 -0
- package/dist/tasks-XOTVICVZ.js.map +1 -0
- package/package.json +73 -3
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PromptCancelledError
|
|
3
|
+
} from "./chunk-IC3TDONV.js";
|
|
4
|
+
import {
|
|
5
|
+
printJson
|
|
6
|
+
} from "./chunk-QA3O6RA2.js";
|
|
7
|
+
import {
|
|
8
|
+
detectProjectRoot
|
|
9
|
+
} from "./chunk-DZ2FDABF.js";
|
|
10
|
+
import {
|
|
11
|
+
CliError,
|
|
12
|
+
EXIT
|
|
13
|
+
} from "./chunk-7XVXEO7U.js";
|
|
14
|
+
import {
|
|
15
|
+
ProjectBrain,
|
|
16
|
+
configSchema,
|
|
17
|
+
projectPaths
|
|
18
|
+
} from "./chunk-3Q2KCXLN.js";
|
|
19
|
+
import "./chunk-MC7RIPL7.js";
|
|
20
|
+
|
|
21
|
+
// src/commands/init.ts
|
|
22
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
23
|
+
import { basename, join as join2, relative } from "path";
|
|
24
|
+
|
|
25
|
+
// src/lib/validate-detect.ts
|
|
26
|
+
import { existsSync, readFileSync } from "fs";
|
|
27
|
+
import { join } from "path";
|
|
28
|
+
function readPackageJson(root) {
|
|
29
|
+
try {
|
|
30
|
+
const value = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
|
|
31
|
+
return value && typeof value === "object" ? value : void 0;
|
|
32
|
+
} catch {
|
|
33
|
+
return void 0;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function detectPackageManager(root, pkg) {
|
|
37
|
+
const declared = pkg?.packageManager?.split("@")[0];
|
|
38
|
+
if (declared === "pnpm" || declared === "yarn" || declared === "bun" || declared === "npm") {
|
|
39
|
+
return declared;
|
|
40
|
+
}
|
|
41
|
+
if (existsSync(join(root, "pnpm-lock.yaml"))) return "pnpm";
|
|
42
|
+
if (existsSync(join(root, "yarn.lock"))) return "yarn";
|
|
43
|
+
if (existsSync(join(root, "bun.lock")) || existsSync(join(root, "bun.lockb"))) return "bun";
|
|
44
|
+
return "npm";
|
|
45
|
+
}
|
|
46
|
+
var CANDIDATES = {
|
|
47
|
+
lint: ["lint", "lint:check", "check:lint"],
|
|
48
|
+
typecheck: ["typecheck", "type-check", "check-types", "check:types", "types", "tsc"],
|
|
49
|
+
test: ["test", "test:unit", "test:ci"]
|
|
50
|
+
};
|
|
51
|
+
var NPM_PLACEHOLDER = /no test specified/i;
|
|
52
|
+
function runScript(pm, script) {
|
|
53
|
+
if (script === "test") return `${pm} test`;
|
|
54
|
+
switch (pm) {
|
|
55
|
+
case "npm":
|
|
56
|
+
return `npm run ${script}`;
|
|
57
|
+
case "bun":
|
|
58
|
+
return `bun run ${script}`;
|
|
59
|
+
default:
|
|
60
|
+
return `${pm} ${script}`;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function detectValidateCommands(pkg, pm) {
|
|
64
|
+
const scripts = pkg?.scripts ?? {};
|
|
65
|
+
const found = {};
|
|
66
|
+
for (const [kind, names] of Object.entries(CANDIDATES)) {
|
|
67
|
+
const script = names.find((name) => {
|
|
68
|
+
const body = scripts[name];
|
|
69
|
+
return typeof body === "string" && body.trim() !== "" && !NPM_PLACEHOLDER.test(body);
|
|
70
|
+
});
|
|
71
|
+
if (script) found[kind] = runScript(pm, script);
|
|
72
|
+
}
|
|
73
|
+
return found;
|
|
74
|
+
}
|
|
75
|
+
function starterConfig(validate) {
|
|
76
|
+
return { runner: { validate, judge: { kind: "none" } } };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/commands/init.ts
|
|
80
|
+
var LOCK_IGNORE = ".davecode/.lock";
|
|
81
|
+
function ensureLockIgnored(root) {
|
|
82
|
+
const path = join2(root, ".gitignore");
|
|
83
|
+
const current = existsSync2(path) ? readFileSync2(path, "utf8") : "";
|
|
84
|
+
const lines = current.split(/\r?\n/).map((l) => l.trim());
|
|
85
|
+
if (lines.includes(LOCK_IGNORE) || lines.includes(".davecode/") || lines.includes(".davecode")) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
const prefix = current === "" || current.endsWith("\n") ? current : `${current}
|
|
89
|
+
`;
|
|
90
|
+
writeFileSync(
|
|
91
|
+
path,
|
|
92
|
+
`${prefix}${current === "" ? "" : "\n"}# DaveCode runtime lock
|
|
93
|
+
${LOCK_IGNORE}
|
|
94
|
+
`
|
|
95
|
+
);
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
async function initCommand(ctx, opts, deps = {}) {
|
|
99
|
+
const root = await detectProjectRoot(ctx.cwd, ctx.home) ?? ctx.cwd;
|
|
100
|
+
const paths = projectPaths(root);
|
|
101
|
+
const pkg = readPackageJson(root);
|
|
102
|
+
const name = opts.name?.trim() || pkg?.name || basename(root);
|
|
103
|
+
const before = {
|
|
104
|
+
state: existsSync2(paths.state),
|
|
105
|
+
architecture: existsSync2(paths.architecture),
|
|
106
|
+
taskGraph: existsSync2(paths.taskGraph)
|
|
107
|
+
};
|
|
108
|
+
await ProjectBrain.init(root, { name });
|
|
109
|
+
const created = Object.entries(before).filter(([, existed]) => !existed).map(([file]) => file);
|
|
110
|
+
const pm = detectPackageManager(root, pkg);
|
|
111
|
+
const validate = detectValidateCommands(pkg, pm);
|
|
112
|
+
let configWritten = false;
|
|
113
|
+
let configSkipped;
|
|
114
|
+
if (existsSync2(paths.config)) {
|
|
115
|
+
configSkipped = "exists";
|
|
116
|
+
} else if (opts.config === false) {
|
|
117
|
+
configSkipped = "declined";
|
|
118
|
+
} else {
|
|
119
|
+
let write = opts.config === true;
|
|
120
|
+
if (!write && ctx.interactive) {
|
|
121
|
+
const prompter = deps.prompter ?? (await import("./prompts-TZ7UC42M.js")).createInkPrompter({
|
|
122
|
+
stdout: ctx.io.stdout,
|
|
123
|
+
stdin: ctx.io.stdin,
|
|
124
|
+
theme: ctx.theme
|
|
125
|
+
});
|
|
126
|
+
const summary = Object.entries(validate).map(([k, v]) => `${k}: ${v}`).join(", ");
|
|
127
|
+
try {
|
|
128
|
+
write = await prompter.confirm(
|
|
129
|
+
summary ? `Write .davecode/config.json with runner.validate (${summary})?` : "Write a starter .davecode/config.json (no lint/typecheck/test scripts found)?",
|
|
130
|
+
true
|
|
131
|
+
);
|
|
132
|
+
} catch (err) {
|
|
133
|
+
if (!(err instanceof PromptCancelledError)) throw err;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (write) {
|
|
137
|
+
const content = starterConfig(validate);
|
|
138
|
+
const check = configSchema.safeParse(content);
|
|
139
|
+
if (!check.success) throw new CliError("Generated config is invalid (please report a bug)");
|
|
140
|
+
writeFileSync(paths.config, `${JSON.stringify(content, null, 2)}
|
|
141
|
+
`);
|
|
142
|
+
configWritten = true;
|
|
143
|
+
} else {
|
|
144
|
+
configSkipped = ctx.interactive ? "declined" : "non-interactive";
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const ignoreUpdated = existsSync2(join2(root, ".git")) ? ensureLockIgnored(root) : false;
|
|
148
|
+
if (ctx.json) {
|
|
149
|
+
printJson(ctx, {
|
|
150
|
+
root,
|
|
151
|
+
name,
|
|
152
|
+
created,
|
|
153
|
+
config: configWritten ? paths.config : null,
|
|
154
|
+
validate,
|
|
155
|
+
gitignoreUpdated: ignoreUpdated
|
|
156
|
+
});
|
|
157
|
+
return EXIT.ok;
|
|
158
|
+
}
|
|
159
|
+
const { theme } = ctx;
|
|
160
|
+
const rel = (p) => relative(ctx.cwd, p) || ".";
|
|
161
|
+
ctx.out(
|
|
162
|
+
`${theme.ok(theme.glyph.ok)} Project brain ready for ${theme.bold(name)} ${theme.dim(rel(paths.dir))}`
|
|
163
|
+
);
|
|
164
|
+
const files = [
|
|
165
|
+
["STATE.md", paths.state, before.state],
|
|
166
|
+
["ARCHITECTURE.md", paths.architecture, before.architecture],
|
|
167
|
+
["TASK_GRAPH.json", paths.taskGraph, before.taskGraph]
|
|
168
|
+
];
|
|
169
|
+
for (const [label, , existed] of files) {
|
|
170
|
+
ctx.out(` ${existed ? theme.dim("kept ") : theme.accent("created")} ${label}`);
|
|
171
|
+
}
|
|
172
|
+
if (configWritten) {
|
|
173
|
+
ctx.out(` ${theme.accent("created")} config.json`);
|
|
174
|
+
for (const [kind, command] of Object.entries(validate)) {
|
|
175
|
+
ctx.out(theme.dim(` runner.validate.${kind} = ${command}`));
|
|
176
|
+
}
|
|
177
|
+
} else if (configSkipped === "exists") {
|
|
178
|
+
ctx.out(` ${theme.dim("kept ")} config.json`);
|
|
179
|
+
} else if (configSkipped === "non-interactive") {
|
|
180
|
+
ctx.out(theme.dim(" config.json not written (pass --config to create a starter config)"));
|
|
181
|
+
}
|
|
182
|
+
if (ignoreUpdated)
|
|
183
|
+
ctx.out(` ${theme.accent("updated")} .gitignore ${theme.dim(`(${LOCK_IGNORE})`)}`);
|
|
184
|
+
ctx.out();
|
|
185
|
+
ctx.out(theme.dim("Next:"));
|
|
186
|
+
ctx.out(` ${theme.accent('davecode tasks add setup "Describe the first task"')}`);
|
|
187
|
+
ctx.out(` ${theme.accent("davecode start")}`);
|
|
188
|
+
return EXIT.ok;
|
|
189
|
+
}
|
|
190
|
+
export {
|
|
191
|
+
ensureLockIgnored,
|
|
192
|
+
initCommand
|
|
193
|
+
};
|
|
194
|
+
//# sourceMappingURL=init-AXYIWAB3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/init.ts","../src/lib/validate-detect.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { basename, join, relative } from 'node:path';\nimport { configSchema, ProjectBrain, projectPaths } from '@davecode/core';\nimport { type CliContext, printJson } from '../context';\nimport { CliError, EXIT } from '../errors';\nimport { PromptCancelledError, type Prompter } from '../lib/prompter';\nimport {\n detectPackageManager,\n detectValidateCommands,\n readPackageJson,\n starterConfig,\n} from '../lib/validate-detect';\nimport { detectProjectRoot } from '../runtime';\n\nexport interface InitOptions {\n name?: string;\n /** `--config` writes the starter config without asking; `--no-config` skips it. */\n config?: boolean;\n}\n\nexport interface InitDeps {\n prompter?: Prompter;\n}\n\nconst LOCK_IGNORE = '.davecode/.lock';\n\n/** Make sure `.davecode/.lock` is git-ignored. Returns true when `.gitignore` changed. */\nexport function ensureLockIgnored(root: string): boolean {\n const path = join(root, '.gitignore');\n const current = existsSync(path) ? readFileSync(path, 'utf8') : '';\n const lines = current.split(/\\r?\\n/).map((l) => l.trim());\n if (lines.includes(LOCK_IGNORE) || lines.includes('.davecode/') || lines.includes('.davecode')) {\n return false;\n }\n const prefix = current === '' || current.endsWith('\\n') ? current : `${current}\\n`;\n writeFileSync(\n path,\n `${prefix}${current === '' ? '' : '\\n'}# DaveCode runtime lock\\n${LOCK_IGNORE}\\n`,\n );\n return true;\n}\n\nexport async function initCommand(\n ctx: CliContext,\n opts: InitOptions,\n deps: InitDeps = {},\n): Promise<number> {\n const root = (await detectProjectRoot(ctx.cwd, ctx.home)) ?? ctx.cwd;\n const paths = projectPaths(root);\n const pkg = readPackageJson(root);\n const name = opts.name?.trim() || pkg?.name || basename(root);\n\n const before = {\n state: existsSync(paths.state),\n architecture: existsSync(paths.architecture),\n taskGraph: existsSync(paths.taskGraph),\n };\n await ProjectBrain.init(root, { name });\n const created = Object.entries(before)\n .filter(([, existed]) => !existed)\n .map(([file]) => file);\n\n // Starter config with validation commands detected from package.json.\n const pm = detectPackageManager(root, pkg);\n const validate = detectValidateCommands(pkg, pm);\n let configWritten = false;\n let configSkipped: string | undefined;\n if (existsSync(paths.config)) {\n configSkipped = 'exists';\n } else if (opts.config === false) {\n configSkipped = 'declined';\n } else {\n let write = opts.config === true;\n if (!write && ctx.interactive) {\n const prompter =\n deps.prompter ??\n (await import('../tui/prompts')).createInkPrompter({\n stdout: ctx.io.stdout,\n stdin: ctx.io.stdin,\n theme: ctx.theme,\n });\n const summary = Object.entries(validate)\n .map(([k, v]) => `${k}: ${v}`)\n .join(', ');\n try {\n write = await prompter.confirm(\n summary\n ? `Write .davecode/config.json with runner.validate (${summary})?`\n : 'Write a starter .davecode/config.json (no lint/typecheck/test scripts found)?',\n true,\n );\n } catch (err) {\n if (!(err instanceof PromptCancelledError)) throw err;\n }\n }\n if (write) {\n const content = starterConfig(validate);\n const check = configSchema.safeParse(content);\n if (!check.success) throw new CliError('Generated config is invalid (please report a bug)');\n writeFileSync(paths.config, `${JSON.stringify(content, null, 2)}\\n`);\n configWritten = true;\n } else {\n configSkipped = ctx.interactive ? 'declined' : 'non-interactive';\n }\n }\n\n const ignoreUpdated = existsSync(join(root, '.git')) ? ensureLockIgnored(root) : false;\n\n if (ctx.json) {\n printJson(ctx, {\n root,\n name,\n created,\n config: configWritten ? paths.config : null,\n validate,\n gitignoreUpdated: ignoreUpdated,\n });\n return EXIT.ok;\n }\n\n const { theme } = ctx;\n const rel = (p: string) => relative(ctx.cwd, p) || '.';\n ctx.out(\n `${theme.ok(theme.glyph.ok)} Project brain ready for ${theme.bold(name)} ${theme.dim(rel(paths.dir))}`,\n );\n const files: Array<[string, string, boolean]> = [\n ['STATE.md', paths.state, before.state],\n ['ARCHITECTURE.md', paths.architecture, before.architecture],\n ['TASK_GRAPH.json', paths.taskGraph, before.taskGraph],\n ];\n for (const [label, , existed] of files) {\n ctx.out(` ${existed ? theme.dim('kept ') : theme.accent('created')} ${label}`);\n }\n if (configWritten) {\n ctx.out(` ${theme.accent('created')} config.json`);\n for (const [kind, command] of Object.entries(validate)) {\n ctx.out(theme.dim(` runner.validate.${kind} = ${command}`));\n }\n } else if (configSkipped === 'exists') {\n ctx.out(` ${theme.dim('kept ')} config.json`);\n } else if (configSkipped === 'non-interactive') {\n ctx.out(theme.dim(' config.json not written (pass --config to create a starter config)'));\n }\n if (ignoreUpdated)\n ctx.out(` ${theme.accent('updated')} .gitignore ${theme.dim(`(${LOCK_IGNORE})`)}`);\n\n ctx.out();\n ctx.out(theme.dim('Next:'));\n ctx.out(` ${theme.accent('davecode tasks add setup \"Describe the first task\"')}`);\n ctx.out(` ${theme.accent('davecode start')}`);\n return EXIT.ok;\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\nexport type PackageManager = 'pnpm' | 'yarn' | 'bun' | 'npm';\n\nexport interface PackageJson {\n name?: string;\n packageManager?: string;\n scripts?: Record<string, string>;\n}\n\nexport function readPackageJson(root: string): PackageJson | undefined {\n try {\n const value: unknown = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'));\n return value && typeof value === 'object' ? (value as PackageJson) : undefined;\n } catch {\n return undefined;\n }\n}\n\n/** From `packageManager` in package.json, then lockfiles; npm otherwise. */\nexport function detectPackageManager(root: string, pkg?: PackageJson): PackageManager {\n const declared = pkg?.packageManager?.split('@')[0];\n if (declared === 'pnpm' || declared === 'yarn' || declared === 'bun' || declared === 'npm') {\n return declared;\n }\n if (existsSync(join(root, 'pnpm-lock.yaml'))) return 'pnpm';\n if (existsSync(join(root, 'yarn.lock'))) return 'yarn';\n if (existsSync(join(root, 'bun.lock')) || existsSync(join(root, 'bun.lockb'))) return 'bun';\n return 'npm';\n}\n\nconst CANDIDATES = {\n lint: ['lint', 'lint:check', 'check:lint'],\n typecheck: ['typecheck', 'type-check', 'check-types', 'check:types', 'types', 'tsc'],\n test: ['test', 'test:unit', 'test:ci'],\n} as const;\n\n/** npm's placeholder `test` script, which always fails. */\nconst NPM_PLACEHOLDER = /no test specified/i;\n\nexport function runScript(pm: PackageManager, script: string): string {\n if (script === 'test') return `${pm} test`;\n switch (pm) {\n case 'npm':\n return `npm run ${script}`;\n case 'bun':\n return `bun run ${script}`;\n default:\n return `${pm} ${script}`;\n }\n}\n\nexport interface ValidateCommands {\n lint?: string;\n typecheck?: string;\n test?: string;\n}\n\n/** `runner.validate` commands for the lint/typecheck/test scripts a package.json defines. */\nexport function detectValidateCommands(\n pkg: PackageJson | undefined,\n pm: PackageManager,\n): ValidateCommands {\n const scripts = pkg?.scripts ?? {};\n const found: ValidateCommands = {};\n for (const [kind, names] of Object.entries(CANDIDATES) as Array<\n [keyof ValidateCommands, readonly string[]]\n >) {\n const script = names.find((name) => {\n const body = scripts[name];\n return typeof body === 'string' && body.trim() !== '' && !NPM_PLACEHOLDER.test(body);\n });\n if (script) found[kind] = runScript(pm, script);\n }\n return found;\n}\n\n/** Starter `.davecode/config.json` content. */\nexport function starterConfig(validate: ValidateCommands): Record<string, unknown> {\n return { runner: { validate, judge: { kind: 'none' } } };\n}\n"],"mappings":"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","names":["existsSync","readFileSync","join","join","existsSync","readFileSync"]}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PromptCancelledError
|
|
3
|
+
} from "./chunk-IC3TDONV.js";
|
|
4
|
+
import {
|
|
5
|
+
ThemeProvider,
|
|
6
|
+
useTheme
|
|
7
|
+
} from "./chunk-PGPXE6LF.js";
|
|
8
|
+
import "./chunk-MC7RIPL7.js";
|
|
9
|
+
|
|
10
|
+
// src/tui/prompts.tsx
|
|
11
|
+
import { Box, render, Text, useInput } from "ink";
|
|
12
|
+
import { useState } from "react";
|
|
13
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
14
|
+
function sanitizeLine(input) {
|
|
15
|
+
return input.replace(/[\u0000-\u001f\u007f]/g, "");
|
|
16
|
+
}
|
|
17
|
+
function Question({ message, answer }) {
|
|
18
|
+
const theme = useTheme();
|
|
19
|
+
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
20
|
+
/* @__PURE__ */ jsx(Text, { color: answer === void 0 ? theme.ink.accent : theme.ink.ok, children: answer === void 0 ? "?" : theme.glyph.ok }),
|
|
21
|
+
" ",
|
|
22
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: message }),
|
|
23
|
+
answer !== void 0 ? /* @__PURE__ */ jsxs(Text, { dimColor: theme.ink.dim, children: [
|
|
24
|
+
" ",
|
|
25
|
+
answer
|
|
26
|
+
] }) : null
|
|
27
|
+
] });
|
|
28
|
+
}
|
|
29
|
+
function SelectPrompt({
|
|
30
|
+
message,
|
|
31
|
+
choices,
|
|
32
|
+
initial,
|
|
33
|
+
done,
|
|
34
|
+
cancel
|
|
35
|
+
}) {
|
|
36
|
+
const theme = useTheme();
|
|
37
|
+
const start = Math.max(
|
|
38
|
+
0,
|
|
39
|
+
choices.findIndex((c) => c.value === initial)
|
|
40
|
+
);
|
|
41
|
+
const [index, setIndex] = useState(start);
|
|
42
|
+
const [chosen, setChosen] = useState();
|
|
43
|
+
useInput((input, key) => {
|
|
44
|
+
if (chosen) return;
|
|
45
|
+
if (key.escape || key.ctrl && input === "c") return cancel();
|
|
46
|
+
if (key.upArrow || input === "k") setIndex((i) => (i - 1 + choices.length) % choices.length);
|
|
47
|
+
else if (key.downArrow || input === "j") setIndex((i) => (i + 1) % choices.length);
|
|
48
|
+
else if (key.return) {
|
|
49
|
+
const choice = choices[index];
|
|
50
|
+
setChosen(choice);
|
|
51
|
+
done(choice.value);
|
|
52
|
+
} else if (/^[1-9]$/.test(input) && Number(input) <= choices.length) {
|
|
53
|
+
const choice = choices[Number(input) - 1];
|
|
54
|
+
setIndex(Number(input) - 1);
|
|
55
|
+
setChosen(choice);
|
|
56
|
+
done(choice.value);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
if (chosen) return /* @__PURE__ */ jsx(Question, { message, answer: chosen.label });
|
|
60
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
61
|
+
/* @__PURE__ */ jsx(Question, { message }),
|
|
62
|
+
choices.map((choice, i) => {
|
|
63
|
+
const active = i === index;
|
|
64
|
+
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
65
|
+
/* @__PURE__ */ jsxs(Text, { bold: active, dimColor: !active && theme.ink.dim, children: [
|
|
66
|
+
active ? ` ${theme.glyph.prompt} ` : " ",
|
|
67
|
+
choices.length <= 9 ? `${i + 1}. ` : "",
|
|
68
|
+
choice.label
|
|
69
|
+
] }),
|
|
70
|
+
choice.hint ? /* @__PURE__ */ jsxs(Text, { dimColor: theme.ink.dim, children: [
|
|
71
|
+
" ",
|
|
72
|
+
choice.hint
|
|
73
|
+
] }) : null
|
|
74
|
+
] }, choice.value);
|
|
75
|
+
}),
|
|
76
|
+
/* @__PURE__ */ jsx(Text, { dimColor: theme.ink.dim, children: choices.length <= 9 ? " \u2191/\u2193 or a number to choose, Enter to confirm, Esc to cancel" : " \u2191/\u2193 to move, Enter to select, Esc to cancel" })
|
|
77
|
+
] });
|
|
78
|
+
}
|
|
79
|
+
function TextPrompt({
|
|
80
|
+
message,
|
|
81
|
+
initial = "",
|
|
82
|
+
placeholder,
|
|
83
|
+
validate,
|
|
84
|
+
mask,
|
|
85
|
+
optional,
|
|
86
|
+
done,
|
|
87
|
+
cancel
|
|
88
|
+
}) {
|
|
89
|
+
const theme = useTheme();
|
|
90
|
+
const [value, setValue] = useState(initial);
|
|
91
|
+
const [error, setError] = useState();
|
|
92
|
+
const [submitted, setSubmitted] = useState(false);
|
|
93
|
+
useInput((input, key) => {
|
|
94
|
+
if (submitted) return;
|
|
95
|
+
if (key.escape || key.ctrl && input === "c") return cancel();
|
|
96
|
+
if (key.return) {
|
|
97
|
+
if (mask && !optional && value.trim() === "") {
|
|
98
|
+
setError("a value is required");
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const problem = validate?.(value);
|
|
102
|
+
if (problem) {
|
|
103
|
+
setError(problem);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
setSubmitted(true);
|
|
107
|
+
done(value);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (key.backspace || key.delete) {
|
|
111
|
+
setValue((v) => [...v].slice(0, -1).join(""));
|
|
112
|
+
} else if (key.ctrl && input === "u") {
|
|
113
|
+
setValue("");
|
|
114
|
+
} else if (!key.ctrl && !key.meta) {
|
|
115
|
+
const text = sanitizeLine(input);
|
|
116
|
+
if (text) setValue((v) => v + text);
|
|
117
|
+
}
|
|
118
|
+
setError(void 0);
|
|
119
|
+
});
|
|
120
|
+
const shown = mask ? "\u2022".repeat(Math.min([...value].length, 32)) : value;
|
|
121
|
+
if (submitted) {
|
|
122
|
+
const answer = mask ? value ? "set" : "skipped" : value || "(empty)";
|
|
123
|
+
return /* @__PURE__ */ jsx(Question, { message, answer });
|
|
124
|
+
}
|
|
125
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
126
|
+
/* @__PURE__ */ jsxs(Text, { children: [
|
|
127
|
+
/* @__PURE__ */ jsx(Text, { color: theme.ink.accent, children: "?" }),
|
|
128
|
+
" ",
|
|
129
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: message }),
|
|
130
|
+
" ",
|
|
131
|
+
shown ? /* @__PURE__ */ jsx(Text, { children: shown }) : /* @__PURE__ */ jsx(Text, { dimColor: theme.ink.dim, children: placeholder ?? "" }),
|
|
132
|
+
/* @__PURE__ */ jsx(Text, { inverse: true, children: " " })
|
|
133
|
+
] }),
|
|
134
|
+
error ? /* @__PURE__ */ jsxs(Text, { color: theme.ink.error, children: [
|
|
135
|
+
" ",
|
|
136
|
+
error
|
|
137
|
+
] }) : null
|
|
138
|
+
] });
|
|
139
|
+
}
|
|
140
|
+
function ConfirmPrompt({
|
|
141
|
+
message,
|
|
142
|
+
initial,
|
|
143
|
+
done,
|
|
144
|
+
cancel
|
|
145
|
+
}) {
|
|
146
|
+
const theme = useTheme();
|
|
147
|
+
const [answer, setAnswer] = useState();
|
|
148
|
+
useInput((input, key) => {
|
|
149
|
+
if (answer !== void 0) return;
|
|
150
|
+
if (key.escape || key.ctrl && input === "c") return cancel();
|
|
151
|
+
let value;
|
|
152
|
+
if (key.return) value = initial;
|
|
153
|
+
else if (/^y$/i.test(input)) value = true;
|
|
154
|
+
else if (/^n$/i.test(input)) value = false;
|
|
155
|
+
if (value === void 0) return;
|
|
156
|
+
setAnswer(value);
|
|
157
|
+
done(value);
|
|
158
|
+
});
|
|
159
|
+
if (answer !== void 0) return /* @__PURE__ */ jsx(Question, { message, answer: answer ? "yes" : "no" });
|
|
160
|
+
return /* @__PURE__ */ jsxs(Text, { children: [
|
|
161
|
+
/* @__PURE__ */ jsx(Text, { color: theme.ink.accent, children: "?" }),
|
|
162
|
+
" ",
|
|
163
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: message }),
|
|
164
|
+
" ",
|
|
165
|
+
/* @__PURE__ */ jsx(Text, { dimColor: theme.ink.dim, children: initial ? "(Y/n)" : "(y/N)" })
|
|
166
|
+
] });
|
|
167
|
+
}
|
|
168
|
+
function createInkPrompter(options) {
|
|
169
|
+
const ask = (element) => new Promise((resolve, reject) => {
|
|
170
|
+
let result;
|
|
171
|
+
const instance = render(
|
|
172
|
+
/* @__PURE__ */ jsx(ThemeProvider, { theme: options.theme, children: element({
|
|
173
|
+
done: (value) => {
|
|
174
|
+
result = { ok: true, value };
|
|
175
|
+
setImmediate(() => instance.unmount());
|
|
176
|
+
},
|
|
177
|
+
cancel: () => {
|
|
178
|
+
result = { ok: false };
|
|
179
|
+
setImmediate(() => instance.unmount());
|
|
180
|
+
}
|
|
181
|
+
}) }),
|
|
182
|
+
{
|
|
183
|
+
stdout: options.stdout,
|
|
184
|
+
stdin: options.stdin,
|
|
185
|
+
exitOnCtrlC: false,
|
|
186
|
+
patchConsole: false
|
|
187
|
+
}
|
|
188
|
+
);
|
|
189
|
+
instance.waitUntilExit().then(
|
|
190
|
+
() => {
|
|
191
|
+
if (result?.ok) resolve(result.value);
|
|
192
|
+
else reject(new PromptCancelledError());
|
|
193
|
+
},
|
|
194
|
+
(err) => reject(err)
|
|
195
|
+
);
|
|
196
|
+
});
|
|
197
|
+
return {
|
|
198
|
+
select: (message, choices, initial) => ask((s) => /* @__PURE__ */ jsx(
|
|
199
|
+
SelectPrompt,
|
|
200
|
+
{
|
|
201
|
+
message,
|
|
202
|
+
choices,
|
|
203
|
+
...initial !== void 0 ? { initial } : {},
|
|
204
|
+
...s
|
|
205
|
+
}
|
|
206
|
+
)),
|
|
207
|
+
text: (message, opts = {}) => ask((s) => /* @__PURE__ */ jsx(TextPrompt, { message, ...opts, ...s })),
|
|
208
|
+
secret: (message, opts = {}) => ask((s) => /* @__PURE__ */ jsx(TextPrompt, { message, mask: true, ...opts, ...s })),
|
|
209
|
+
confirm: (message, initial = false) => ask((s) => /* @__PURE__ */ jsx(ConfirmPrompt, { message, initial, ...s }))
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
export {
|
|
213
|
+
ConfirmPrompt,
|
|
214
|
+
SelectPrompt,
|
|
215
|
+
TextPrompt,
|
|
216
|
+
createInkPrompter,
|
|
217
|
+
sanitizeLine
|
|
218
|
+
};
|
|
219
|
+
//# sourceMappingURL=prompts-TZ7UC42M.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tui/prompts.tsx"],"sourcesContent":["import { Box, render, Text, useInput } from 'ink';\nimport { type ReactElement, useState } from 'react';\nimport type { InStream, OutStream } from '../context';\nimport {\n type Choice,\n PromptCancelledError,\n type Prompter,\n type SecretOptions,\n type TextOptions,\n} from '../lib/prompter';\nimport type { Theme } from '../ui/theme';\nimport { ThemeProvider, useTheme } from './theme-context';\n\n/** Drop line breaks and control characters from typed or pasted single-line input. */\nexport function sanitizeLine(input: string): string {\n // biome-ignore lint/suspicious/noControlCharactersInRegex: stripping control characters\n return input.replace(/[\\u0000-\\u001f\\u007f]/g, '');\n}\n\ninterface Settle<T> {\n done: (value: T) => void;\n cancel: () => void;\n}\n\nfunction Question({ message, answer }: { message: string; answer?: string }) {\n const theme = useTheme();\n return (\n <Text>\n <Text color={answer === undefined ? theme.ink.accent : theme.ink.ok}>\n {answer === undefined ? '?' : theme.glyph.ok}\n </Text>{' '}\n <Text bold>{message}</Text>\n {answer !== undefined ? <Text dimColor={theme.ink.dim}> {answer}</Text> : null}\n </Text>\n );\n}\n\nexport function SelectPrompt<T extends string>({\n message,\n choices,\n initial,\n done,\n cancel,\n}: { message: string; choices: Choice<T>[]; initial?: T } & Settle<T>) {\n const theme = useTheme();\n const start = Math.max(\n 0,\n choices.findIndex((c) => c.value === initial),\n );\n const [index, setIndex] = useState(start);\n const [chosen, setChosen] = useState<Choice<T> | undefined>();\n\n useInput((input, key) => {\n if (chosen) return;\n if (key.escape || (key.ctrl && input === 'c')) return cancel();\n if (key.upArrow || input === 'k') setIndex((i) => (i - 1 + choices.length) % choices.length);\n else if (key.downArrow || input === 'j') setIndex((i) => (i + 1) % choices.length);\n else if (key.return) {\n const choice = choices[index]!;\n setChosen(choice);\n done(choice.value);\n } else if (/^[1-9]$/.test(input) && Number(input) <= choices.length) {\n // A number picks its option straight away, as in Claude Code's menus.\n const choice = choices[Number(input) - 1]!;\n setIndex(Number(input) - 1);\n setChosen(choice);\n done(choice.value);\n }\n });\n\n if (chosen) return <Question message={message} answer={chosen.label} />;\n return (\n <Box flexDirection=\"column\">\n <Question message={message} />\n {choices.map((choice, i) => {\n const active = i === index;\n return (\n <Text key={choice.value}>\n <Text bold={active} dimColor={!active && theme.ink.dim}>\n {active ? ` ${theme.glyph.prompt} ` : ' '}\n {choices.length <= 9 ? `${i + 1}. ` : ''}\n {choice.label}\n </Text>\n {choice.hint ? <Text dimColor={theme.ink.dim}> {choice.hint}</Text> : null}\n </Text>\n );\n })}\n <Text dimColor={theme.ink.dim}>\n {choices.length <= 9\n ? ' ↑/↓ or a number to choose, Enter to confirm, Esc to cancel'\n : ' ↑/↓ to move, Enter to select, Esc to cancel'}\n </Text>\n </Box>\n );\n}\n\nexport function TextPrompt({\n message,\n initial = '',\n placeholder,\n validate,\n mask,\n optional,\n done,\n cancel,\n}: TextOptions & SecretOptions & { message: string; mask?: boolean } & Settle<string>) {\n const theme = useTheme();\n const [value, setValue] = useState(initial);\n const [error, setError] = useState<string | undefined>();\n const [submitted, setSubmitted] = useState(false);\n\n useInput((input, key) => {\n if (submitted) return;\n if (key.escape || (key.ctrl && input === 'c')) return cancel();\n if (key.return) {\n if (mask && !optional && value.trim() === '') {\n setError('a value is required');\n return;\n }\n const problem = validate?.(value);\n if (problem) {\n setError(problem);\n return;\n }\n setSubmitted(true);\n done(value);\n return;\n }\n if (key.backspace || key.delete) {\n setValue((v) => [...v].slice(0, -1).join(''));\n } else if (key.ctrl && input === 'u') {\n setValue('');\n } else if (!key.ctrl && !key.meta) {\n const text = sanitizeLine(input);\n if (text) setValue((v) => v + text);\n }\n setError(undefined);\n });\n\n const shown = mask ? '•'.repeat(Math.min([...value].length, 32)) : value;\n if (submitted) {\n const answer = mask ? (value ? 'set' : 'skipped') : value || '(empty)';\n return <Question message={message} answer={answer} />;\n }\n return (\n <Box flexDirection=\"column\">\n <Text>\n <Text color={theme.ink.accent}>?</Text> <Text bold>{message}</Text>{' '}\n {shown ? <Text>{shown}</Text> : <Text dimColor={theme.ink.dim}>{placeholder ?? ''}</Text>}\n <Text inverse> </Text>\n </Text>\n {error ? (\n <Text color={theme.ink.error}>\n {' '}\n {error}\n </Text>\n ) : null}\n </Box>\n );\n}\n\nexport function ConfirmPrompt({\n message,\n initial,\n done,\n cancel,\n}: { message: string; initial: boolean } & Settle<boolean>) {\n const theme = useTheme();\n const [answer, setAnswer] = useState<boolean | undefined>();\n useInput((input, key) => {\n if (answer !== undefined) return;\n if (key.escape || (key.ctrl && input === 'c')) return cancel();\n let value: boolean | undefined;\n if (key.return) value = initial;\n else if (/^y$/i.test(input)) value = true;\n else if (/^n$/i.test(input)) value = false;\n if (value === undefined) return;\n setAnswer(value);\n done(value);\n });\n if (answer !== undefined) return <Question message={message} answer={answer ? 'yes' : 'no'} />;\n return (\n <Text>\n <Text color={theme.ink.accent}>?</Text> <Text bold>{message}</Text>{' '}\n <Text dimColor={theme.ink.dim}>{initial ? '(Y/n)' : '(y/N)'}</Text>\n </Text>\n );\n}\n\nexport interface InkPrompterOptions {\n stdout: OutStream;\n stdin: InStream;\n theme: Theme;\n}\n\n/** Ink-backed prompter: each question is a short-lived Ink app that leaves its answer behind. */\nexport function createInkPrompter(options: InkPrompterOptions): Prompter {\n const ask = <T,>(element: (settle: Settle<T>) => ReactElement): Promise<T> =>\n new Promise<T>((resolve, reject) => {\n let result: { ok: true; value: T } | { ok: false } | undefined;\n const instance = render(\n <ThemeProvider theme={options.theme}>\n {element({\n done: (value) => {\n result = { ok: true, value };\n setImmediate(() => instance.unmount());\n },\n cancel: () => {\n result = { ok: false };\n setImmediate(() => instance.unmount());\n },\n })}\n </ThemeProvider>,\n {\n stdout: options.stdout as NodeJS.WriteStream,\n stdin: options.stdin as NodeJS.ReadStream,\n exitOnCtrlC: false,\n patchConsole: false,\n },\n );\n instance.waitUntilExit().then(\n () => {\n if (result?.ok) resolve(result.value);\n else reject(new PromptCancelledError());\n },\n (err: unknown) => reject(err),\n );\n });\n\n return {\n select: (message, choices, initial) =>\n ask((s) => (\n <SelectPrompt\n message={message}\n choices={choices}\n {...(initial !== undefined ? { initial } : {})}\n {...s}\n />\n )),\n text: (message, opts = {}) => ask((s) => <TextPrompt message={message} {...opts} {...s} />),\n secret: (message, opts = {}) =>\n ask((s) => <TextPrompt message={message} mask {...opts} {...s} />),\n confirm: (message, initial = false) =>\n ask((s) => <ConfirmPrompt message={message} initial={initial} {...s} />),\n };\n}\n"],"mappings":";;;;;;;;;;AAAA,SAAS,KAAK,QAAQ,MAAM,gBAAgB;AAC5C,SAA4B,gBAAgB;AA2BtC,cAIwB,YAJxB;AAdC,SAAS,aAAa,OAAuB;AAElD,SAAO,MAAM,QAAQ,0BAA0B,EAAE;AACnD;AAOA,SAAS,SAAS,EAAE,SAAS,OAAO,GAAyC;AAC3E,QAAM,QAAQ,SAAS;AACvB,SACE,qBAAC,QACC;AAAA,wBAAC,QAAK,OAAO,WAAW,SAAY,MAAM,IAAI,SAAS,MAAM,IAAI,IAC9D,qBAAW,SAAY,MAAM,MAAM,MAAM,IAC5C;AAAA,IAAQ;AAAA,IACR,oBAAC,QAAK,MAAI,MAAE,mBAAQ;AAAA,IACnB,WAAW,SAAY,qBAAC,QAAK,UAAU,MAAM,IAAI,KAAK;AAAA;AAAA,MAAE;AAAA,OAAO,IAAU;AAAA,KAC5E;AAEJ;AAEO,SAAS,aAA+B;AAAA,EAC7C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAuE;AACrE,QAAM,QAAQ,SAAS;AACvB,QAAM,QAAQ,KAAK;AAAA,IACjB;AAAA,IACA,QAAQ,UAAU,CAAC,MAAM,EAAE,UAAU,OAAO;AAAA,EAC9C;AACA,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,KAAK;AACxC,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAgC;AAE5D,WAAS,CAAC,OAAO,QAAQ;AACvB,QAAI,OAAQ;AACZ,QAAI,IAAI,UAAW,IAAI,QAAQ,UAAU,IAAM,QAAO,OAAO;AAC7D,QAAI,IAAI,WAAW,UAAU,IAAK,UAAS,CAAC,OAAO,IAAI,IAAI,QAAQ,UAAU,QAAQ,MAAM;AAAA,aAClF,IAAI,aAAa,UAAU,IAAK,UAAS,CAAC,OAAO,IAAI,KAAK,QAAQ,MAAM;AAAA,aACxE,IAAI,QAAQ;AACnB,YAAM,SAAS,QAAQ,KAAK;AAC5B,gBAAU,MAAM;AAChB,WAAK,OAAO,KAAK;AAAA,IACnB,WAAW,UAAU,KAAK,KAAK,KAAK,OAAO,KAAK,KAAK,QAAQ,QAAQ;AAEnE,YAAM,SAAS,QAAQ,OAAO,KAAK,IAAI,CAAC;AACxC,eAAS,OAAO,KAAK,IAAI,CAAC;AAC1B,gBAAU,MAAM;AAChB,WAAK,OAAO,KAAK;AAAA,IACnB;AAAA,EACF,CAAC;AAED,MAAI,OAAQ,QAAO,oBAAC,YAAS,SAAkB,QAAQ,OAAO,OAAO;AACrE,SACE,qBAAC,OAAI,eAAc,UACjB;AAAA,wBAAC,YAAS,SAAkB;AAAA,IAC3B,QAAQ,IAAI,CAAC,QAAQ,MAAM;AAC1B,YAAM,SAAS,MAAM;AACrB,aACE,qBAAC,QACC;AAAA,6BAAC,QAAK,MAAM,QAAQ,UAAU,CAAC,UAAU,MAAM,IAAI,KAChD;AAAA,mBAAS,IAAI,MAAM,MAAM,MAAM,MAAM;AAAA,UACrC,QAAQ,UAAU,IAAI,GAAG,IAAI,CAAC,OAAO;AAAA,UACrC,OAAO;AAAA,WACV;AAAA,QACC,OAAO,OAAO,qBAAC,QAAK,UAAU,MAAM,IAAI,KAAK;AAAA;AAAA,UAAE,OAAO;AAAA,WAAK,IAAU;AAAA,WAN7D,OAAO,KAOlB;AAAA,IAEJ,CAAC;AAAA,IACD,oBAAC,QAAK,UAAU,MAAM,IAAI,KACvB,kBAAQ,UAAU,IACf,0EACA,0DACN;AAAA,KACF;AAEJ;AAEO,SAAS,WAAW;AAAA,EACzB;AAAA,EACA,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAuF;AACrF,QAAM,QAAQ,SAAS;AACvB,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,OAAO;AAC1C,QAAM,CAAC,OAAO,QAAQ,IAAI,SAA6B;AACvD,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAEhD,WAAS,CAAC,OAAO,QAAQ;AACvB,QAAI,UAAW;AACf,QAAI,IAAI,UAAW,IAAI,QAAQ,UAAU,IAAM,QAAO,OAAO;AAC7D,QAAI,IAAI,QAAQ;AACd,UAAI,QAAQ,CAAC,YAAY,MAAM,KAAK,MAAM,IAAI;AAC5C,iBAAS,qBAAqB;AAC9B;AAAA,MACF;AACA,YAAM,UAAU,WAAW,KAAK;AAChC,UAAI,SAAS;AACX,iBAAS,OAAO;AAChB;AAAA,MACF;AACA,mBAAa,IAAI;AACjB,WAAK,KAAK;AACV;AAAA,IACF;AACA,QAAI,IAAI,aAAa,IAAI,QAAQ;AAC/B,eAAS,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,EAAE,EAAE,KAAK,EAAE,CAAC;AAAA,IAC9C,WAAW,IAAI,QAAQ,UAAU,KAAK;AACpC,eAAS,EAAE;AAAA,IACb,WAAW,CAAC,IAAI,QAAQ,CAAC,IAAI,MAAM;AACjC,YAAM,OAAO,aAAa,KAAK;AAC/B,UAAI,KAAM,UAAS,CAAC,MAAM,IAAI,IAAI;AAAA,IACpC;AACA,aAAS,MAAS;AAAA,EACpB,CAAC;AAED,QAAM,QAAQ,OAAO,SAAI,OAAO,KAAK,IAAI,CAAC,GAAG,KAAK,EAAE,QAAQ,EAAE,CAAC,IAAI;AACnE,MAAI,WAAW;AACb,UAAM,SAAS,OAAQ,QAAQ,QAAQ,YAAa,SAAS;AAC7D,WAAO,oBAAC,YAAS,SAAkB,QAAgB;AAAA,EACrD;AACA,SACE,qBAAC,OAAI,eAAc,UACjB;AAAA,yBAAC,QACC;AAAA,0BAAC,QAAK,OAAO,MAAM,IAAI,QAAQ,eAAC;AAAA,MAAO;AAAA,MAAC,oBAAC,QAAK,MAAI,MAAE,mBAAQ;AAAA,MAAQ;AAAA,MACnE,QAAQ,oBAAC,QAAM,iBAAM,IAAU,oBAAC,QAAK,UAAU,MAAM,IAAI,KAAM,yBAAe,IAAG;AAAA,MAClF,oBAAC,QAAK,SAAO,MAAC,eAAC;AAAA,OACjB;AAAA,IACC,QACC,qBAAC,QAAK,OAAO,MAAM,IAAI,OACpB;AAAA;AAAA,MACA;AAAA,OACH,IACE;AAAA,KACN;AAEJ;AAEO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA4D;AAC1D,QAAM,QAAQ,SAAS;AACvB,QAAM,CAAC,QAAQ,SAAS,IAAI,SAA8B;AAC1D,WAAS,CAAC,OAAO,QAAQ;AACvB,QAAI,WAAW,OAAW;AAC1B,QAAI,IAAI,UAAW,IAAI,QAAQ,UAAU,IAAM,QAAO,OAAO;AAC7D,QAAI;AACJ,QAAI,IAAI,OAAQ,SAAQ;AAAA,aACf,OAAO,KAAK,KAAK,EAAG,SAAQ;AAAA,aAC5B,OAAO,KAAK,KAAK,EAAG,SAAQ;AACrC,QAAI,UAAU,OAAW;AACzB,cAAU,KAAK;AACf,SAAK,KAAK;AAAA,EACZ,CAAC;AACD,MAAI,WAAW,OAAW,QAAO,oBAAC,YAAS,SAAkB,QAAQ,SAAS,QAAQ,MAAM;AAC5F,SACE,qBAAC,QACC;AAAA,wBAAC,QAAK,OAAO,MAAM,IAAI,QAAQ,eAAC;AAAA,IAAO;AAAA,IAAC,oBAAC,QAAK,MAAI,MAAE,mBAAQ;AAAA,IAAQ;AAAA,IACpE,oBAAC,QAAK,UAAU,MAAM,IAAI,KAAM,oBAAU,UAAU,SAAQ;AAAA,KAC9D;AAEJ;AASO,SAAS,kBAAkB,SAAuC;AACvE,QAAM,MAAM,CAAK,YACf,IAAI,QAAW,CAAC,SAAS,WAAW;AAClC,QAAI;AACJ,UAAM,WAAW;AAAA,MACf,oBAAC,iBAAc,OAAO,QAAQ,OAC3B,kBAAQ;AAAA,QACP,MAAM,CAAC,UAAU;AACf,mBAAS,EAAE,IAAI,MAAM,MAAM;AAC3B,uBAAa,MAAM,SAAS,QAAQ,CAAC;AAAA,QACvC;AAAA,QACA,QAAQ,MAAM;AACZ,mBAAS,EAAE,IAAI,MAAM;AACrB,uBAAa,MAAM,SAAS,QAAQ,CAAC;AAAA,QACvC;AAAA,MACF,CAAC,GACH;AAAA,MACA;AAAA,QACE,QAAQ,QAAQ;AAAA,QAChB,OAAO,QAAQ;AAAA,QACf,aAAa;AAAA,QACb,cAAc;AAAA,MAChB;AAAA,IACF;AACA,aAAS,cAAc,EAAE;AAAA,MACvB,MAAM;AACJ,YAAI,QAAQ,GAAI,SAAQ,OAAO,KAAK;AAAA,YAC/B,QAAO,IAAI,qBAAqB,CAAC;AAAA,MACxC;AAAA,MACA,CAAC,QAAiB,OAAO,GAAG;AAAA,IAC9B;AAAA,EACF,CAAC;AAEH,SAAO;AAAA,IACL,QAAQ,CAAC,SAAS,SAAS,YACzB,IAAI,CAAC,MACH;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACC,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,QAC3C,GAAG;AAAA;AAAA,IACN,CACD;AAAA,IACH,MAAM,CAAC,SAAS,OAAO,CAAC,MAAM,IAAI,CAAC,MAAM,oBAAC,cAAW,SAAmB,GAAG,MAAO,GAAG,GAAG,CAAE;AAAA,IAC1F,QAAQ,CAAC,SAAS,OAAO,CAAC,MACxB,IAAI,CAAC,MAAM,oBAAC,cAAW,SAAkB,MAAI,MAAE,GAAG,MAAO,GAAG,GAAG,CAAE;AAAA,IACnE,SAAS,CAAC,SAAS,UAAU,UAC3B,IAAI,CAAC,MAAM,oBAAC,iBAAc,SAAkB,SAAmB,GAAG,GAAG,CAAE;AAAA,EAC3E;AACF;","names":[]}
|