b3-dict 0.0.0-stage → 0.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.md +50 -2
- package/dist/app.d.ts +8 -0
- package/dist/app.js +83 -0
- package/dist/app.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +96 -0
- package/dist/cli.js.map +1 -0
- package/dist/components/answer-view.d.ts +6 -0
- package/dist/components/answer-view.js +27 -0
- package/dist/components/answer-view.js.map +1 -0
- package/dist/components/banner.d.ts +5 -0
- package/dist/components/banner.js +12 -0
- package/dist/components/banner.js.map +1 -0
- package/dist/components/cursor.d.ts +3 -0
- package/dist/components/cursor.js +6 -0
- package/dist/components/cursor.js.map +1 -0
- package/dist/components/gutter-line.d.ts +7 -0
- package/dist/components/gutter-line.js +12 -0
- package/dist/components/gutter-line.js.map +1 -0
- package/dist/components/lookup-list.d.ts +12 -0
- package/dist/components/lookup-list.js +23 -0
- package/dist/components/lookup-list.js.map +1 -0
- package/dist/components/message-block.d.ts +3 -0
- package/dist/components/message-block.js +11 -0
- package/dist/components/message-block.js.map +1 -0
- package/dist/components/query-input.d.ts +7 -0
- package/dist/components/query-input.js +21 -0
- package/dist/components/query-input.js.map +1 -0
- package/dist/lib/args.d.ts +15 -0
- package/dist/lib/args.js +41 -0
- package/dist/lib/args.js.map +1 -0
- package/dist/lib/ask.d.ts +4 -0
- package/dist/lib/ask.js +54 -0
- package/dist/lib/ask.js.map +1 -0
- package/dist/lib/config.d.ts +6 -0
- package/dist/lib/config.js +39 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/db.d.ts +27 -0
- package/dist/lib/db.js +98 -0
- package/dist/lib/db.js.map +1 -0
- package/dist/lib/envelope.d.ts +37 -0
- package/dist/lib/envelope.js +74 -0
- package/dist/lib/envelope.js.map +1 -0
- package/dist/lib/lookup.d.ts +25 -0
- package/dist/lib/lookup.js +41 -0
- package/dist/lib/lookup.js.map +1 -0
- package/dist/lib/messages.d.ts +63 -0
- package/dist/lib/messages.js +131 -0
- package/dist/lib/messages.js.map +1 -0
- package/dist/lib/model.d.ts +138 -0
- package/dist/lib/model.js +172 -0
- package/dist/lib/model.js.map +1 -0
- package/dist/lib/paths.d.ts +8 -0
- package/dist/lib/paths.js +22 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/policy.d.ts +22 -0
- package/dist/lib/policy.js +92 -0
- package/dist/lib/policy.js.map +1 -0
- package/dist/lib/render-text.d.ts +2 -0
- package/dist/lib/render-text.js +28 -0
- package/dist/lib/render-text.js.map +1 -0
- package/dist/lib/theme.d.ts +14 -0
- package/dist/lib/theme.js +16 -0
- package/dist/lib/theme.js.map +1 -0
- package/dist/lib/time.d.ts +1 -0
- package/dist/lib/time.js +23 -0
- package/dist/lib/time.js.map +1 -0
- package/dist/lib/types.d.ts +47 -0
- package/dist/lib/types.js +7 -0
- package/dist/lib/types.js.map +1 -0
- package/dist/screens/home.d.ts +14 -0
- package/dist/screens/home.js +45 -0
- package/dist/screens/home.js.map +1 -0
- package/dist/screens/list.d.ts +9 -0
- package/dist/screens/list.js +13 -0
- package/dist/screens/list.js.map +1 -0
- package/dist/screens/setup.d.ts +5 -0
- package/dist/screens/setup.js +11 -0
- package/dist/screens/setup.js.map +1 -0
- package/package.json +48 -4
package/README.md
CHANGED
|
@@ -1,3 +1,51 @@
|
|
|
1
|
-
#
|
|
1
|
+
# B3 Dict
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
An AI-first dictionary for the terminal. Ask about English words, idioms, or usage in mixed Vietnamese/English; the answer comes back in Vietnamese. Freeform questions, not a bilingual lookup table: "is draggable a real word?", "take for granted nghĩa là gì?", or paste a joke and ask how to read it.
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
Needs Node 22 or newer and an API key from [OpenRouter](https://openrouter.ai/keys) or [OpenAI](https://platform.openai.com/api-keys).
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install
|
|
11
|
+
cp .env.example .env # then paste your key into .env
|
|
12
|
+
npm run build
|
|
13
|
+
npm link # puts `b3dict` on your PATH
|
|
14
|
+
|
|
15
|
+
b3dict # interactive: type a question, press enter
|
|
16
|
+
b3dict "take for granted" # one-shot: print the answer and exit
|
|
17
|
+
b3dict "Is draggable an English word?"
|
|
18
|
+
b3dict "take for granted" --fresh # skip the stored answer, ask again
|
|
19
|
+
b3dict "draggable" --json # raw answer envelope
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Answers are stored in `data/b3-dict.db`, so asking the same thing twice costs nothing the second time. `.env` and `data/` live in this folder no matter where you run `b3dict` from. Installed from npm instead of a checkout, they move to `~/.local/share/b3-dict` (or `$XDG_DATA_HOME/b3-dict`); set `B3_DICT_HOME` to put them anywhere else.
|
|
23
|
+
|
|
24
|
+
In the interactive screen, paste works for multiline questions. Once an answer is up: `r` asks again, `s` saves it to the wordbook, `esc` goes back, `q` quits. Press `↓` from the question box to reach History (everything you've asked) and Wordbook (what you starred); `enter` reopens a stored answer without calling the model, `s` stars or unstars the row. From the empty question box, `esc` or `ctrl+c` quits.
|
|
25
|
+
|
|
26
|
+
## Development
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm run typecheck # Typecheck
|
|
30
|
+
npm run lint # Lint
|
|
31
|
+
npm test # Run tests
|
|
32
|
+
npm run dev -- "<query>" # Run from source without building
|
|
33
|
+
npm run typecheck && npm run lint && npm test && npm run build # Before creating a PR
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Architecture
|
|
37
|
+
|
|
38
|
+
useReducer-based Model → Update → View. State in a single reducer, components dispatch actions, side effects in useEffect. Mirrors Bubbletea's discipline in a React idiom.
|
|
39
|
+
|
|
40
|
+
## Documentation
|
|
41
|
+
|
|
42
|
+
- [CLAUDE.md](./CLAUDE.md) — Development guide
|
|
43
|
+
- [GLOSSARY.md](./GLOSSARY.md) — Domain vocabulary
|
|
44
|
+
- [docs/PRD.md](./docs/PRD.md) — Product requirements
|
|
45
|
+
- [docs/USER-STORIES.md](./docs/USER-STORIES.md) — User stories and acceptance criteria
|
|
46
|
+
- [docs/INK-TUI-UI-UX-GUIDELINE.md](./docs/INK-TUI-UI-UX-GUIDELINE.md) — Ink TUI design system and component specs
|
|
47
|
+
- [docs/INK-TUI-TECH-SPECS.md](./docs/INK-TUI-TECH-SPECS.md) — Technical stack and configuration
|
|
48
|
+
|
|
49
|
+
## License
|
|
50
|
+
|
|
51
|
+
ISC
|
package/dist/app.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type LookupDeps } from './lib/lookup.js';
|
|
2
|
+
import type { Config, ConfigProblem, Result } from './lib/types.js';
|
|
3
|
+
export type AppProps = {
|
|
4
|
+
config: Result<Config, ConfigProblem>;
|
|
5
|
+
envPath: string;
|
|
6
|
+
deps?: Omit<LookupDeps, 'onRetry'>;
|
|
7
|
+
};
|
|
8
|
+
export declare function App({ config, envPath, deps }: AppProps): import("react").JSX.Element;
|
package/dist/app.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, useApp, useInput } from 'ink';
|
|
3
|
+
import { useEffect, useReducer } from 'react';
|
|
4
|
+
import { Banner } from './components/banner.js';
|
|
5
|
+
import { countLookups, getLookup, listLookups, setSaved } from './lib/db.js';
|
|
6
|
+
import { parseStored } from './lib/envelope.js';
|
|
7
|
+
import { lookup } from './lib/lookup.js';
|
|
8
|
+
import { TUI } from './lib/messages.js';
|
|
9
|
+
import { init, intentFor, update } from './lib/model.js';
|
|
10
|
+
import { Home } from './screens/home.js';
|
|
11
|
+
import { List } from './screens/list.js';
|
|
12
|
+
import { Setup } from './screens/setup.js';
|
|
13
|
+
// Reads a list for the screen. A notice rides along when the step before it went wrong.
|
|
14
|
+
function showList(deps, which, index, notice) {
|
|
15
|
+
const rows = listLookups(deps.db, which);
|
|
16
|
+
if (!rows.ok)
|
|
17
|
+
return { type: 'listShown', which, rows: [], index: 0, notice: TUI.listFailed(rows.error) };
|
|
18
|
+
return { type: 'listShown', which, rows: rows.data, index, ...(notice && { notice }) };
|
|
19
|
+
}
|
|
20
|
+
// Intents that read or write the DB. All synchronous, so each returns the message to dispatch.
|
|
21
|
+
function perform(intent, deps) {
|
|
22
|
+
switch (intent.type) {
|
|
23
|
+
case 'openList':
|
|
24
|
+
return showList(deps, intent.which, intent.index);
|
|
25
|
+
case 'openEntry': {
|
|
26
|
+
const { query, origin } = intent;
|
|
27
|
+
const row = getLookup(deps.db, intent.id);
|
|
28
|
+
if (!row.ok)
|
|
29
|
+
return { type: 'openFailed', query, detail: row.error, origin };
|
|
30
|
+
if (!row.data)
|
|
31
|
+
return { type: 'openFailed', query, detail: `no lookup with id ${intent.id}`, origin };
|
|
32
|
+
const answer = parseStored(row.data.answer_json);
|
|
33
|
+
return answer.ok
|
|
34
|
+
? { type: 'entryOpened', entry: row.data, answer: answer.data, origin }
|
|
35
|
+
: { type: 'openFailed', query: row.data.query_text, detail: answer.error, origin };
|
|
36
|
+
}
|
|
37
|
+
case 'toggleSave': {
|
|
38
|
+
const saved = setSaved(deps.db, intent.id, intent.saved, deps.now());
|
|
39
|
+
if (intent.reload) {
|
|
40
|
+
return showList(deps, intent.reload.which, intent.reload.index, saved.ok ? undefined : TUI.saveFailed(saved.error));
|
|
41
|
+
}
|
|
42
|
+
return saved.ok ? { type: 'entrySaved', entry: saved.data } : { type: 'saveFailed', detail: saved.error };
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// The shell: one reducer holds the state, key presses become messages, and the side effects
|
|
47
|
+
// (asking the model, reading lists, flipping the saved flag) happen here.
|
|
48
|
+
export function App({ config, envPath, deps }) {
|
|
49
|
+
const { exit } = useApp();
|
|
50
|
+
const [model, dispatch] = useReducer(update, config, init);
|
|
51
|
+
const { view } = model;
|
|
52
|
+
useInput((input, key) => {
|
|
53
|
+
const intent = intentFor(view, input, key);
|
|
54
|
+
if (!intent)
|
|
55
|
+
return;
|
|
56
|
+
if (intent.type === 'quit') {
|
|
57
|
+
exit();
|
|
58
|
+
}
|
|
59
|
+
else if (intent.type === 'openList' || intent.type === 'openEntry' || intent.type === 'toggleSave') {
|
|
60
|
+
if (deps)
|
|
61
|
+
dispatch(perform(intent, deps));
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
dispatch(intent);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
const request = view.screen === 'asking' ? view.request : undefined;
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
if (!request || !config.ok || !deps)
|
|
70
|
+
return;
|
|
71
|
+
const requestId = request.id;
|
|
72
|
+
void lookup({ query: request.query, route: config.data.route, model: config.data.model, fresh: request.fresh }, { ...deps, onRetry: (event) => dispatch({ type: 'retrying', requestId, event }) }).then((result) => dispatch(result.ok
|
|
73
|
+
? { type: 'answered', requestId, outcome: result.data }
|
|
74
|
+
: { type: 'failed', requestId, failure: result.error }));
|
|
75
|
+
// One lookup per request id. The request object is recreated on every render, and config
|
|
76
|
+
// and deps never change after mount, so the id is the only dependency that matters.
|
|
77
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
78
|
+
}, [request?.id]);
|
|
79
|
+
// One COUNT per Home render; renders happen on key presses, so this is cheap.
|
|
80
|
+
const counted = view.screen === 'home' && deps ? countLookups(deps.db) : undefined;
|
|
81
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Banner, { config: config.ok ? config.data : undefined }), view.screen === 'setup' && _jsx(Setup, { problem: view.problem, envPath: envPath }), view.screen === 'list' && deps && _jsx(List, { view: view, now: deps.now() }), view.screen !== 'setup' && view.screen !== 'list' && config.ok && (_jsx(Home, { view: view, config: config.data, counts: counted?.ok ? counted.data : undefined, notice: counted && !counted.ok ? TUI.countFailed(counted.error) : undefined }))] }));
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=app.js.map
|
package/dist/app.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.tsx"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { setTimeout as sleep } from 'node:timers/promises';
|
|
4
|
+
import { nanoid } from 'nanoid';
|
|
5
|
+
import { parseCommand } from './lib/args.js';
|
|
6
|
+
import { createAsk } from './lib/ask.js';
|
|
7
|
+
import { resolveConfig } from './lib/config.js';
|
|
8
|
+
import { openDb } from './lib/db.js';
|
|
9
|
+
import { lookup } from './lib/lookup.js';
|
|
10
|
+
import { CLI, configProblemText, failureText, HELP, retryText, TUI } from './lib/messages.js';
|
|
11
|
+
import { DB_PATH, ENV_PATH, PACKAGE_JSON_PATH } from './lib/paths.js';
|
|
12
|
+
import { renderText } from './lib/render-text.js';
|
|
13
|
+
import { EXIT, messageOf } from './lib/types.js';
|
|
14
|
+
const out = (text) => process.stdout.write(`${text}\n`);
|
|
15
|
+
const warn = (text) => process.stderr.write(`${text}\n`);
|
|
16
|
+
const depsFor = (config, db) => ({
|
|
17
|
+
db,
|
|
18
|
+
ask: createAsk(config),
|
|
19
|
+
sleep: (ms) => sleep(ms),
|
|
20
|
+
now: () => new Date(),
|
|
21
|
+
newId: () => nanoid(),
|
|
22
|
+
});
|
|
23
|
+
async function main(argv) {
|
|
24
|
+
const command = parseCommand(argv);
|
|
25
|
+
if (!command.ok) {
|
|
26
|
+
warn(`✗ ${command.error}\n\n${HELP}`);
|
|
27
|
+
return EXIT.usage;
|
|
28
|
+
}
|
|
29
|
+
switch (command.data.mode) {
|
|
30
|
+
case 'help':
|
|
31
|
+
out(HELP);
|
|
32
|
+
return EXIT.ok;
|
|
33
|
+
case 'version':
|
|
34
|
+
out(JSON.parse(readFileSync(PACKAGE_JSON_PATH, 'utf8')).version);
|
|
35
|
+
return EXIT.ok;
|
|
36
|
+
}
|
|
37
|
+
// Variables already in the environment win over .env.
|
|
38
|
+
if (existsSync(ENV_PATH))
|
|
39
|
+
process.loadEnvFile(ENV_PATH);
|
|
40
|
+
return command.data.mode === 'tui'
|
|
41
|
+
? runTui(resolveConfig(process.env))
|
|
42
|
+
: runOneShot(command.data, resolveConfig(process.env, command.data.model));
|
|
43
|
+
}
|
|
44
|
+
async function runOneShot({ query, fresh, json, model }, config) {
|
|
45
|
+
if (!config.ok) {
|
|
46
|
+
warn(configProblemText(config.error, ENV_PATH));
|
|
47
|
+
return EXIT.noKey;
|
|
48
|
+
}
|
|
49
|
+
const db = openDb(DB_PATH);
|
|
50
|
+
if (!db.ok) {
|
|
51
|
+
warn(CLI.dbOpenFailed(DB_PATH, db.error));
|
|
52
|
+
return EXIT.unexpected;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const result = await lookup(
|
|
56
|
+
// An explicit --model asks to hear from that model, so it never settles for a stored answer.
|
|
57
|
+
{ query, route: config.data.route, model: config.data.model, fresh: fresh || model !== undefined }, { ...depsFor(config.data, db.data), onRetry: (event) => warn(`${retryText(event)} (${event.detail})`) });
|
|
58
|
+
if (!result.ok) {
|
|
59
|
+
warn(failureText(result.error, config.data, query));
|
|
60
|
+
return result.error.reason === 'unexpected' ? EXIT.unexpected : EXIT.provider;
|
|
61
|
+
}
|
|
62
|
+
// --json prints the envelope exactly as stored, so a pipe sees the same bytes history holds.
|
|
63
|
+
out(json ? result.data.entry.answer_json : renderText(result.data.answer, query));
|
|
64
|
+
return EXIT.ok;
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
db.data.close();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async function runTui(config) {
|
|
71
|
+
if (!process.stdin.isTTY) {
|
|
72
|
+
warn(`✗ ${TUI.needsTerminal}`);
|
|
73
|
+
return EXIT.usage;
|
|
74
|
+
}
|
|
75
|
+
const db = config.ok ? openDb(DB_PATH) : undefined;
|
|
76
|
+
if (db && !db.ok) {
|
|
77
|
+
warn(CLI.dbOpenFailed(DB_PATH, db.error));
|
|
78
|
+
return EXIT.unexpected;
|
|
79
|
+
}
|
|
80
|
+
// Loaded here so a one-shot lookup never pays for Ink and React.
|
|
81
|
+
const [{ render }, { createElement }, { App }] = await Promise.all([import('ink'), import('react'), import('./app.js')]);
|
|
82
|
+
const deps = config.ok && db?.ok ? depsFor(config.data, db.data) : undefined;
|
|
83
|
+
const app = render(createElement(App, { config, envPath: ENV_PATH, deps }));
|
|
84
|
+
await app.waitUntilExit();
|
|
85
|
+
if (db?.ok)
|
|
86
|
+
db.data.close();
|
|
87
|
+
// A lookup may still be in flight after the user quits; don't let it hold the process open.
|
|
88
|
+
process.exit(config.ok ? EXIT.ok : EXIT.noKey);
|
|
89
|
+
}
|
|
90
|
+
main(process.argv.slice(2)).then((code) => {
|
|
91
|
+
process.exitCode = code;
|
|
92
|
+
}, (error) => {
|
|
93
|
+
warn(CLI.unknownError(messageOf(error)));
|
|
94
|
+
process.exitCode = EXIT.unexpected;
|
|
95
|
+
});
|
|
96
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { Fragment } from 'react';
|
|
4
|
+
import { bareIpa, displayTerm } from '../lib/envelope.js';
|
|
5
|
+
import { TUI } from '../lib/messages.js';
|
|
6
|
+
import { C } from '../lib/theme.js';
|
|
7
|
+
import { GutterLine } from './gutter-line.js';
|
|
8
|
+
// A gutter row set in from the sense it belongs to. Padding, not spaces, so wrapped text stays aligned.
|
|
9
|
+
const Indented = ({ by = 2, children }) => (_jsx(GutterLine, { children: _jsx(Box, { paddingLeft: by, children: children }) }));
|
|
10
|
+
const Star = ({ saved }) => (saved ? _jsx(Text, { color: C.green, children: " \u2605" }) : null);
|
|
11
|
+
// The full answer for the TUI. Like the plain-text renderer, each kind reads only its own fields.
|
|
12
|
+
export function AnswerView({ answer, query, saved }) {
|
|
13
|
+
const term = displayTerm(answer, query);
|
|
14
|
+
switch (answer.kind) {
|
|
15
|
+
case 'word-check':
|
|
16
|
+
return (_jsxs(_Fragment, { children: [_jsxs(GutterLine, { children: [_jsx(Text, { color: C.label, bold: true, children: term }), _jsx(Text, { color: C.overlay1, children: " \u2014 " }), _jsx(Text, { color: answer.isWord ? C.green : C.red, children: answer.isWord ? TUI.isWord : TUI.notWord }), _jsx(Star, { saved: saved })] }), answer.verdictVi && (_jsx(GutterLine, { children: _jsx(Text, { color: C.label, children: answer.verdictVi }) })), !answer.isWord &&
|
|
17
|
+
(answer.suggestions ?? []).map((s) => (_jsx(GutterLine, { children: _jsxs(Text, { color: C.label, children: [_jsxs(Text, { color: C.green, children: ["\u2192 ", s.term] }), ": ", s.reasonVi] }) }, s.term)))] }));
|
|
18
|
+
case 'meaning':
|
|
19
|
+
return (_jsxs(_Fragment, { children: [_jsxs(GutterLine, { children: [_jsx(Text, { color: C.label, bold: true, children: term }), _jsx(Star, { saved: saved })] }), answer.ipa && (_jsx(GutterLine, { children: _jsxs(Text, { color: C.overlay1, children: ["/", bareIpa(answer.ipa), "/"] }) })), (answer.entries ?? []).map((entry, index) => (_jsx(EntryLines, { entry: entry }, index)))] }));
|
|
20
|
+
case 'interpretation':
|
|
21
|
+
return (_jsxs(_Fragment, { children: [_jsxs(GutterLine, { children: [_jsxs(Text, { color: C.overlay1, children: ["\"", (answer.subject ?? query).trim().replace(/\s*\n\s*/g, ' / '), "\""] }), _jsx(Star, { saved: saved })] }), _jsx(GutterLine, {}), _jsx(GutterLine, { children: _jsx(Text, { color: C.label, children: (answer.explanationVi ?? '').trim() }) }), (answer.nuances ?? []).map((nuance) => (_jsx(GutterLine, { children: _jsxs(Text, { color: C.overlay1, children: ["\u2022 ", nuance] }) }, nuance)))] }));
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function EntryLines({ entry }) {
|
|
25
|
+
return (_jsxs(_Fragment, { children: [_jsx(GutterLine, {}), _jsx(GutterLine, { children: _jsxs(Text, { color: C.label, children: [_jsxs(Text, { color: C.sapphire, children: ["[", entry.partOfSpeech, entry.register ? ` · ${entry.register}` : '', "]"] }), ' ', entry.glossVi] }) }), entry.synonyms.length > 0 && (_jsx(Indented, { children: _jsxs(Text, { color: C.overlay1, children: ["\u2248 ", entry.synonyms.join(', ')] }) })), entry.examples.map((example) => (_jsxs(Fragment, { children: [_jsx(Indented, { children: _jsxs(Text, { color: C.label, children: ["\"", example.text, "\""] }) }), _jsx(Indented, { by: 3, children: _jsxs(Text, { color: C.overlay1, children: ["\u2014 ", example.glossVi] }) })] }, example.text)))] }));
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=answer-view.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"answer-view.js","sourceRoot":"","sources":["../../src/components/answer-view.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { TUI } from '../lib/messages.js';
|
|
4
|
+
import { C } from '../lib/theme.js';
|
|
5
|
+
const ART = ['█▄▄ ▀▀█ █▀▄ █ █▀▀ ▀█▀', '█▄█ ▄██ █▄▀ █ █▄▄ █'];
|
|
6
|
+
// "qwen/qwen3-235b-a22b-2507" reads better without its vendor prefix.
|
|
7
|
+
export const shortModel = (model) => model.slice(model.lastIndexOf('/') + 1);
|
|
8
|
+
export function Banner({ config }) {
|
|
9
|
+
const where = config ? ` · ${config.route} · ${shortModel(config.model)}` : '';
|
|
10
|
+
return (_jsxs(Box, { flexDirection: "column", children: [ART.map((line) => (_jsx(Text, { color: C.mauve, bold: true, children: line }, line))), _jsxs(Text, { children: [TUI.subtitle, where] }), _jsx(Text, { color: C.overlay1, children: '─'.repeat(46) })] }));
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=banner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"banner.js","sourceRoot":"","sources":["../../src/components/banner.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AACzC,OAAO,EAAE,CAAC,EAAE,MAAM,iBAAiB,CAAC;AAGpC,MAAM,GAAG,GAAG,CAAC,yBAAyB,EAAE,wBAAwB,CAAC,CAAC;AAElE,sEAAsE;AACtE,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;AAE7F,MAAM,UAAU,MAAM,CAAC,EAAE,MAAM,EAAuB;IACpD,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,KAAK,MAAM,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/E,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACxB,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACjB,KAAC,IAAI,IAAY,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,kBAClC,IAAI,IADI,IAAI,CAER,CACR,CAAC,EACF,MAAC,IAAI,eACF,GAAG,CAAC,QAAQ,EACZ,KAAK,IACD,EACP,KAAC,IAAI,IAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,YAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,GAAQ,IAC5C,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Text } from 'ink';
|
|
3
|
+
import { C } from '../lib/theme.js';
|
|
4
|
+
// The two-column row marker every list and input shares: ❯ on the active row, blank elsewhere.
|
|
5
|
+
export const Cursor = ({ active }) => (_jsx(Text, { color: C.mauve, bold: true, children: active ? '❯ ' : ' ' }));
|
|
6
|
+
//# sourceMappingURL=cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/components/cursor.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC3B,OAAO,EAAE,CAAC,EAAE,MAAM,iBAAiB,CAAC;AAEpC,+FAA+F;AAC/F,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,EAAE,MAAM,EAAuB,EAAE,EAAE,CAAC,CACzD,KAAC,IAAI,IAAC,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,kBACvB,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAChB,CACR,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { C } from '../lib/theme.js';
|
|
4
|
+
// One row inside a gutter block. The │ is the box's left border, so text that wraps keeps its gutter.
|
|
5
|
+
export function GutterLine({ children }) {
|
|
6
|
+
return (_jsx(Box, { borderStyle: "single", borderColor: C.overlay1, borderTop: false, borderRight: false, borderBottom: false, paddingLeft: 2, children: children ?? _jsx(Text, { children: " " }) }));
|
|
7
|
+
}
|
|
8
|
+
// The one gutter block a screen has: ┌, its rows, └.
|
|
9
|
+
export function Gutter({ children }) {
|
|
10
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: C.overlay1, children: "\u250C" }), children, _jsx(Text, { color: C.overlay1, children: "\u2514" })] }));
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=gutter-line.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gutter-line.js","sourceRoot":"","sources":["../../src/components/gutter-line.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAEhC,OAAO,EAAE,CAAC,EAAE,MAAM,iBAAiB,CAAC;AAEpC,sGAAsG;AACtG,MAAM,UAAU,UAAU,CAAC,EAAE,QAAQ,EAA4B;IAC/D,OAAO,CACL,KAAC,GAAG,IACF,WAAW,EAAC,QAAQ,EACpB,WAAW,EAAE,CAAC,CAAC,QAAQ,EACvB,SAAS,EAAE,KAAK,EAChB,WAAW,EAAE,KAAK,EAClB,YAAY,EAAE,KAAK,EACnB,WAAW,EAAE,CAAC,YAEb,QAAQ,IAAI,KAAC,IAAI,oBAAS,GACvB,CACP,CAAC;AACJ,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,MAAM,CAAC,EAAE,QAAQ,EAA2B;IAC1D,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,KAAC,IAAI,IAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,uBAAU,EAChC,QAAQ,EACT,KAAC,IAAI,IAAC,KAAK,EAAE,CAAC,CAAC,QAAQ,uBAAU,IAC7B,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ListKind, ListRow } from '../lib/db.js';
|
|
2
|
+
export declare const MAX_VISIBLE = 12;
|
|
3
|
+
export declare const oneLine: (text: string) => string;
|
|
4
|
+
type Props = {
|
|
5
|
+
rows: ListRow[];
|
|
6
|
+
index: number;
|
|
7
|
+
which: ListKind;
|
|
8
|
+
now: Date;
|
|
9
|
+
maxVisible?: number;
|
|
10
|
+
};
|
|
11
|
+
export declare function LookupList({ rows, index, which, now, maxVisible }: Props): import("react").JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { TUI } from '../lib/messages.js';
|
|
4
|
+
import { C } from '../lib/theme.js';
|
|
5
|
+
import { relativeTime } from '../lib/time.js';
|
|
6
|
+
import { Cursor } from './cursor.js';
|
|
7
|
+
import { GutterLine } from './gutter-line.js';
|
|
8
|
+
export const MAX_VISIBLE = 12;
|
|
9
|
+
const QUERY_WIDTH = 34;
|
|
10
|
+
const KIND_WIDTH = 16;
|
|
11
|
+
// A multiline question becomes one line; Ink truncates it to the column with an ellipsis.
|
|
12
|
+
export const oneLine = (text) => text.trim().replace(/\s*\n\s*/g, ' ');
|
|
13
|
+
// The scrolling list History and Wordbook share. The window slides so the cursor stays inside it.
|
|
14
|
+
export function LookupList({ rows, index, which, now, maxVisible = MAX_VISIBLE }) {
|
|
15
|
+
const start = Math.max(0, Math.min(index - maxVisible + 1, rows.length - maxVisible));
|
|
16
|
+
const end = Math.min(rows.length, start + maxVisible);
|
|
17
|
+
return (_jsxs(_Fragment, { children: [start > 0 && (_jsx(GutterLine, { children: _jsx(Text, { color: C.overlay0, children: TUI.moreAbove(start) }) })), rows.slice(start, end).map((row, offset) => {
|
|
18
|
+
const active = start + offset === index;
|
|
19
|
+
const when = which === 'wordbook' ? TUI.savedAgo(relativeTime(row.saved_at ?? row.created_at, now)) : relativeTime(row.created_at, now);
|
|
20
|
+
return (_jsxs(GutterLine, { children: [_jsx(Cursor, { active: active }), _jsx(Text, { color: C.green, children: row.saved ? '★ ' : ' ' }), _jsx(Box, { width: QUERY_WIDTH, children: _jsx(Text, { color: active ? C.mauve : C.label, bold: active, wrap: "truncate-end", children: oneLine(row.query_text) }) }), _jsx(Box, { width: KIND_WIDTH, marginLeft: 1, children: _jsx(Text, { color: C.overlay0, children: row.kind }) }), _jsx(Text, { color: C.overlay0, children: when })] }, row.id));
|
|
21
|
+
}), end < rows.length && (_jsx(GutterLine, { children: _jsx(Text, { color: C.overlay0, children: TUI.moreBelow(rows.length - end) }) }))] }));
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=lookup-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lookup-list.js","sourceRoot":"","sources":["../../src/components/lookup-list.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Text } from 'ink';
|
|
3
|
+
import { C } from '../lib/theme.js';
|
|
4
|
+
import { GutterLine } from './gutter-line.js';
|
|
5
|
+
// A multi-line message inside the gutter: the first line is the red headline, the rest explain.
|
|
6
|
+
// Blank lines in the text stay blank rows. Setup problems and failed lookups both read this way.
|
|
7
|
+
export function MessageBlock({ text }) {
|
|
8
|
+
const [headline, ...rest] = text.split('\n');
|
|
9
|
+
return (_jsxs(_Fragment, { children: [_jsx(GutterLine, { children: _jsx(Text, { color: C.red, bold: true, children: headline }) }), rest.map((line, index) => (_jsx(GutterLine, { children: line === '' ? undefined : _jsx(Text, { color: C.label, children: line }) }, index)))] }));
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=message-block.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-block.js","sourceRoot":"","sources":["../../src/components/message-block.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAC3B,OAAO,EAAE,CAAC,EAAE,MAAM,iBAAiB,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,gGAAgG;AAChG,iGAAiG;AACjG,MAAM,UAAU,YAAY,CAAC,EAAE,IAAI,EAAoB;IACrD,MAAM,CAAC,QAAQ,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC7C,OAAO,CACL,8BACE,KAAC,UAAU,cACT,KAAC,IAAI,IAAC,KAAK,EAAE,CAAC,CAAC,GAAG,EAAE,IAAI,kBACrB,QAAQ,GACJ,GACI,EACZ,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CACzB,KAAC,UAAU,cAAc,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAC,IAAI,IAAC,KAAK,EAAE,CAAC,CAAC,KAAK,YAAG,IAAI,GAAQ,IAArE,KAAK,CAA8E,CACrG,CAAC,IACD,CACJ,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { C } from '../lib/theme.js';
|
|
4
|
+
import { Cursor } from './cursor.js';
|
|
5
|
+
// Draws the question. With a draft it's the live input and shows the cursor; with plain text it's a
|
|
6
|
+
// read-only echo. Key handling lives in the app, not here.
|
|
7
|
+
export function QueryInput(props) {
|
|
8
|
+
const live = 'draft' in props && props.active !== false;
|
|
9
|
+
const text = 'draft' in props ? props.draft.text : props.text;
|
|
10
|
+
const cursor = live && 'draft' in props ? props.draft.cursor : -1;
|
|
11
|
+
let offset = 0;
|
|
12
|
+
const lines = text.split('\n').map((line, index) => {
|
|
13
|
+
const start = offset;
|
|
14
|
+
offset += line.length + 1;
|
|
15
|
+
const at = cursor - start;
|
|
16
|
+
const hasCursor = at >= 0 && at <= line.length;
|
|
17
|
+
return (_jsxs(Box, { children: [_jsx(Cursor, { active: index === 0 }), _jsx(Text, { color: live ? C.inputing : C.label, children: hasCursor ? (_jsxs(_Fragment, { children: [line.slice(0, at), _jsx(Text, { inverse: true, children: line[at] ?? ' ' }), line.slice(at + 1)] })) : (line || ' ') })] }, index));
|
|
18
|
+
});
|
|
19
|
+
return _jsx(Box, { flexDirection: "column", children: lines });
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=query-input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-input.js","sourceRoot":"","sources":["../../src/components/query-input.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAEhC,OAAO,EAAE,CAAC,EAAE,MAAM,iBAAiB,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,oGAAoG;AACpG,2DAA2D;AAC3D,MAAM,UAAU,UAAU,CAAC,KAA8D;IACvF,MAAM,IAAI,GAAG,OAAO,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC;IACxD,MAAM,IAAI,GAAG,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;IAC9D,MAAM,MAAM,GAAG,IAAI,IAAI,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAElE,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QACjD,MAAM,KAAK,GAAG,MAAM,CAAC;QACrB,MAAM,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;QAC1B,MAAM,EAAE,GAAG,MAAM,GAAG,KAAK,CAAC;QAC1B,MAAM,SAAS,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,IAAI,CAAC,MAAM,CAAC;QAC/C,OAAO,CACL,MAAC,GAAG,eACF,KAAC,MAAM,IAAC,MAAM,EAAE,KAAK,KAAK,CAAC,GAAI,EAC/B,KAAC,IAAI,IAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,YACrC,SAAS,CAAC,CAAC,CAAC,CACX,8BACG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAClB,KAAC,IAAI,IAAC,OAAO,kBAAE,IAAI,CAAC,EAAE,CAAC,IAAI,GAAG,GAAQ,EACrC,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,IAClB,CACJ,CAAC,CAAC,CAAC,CACF,IAAI,IAAI,GAAG,CACZ,GACI,KAZC,KAAK,CAaT,CACP,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO,KAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,YAAE,KAAK,GAAO,CAAC;AACnD,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type Result } from './types.js';
|
|
2
|
+
export type Command = {
|
|
3
|
+
mode: 'help';
|
|
4
|
+
} | {
|
|
5
|
+
mode: 'version';
|
|
6
|
+
} | {
|
|
7
|
+
mode: 'tui';
|
|
8
|
+
} | {
|
|
9
|
+
mode: 'ask';
|
|
10
|
+
query: string;
|
|
11
|
+
fresh: boolean;
|
|
12
|
+
json: boolean;
|
|
13
|
+
model?: string;
|
|
14
|
+
};
|
|
15
|
+
export declare function parseCommand(argv: string[]): Result<Command>;
|
package/dist/lib/args.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { parseArgs } from 'node:util';
|
|
2
|
+
import { CLI } from './messages.js';
|
|
3
|
+
import { err, messageOf, ok } from './types.js';
|
|
4
|
+
// argv without the node binary and script path. The error is a Vietnamese line for stderr.
|
|
5
|
+
export function parseCommand(argv) {
|
|
6
|
+
let parsed;
|
|
7
|
+
try {
|
|
8
|
+
parsed = parseArgs({
|
|
9
|
+
args: argv,
|
|
10
|
+
allowPositionals: true,
|
|
11
|
+
options: {
|
|
12
|
+
fresh: { type: 'boolean' },
|
|
13
|
+
json: { type: 'boolean' },
|
|
14
|
+
model: { type: 'string' },
|
|
15
|
+
help: { type: 'boolean', short: 'h' },
|
|
16
|
+
version: { type: 'boolean', short: 'V' },
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
catch (e) {
|
|
21
|
+
const message = messageOf(e);
|
|
22
|
+
const unknown = /^Unknown option '([^']+)'/.exec(message);
|
|
23
|
+
return err(unknown ? CLI.unknownOption(unknown[1]) : CLI.badArguments(message));
|
|
24
|
+
}
|
|
25
|
+
const { values, positionals } = parsed;
|
|
26
|
+
if (values.help)
|
|
27
|
+
return ok({ mode: 'help' });
|
|
28
|
+
if (values.version)
|
|
29
|
+
return ok({ mode: 'version' });
|
|
30
|
+
if (argv.length === 0)
|
|
31
|
+
return ok({ mode: 'tui' });
|
|
32
|
+
// Unquoted words are joined, so `b3dict take for granted` works like the quoted form.
|
|
33
|
+
const query = positionals.join(' ');
|
|
34
|
+
if (query.trim() === '')
|
|
35
|
+
return err(CLI.missingQuery);
|
|
36
|
+
const model = values.model?.trim();
|
|
37
|
+
if (values.model !== undefined && !model)
|
|
38
|
+
return err(CLI.missingModel);
|
|
39
|
+
return ok({ mode: 'ask', query, fresh: values.fresh ?? false, json: values.json ?? false, model });
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=args.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/lib/args.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE,EAAe,MAAM,YAAY,CAAC;AAQ7D,2FAA2F;AAC3F,MAAM,UAAU,YAAY,CAAC,IAAc;IACzC,IAAI,MAAM,CAAC;IACX,IAAI,CAAC;QACH,MAAM,GAAG,SAAS,CAAC;YACjB,IAAI,EAAE,IAAI;YACV,gBAAgB,EAAE,IAAI;YACtB,OAAO,EAAE;gBACP,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;gBAC1B,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;gBACzB,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE;gBACzB,IAAI,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE;gBACrC,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE;aACzC;SACF,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,MAAM,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;QAC7B,MAAM,OAAO,GAAG,2BAA2B,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC1D,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;IAClF,CAAC;IAED,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IACvC,IAAI,MAAM,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7C,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;IACnD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAElD,sFAAsF;IACtF,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAEtD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC;IACnC,IAAI,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,KAAK;QAAE,OAAO,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAEvE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;AACrG,CAAC"}
|