dsh-working-activity 0.3.0 → 0.3.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/lib/client.js CHANGED
@@ -16,11 +16,11 @@ window.__ModuleLoader__.load({
16
16
  document.head.appendChild(tag);
17
17
  }
18
18
  var WorkingLine_module_css_default = {
19
+ "tools": "TG_uoa_tools",
19
20
  "line": "TG_uoa_line",
21
+ "working-pulse": "TG_uoa_working-pulse",
20
22
  "marker": "TG_uoa_marker",
21
- "tools": "TG_uoa_tools",
22
- "text": "TG_uoa_text",
23
- "working-pulse": "TG_uoa_working-pulse"
23
+ "text": "TG_uoa_text"
24
24
  };
25
25
  //#endregion
26
26
  //#region src/client/WorkingLine.tsx
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Working-activity user configuration, mirroring the pi extension's
3
+ * `~/.pi/agent/working-activity.json` shape: the UI owns the file (dsh-tui
4
+ * persists `frames` at `~/.dsh-tui/working-activity.json`) and consumers use
5
+ * the pure parser here to honor `mode` / `features` / `customPhrases` /
6
+ * `customActions` without duplicating validation. No I/O — filesystem
7
+ * ownership stays with the host.
8
+ * @module @deepseek-ai/dsh-working-activity/config
9
+ */
10
+ /** Feature flags that can be switched independently (pi extension parity). */
11
+ export declare const FEATURE_FLAGS: readonly ["phrases", "rareEggs", "nightPhrases", "weekend", "holidays", "combo", "failPhrases", "modelQuips", "shimmer", "continuePhrases", "cost"];
12
+ /** Feature-flag name type. */
13
+ export type FeatureFlag = (typeof FEATURE_FLAGS)[number];
14
+ /** Chinese labels for the settings panel / docs. */
15
+ export declare const FEATURE_LABELS: Record<FeatureFlag, string>;
16
+ /** The file-based config, same keys as the pi extension. */
17
+ export type WorkingActivityConfig = {
18
+ /** Frame preset name (`random` picks one per process). */
19
+ frames?: string;
20
+ /** Extra thinking phrases appended to the base pool. */
21
+ customPhrases?: readonly string[];
22
+ /** Exact tool-name → action-copy pools (case-insensitive match). */
23
+ customActions?: Readonly<Record<string, readonly string[]>>;
24
+ /** Inject the `⏵` self-narration contract into the system prompt. */
25
+ narrate?: boolean;
26
+ /** Debug logging to `working-activity-debug.log`. */
27
+ debugLog?: boolean;
28
+ /** Context usage warning threshold in percent. */
29
+ contextWarnAt?: number;
30
+ /** Context usage danger threshold in percent. */
31
+ contextDangerAt?: number;
32
+ /** Show estimated output tokens per second. */
33
+ showTokPerSec?: boolean;
34
+ /** Hourly work-reminder threshold (0–24, 0 = off). */
35
+ workRemindAt?: number;
36
+ /** lively: full flourish (default) / minimal: functional labels only. */
37
+ mode?: 'lively' | 'minimal';
38
+ /** Per-feature switches; explicit values override `mode` defaults. */
39
+ features?: Readonly<Record<string, boolean>>;
40
+ };
41
+ /** The defaults when nothing is configured. */
42
+ export declare const DEFAULT_ACTIVITY_CONFIG: WorkingActivityConfig;
43
+ /** Result of parsing a config file. */
44
+ export type ConfigReadResult = {
45
+ readonly config: WorkingActivityConfig;
46
+ /** Raw parsed object, preserving unknown keys for lossless write-back. */
47
+ readonly raw: Record<string, unknown>;
48
+ /** Parse error message, when the text was not a valid config object. */
49
+ readonly error?: string;
50
+ };
51
+ /** Clamp `dangerAt` to be at least `warnAt` (percent thresholds). */
52
+ export declare function normalizeThresholds(cfg: WorkingActivityConfig): WorkingActivityConfig;
53
+ /**
54
+ * Parse a `working-activity.json` text into a validated config. Unknown keys
55
+ * are preserved in `raw` (for lossless write-back); invalid values are
56
+ * dropped. A non-object root yields the defaults plus an `error`.
57
+ * @param text - Raw file contents.
58
+ * @returns Validated config + raw object (+ optional error).
59
+ */
60
+ export declare function parseWorkingActivityConfig(text: string): ConfigReadResult;
61
+ /**
62
+ * Whether a feature is on. Explicit `features` entries win; otherwise
63
+ * `minimal` mode turns everything off (except `cost`, always on).
64
+ * @param config - Parsed config.
65
+ * @param name - Feature flag name.
66
+ */
67
+ export declare function featureOn(config: WorkingActivityConfig, name: FeatureFlag): boolean;
68
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,8EAA8E;AAC9E,eAAO,MAAM,aAAa,qJAYhB,CAAA;AAEV,8BAA8B;AAC9B,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAA;AAExD,oDAAoD;AACpD,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAYtD,CAAA;AAED,4DAA4D;AAC5D,MAAM,MAAM,qBAAqB,GAAG;IAClC,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,wDAAwD;IACxD,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,oEAAoE;IACpE,aAAa,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC,CAAA;IAC3D,qEAAqE;IACrE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,qDAAqD;IACrD,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,kDAAkD;IAClD,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,iDAAiD;IACjD,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,+CAA+C;IAC/C,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,yEAAyE;IACzE,IAAI,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAA;IAC3B,sEAAsE;IACtE,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;CAC7C,CAAA;AAED,+CAA+C;AAC/C,eAAO,MAAM,uBAAuB,EAAE,qBAGrC,CAAA;AAED,uCAAuC;AACvC,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAA;IACtC,0EAA0E;IAC1E,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACrC,wEAAwE;IACxE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAMD,qEAAqE;AACrE,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,qBAAqB,GAAG,qBAAqB,CAIrF;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAwDzE;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,qBAAqB,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAKnF"}
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Working-activity user configuration, mirroring the pi extension's
3
+ * `~/.pi/agent/working-activity.json` shape: the UI owns the file (dsh-tui
4
+ * persists `frames` at `~/.dsh-tui/working-activity.json`) and consumers use
5
+ * the pure parser here to honor `mode` / `features` / `customPhrases` /
6
+ * `customActions` without duplicating validation. No I/O — filesystem
7
+ * ownership stays with the host.
8
+ * @module @deepseek-ai/dsh-working-activity/config
9
+ */
10
+ import { DEFAULT_PRESET, isPresetName } from './frames.js';
11
+ /** Feature flags that can be switched independently (pi extension parity). */
12
+ export const FEATURE_FLAGS = [
13
+ 'phrases', // 俏皮文案池(思考/等待/收尾/工具动词)
14
+ 'rareEggs', // 稀有彩虹彩蛋
15
+ 'nightPhrases', // 深夜文案
16
+ 'weekend', // 周末问候
17
+ 'holidays', // 节假日彩蛋
18
+ 'combo', // 连击火力全开
19
+ 'failPhrases', // 失败文案池
20
+ 'modelQuips', // 模型切换梗
21
+ 'shimmer', // 文案星辉扫过/彩虹流光
22
+ 'continuePhrases', // 打断后接梗
23
+ 'cost', // 成本与 token 核算(结束时展示)
24
+ ];
25
+ /** Chinese labels for the settings panel / docs. */
26
+ export const FEATURE_LABELS = {
27
+ phrases: '俏皮文案',
28
+ rareEggs: '稀有彩蛋',
29
+ nightPhrases: '深夜文案',
30
+ weekend: '周末问候',
31
+ holidays: '节假日彩蛋',
32
+ combo: '工具连击',
33
+ failPhrases: '失败文案',
34
+ modelQuips: '模型切换梗',
35
+ shimmer: '星辉效果',
36
+ continuePhrases: '打断接梗',
37
+ cost: '成本与 token',
38
+ };
39
+ /** The defaults when nothing is configured. */
40
+ export const DEFAULT_ACTIVITY_CONFIG = {
41
+ frames: DEFAULT_PRESET,
42
+ narrate: true,
43
+ };
44
+ function errorText(error) {
45
+ return error instanceof Error ? error.message : String(error);
46
+ }
47
+ /** Clamp `dangerAt` to be at least `warnAt` (percent thresholds). */
48
+ export function normalizeThresholds(cfg) {
49
+ const warnAt = cfg.contextWarnAt ?? 80;
50
+ const dangerAt = cfg.contextDangerAt ?? 95;
51
+ return dangerAt < warnAt ? { ...cfg, contextDangerAt: warnAt } : cfg;
52
+ }
53
+ /**
54
+ * Parse a `working-activity.json` text into a validated config. Unknown keys
55
+ * are preserved in `raw` (for lossless write-back); invalid values are
56
+ * dropped. A non-object root yields the defaults plus an `error`.
57
+ * @param text - Raw file contents.
58
+ * @returns Validated config + raw object (+ optional error).
59
+ */
60
+ export function parseWorkingActivityConfig(text) {
61
+ let parsed;
62
+ try {
63
+ parsed = JSON.parse(text);
64
+ }
65
+ catch (error) {
66
+ return { config: { ...DEFAULT_ACTIVITY_CONFIG }, raw: {}, error: errorText(error) };
67
+ }
68
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
69
+ return {
70
+ config: { ...DEFAULT_ACTIVITY_CONFIG },
71
+ raw: {},
72
+ error: '配置根节点必须是 JSON 对象',
73
+ };
74
+ }
75
+ const raw = parsed;
76
+ const config = { ...DEFAULT_ACTIVITY_CONFIG };
77
+ if (typeof raw.frames === 'string' && isPresetName(raw.frames))
78
+ config.frames = raw.frames;
79
+ if (Array.isArray(raw.customPhrases)) {
80
+ const phrases = raw.customPhrases.filter((s) => typeof s === 'string' && s.trim().length > 0);
81
+ if (phrases.length > 0)
82
+ config.customPhrases = phrases;
83
+ }
84
+ if (raw.customActions !== null && typeof raw.customActions === 'object' && !Array.isArray(raw.customActions)) {
85
+ const customActions = {};
86
+ for (const [key, value] of Object.entries(raw.customActions)) {
87
+ const actions = Array.isArray(value)
88
+ ? value.filter((entry) => typeof entry === 'string' && entry.trim().length > 0)
89
+ .map((entry) => entry.trim())
90
+ : [];
91
+ if (key.trim().length > 0 && actions.length > 0)
92
+ customActions[key.trim()] = actions;
93
+ }
94
+ if (Object.keys(customActions).length > 0)
95
+ config.customActions = customActions;
96
+ }
97
+ if (typeof raw.narrate === 'boolean')
98
+ config.narrate = raw.narrate;
99
+ if (typeof raw.debugLog === 'boolean')
100
+ config.debugLog = raw.debugLog;
101
+ if (typeof raw.contextWarnAt === 'number' && Number.isFinite(raw.contextWarnAt)
102
+ && raw.contextWarnAt >= 0 && raw.contextWarnAt <= 100) {
103
+ config.contextWarnAt = raw.contextWarnAt;
104
+ }
105
+ if (typeof raw.contextDangerAt === 'number' && Number.isFinite(raw.contextDangerAt)
106
+ && raw.contextDangerAt >= 0 && raw.contextDangerAt <= 100) {
107
+ config.contextDangerAt = raw.contextDangerAt;
108
+ }
109
+ if (typeof raw.showTokPerSec === 'boolean')
110
+ config.showTokPerSec = raw.showTokPerSec;
111
+ if (typeof raw.workRemindAt === 'number' && Number.isFinite(raw.workRemindAt)
112
+ && raw.workRemindAt >= 0 && raw.workRemindAt <= 24) {
113
+ config.workRemindAt = raw.workRemindAt;
114
+ }
115
+ if (raw.mode === 'lively' || raw.mode === 'minimal')
116
+ config.mode = raw.mode;
117
+ if (raw.features !== null && typeof raw.features === 'object' && !Array.isArray(raw.features)) {
118
+ const features = {};
119
+ for (const [key, value] of Object.entries(raw.features)) {
120
+ if (typeof value === 'boolean')
121
+ features[key] = value;
122
+ }
123
+ if (Object.keys(features).length > 0)
124
+ config.features = features;
125
+ }
126
+ return { config: normalizeThresholds(config), raw };
127
+ }
128
+ /**
129
+ * Whether a feature is on. Explicit `features` entries win; otherwise
130
+ * `minimal` mode turns everything off (except `cost`, always on).
131
+ * @param config - Parsed config.
132
+ * @param name - Feature flag name.
133
+ */
134
+ export function featureOn(config, name) {
135
+ const explicit = config.features?.[name];
136
+ if (typeof explicit === 'boolean')
137
+ return explicit;
138
+ if (name === 'cost')
139
+ return true;
140
+ return config.mode !== 'minimal';
141
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Working-activity indicator frame presets — the single home for every
3
+ * spinner animation, ported from the pi working-activity extension
4
+ * (`FRAME_PRESETS`) plus the dsh-tui additions (moon8 / rainbow / whale
5
+ * family / clock / traffic lights). Consumers (TUI row, Web slot) render the
6
+ * current frame next to the live working line; the preset name is chosen by
7
+ * the UI and persisted in `working-activity.json` (`frames` key).
8
+ *
9
+ * `\uFE0E` forces text rendering so Windows never paints the glyphs as color
10
+ * emoji (the green-block problem); emoji presets (moon8, clock, whales)
11
+ * deliberately omit it to keep colorful rendering on modern terminals.
12
+ * @module @deepseek-ai/dsh-working-activity/frames
13
+ */
14
+ /** One working-activity preset: the frame sequence and the per-frame interval. */
15
+ export interface FramePreset {
16
+ readonly frames: readonly string[];
17
+ readonly intervalMs: number;
18
+ }
19
+ /** Named working-activity frame presets, keyed by preset name (`claude`, `moon`, `sand`, ...). */
20
+ export declare const FRAME_PRESETS: Record<string, FramePreset>;
21
+ /** The default preset (moon8: the smoothest of the emoji moons). */
22
+ export declare const DEFAULT_PRESET = "moon8";
23
+ /** Every selectable preset name, `random` first (the pi selector order). */
24
+ export declare const PRESET_NAMES: readonly string[];
25
+ /**
26
+ * Whether `name` selects a known preset or `random`.
27
+ * @param name - Candidate preset name.
28
+ * @returns True when the name resolves to a preset.
29
+ */
30
+ export declare function isPresetName(name: string): boolean;
31
+ /**
32
+ * Resolve a preset name (`random` picks one per process).
33
+ * @param name - Preset name, or undefined for the default.
34
+ * @returns The matching preset; unknown or absent names fall back to the default.
35
+ */
36
+ export declare function resolvePreset(name: string | undefined): FramePreset;
37
+ //# sourceMappingURL=frames.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frames.d.ts","sourceRoot":"","sources":["../../src/frames.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,kFAAkF;AAClF,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;IAClC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAED,kGAAkG;AAClG,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAkFrD,CAAA;AAED,oEAAoE;AACpE,eAAO,MAAM,cAAc,UAAU,CAAA;AAErC,4EAA4E;AAC5E,eAAO,MAAM,YAAY,EAAE,SAAS,MAAM,EAA8C,CAAA;AAExF;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAElD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,WAAW,CASnE"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Working-activity indicator frame presets — the single home for every
3
+ * spinner animation, ported from the pi working-activity extension
4
+ * (`FRAME_PRESETS`) plus the dsh-tui additions (moon8 / rainbow / whale
5
+ * family / clock / traffic lights). Consumers (TUI row, Web slot) render the
6
+ * current frame next to the live working line; the preset name is chosen by
7
+ * the UI and persisted in `working-activity.json` (`frames` key).
8
+ *
9
+ * `\uFE0E` forces text rendering so Windows never paints the glyphs as color
10
+ * emoji (the green-block problem); emoji presets (moon8, clock, whales)
11
+ * deliberately omit it to keep colorful rendering on modern terminals.
12
+ * @module @deepseek-ai/dsh-working-activity/frames
13
+ */
14
+ /** Text-variant selector: keep symbols monochrome on Windows. */
15
+ const TE = '\uFE0E';
16
+ /** Named working-activity frame presets, keyed by preset name (`claude`, `moon`, `sand`, ...). */
17
+ export const FRAME_PRESETS = {
18
+ // Claude Code's real sequence: · ✢ * ✶ ✻ ✽ forward + backward.
19
+ claude: {
20
+ frames: ['·', `✢${TE}`, '*', `✶${TE}`, `✻${TE}`, `✽${TE}`, `✻${TE}`, `✶${TE}`, '*', `✢${TE}`],
21
+ intervalMs: 150,
22
+ },
23
+ star2: { frames: [`✶${TE}`, `✸${TE}`, `✹${TE}`, `✺${TE}`, `✹${TE}`, `✷${TE}`], intervalMs: 140 },
24
+ sand: {
25
+ frames: ['⠁', '⠂', '⠄', '⡀', '⡈', '⡐', '⡠', '⣀', '⣁', '⣂', '⣄', '⣌', '⣔', '⣤', '⣥', '⣦', '⣮', '⣶', '⣷', '⣿', '⡿', '⠿', '⢟', '⠟', '⡛', '⠛', '⠫', '⢋', '⠋', '⠍', '⡉', '⠉', '⠑', '⠡', '⢁'],
26
+ intervalMs: 120,
27
+ },
28
+ triangle: { frames: ['◢', '◣', '◤', '◥'], intervalMs: 180 },
29
+ box: { frames: ['▖', '▘', '▝', '▗'], intervalMs: 180 },
30
+ box2: { frames: ['▌', '▀', '▐', '▄'], intervalMs: 180 },
31
+ corners: { frames: ['◰', '◳', '◲', '◱'], intervalMs: 190 },
32
+ point: { frames: ['∙∙∙', '●∙∙', '∙●∙', '∙∙●', '∙∙∙'], intervalMs: 190 },
33
+ layer: { frames: ['-', '=', '≡'], intervalMs: 220 },
34
+ flip: { frames: ['_', '_', '_', '-', '`', '`', "'", '´', '-', '_', '_', '_'], intervalMs: 140 },
35
+ aesthetic: {
36
+ frames: ['▰▱▱▱▱▱▱', '▰▰▱▱▱▱▱', '▰▰▰▱▱▱▱', '▰▰▰▰▱▱▱', '▰▰▰▰▰▱▱', '▰▰▰▰▰▰▱', '▰▰▰▰▰▰▰', '▰▱▱▱▱▱▱'],
37
+ intervalMs: 140,
38
+ },
39
+ hamburger: { frames: ['☱', '☲', '☴'], intervalMs: 220 },
40
+ moon: { frames: ['◐', '◓', '◑', '◒'], intervalMs: 240 },
41
+ // kimi-code MoonLoader 同款:8 帧 emoji 月相,120ms 一帧,比半圆版更丝滑。
42
+ // 不带 \uFE0E:保留彩色 emoji 渲染(Windows Terminal 等现代终端效果最佳)。
43
+ moon8: { frames: ['🌑', '🌒', '🌓', '🌔', '🌕', '🌖', '🌗', '🌘'], intervalMs: 120 },
44
+ // 鲸鱼喷水:🐳 固定,水柱升起(· → | → ║)再回落,顶珠 ° 模拟水花(对齐 pi 版)。
45
+ 'whale-spout': {
46
+ frames: ['🐳 ', '🐳° ', '🐳|°', '🐳║°', '🐳|°', '🐳° ', '🐳 '],
47
+ intervalMs: 160,
48
+ },
49
+ // 鲸鱼转圈:🐳 + 环绕方向指示(逆时针转圈语义,对齐 pi 版)。
50
+ 'whale-spin': {
51
+ frames: ['🐳→', '🐳↘', '🐳↓', '🐳↙', '🐳←', '🐳↖', '🐳↑', '🐳↗'],
52
+ intervalMs: 150,
53
+ },
54
+ // 鲸鱼吐泡泡:🐳 固定,泡泡从头顶冒出(大泡 ○ 先行、小泡 ∘ 跟上)向右飘走。
55
+ 'whale-bubbles': {
56
+ frames: ['🐳 ', '🐳○ ', '🐳 ○', '🐳 ', '🐳∘ ', '🐳 ∘', '🐳 '],
57
+ intervalMs: 170,
58
+ },
59
+ // 时钟:12 个整点表盘循环(🕛 → 🕚),emoji 彩色渲染(同 moon8,不带 \uFE0E)。
60
+ clock: {
61
+ frames: ['🕛', '🕐', '🕑', '🕒', '🕓', '🕔', '🕕', '🕖', '🕗', '🕘', '🕙', '🕚'],
62
+ intervalMs: 300,
63
+ },
64
+ // 红绿灯:🔴 → 🟡 → 🟢 循环,一轮 1.2s,节奏从容。
65
+ traffic_lights: {
66
+ frames: ['🔴', '🟡', '🟢'],
67
+ intervalMs: 400,
68
+ },
69
+ comet: {
70
+ frames: ['● ', ' ● ', ' ● ', ' ● ', ' ●', ' ● ', ' ● ', ' ● '],
71
+ intervalMs: 160,
72
+ },
73
+ breathe: { frames: ['▁', '▃', '▅', '▇', '▅', '▃'], intervalMs: 210 },
74
+ dots: { frames: ['⣾', '⣷', '⣯', '⣟', '⡿', '⢿', '⣻', '⣽'], intervalMs: 140 },
75
+ arrow: { frames: ['←', '↖', '↑', '↗', '→', '↘', '↓', '↙'], intervalMs: 160 },
76
+ spark: { frames: ['·', '∘', '°', '✧', '°', '∘'], intervalMs: 240 },
77
+ bar: { frames: ['▏', '▎', '▍', '▌', '▋', '▊', '▉', '█', '▉', '▊', '▋', '▌', '▍', '▎'], intervalMs: 120 },
78
+ braille: { frames: ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'], intervalMs: 120 },
79
+ arc: { frames: ['◜', '◠', '◝', '◞', '◡', '◟'], intervalMs: 160 },
80
+ circle: { frames: ['◴', '◷', '◶', '◵'], intervalMs: 190 },
81
+ grow: { frames: ['.', 'o', 'O', '0', 'O', 'o'], intervalMs: 210 },
82
+ noise: { frames: ['▓', '▒', '░', '▒'], intervalMs: 160 },
83
+ bounce: { frames: ['⠁', '⠂', '⠄', '⡀', '⢀', '⠠', '⠐', '⠈'], intervalMs: 140 },
84
+ rainbow: {
85
+ frames: ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█', '▇', '▆', '▅', '▄', '▃', '▂'],
86
+ intervalMs: 120,
87
+ },
88
+ // 双端进度条往返(pi 版独有,补全并集)。
89
+ bar2: {
90
+ frames: [
91
+ '[ ]', '[= ]', '[== ]', '[=== ]', '[ ===]',
92
+ '[ ==]', '[ =]', '[ ]', '[ =]', '[ ==]',
93
+ '[ ===]', '[=== ]', '[== ]', '[= ]',
94
+ ],
95
+ intervalMs: 140,
96
+ },
97
+ dqpb: { frames: ['d', 'q', 'p', 'b'], intervalMs: 210 },
98
+ toggle: { frames: ['⊶', '⊷'], intervalMs: 300 },
99
+ };
100
+ /** The default preset (moon8: the smoothest of the emoji moons). */
101
+ export const DEFAULT_PRESET = 'moon8';
102
+ /** Every selectable preset name, `random` first (the pi selector order). */
103
+ export const PRESET_NAMES = ['random', ...Object.keys(FRAME_PRESETS)];
104
+ /**
105
+ * Whether `name` selects a known preset or `random`.
106
+ * @param name - Candidate preset name.
107
+ * @returns True when the name resolves to a preset.
108
+ */
109
+ export function isPresetName(name) {
110
+ return name === 'random' || Object.hasOwn(FRAME_PRESETS, name);
111
+ }
112
+ /**
113
+ * Resolve a preset name (`random` picks one per process).
114
+ * @param name - Preset name, or undefined for the default.
115
+ * @returns The matching preset; unknown or absent names fall back to the default.
116
+ */
117
+ export function resolvePreset(name) {
118
+ if (name === 'random') {
119
+ const names = Object.keys(FRAME_PRESETS);
120
+ // FRAME_PRESETS is non-empty by construction, so any random index is in
121
+ // range and the lookup always lands on a preset.
122
+ const pick = names[Math.floor(Math.random() * names.length)];
123
+ return FRAME_PRESETS[pick];
124
+ }
125
+ return FRAME_PRESETS[name ?? ''] ?? FRAME_PRESETS[DEFAULT_PRESET];
126
+ }
@@ -42,6 +42,19 @@ export type Config = {
42
42
  /** UI language: `auto` follows `DSH_TUI_LANG` → `~/.dsh-tui/lang.json` →
43
43
  * OS locale → zh; `zh`/`en` pin the copy directly. */
44
44
  lang?: 'auto' | 'zh' | 'en';
45
+ /** Default frame preset name (informational for UI consumers; the TUI
46
+ * resolves the persisted `frames` choice itself). */
47
+ frames?: string;
48
+ /** lively: full flourish (default) / minimal: functional labels only. */
49
+ mode?: 'lively' | 'minimal';
50
+ /** Per-feature switches; explicit values override `mode` defaults. */
51
+ features?: Record<string, boolean>;
52
+ /** Extra thinking phrases appended to the base pool. */
53
+ customPhrases?: string[];
54
+ /** Show an estimated tokens/s prefix while streaming. */
55
+ showTokPerSec?: boolean;
56
+ /** Work reminder after this many turn-hours (0 = off). */
57
+ workRemindAt?: number;
45
58
  };
46
59
  export declare const Config: Schemastery<Config>;
47
60
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAclD,mBAAmB,aAAa,CAAA;AAEhC,eAAO,MAAM,IAAI,qBAAqB,CAAA;AAEtC,wDAAwD;AACxD,MAAM,MAAM,MAAM,GAAG;IACnB,gEAAgE;IAChE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;;;mCAM+B;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,gFAAgF;IAChF,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;IACxC,oFAAoF;IACpF,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;2DACuD;IACvD,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAA;CAC5B,CAAA;AAMD,eAAO,MAAM,MAAM,EAAE,WAAW,CAAC,MAAM,CASrC,CAAA;AAsBF;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,MAAW,GAAG,IAAI,CAsI7D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAelD,mBAAmB,aAAa,CAAA;AAEhC,eAAO,MAAM,IAAI,qBAAqB,CAAA;AAEtC,wDAAwD;AACxD,MAAM,MAAM,MAAM,GAAG;IACnB,gEAAgE;IAChE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;;;mCAM+B;IAC/B,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,gFAAgF;IAChF,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,oEAAoE;IACpE,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;IACxC,oFAAoF;IACpF,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;2DACuD;IACvD,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAA;IAC3B;0DACsD;IACtD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,yEAAyE;IACzE,IAAI,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAA;IAC3B,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC,wDAAwD;IACxD,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;IACxB,yDAAyD;IACzD,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAMD,eAAO,MAAM,MAAM,EAAE,WAAW,CAAC,MAAM,CAerC,CAAA;AA4BF;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,MAAW,GAAG,IAAI,CAqJ7D"}
@@ -18,6 +18,7 @@ import z from '@deepseek-ai/schemastery';
18
18
  import { ActivityTracker } from './status.js';
19
19
  import { registerActivityEventType } from './registration.js';
20
20
  import { setLangOverride, t } from './lang.js';
21
+ import { DEFAULT_PRESET } from './frames.js';
21
22
  export const name = 'working-activity';
22
23
  // Explicit annotation: the inferred z.dict output references cosmokit's
23
24
  // Dict through a pnpm-virtual path, which is not portable in declaration
@@ -32,6 +33,12 @@ export const Config = z.object({
32
33
  customActions: z.dict(z.array(z.string())).default({}),
33
34
  narrate: z.boolean().default(true),
34
35
  lang: z.union(['auto', 'zh', 'en']).default('auto'),
36
+ frames: z.string().default(DEFAULT_PRESET),
37
+ mode: z.union(['lively', 'minimal']).default('lively'),
38
+ features: z.dict(z.boolean()).default({}),
39
+ customPhrases: z.array(z.string()).default([]),
40
+ showTokPerSec: z.boolean().default(false),
41
+ workRemindAt: z.number().min(0).max(24).default(0),
35
42
  });
36
43
  /**
37
44
  * Wire the working-activity plugin.
@@ -46,7 +53,8 @@ export function apply(ctx, config = {}) {
46
53
  // in processes where publishing itself is off. See registration.ts.
47
54
  registerActivityEventType();
48
55
  const resolved = {
49
- phrases: config.phrases ?? true,
56
+ // `mode: minimal` renders functional labels only (pi extension parity).
57
+ phrases: config.phrases ?? config.mode !== 'minimal',
50
58
  publish: config.publish ?? false,
51
59
  tickMs: config.tickMs ?? 500,
52
60
  publishIntervalMs: config.publishIntervalMs ?? 2000,
@@ -54,6 +62,12 @@ export function apply(ctx, config = {}) {
54
62
  narrate: config.narrate ?? true,
55
63
  lang: config.lang ?? 'auto',
56
64
  customActions: config.customActions ?? {},
65
+ frames: config.frames ?? DEFAULT_PRESET,
66
+ mode: config.mode ?? 'lively',
67
+ features: config.features ?? {},
68
+ customPhrases: config.customPhrases ?? [],
69
+ showTokPerSec: config.showTokPerSec ?? false,
70
+ workRemindAt: config.workRemindAt ?? 0,
57
71
  };
58
72
  // A pinned plugin-level language beats the env/file chain; releasing it on
59
73
  // dispose restores `auto` for any other composition in the process.
@@ -85,7 +99,15 @@ export function apply(ctx, config = {}) {
85
99
  const trackerFor = (session) => {
86
100
  let tracker = trackers.get(session);
87
101
  if (tracker === undefined) {
88
- tracker = new ActivityTracker({ phrases: resolved.phrases, detailLimit: resolved.detailLimit, showIdle: false }, Date.now, resolved.customActions);
102
+ tracker = new ActivityTracker({
103
+ phrases: resolved.phrases,
104
+ detailLimit: resolved.detailLimit,
105
+ showIdle: false,
106
+ features: resolved.features,
107
+ customPhrases: resolved.customPhrases,
108
+ showTokPerSec: resolved.showTokPerSec,
109
+ workRemindAt: resolved.workRemindAt,
110
+ }, Date.now, resolved.customActions);
89
111
  trackers.set(session, tracker);
90
112
  }
91
113
  return tracker;
@@ -62,6 +62,21 @@ declare const dict: {
62
62
  readonly zh: "{{count}} 工具";
63
63
  readonly en: "{{count}} tools";
64
64
  };
65
+ /** Consecutive-tool streak badge (replaces the old flame emoji). */
66
+ readonly 'tool-streak': {
67
+ readonly zh: "工具x{{count}}";
68
+ readonly en: "tool x{{count}}";
69
+ };
70
+ /** Subagent count in the done summary. */
71
+ readonly 'subagent-count': {
72
+ readonly zh: "子代理 {{count}} 个";
73
+ readonly en: "{{count}} subagents";
74
+ };
75
+ /** Work-reminder copy after `workRemindAt` turn-hours. */
76
+ readonly 'work-remind': {
77
+ readonly zh: "已连续工作 {{hours}} 小时,歇会儿?";
78
+ readonly en: "Worked {{hours}}h straight — take a break?";
79
+ };
65
80
  };
66
81
  export type I18nKey = keyof typeof dict;
67
82
  export type I18nParams = Record<string, string | number>;
@@ -1 +1 @@
1
- {"version":3,"file":"lang.d.ts","sourceRoot":"","sources":["../../src/lang.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAMH,MAAM,MAAM,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAE9B,+DAA+D;AAC/D,eAAO,MAAM,KAAK,uBAAwB,CAAA;AAK1C,QAAA,MAAM,IAAI;;;;;IAQR,wCAAwC;;;;;;;;;IAGxC,2DAA2D;;;;;IAE3D,mEAAmE;;;;;IAEnE,+BAA+B;;;;;;;;;;;;;CAOvB,CAAA;AAEV,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,IAAI,CAAA;AACvC,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAA;AAKxD,2EAA2E;AAC3E,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAEzD;AAED,qCAAqC;AACrC,wBAAgB,OAAO,IAAI,IAAI,CAK9B;AAED,wEAAwE;AACxE,wBAAgB,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,UAAe,GAAG,MAAM,CAM/D;AAED,gDAAgD;AAChD,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,IAAI,CAEpD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,IAAI,GAAG,SAAS,CAmB/C;AAKD;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAWvC"}
1
+ {"version":3,"file":"lang.d.ts","sourceRoot":"","sources":["../../src/lang.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAMH,MAAM,MAAM,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAE9B,+DAA+D;AAC/D,eAAO,MAAM,KAAK,uBAAwB,CAAA;AAK1C,QAAA,MAAM,IAAI;;;;;IAQR,wCAAwC;;;;;;;;;IAGxC,2DAA2D;;;;;IAE3D,mEAAmE;;;;;IAEnE,+BAA+B;;;;;;;;;;;;;IAO/B,oEAAoE;;;;;IAEpE,0CAA0C;;;;;IAE1C,0DAA0D;;;;;CAElD,CAAA;AAEV,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,IAAI,CAAA;AACvC,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAA;AAKxD,2EAA2E;AAC3E,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAEzD;AAED,qCAAqC;AACrC,wBAAgB,OAAO,IAAI,IAAI,CAK9B;AAED,wEAAwE;AACxE,wBAAgB,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,UAAe,GAAG,MAAM,CAM/D;AAED,gDAAgD;AAChD,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,IAAI,CAEpD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,IAAI,GAAG,SAAS,CAmB/C;AAKD;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAWvC"}
package/lib/types/lang.js CHANGED
@@ -50,6 +50,12 @@ const dict = {
50
50
  },
51
51
  'tool-count-one': { zh: '{{count}} 工具', en: '{{count}} tool' },
52
52
  'tool-count-many': { zh: '{{count}} 工具', en: '{{count}} tools' },
53
+ /** Consecutive-tool streak badge (replaces the old flame emoji). */
54
+ 'tool-streak': { zh: '工具x{{count}}', en: 'tool x{{count}}' },
55
+ /** Subagent count in the done summary. */
56
+ 'subagent-count': { zh: '子代理 {{count}} 个', en: '{{count}} subagents' },
57
+ /** Work-reminder copy after `workRemindAt` turn-hours. */
58
+ 'work-remind': { zh: '已连续工作 {{hours}} 小时,歇会儿?', en: 'Worked {{hours}}h straight — take a break?' },
53
59
  };
54
60
  /** Explicit pin set by plugin config or tests; `auto` restores the chain. */
55
61
  let override = 'auto';
@@ -55,6 +55,90 @@ export declare const EN_FAIL_PHRASES: readonly string[];
55
55
  export declare const EN_DONE_PHRASES: readonly string[];
56
56
  /** English night-owl phrases. */
57
57
  export declare const EN_NIGHT_PHRASES: readonly string[];
58
+ /** Rare easter-egg phrases (1/150 draw, pi `RARE_PHRASES`). */
59
+ export declare const RARE_PHRASES: readonly string[];
60
+ /** English rare easter-egg phrases. */
61
+ export declare const EN_RARE_PHRASES: readonly string[];
62
+ /** Chance of drawing a rare phrase per thinking rotation. */
63
+ export declare const RARE_CHANCE: number;
64
+ /** Weekend-greeting phrases (once per turn on Sat/Sun). */
65
+ export declare const WEEKEND_PHRASES: readonly string[];
66
+ /** English weekend greetings. */
67
+ export declare const EN_WEEKEND_PHRASES: readonly string[];
68
+ /** Whether `date` falls on a weekend. */
69
+ export declare function isWeekend(date: Date): boolean;
70
+ /** Holiday copy keyed by `MM-DD` (zh). */
71
+ export declare const HOLIDAY_PHRASES: Readonly<Record<string, readonly string[]>>;
72
+ /** English holiday copy keyed by `MM-DD`. */
73
+ export declare const EN_HOLIDAY_PHRASES: Readonly<Record<string, readonly string[]>>;
74
+ /** Lunar New Year copy (approximated by Gregorian dates, extended yearly). */
75
+ export declare const LUNAR_NEW_YEAR_PHRASES: readonly string[];
76
+ /** English Lunar New Year copy. */
77
+ export declare const EN_LUNAR_NEW_YEAR_PHRASES: readonly string[];
78
+ /** Gregorian dates marked as Lunar New Year (2025–2027, extend yearly). */
79
+ export declare const LUNAR_NEW_YEAR_DAYS: Readonly<Record<string, true>>;
80
+ /** Phrases shown after the user interrupts and the model resumes. */
81
+ export declare const CONTINUE_PHRASES: readonly string[];
82
+ /** English post-interruption phrases. */
83
+ export declare const EN_CONTINUE_PHRASES: readonly string[];
84
+ /** Phrases after a successful context compaction. */
85
+ export declare const COMPACT_PHRASES: readonly string[];
86
+ /** English post-compaction phrases. */
87
+ export declare const EN_COMPACT_PHRASES: readonly string[];
88
+ /** Urgent phrases when compaction is forced by overflow. */
89
+ export declare const OVERFLOW_PHRASES: readonly string[];
90
+ /** English overflow phrases. */
91
+ export declare const EN_OVERFLOW_PHRASES: readonly string[];
92
+ /** Phrases when retrying after a forced compaction. */
93
+ export declare const COMPACT_RETRY_PHRASES: readonly string[];
94
+ /** English compact-retry phrases. */
95
+ export declare const EN_COMPACT_RETRY_PHRASES: readonly string[];
96
+ /** Model-switch quips keyed by a lowercase substring of the model id. */
97
+ export declare const MODEL_QUIPS: Readonly<Record<string, readonly string[]>>;
98
+ /** English model-switch quips. */
99
+ export declare const EN_MODEL_QUIPS: Readonly<Record<string, readonly string[]>>;
100
+ /** Detect a holiday for `date`, Lunar New Year first. */
101
+ export declare function holidayPhrase(date: Date): string | null;
102
+ /** Pick a rare easter-egg phrase in the active language. */
103
+ export declare function rarePhrase(previous?: string): string;
104
+ /** Pick a weekend greeting in the active language. */
105
+ export declare function weekendPhrase(previous?: string): string;
106
+ /** Pick a post-interruption phrase in the active language. */
107
+ export declare function continuePhrase(): string;
108
+ /** Pick a post-compaction phrase in the active language. */
109
+ export declare function compactPhrase(): string;
110
+ /** Pick an overflow phrase in the active language. */
111
+ export declare function overflowPhrase(): string;
112
+ /** Pick a compact-retry phrase in the active language. */
113
+ export declare function compactRetryPhrase(): string;
114
+ /**
115
+ * Quip for a model id (substring match), or null when no pool matches.
116
+ * @param modelId - Model id, e.g. `deepseek-chat` or `gpt-4o`.
117
+ */
118
+ export declare function modelQuip(modelId: string): string | null;
119
+ /** Feature toggles for the lively phrase selector. */
120
+ export interface LivelyFeatures {
121
+ /** Allow rare 1/150 easter eggs. */
122
+ readonly rareEggs?: boolean;
123
+ /** Allow weekend greetings on Sat/Sun. */
124
+ readonly weekend?: boolean;
125
+ /** Allow date-matched holiday / Lunar New Year copy. */
126
+ readonly holidays?: boolean;
127
+ /** Mix night-owl copy between 00:00 and 06:00. */
128
+ readonly nightPhrases?: boolean;
129
+ }
130
+ /**
131
+ * Pick a thinking phrase with the pi extension's egg order: holiday first,
132
+ * then rare (1/150), then weekend, then the elapsed-time tiers with night
133
+ * mixing. Every egg is gated by `features` (defaults all on).
134
+ * @param elapsedMs - Milliseconds spent thinking in the current phase.
135
+ * @param previous - Previously shown phrase, to avoid repeats.
136
+ * @param night - Whether the night window is active (mixes night copy).
137
+ * @param now - Current wall-clock time (injectable for tests).
138
+ * @param features - Feature toggles; absent flags default to on.
139
+ * @param extra - User custom phrases appended to the base thinking pool.
140
+ */
141
+ export declare function livelyThinkingPhrase(elapsedMs: number, previous?: string, night?: boolean, now?: Date, features?: LivelyFeatures, extra?: readonly string[]): string;
58
142
  /** Common git tool names / bash commands containing `git `. */
59
143
  export declare const GIT_TOOL_RE: RegExp;
60
144
  /** Detect the 00:00–06:00 night window (local time). */
@@ -67,8 +151,9 @@ export declare function waitingPool(): readonly string[];
67
151
  * @param elapsedMs - Milliseconds spent thinking in the current phase.
68
152
  * @param previous - Previously shown phrase, to avoid repeats.
69
153
  * @param night - Mix night-owl copy into the pool.
154
+ * @param extra - User custom phrases appended to the base (non-tier) pool.
70
155
  */
71
- export declare function thinkingPhrase(elapsedMs: number, previous?: string, night?: boolean): string;
156
+ export declare function thinkingPhrase(elapsedMs: number, previous?: string, night?: boolean, extra?: readonly string[]): string;
72
157
  /** Pick a waiting phrase in the active language. */
73
158
  export declare function waitingPhrase(previous?: string): string;
74
159
  /** Pick a tool-failure phrase in the active language. */