@moewolf/moe-icons-cli 0.0.1 → 0.0.3
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 +249 -10
- package/bin/check-node.cjs +36 -0
- package/bin/moeicons.js +12 -1
- package/dist/api/client.d.ts +13 -2
- package/dist/api/client.js +145 -21
- package/dist/auth/device-login.d.ts +10 -7
- package/dist/auth/device-login.js +60 -37
- package/dist/auth/login-schemas.d.ts +24 -0
- package/dist/auth/login-schemas.js +67 -0
- package/dist/catalog/catalog.d.ts +11 -0
- package/dist/catalog/catalog.js +43 -0
- package/dist/catalog/catalog.json +12768 -3965
- package/dist/cli.d.ts +11 -2
- package/dist/cli.js +773 -147
- package/dist/commands/parser.d.ts +13 -2
- package/dist/commands/parser.js +138 -50
- package/dist/config-package/generated/SOURCE.json +25 -0
- package/dist/config-package/generated/moeicons-config.schema.json +191 -0
- package/dist/config-package/generated/moeicons.config.jsonc +42 -0
- package/dist/config-package/generated/render-config.cjs +145 -0
- package/dist/config-package/generated/validate-config.cjs +293 -0
- package/dist/config-package/generated-config.d.ts +32 -0
- package/dist/config-package/generated-config.js +19 -0
- package/dist/core/auth.d.ts +24 -0
- package/dist/core/auth.js +127 -8
- package/dist/core/bitmap-assets.d.ts +19 -5
- package/dist/core/bitmap-assets.js +40 -31
- package/dist/core/bitmap-shard-budget.d.ts +43 -0
- package/dist/core/bitmap-shard-budget.js +16 -0
- package/dist/core/bitmap-shard-cache.d.ts +68 -0
- package/dist/core/bitmap-shard-cache.js +115 -0
- package/dist/core/bitmap-shard-download.d.ts +130 -0
- package/dist/core/bitmap-shard-download.js +359 -0
- package/dist/core/bitmap-shard-resolver.d.ts +129 -0
- package/dist/core/bitmap-shard-resolver.js +262 -0
- package/dist/core/bitmap-shards.d.ts +70 -0
- package/dist/core/bitmap-shards.js +249 -0
- package/dist/core/bootstrap.d.ts +37 -0
- package/dist/core/bootstrap.js +54 -0
- package/dist/core/cache.d.ts +34 -0
- package/dist/core/cache.js +96 -0
- package/dist/core/context.d.ts +26 -0
- package/dist/core/doctor.d.ts +48 -0
- package/dist/core/doctor.js +190 -0
- package/dist/core/free-download.d.ts +64 -6
- package/dist/core/free-download.js +402 -30
- package/dist/core/generate.d.ts +29 -0
- package/dist/core/generate.js +341 -65
- package/dist/core/icon-selection.d.ts +8 -0
- package/dist/core/icon-selection.js +19 -0
- package/dist/core/install.d.ts +14 -0
- package/dist/core/install.js +132 -13
- package/dist/core/library-update.d.ts +5 -1
- package/dist/core/library-update.js +176 -17
- package/dist/core/local-test-env.d.ts +24 -0
- package/dist/core/local-test-env.js +43 -0
- package/dist/core/metadata-sync.d.ts +30 -0
- package/dist/core/metadata-sync.js +97 -0
- package/dist/core/pro-download.d.ts +63 -0
- package/dist/core/pro-download.js +247 -27
- package/dist/core/pro-install.d.ts +11 -3
- package/dist/core/pro-install.js +181 -19
- package/dist/core/pro-resources.d.ts +79 -0
- package/dist/core/pro-resources.js +204 -0
- package/dist/core/pro-selected-download.d.ts +18 -0
- package/dist/core/pro-selected-download.js +269 -0
- package/dist/core/release-descriptor.d.ts +42 -1
- package/dist/core/release-descriptor.js +117 -9
- package/dist/core/resource-variant.d.ts +27 -10
- package/dist/core/resource-variant.js +75 -34
- package/dist/core/selected-resources.d.ts +59 -0
- package/dist/core/selected-resources.js +350 -0
- package/dist/core/signed-artifact.d.ts +8 -1
- package/dist/core/signed-artifact.js +35 -11
- package/dist/core/target-subtree.d.ts +8 -0
- package/dist/core/target-subtree.js +54 -3
- package/dist/core/version-service.d.ts +11 -0
- package/dist/core/version-service.js +129 -24
- package/dist/core/wizard.d.ts +20 -17
- package/dist/core/wizard.js +66 -28
- package/dist/errors/errors.d.ts +2 -1
- package/dist/errors/errors.js +1 -0
- package/dist/generator/generate.d.ts +1 -1
- package/dist/generator/generate.js +420 -213
- package/dist/generator/shared/SOURCE.json +7 -0
- package/dist/generator/shared/svg-model.cjs +156 -0
- package/dist/generator/svg-model.d.ts +16 -0
- package/dist/generator/svg-model.js +4 -0
- package/dist/generator/theme-resolve.d.ts +2 -0
- package/dist/generator/theme-resolve.js +57 -26
- package/dist/mcp/server.d.ts +23 -36
- package/dist/mcp/server.js +74 -152
- package/dist/mcp/services.d.ts +4 -0
- package/dist/mcp/services.js +230 -0
- package/dist/metadata/archive.d.ts +38 -0
- package/dist/metadata/archive.js +94 -0
- package/dist/metadata/manifest.d.ts +33 -0
- package/dist/metadata/manifest.js +136 -0
- package/dist/metadata/version.d.ts +22 -0
- package/dist/metadata/version.js +53 -0
- package/dist/project/anchors/application.d.ts +13 -0
- package/dist/project/anchors/application.js +377 -0
- package/dist/project/anchors/apply.d.ts +28 -0
- package/dist/project/anchors/apply.js +65 -0
- package/dist/project/anchors/diagnose.d.ts +26 -0
- package/dist/project/anchors/diagnose.js +90 -0
- package/dist/project/anchors/helpers.d.ts +20 -0
- package/dist/project/anchors/helpers.js +55 -0
- package/dist/project/anchors/moeicons-config.d.ts +21 -0
- package/dist/project/anchors/moeicons-config.js +83 -0
- package/dist/project/anchors/parse.d.ts +14 -0
- package/dist/project/anchors/parse.js +36 -0
- package/dist/project/anchors/project-manifest.d.ts +19 -0
- package/dist/project/anchors/project-manifest.js +109 -0
- package/dist/project/anchors/style.d.ts +12 -0
- package/dist/project/anchors/style.js +87 -0
- package/dist/project/anchors/types.d.ts +32 -0
- package/dist/project/anchors/types.js +9 -0
- package/dist/project/config.d.ts +59 -5
- package/dist/project/config.js +234 -102
- package/dist/project/install-metadata.d.ts +23 -2
- package/dist/project/install-metadata.js +58 -7
- package/dist/project/install.d.ts +24 -4
- package/dist/project/install.js +402 -190
- package/dist/project/project-lock.d.ts +2 -2
- package/dist/project/project-lock.js +4 -4
- package/dist/project/tailwind.d.ts +2 -2
- package/dist/project/tailwind.js +3 -4
- package/dist/project/tar-gz.d.ts +18 -1
- package/dist/project/tar-gz.js +40 -3
- package/dist/tui/components.d.ts +62 -0
- package/dist/tui/components.js +100 -0
- package/dist/tui/primitives.d.ts +1 -0
- package/dist/tui/primitives.js +8 -1
- package/dist/ui/banner.d.ts +31 -3
- package/dist/ui/banner.js +120 -25
- package/dist/ui/branded-prompts.d.ts +41 -0
- package/dist/ui/branded-prompts.js +80 -0
- package/dist/ui/clack.d.ts +6 -3
- package/dist/ui/clack.js +56 -5
- package/dist/ui/create-ui.d.ts +1 -0
- package/dist/ui/create-ui.js +5 -1
- package/dist/ui/generated/wordmark-large.d.ts +17 -0
- package/dist/ui/generated/wordmark-large.js +28 -0
- package/dist/ui/generated/wordmark.d.ts +2 -0
- package/dist/ui/generated/wordmark.js +2 -0
- package/dist/ui/non-interactive.js +8 -0
- package/dist/ui/stream.js +30 -0
- package/dist/ui/theme.d.ts +42 -0
- package/dist/ui/theme.js +59 -0
- package/package.json +14 -6
|
@@ -7,5 +7,5 @@ export interface ProjectLockIo {
|
|
|
7
7
|
readonly now: () => number;
|
|
8
8
|
readonly pid: number;
|
|
9
9
|
}
|
|
10
|
-
export declare function withProjectLock<T>(projectRoot: string, operation: "install" | "update" | "reload", work: () => Promise<T> | T, io?: ProjectLockIo): Promise<T>;
|
|
11
|
-
export declare function withProjectLockSync<T>(projectRoot: string, operation: "install" | "update" | "reload", work: () => T, io?: ProjectLockIo): T;
|
|
10
|
+
export declare function withProjectLock<T>(projectRoot: string, operation: "install" | "update" | "reload" | "doctor" | "recover", work: () => Promise<T> | T, io?: ProjectLockIo): Promise<T>;
|
|
11
|
+
export declare function withProjectLockSync<T>(projectRoot: string, operation: "install" | "update" | "reload" | "doctor" | "recover", work: () => T, io?: ProjectLockIo): T;
|
|
@@ -15,8 +15,8 @@ const defaultIo = {
|
|
|
15
15
|
process.kill(pid, 0);
|
|
16
16
|
return true;
|
|
17
17
|
}
|
|
18
|
-
catch {
|
|
19
|
-
return
|
|
18
|
+
catch (error) {
|
|
19
|
+
return error.code !== "ESRCH";
|
|
20
20
|
} },
|
|
21
21
|
now: Date.now,
|
|
22
22
|
pid: process.pid,
|
|
@@ -27,7 +27,7 @@ export async function withProjectLock(projectRoot, operation, work, io = default
|
|
|
27
27
|
let stale = false;
|
|
28
28
|
try {
|
|
29
29
|
const value = JSON.parse(io.read(path));
|
|
30
|
-
stale = typeof value.pid === "number" && typeof value.createdAt === "number" && io.now() - value.createdAt > 30 * 60_000 && !io.isProcessAlive(value.pid);
|
|
30
|
+
stale = typeof value.pid === "number" && typeof value.createdAt === "number" && (operation === "recover" || io.now() - value.createdAt > 30 * 60_000) && !io.isProcessAlive(value.pid);
|
|
31
31
|
}
|
|
32
32
|
catch {
|
|
33
33
|
stale = false;
|
|
@@ -56,7 +56,7 @@ export function withProjectLockSync(projectRoot, operation, work, io = defaultIo
|
|
|
56
56
|
let stale = false;
|
|
57
57
|
try {
|
|
58
58
|
const value = JSON.parse(io.read(path));
|
|
59
|
-
stale = typeof value.pid === "number" && typeof value.createdAt === "number" && io.now() - value.createdAt > 30 * 60_000 && !io.isProcessAlive(value.pid);
|
|
59
|
+
stale = typeof value.pid === "number" && typeof value.createdAt === "number" && (operation === "recover" || io.now() - value.createdAt > 30 * 60_000) && !io.isProcessAlive(value.pid);
|
|
60
60
|
}
|
|
61
61
|
catch {
|
|
62
62
|
stale = false;
|
|
@@ -31,8 +31,8 @@ export type ContentInjectResult = {
|
|
|
31
31
|
export declare function injectContentGlob(source: string, glob: string): ContentInjectResult;
|
|
32
32
|
export declare function moeiconsContentGlob(outputDir: string, target?: "react" | "vue" | "vanilla" | "assets"): string;
|
|
33
33
|
/**
|
|
34
|
-
* Apply Tailwind content injection.
|
|
35
|
-
*
|
|
34
|
+
* Apply Tailwind v3 content injection. Tailwind v4 discovers source files
|
|
35
|
+
* automatically, so generated components need no config rewrite.
|
|
36
36
|
*/
|
|
37
37
|
export declare function planTailwindIntegration(projectRoot: string, outputDir: string, options: {
|
|
38
38
|
readonly noTailwind: boolean;
|
package/dist/project/tailwind.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { CliError } from "../errors/index.js";
|
|
4
3
|
import { CLSX_VERSION_RANGE, TAILWIND_MERGE_VERSION_RANGE } from "../generator/cn.js";
|
|
5
4
|
import { applyEdits, modify } from "jsonc-parser";
|
|
6
5
|
const CONFIG_CANDIDATES = [
|
|
@@ -92,8 +91,8 @@ export function moeiconsContentGlob(outputDir, target) {
|
|
|
92
91
|
return `./${normalized}/**/*.{${extensions}}`;
|
|
93
92
|
}
|
|
94
93
|
/**
|
|
95
|
-
* Apply Tailwind content injection.
|
|
96
|
-
*
|
|
94
|
+
* Apply Tailwind v3 content injection. Tailwind v4 discovers source files
|
|
95
|
+
* automatically, so generated components need no config rewrite.
|
|
97
96
|
*/
|
|
98
97
|
export function planTailwindIntegration(projectRoot, outputDir, options) {
|
|
99
98
|
if (options.noTailwind) {
|
|
@@ -107,7 +106,7 @@ export function planTailwindIntegration(projectRoot, outputDir, options) {
|
|
|
107
106
|
return { files: [], notes: ["no Tailwind config found; skipped content injection"] };
|
|
108
107
|
}
|
|
109
108
|
if (detected.kind === "v4") {
|
|
110
|
-
|
|
109
|
+
return { files: [], notes: ["Tailwind CSS v4 detected; generated icons use explicit dimensions and need no config rewrite"] };
|
|
111
110
|
}
|
|
112
111
|
if (detected.kind === "unknown") {
|
|
113
112
|
return {
|
package/dist/project/tar-gz.d.ts
CHANGED
|
@@ -1,10 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Combined release archives contain all four target trees. The current Pro
|
|
3
|
+
* contract is roughly 60k regular files (9 groups × 554 icons), so the former
|
|
4
|
+
* 20k cap rejected valid official packages. The six-spec bitmap matrix expands
|
|
5
|
+
* to ~205 MiB (tar stream ~257 MiB), so the expanded ceiling covers that with
|
|
6
|
+
* headroom while staying finite for zip-bomb protection. `extractTarGz` enforces
|
|
7
|
+
* the same bound during gunzip via `maxOutputLength`, before the tar stream is
|
|
8
|
+
* materialised.
|
|
9
|
+
*/
|
|
10
|
+
export declare const ICON_ARCHIVE_MAX_ENTRIES = 100000;
|
|
11
|
+
export declare const ICON_ARCHIVE_MAX_EXPANDED_BYTES: number;
|
|
1
12
|
/** Build a gzipped POSIX tar from in-memory files (test fixtures and unpacking). */
|
|
2
13
|
export declare function createTarGz(files: Readonly<Record<string, string | Uint8Array>>): Uint8Array;
|
|
3
14
|
export type ExtractedTarFiles = {
|
|
4
15
|
readonly files: Record<string, Uint8Array>;
|
|
5
16
|
readonly errors: string[];
|
|
6
17
|
};
|
|
7
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* Gunzip + unpack tar. Rejects absolute paths, `..`, symlinks and oversized
|
|
20
|
+
* archives. Path safety uses POSIX `/` separators (the tar format mandates `/`);
|
|
21
|
+
* a Windows-style separator would appear literally inside a name and never be
|
|
22
|
+
* treated as a path separator, so `..\evil` is inert (no traversal), which is
|
|
23
|
+
* the documented known assumption.
|
|
24
|
+
*/
|
|
8
25
|
export declare function extractTarGz(bytes: Uint8Array, limits: {
|
|
9
26
|
maxEntries: number;
|
|
10
27
|
maxExpandedBytes: number;
|
package/dist/project/tar-gz.js
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import { gzipSync, gunzipSync } from "node:zlib";
|
|
2
2
|
const BLOCK = 512;
|
|
3
|
+
/**
|
|
4
|
+
* Combined release archives contain all four target trees. The current Pro
|
|
5
|
+
* contract is roughly 60k regular files (9 groups × 554 icons), so the former
|
|
6
|
+
* 20k cap rejected valid official packages. The six-spec bitmap matrix expands
|
|
7
|
+
* to ~205 MiB (tar stream ~257 MiB), so the expanded ceiling covers that with
|
|
8
|
+
* headroom while staying finite for zip-bomb protection. `extractTarGz` enforces
|
|
9
|
+
* the same bound during gunzip via `maxOutputLength`, before the tar stream is
|
|
10
|
+
* materialised.
|
|
11
|
+
*/
|
|
12
|
+
export const ICON_ARCHIVE_MAX_ENTRIES = 100_000;
|
|
13
|
+
export const ICON_ARCHIVE_MAX_EXPANDED_BYTES = 512 * 1024 * 1024;
|
|
14
|
+
/** True when a zlib gunzip failure means the bounded output was exceeded. */
|
|
15
|
+
function isOutputLimitError(error) {
|
|
16
|
+
const code = error?.code;
|
|
17
|
+
const message = error instanceof Error ? error.message : "";
|
|
18
|
+
return code === "ERR_BUFFER_TOO_LARGE" || /larger than|maxOutputLength|output length/i.test(message);
|
|
19
|
+
}
|
|
3
20
|
function checksumHeader(header) {
|
|
4
21
|
let sum = 0;
|
|
5
22
|
for (let i = 0; i < BLOCK; i += 1)
|
|
@@ -48,15 +65,25 @@ function readOctal(buf, offset, length) {
|
|
|
48
65
|
return 0;
|
|
49
66
|
return Number.parseInt(raw, 8);
|
|
50
67
|
}
|
|
51
|
-
/**
|
|
68
|
+
/**
|
|
69
|
+
* Gunzip + unpack tar. Rejects absolute paths, `..`, symlinks and oversized
|
|
70
|
+
* archives. Path safety uses POSIX `/` separators (the tar format mandates `/`);
|
|
71
|
+
* a Windows-style separator would appear literally inside a name and never be
|
|
72
|
+
* treated as a path separator, so `..\evil` is inert (no traversal), which is
|
|
73
|
+
* the documented known assumption.
|
|
74
|
+
*/
|
|
52
75
|
export function extractTarGz(bytes, limits) {
|
|
53
76
|
const files = {};
|
|
54
77
|
const errors = [];
|
|
55
78
|
let data;
|
|
56
79
|
try {
|
|
57
|
-
|
|
80
|
+
// Bound the decompressed stream itself so a zip bomb fails during gunzip,
|
|
81
|
+
// before the tar bytes are materialised.
|
|
82
|
+
data = gunzipSync(bytes, { maxOutputLength: limits.maxExpandedBytes });
|
|
58
83
|
}
|
|
59
|
-
catch {
|
|
84
|
+
catch (error) {
|
|
85
|
+
if (isOutputLimitError(error))
|
|
86
|
+
return { files, errors: ["expanded size exceeds limit"] };
|
|
60
87
|
return { files, errors: ["invalid gzip"] };
|
|
61
88
|
}
|
|
62
89
|
let offset = 0;
|
|
@@ -73,6 +100,12 @@ export function extractTarGz(bytes, limits) {
|
|
|
73
100
|
const padded = Math.ceil(size / BLOCK) * BLOCK;
|
|
74
101
|
const content = data.subarray(offset, offset + size);
|
|
75
102
|
offset += padded;
|
|
103
|
+
if (type === 0x31 || type === 0x32) {
|
|
104
|
+
// Hard-link (typeflag '1') and symlink (typeflag '2') entries are
|
|
105
|
+
// rejected with an explicit error, never silently skipped.
|
|
106
|
+
errors.push(`link entries are not allowed: ${name || "<unnamed>"}`);
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
76
109
|
if (type !== 0 && type !== 0x30)
|
|
77
110
|
continue;
|
|
78
111
|
if (!name || name.endsWith("/"))
|
|
@@ -86,6 +119,10 @@ export function extractTarGz(bytes, limits) {
|
|
|
86
119
|
errors.push(`unsafe path "${name}"`);
|
|
87
120
|
continue;
|
|
88
121
|
}
|
|
122
|
+
if (name in files) {
|
|
123
|
+
errors.push(`duplicate entry "${name}"`);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
89
126
|
expanded += content.byteLength;
|
|
90
127
|
if (expanded > limits.maxExpandedBytes) {
|
|
91
128
|
errors.push("expanded size exceeds limit");
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { UiTheme } from "../ui/theme.js";
|
|
2
|
+
/**
|
|
3
|
+
* W3-A/C/D/E: pure TUI component renderers.
|
|
4
|
+
*
|
|
5
|
+
* Kept dependency-free (no stream/Clack/process access) so every frame is unit
|
|
6
|
+
* testable and the adapters (`ui/clack.ts`, `ui/stream.ts`,
|
|
7
|
+
* `ui/non-interactive.ts`) only own I/O. Colour is always supplied through the
|
|
8
|
+
* shared `UiTheme` (white / brand blue / brand red + a dim intensity), never a
|
|
9
|
+
* hardcoded escape, and every renderer degrades to plain text when colour is
|
|
10
|
+
* disabled or the output is not a TTY.
|
|
11
|
+
*/
|
|
12
|
+
export declare const PROGRESS_GLYPHS: Readonly<{
|
|
13
|
+
filled: "█";
|
|
14
|
+
empty: "░";
|
|
15
|
+
}>;
|
|
16
|
+
/** W3-C running animation frames. Deterministic by frame index. */
|
|
17
|
+
export declare const TASK_RUNNING_FRAMES: readonly string[];
|
|
18
|
+
export type TaskState = "running" | "success" | "failure" | "timeout";
|
|
19
|
+
export type HistoryState = "running" | "success" | "failure" | "timeout";
|
|
20
|
+
export declare function formatRate(bytes: number, elapsedMs: number): string | undefined;
|
|
21
|
+
export interface ProgressBarInput {
|
|
22
|
+
readonly label: string;
|
|
23
|
+
readonly done?: number;
|
|
24
|
+
readonly total?: number;
|
|
25
|
+
readonly detail?: string;
|
|
26
|
+
readonly columns?: number;
|
|
27
|
+
readonly theme: UiTheme;
|
|
28
|
+
/** Elapsed wall time in ms; enables the rate suffix. */
|
|
29
|
+
readonly elapsedMs?: number;
|
|
30
|
+
/** Fixed bar width override (tests); otherwise derived from `columns`. */
|
|
31
|
+
readonly barWidth?: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* W3-A: one-line progress bar with percentage, transferred/total bytes, an
|
|
35
|
+
* optional rate and a detail suffix. Unknown total renders a byte-only line
|
|
36
|
+
* (never a fake percentage). `columns <= 0` (non-TTY) collapses to a log line.
|
|
37
|
+
*/
|
|
38
|
+
export declare function renderProgressBar(input: ProgressBarInput): string;
|
|
39
|
+
export interface TaskStatusInput {
|
|
40
|
+
readonly state: TaskState;
|
|
41
|
+
readonly label: string;
|
|
42
|
+
readonly detail?: string;
|
|
43
|
+
readonly frame?: number;
|
|
44
|
+
readonly theme: UiTheme;
|
|
45
|
+
}
|
|
46
|
+
/** W3-C: strict four-state task status with a lightweight running animation. */
|
|
47
|
+
export declare function renderTaskStatus(input: TaskStatusInput): string;
|
|
48
|
+
export interface KeyHint {
|
|
49
|
+
readonly key: string;
|
|
50
|
+
readonly label: string;
|
|
51
|
+
}
|
|
52
|
+
export declare const SELECT_KEY_HINTS: readonly KeyHint[];
|
|
53
|
+
export declare const CONFIRM_KEY_HINTS: readonly KeyHint[];
|
|
54
|
+
/** W3-D: low-opacity, small key hints for a pure-keyboard workflow. */
|
|
55
|
+
export declare function renderKeyHints(hints: readonly KeyHint[], theme: UiTheme): string;
|
|
56
|
+
export interface HistoryEntry {
|
|
57
|
+
readonly label: string;
|
|
58
|
+
readonly state: HistoryState;
|
|
59
|
+
readonly detail?: string;
|
|
60
|
+
}
|
|
61
|
+
/** W3-E2: single-line recent-task preview with colour-coded state. */
|
|
62
|
+
export declare function renderHistoryBar(entry: HistoryEntry | undefined, theme: UiTheme): string | undefined;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { formatBytes } from "../metadata/version.js";
|
|
2
|
+
import { visibleWidth } from "../ui/banner.js";
|
|
3
|
+
/**
|
|
4
|
+
* W3-A/C/D/E: pure TUI component renderers.
|
|
5
|
+
*
|
|
6
|
+
* Kept dependency-free (no stream/Clack/process access) so every frame is unit
|
|
7
|
+
* testable and the adapters (`ui/clack.ts`, `ui/stream.ts`,
|
|
8
|
+
* `ui/non-interactive.ts`) only own I/O. Colour is always supplied through the
|
|
9
|
+
* shared `UiTheme` (white / brand blue / brand red + a dim intensity), never a
|
|
10
|
+
* hardcoded escape, and every renderer degrades to plain text when colour is
|
|
11
|
+
* disabled or the output is not a TTY.
|
|
12
|
+
*/
|
|
13
|
+
export const PROGRESS_GLYPHS = Object.freeze({
|
|
14
|
+
filled: "█",
|
|
15
|
+
empty: "░",
|
|
16
|
+
});
|
|
17
|
+
/** W3-C running animation frames. Deterministic by frame index. */
|
|
18
|
+
export const TASK_RUNNING_FRAMES = Object.freeze(["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]);
|
|
19
|
+
function clamp(value, min, max) {
|
|
20
|
+
return Math.min(Math.max(value, min), max);
|
|
21
|
+
}
|
|
22
|
+
export function formatRate(bytes, elapsedMs) {
|
|
23
|
+
if (!Number.isFinite(bytes) || !Number.isFinite(elapsedMs) || elapsedMs <= 0 || bytes <= 0)
|
|
24
|
+
return undefined;
|
|
25
|
+
const perSecond = bytes / (elapsedMs / 1000);
|
|
26
|
+
return `${formatBytes(Math.round(perSecond))}/s`;
|
|
27
|
+
}
|
|
28
|
+
function progressBarWidth(columns, labelWidth, suffixWidth) {
|
|
29
|
+
const available = columns - labelWidth - suffixWidth - 4;
|
|
30
|
+
return clamp(available, 8, 40);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* W3-A: one-line progress bar with percentage, transferred/total bytes, an
|
|
34
|
+
* optional rate and a detail suffix. Unknown total renders a byte-only line
|
|
35
|
+
* (never a fake percentage). `columns <= 0` (non-TTY) collapses to a log line.
|
|
36
|
+
*/
|
|
37
|
+
export function renderProgressBar(input) {
|
|
38
|
+
const { theme, label } = input;
|
|
39
|
+
const done = Math.max(0, input.done ?? 0);
|
|
40
|
+
const total = input.total !== undefined && input.total > 0 ? input.total : undefined;
|
|
41
|
+
const columns = input.columns ?? 80;
|
|
42
|
+
const detail = input.detail ? ` ${theme.dim(input.detail)}` : "";
|
|
43
|
+
const rate = input.elapsedMs !== undefined ? formatRate(done, input.elapsedMs) : undefined;
|
|
44
|
+
if (columns <= 0) {
|
|
45
|
+
const suffix = total !== undefined ? `${formatBytes(done)}/${formatBytes(total)}` : formatBytes(done);
|
|
46
|
+
return `${label} ${suffix}${rate ? ` (${rate})` : ""}${input.detail ? ` ${input.detail}` : ""}`;
|
|
47
|
+
}
|
|
48
|
+
if (total === undefined) {
|
|
49
|
+
return `${label} ${formatBytes(done)}${rate ? ` (${rate})` : ""}${detail}`;
|
|
50
|
+
}
|
|
51
|
+
const ratio = clamp(done / total, 0, 1);
|
|
52
|
+
const percent = Math.floor(ratio * 100);
|
|
53
|
+
const suffix = `${String(percent).padStart(3, " ")}% ${formatBytes(done)}/${formatBytes(total)}${rate ? ` ${rate}` : ""}`;
|
|
54
|
+
// Budget by VISIBLE width: the label/suffix may carry ANSI (e.g. a dim rate),
|
|
55
|
+
// which must not eat into the bar's column budget.
|
|
56
|
+
const width = input.barWidth ?? progressBarWidth(columns, visibleWidth(label), visibleWidth(suffix));
|
|
57
|
+
const filled = Math.round(width * ratio);
|
|
58
|
+
const bar = `${theme.blue(PROGRESS_GLYPHS.filled.repeat(filled))}${theme.dim(PROGRESS_GLYPHS.empty.repeat(width - filled))}`;
|
|
59
|
+
return `${label} ${bar} ${suffix}${detail}`;
|
|
60
|
+
}
|
|
61
|
+
/** W3-C: strict four-state task status with a lightweight running animation. */
|
|
62
|
+
export function renderTaskStatus(input) {
|
|
63
|
+
const { theme, state, label } = input;
|
|
64
|
+
const detail = input.detail ? ` ${theme.dim(input.detail)}` : "";
|
|
65
|
+
if (state === "success")
|
|
66
|
+
return `${theme.blue(theme.symbols.success)} ${label}${detail}`;
|
|
67
|
+
if (state === "failure")
|
|
68
|
+
return `${theme.red(theme.symbols.failure)} ${label}${detail}`;
|
|
69
|
+
if (state === "timeout")
|
|
70
|
+
return `${theme.red(theme.symbols.timeout)} ${label}${detail}`;
|
|
71
|
+
const frame = TASK_RUNNING_FRAMES[Math.abs(input.frame ?? 0) % TASK_RUNNING_FRAMES.length] ?? "…";
|
|
72
|
+
return `${theme.blue(frame)} ${label}${detail}`;
|
|
73
|
+
}
|
|
74
|
+
export const SELECT_KEY_HINTS = Object.freeze([
|
|
75
|
+
{ key: "↑/↓", label: "move" },
|
|
76
|
+
{ key: "1-9", label: "select" },
|
|
77
|
+
{ key: "Enter", label: "submit" },
|
|
78
|
+
{ key: "Esc", label: "cancel" },
|
|
79
|
+
]);
|
|
80
|
+
export const CONFIRM_KEY_HINTS = Object.freeze([
|
|
81
|
+
{ key: "Y/N", label: "choose" },
|
|
82
|
+
{ key: "Enter", label: "submit" },
|
|
83
|
+
{ key: "Esc", label: "cancel" },
|
|
84
|
+
]);
|
|
85
|
+
/** W3-D: low-opacity, small key hints for a pure-keyboard workflow. */
|
|
86
|
+
export function renderKeyHints(hints, theme) {
|
|
87
|
+
return theme.dim(hints.map((hint) => `${hint.key} ${hint.label}`).join(" · "));
|
|
88
|
+
}
|
|
89
|
+
/** W3-E2: single-line recent-task preview with colour-coded state. */
|
|
90
|
+
export function renderHistoryBar(entry, theme) {
|
|
91
|
+
if (!entry)
|
|
92
|
+
return undefined;
|
|
93
|
+
if (entry.state === "success")
|
|
94
|
+
return `${theme.blue(theme.symbols.success)} ${entry.label}${entry.detail ? ` ${theme.dim(entry.detail)}` : ""}`;
|
|
95
|
+
if (entry.state === "failure")
|
|
96
|
+
return `${theme.red(theme.symbols.failure)} ${entry.label}${entry.detail ? ` ${theme.dim(entry.detail)}` : ""}`;
|
|
97
|
+
if (entry.state === "timeout")
|
|
98
|
+
return `${theme.red(theme.symbols.timeout)} ${entry.label}${entry.detail ? ` ${theme.dim(entry.detail)}` : ""}`;
|
|
99
|
+
return `${theme.dim(theme.symbols.pending)} ${entry.label}${entry.detail ? ` ${theme.dim(entry.detail)}` : ""}`;
|
|
100
|
+
}
|
package/dist/tui/primitives.d.ts
CHANGED
package/dist/tui/primitives.js
CHANGED
|
@@ -14,6 +14,10 @@ export function renderBanner(deps, banner) {
|
|
|
14
14
|
deps.streams.write(banner);
|
|
15
15
|
deps.streams.write("\n");
|
|
16
16
|
}
|
|
17
|
+
function formatIndexedLabel(index, total, label) {
|
|
18
|
+
const width = String(Math.max(total, 1)).length;
|
|
19
|
+
return `${String(index + 1).padStart(width, " ")}. ${label}`;
|
|
20
|
+
}
|
|
17
21
|
/** Interactive single-choice selection; non-TTY rejects with guidance. */
|
|
18
22
|
export async function select(deps, prompt, options) {
|
|
19
23
|
assertNotAborted(deps);
|
|
@@ -21,8 +25,11 @@ export async function select(deps, prompt, options) {
|
|
|
21
25
|
throw new CliError("NOT_TTY", "interactive selection requires a TTY; use a specific command or --json");
|
|
22
26
|
}
|
|
23
27
|
deps.streams.write(`${prompt}\n`);
|
|
28
|
+
const total = options.choices.length;
|
|
24
29
|
options.choices.forEach((choice, i) => {
|
|
25
|
-
|
|
30
|
+
if (choice.separatorBefore)
|
|
31
|
+
deps.streams.write("\n");
|
|
32
|
+
deps.streams.write(` ${formatIndexedLabel(i, total, choice.label)}\n`);
|
|
26
33
|
});
|
|
27
34
|
if (options.canCancel)
|
|
28
35
|
deps.streams.write(" 0) Cancel\n");
|
package/dist/ui/banner.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { MOEICONS_BANNER } from "./generated/wordmark.js";
|
|
2
|
+
import { type UiTheme } from "./theme.js";
|
|
3
|
+
export { MOEICONS_BANNER };
|
|
4
|
+
export { MOEICONS_WORDMARK_LARGE, WORDMARK_LARGE_GLYPHS, WORDMARK_LARGE_WIDTH, } from "./generated/wordmark-large.js";
|
|
5
|
+
/** Figlet / mid tier when columns are in [52, 101). */
|
|
6
|
+
export declare const MIN_FIGLET_COLUMNS = 52;
|
|
7
|
+
/** Large Unicode wordmark when columns >= 101. */
|
|
8
|
+
export declare const MIN_LARGE_COLUMNS = 101;
|
|
9
|
+
export declare const CLI_NOTICE_LINES: readonly ["Run `moeicons` from your project root.", "React/Vue targets require the matching framework.", "Automatic Tailwind integration supports v3 only.", "Vanilla and Assets do not require React or Vue."];
|
|
3
10
|
/** Visible terminal width: ANSI sequences and combining marks take no columns. */
|
|
4
11
|
export declare function visibleWidth(value: string): number;
|
|
5
12
|
/** Render an adaptive notice box without splitting ANSI escapes or Unicode characters. */
|
|
@@ -7,5 +14,26 @@ export declare function renderNoticeBox(message: string | readonly string[], opt
|
|
|
7
14
|
readonly width?: number;
|
|
8
15
|
readonly unicode?: boolean;
|
|
9
16
|
}): string;
|
|
10
|
-
export
|
|
17
|
+
export interface RenderBannerOptions {
|
|
18
|
+
readonly columns: number;
|
|
19
|
+
readonly color: boolean;
|
|
20
|
+
}
|
|
21
|
+
export type WordmarkTier = "large" | "figlet" | "single";
|
|
22
|
+
/** Left-pad lines onto a canvas. Never adds trailing spaces. */
|
|
23
|
+
export declare function centerLines(lines: readonly string[], width?: number): string[];
|
|
24
|
+
export declare function selectWordmarkTier(columns: number): WordmarkTier;
|
|
25
|
+
/**
|
|
26
|
+
* Paint ░ runs with brand blue/red by frozen glyph intervals.
|
|
27
|
+
* ██ keep the default foreground. Disable color with theme.enabled=false.
|
|
28
|
+
*/
|
|
29
|
+
export declare function paintWordmarkLarge(lines: readonly string[], theme: UiTheme): string[];
|
|
30
|
+
/** Wordmark only (no notices). Used by wizard startup ordering. */
|
|
31
|
+
export declare function renderWordmarkText(options: RenderBannerOptions): string;
|
|
32
|
+
/** Project-root / dependency notice box for the current terminal width. */
|
|
33
|
+
export declare function renderProjectNotice(columns: number): string;
|
|
34
|
+
/**
|
|
35
|
+
* Combined wordmark + project notice.
|
|
36
|
+
* Wizard prefers renderWordmarkText then version/update then renderProjectNotice.
|
|
37
|
+
*/
|
|
38
|
+
export declare function renderBannerText(options: RenderBannerOptions): string;
|
|
11
39
|
export declare const CLI_VERSION: string;
|
package/dist/ui/banner.js
CHANGED
|
@@ -1,34 +1,33 @@
|
|
|
1
1
|
import packageJson from "../../package.json" with { type: "json" };
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
].join("\n");
|
|
2
|
+
import { MOEICONS_BANNER } from "./generated/wordmark.js";
|
|
3
|
+
import { MOEICONS_WORDMARK_LARGE, WORDMARK_LARGE_GLYPHS, WORDMARK_LARGE_WIDTH, } from "./generated/wordmark-large.js";
|
|
4
|
+
import { ANSI_FG_RESET, createTheme } from "./theme.js";
|
|
5
|
+
export { MOEICONS_BANNER };
|
|
6
|
+
export { MOEICONS_WORDMARK_LARGE, WORDMARK_LARGE_GLYPHS, WORDMARK_LARGE_WIDTH, } from "./generated/wordmark-large.js";
|
|
7
|
+
const CANVAS_WIDTH = 47;
|
|
8
|
+
/** Figlet / mid tier when columns are in [52, 101). */
|
|
9
|
+
export const MIN_FIGLET_COLUMNS = 52;
|
|
10
|
+
/** Large Unicode wordmark when columns >= 101. */
|
|
11
|
+
export const MIN_LARGE_COLUMNS = WORDMARK_LARGE_WIDTH;
|
|
13
12
|
const ANSI_ESCAPE = new RegExp(`${String.fromCharCode(27)}\\[[0-?]*[ -/]*[@-~]`, "g");
|
|
14
13
|
const COMBINING_MARK = /\p{Mark}/u;
|
|
15
14
|
const CONTROL = /[\p{Cc}\p{Cf}]/u;
|
|
16
15
|
const WIDE_CODEPOINT = "[" +
|
|
17
|
-
"\u1100-\u115f" +
|
|
18
|
-
"\u2e80-\u303e" +
|
|
19
|
-
"\u3041-\u33ff" +
|
|
20
|
-
"\u3400-\u4dbf" +
|
|
21
|
-
"\u4e00-\u9fff" +
|
|
22
|
-
"\ua000-\ua4cf" +
|
|
23
|
-
"\uac00-\ud7a3" +
|
|
24
|
-
"\uf900-\ufaff" +
|
|
25
|
-
"\ufe30-\ufe4f" +
|
|
26
|
-
"\uff00-\uff60" +
|
|
27
|
-
"\uffe0-\uffe6" +
|
|
16
|
+
"\u1100-\u115f" +
|
|
17
|
+
"\u2e80-\u303e" +
|
|
18
|
+
"\u3041-\u33ff" +
|
|
19
|
+
"\u3400-\u4dbf" +
|
|
20
|
+
"\u4e00-\u9fff" +
|
|
21
|
+
"\ua000-\ua4cf" +
|
|
22
|
+
"\uac00-\ud7a3" +
|
|
23
|
+
"\uf900-\ufaff" +
|
|
24
|
+
"\ufe30-\ufe4f" +
|
|
25
|
+
"\uff00-\uff60" +
|
|
26
|
+
"\uffe0-\uffe6" +
|
|
28
27
|
"]";
|
|
29
28
|
const WIDE_CHAR = new RegExp(WIDE_CODEPOINT, "u");
|
|
30
29
|
export const CLI_NOTICE_LINES = [
|
|
31
|
-
"Run moeicons from your project root.",
|
|
30
|
+
"Run `moeicons` from your project root.",
|
|
32
31
|
"React/Vue targets require the matching framework.",
|
|
33
32
|
"Automatic Tailwind integration supports v3 only.",
|
|
34
33
|
"Vanilla and Assets do not require React or Vue.",
|
|
@@ -99,7 +98,103 @@ export function renderNoticeBox(message, options = {}) {
|
|
|
99
98
|
`${bottom}${horizontal.repeat(contentWidth + 2)}${bottomRight}`,
|
|
100
99
|
].join("\n");
|
|
101
100
|
}
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
function normalizeColumns(columns) {
|
|
102
|
+
if (!Number.isFinite(columns) || columns <= 0)
|
|
103
|
+
return 80;
|
|
104
|
+
return columns;
|
|
105
|
+
}
|
|
106
|
+
function contentLines(value) {
|
|
107
|
+
const lines = value.split("\n");
|
|
108
|
+
while (lines[0] === "")
|
|
109
|
+
lines.shift();
|
|
110
|
+
while (lines.length > 0 && lines[lines.length - 1] === "")
|
|
111
|
+
lines.pop();
|
|
112
|
+
return lines;
|
|
113
|
+
}
|
|
114
|
+
/** Left-pad lines onto a canvas. Never adds trailing spaces. */
|
|
115
|
+
export function centerLines(lines, width = CANVAS_WIDTH) {
|
|
116
|
+
return lines.map((line) => {
|
|
117
|
+
const pad = Math.max(0, Math.floor((width - visibleWidth(line)) / 2));
|
|
118
|
+
return pad > 0 ? `${" ".repeat(pad)}${line}` : line;
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
export function selectWordmarkTier(columns) {
|
|
122
|
+
const normalized = normalizeColumns(columns);
|
|
123
|
+
if (normalized >= MIN_LARGE_COLUMNS)
|
|
124
|
+
return "large";
|
|
125
|
+
if (normalized >= MIN_FIGLET_COLUMNS)
|
|
126
|
+
return "figlet";
|
|
127
|
+
return "single";
|
|
128
|
+
}
|
|
129
|
+
function glyphShadeAt(column) {
|
|
130
|
+
for (const glyph of WORDMARK_LARGE_GLYPHS) {
|
|
131
|
+
if (column >= glyph.start && column < glyph.end)
|
|
132
|
+
return glyph.shade;
|
|
133
|
+
}
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Paint ░ runs with brand blue/red by frozen glyph intervals.
|
|
138
|
+
* ██ keep the default foreground. Disable color with theme.enabled=false.
|
|
139
|
+
*/
|
|
140
|
+
export function paintWordmarkLarge(lines, theme) {
|
|
141
|
+
// Color open sequences come only from theme (no RGB/ANSI literals here).
|
|
142
|
+
const openBlue = theme.openBlue;
|
|
143
|
+
const openRed = theme.openRed;
|
|
144
|
+
return lines.map((line) => {
|
|
145
|
+
const padded = line.padEnd(WORDMARK_LARGE_WIDTH, " ");
|
|
146
|
+
if (!theme.enabled)
|
|
147
|
+
return padded.replace(/ +$/u, "");
|
|
148
|
+
let result = "";
|
|
149
|
+
let active;
|
|
150
|
+
for (let column = 0; column < WORDMARK_LARGE_WIDTH; column += 1) {
|
|
151
|
+
const character = padded[column] ?? " ";
|
|
152
|
+
if (character === "░") {
|
|
153
|
+
const shade = glyphShadeAt(column) ?? "blue";
|
|
154
|
+
if (active !== shade) {
|
|
155
|
+
if (active !== undefined)
|
|
156
|
+
result += ANSI_FG_RESET;
|
|
157
|
+
result += shade === "blue" ? openBlue : openRed;
|
|
158
|
+
active = shade;
|
|
159
|
+
}
|
|
160
|
+
result += character;
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
if (active !== undefined) {
|
|
164
|
+
result += ANSI_FG_RESET;
|
|
165
|
+
active = undefined;
|
|
166
|
+
}
|
|
167
|
+
result += character;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (active !== undefined)
|
|
171
|
+
result += ANSI_FG_RESET;
|
|
172
|
+
return result.replace(/ +$/u, "");
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
/** Wordmark only (no notices). Used by wizard startup ordering. */
|
|
176
|
+
export function renderWordmarkText(options) {
|
|
177
|
+
const columns = normalizeColumns(options.columns);
|
|
178
|
+
const tier = selectWordmarkTier(columns);
|
|
179
|
+
if (tier === "single")
|
|
180
|
+
return "MOEICONS";
|
|
181
|
+
if (tier === "figlet")
|
|
182
|
+
return centerLines(contentLines(MOEICONS_BANNER)).join("\n");
|
|
183
|
+
const theme = createTheme(options.color);
|
|
184
|
+
return paintWordmarkLarge([...MOEICONS_WORDMARK_LARGE], theme).join("\n");
|
|
185
|
+
}
|
|
186
|
+
/** Project-root / dependency notice box for the current terminal width. */
|
|
187
|
+
export function renderProjectNotice(columns) {
|
|
188
|
+
return renderNoticeBox(CLI_NOTICE_LINES, { width: Math.max(20, normalizeColumns(columns)) });
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Combined wordmark + project notice.
|
|
192
|
+
* Wizard prefers renderWordmarkText then version/update then renderProjectNotice.
|
|
193
|
+
*/
|
|
194
|
+
export function renderBannerText(options) {
|
|
195
|
+
const columns = normalizeColumns(options.columns);
|
|
196
|
+
const wordmark = renderWordmarkText(options);
|
|
197
|
+
const notice = renderProjectNotice(columns);
|
|
198
|
+
return `${wordmark}\n${notice}\n`;
|
|
104
199
|
}
|
|
105
200
|
export const CLI_VERSION = packageJson.version;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { isCancel } from "@clack/core";
|
|
2
|
+
import type { Readable, Writable } from "node:stream";
|
|
3
|
+
import type { UiChoice } from "../core/context.js";
|
|
4
|
+
import type { UiTheme } from "./theme.js";
|
|
5
|
+
export { isCancel };
|
|
6
|
+
export interface BrandedPromptIo {
|
|
7
|
+
readonly theme: UiTheme;
|
|
8
|
+
readonly input?: Readable;
|
|
9
|
+
readonly output?: Writable;
|
|
10
|
+
readonly signal?: AbortSignal;
|
|
11
|
+
}
|
|
12
|
+
export interface SelectFrameOption {
|
|
13
|
+
readonly value: string;
|
|
14
|
+
readonly label: string;
|
|
15
|
+
readonly separatorBefore?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface SelectFrameState {
|
|
18
|
+
readonly state: string;
|
|
19
|
+
readonly cursor: number;
|
|
20
|
+
readonly options: readonly SelectFrameOption[];
|
|
21
|
+
}
|
|
22
|
+
export interface ConfirmFrameState {
|
|
23
|
+
readonly state: string;
|
|
24
|
+
readonly value: boolean;
|
|
25
|
+
}
|
|
26
|
+
/** W3-D: opt-in key hints so existing golden output stays stable. */
|
|
27
|
+
export interface FrameRenderOptions {
|
|
28
|
+
readonly hints?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** Format `1. label` with stable width for lists of 10+. */
|
|
31
|
+
export declare function formatChoiceNumber(index: number, total: number, label: string): string;
|
|
32
|
+
/** Renders every option with 1-based numbers. Back separators are blank lines. */
|
|
33
|
+
export declare function renderSelectFrame(prompt: SelectFrameState, message: string, theme: UiTheme, options?: FrameRenderOptions): string;
|
|
34
|
+
export declare function renderConfirmFrame(prompt: ConfirmFrameState, message: string, theme: UiTheme, options?: FrameRenderOptions): string;
|
|
35
|
+
export declare function brandedSelect(options: BrandedPromptIo & {
|
|
36
|
+
readonly message: string;
|
|
37
|
+
readonly choices: readonly UiChoice[];
|
|
38
|
+
}): Promise<string | symbol>;
|
|
39
|
+
export declare function brandedConfirm(options: BrandedPromptIo & {
|
|
40
|
+
readonly message: string;
|
|
41
|
+
}): Promise<boolean | symbol>;
|