

























@@ -4,7 +4,6 @@ import {
44decorativeEmoji,
55decorativePrefix,
66stripDecorativeEmojiForTerminal,
7-supportsDecorativeEmoji,
87type DecorativeEmojiOptions,
98} from "../../packages/terminal-core/src/decorative-emoji.js";
109import { isRich, theme } from "../../packages/terminal-core/src/theme.js";
@@ -24,23 +23,6 @@ type BannerOptions = TaglineOptions & {
24232524let bannerEmitted = false;
262527-// Use grapheme segmentation so decorative emoji and block art split without corrupting clusters.
28-const graphemeSegmenter =
29-typeof Intl !== "undefined" && "Segmenter" in Intl
30- ? new Intl.Segmenter(undefined, { granularity: "grapheme" })
31- : null;
32-33-function splitGraphemes(value: string): string[] {
34-if (!graphemeSegmenter) {
35-return Array.from(value);
36-}
37-try {
38-return Array.from(graphemeSegmenter.segment(value), (seg) => seg.segment);
39-} catch {
40-return Array.from(value);
41-}
42-}
43-4426const hasJsonFlag = (argv: string[]) =>
4527argv.some((arg) => arg === "--json" || arg.startsWith("--json="));
4628@@ -110,69 +92,6 @@ export function formatCliBannerLine(version: string, options: BannerOptions = {}
11092return `${line1}\n${line2}`;
11193}
11294113-const LOBSTER_ASCII_BODY = [
114-"▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄",
115-"██░▄▄▄░██░▄▄░██░▄▄▄██░▀██░██░▄▄▀██░████░▄▄▀██░███░██",
116-"██░███░██░▀▀░██░▄▄▄██░█░█░██░█████░████░▀▀░██░█░█░██",
117-"██░▀▀▀░██░█████░▀▀▀██░██▄░██░▀▀▄██░▀▀░█░██░██▄▀▄▀▄██",
118-"▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀",
119-];
120-121-function centerText(text: string, width: number): string {
122-const pad = Math.max(0, width - visibleWidth(text));
123-const left = Math.floor(pad / 2);
124-const right = pad - left;
125-return `${" ".repeat(left)}${text}${" ".repeat(right)}`;
126-}
127-128-function formatCliBannerArtLines(options: BannerOptions): string[] {
129-const width = visibleWidth(LOBSTER_ASCII_BODY[0] ?? "");
130-const emojiOptions = resolveEmojiOptions(options);
131-const title = supportsDecorativeEmoji(emojiOptions) ? "🦞 OPENCLAW 🦞" : "OPENCLAW";
132-return [...LOBSTER_ASCII_BODY, centerText(title, width), " "];
133-}
134-135-/** Format the large decorative OpenClaw banner art. */
136-export function formatCliBannerArt(options: BannerOptions = {}): string {
137-const rich = options.richTty ?? isRich();
138-const lines = formatCliBannerArtLines(options);
139-if (!rich) {
140-return lines.join("\n");
141-}
142-143-const colorChar = (ch: string) => {
144-if (ch === "█") {
145-return theme.accentBright(ch);
146-}
147-if (ch === "░") {
148-return theme.accentDim(ch);
149-}
150-if (ch === "▀") {
151-return theme.accent(ch);
152-}
153-return theme.muted(ch);
154-};
155-156-const emojiOptions = resolveEmojiOptions(options);
157-const icon = decorativeEmoji("🦞", emojiOptions);
158-const colored = lines.map((line) => {
159-if (line.includes("OPENCLAW")) {
160-if (!icon) {
161-return theme.info(centerText("OPENCLAW", visibleWidth(line)));
162-}
163-return (
164-theme.muted(" ") +
165-theme.accent(icon) +
166-theme.info(" OPENCLAW ") +
167-theme.accent(icon)
168-);
169-}
170-return splitGraphemes(line).map(colorChar).join("");
171-});
172-173-return colored.join("\n");
174-}
175-17695/** Emit the CLI banner once for interactive, non-JSON, non-version invocations. */
17796export function emitCliBanner(version: string, options: BannerOptions = {}) {
17897if (bannerEmitted) {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。