@calliopelabs/cli 0.8.20 → 2.0.2
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/dist/agents/agent-config-loader.d.ts +60 -0
- package/dist/agents/agent-config-loader.d.ts.map +1 -0
- package/dist/agents/agent-config-loader.js +402 -0
- package/dist/agents/agent-config-loader.js.map +1 -0
- package/dist/agents/agent-config-presets.d.ts +10 -0
- package/dist/agents/agent-config-presets.d.ts.map +1 -0
- package/dist/agents/agent-config-presets.js +940 -0
- package/dist/agents/agent-config-presets.js.map +1 -0
- package/dist/agents/agent-config-types.d.ts +145 -0
- package/dist/agents/agent-config-types.d.ts.map +1 -0
- package/dist/agents/agent-config-types.js +12 -0
- package/dist/agents/agent-config-types.js.map +1 -0
- package/dist/{agterm → agents}/agent-detection.d.ts +1 -1
- package/dist/{agterm → agents}/agent-detection.d.ts.map +1 -1
- package/dist/{agterm → agents}/agent-detection.js +21 -5
- package/dist/agents/agent-detection.js.map +1 -0
- package/dist/agents/aggregator.d.ts +19 -0
- package/dist/agents/aggregator.d.ts.map +1 -0
- package/dist/agents/aggregator.js +141 -0
- package/dist/agents/aggregator.js.map +1 -0
- package/dist/{agterm → agents}/cli-backend.d.ts +1 -1
- package/dist/{agterm → agents}/cli-backend.d.ts.map +1 -1
- package/dist/{agterm → agents}/cli-backend.js +90 -12
- package/dist/agents/cli-backend.js.map +1 -0
- package/dist/agents/council-types.d.ts +113 -0
- package/dist/agents/council-types.d.ts.map +1 -0
- package/dist/agents/council-types.js +81 -0
- package/dist/agents/council-types.js.map +1 -0
- package/dist/agents/council.d.ts +107 -0
- package/dist/agents/council.d.ts.map +1 -0
- package/dist/agents/council.js +586 -0
- package/dist/agents/council.js.map +1 -0
- package/dist/agents/decomposer.d.ts +33 -0
- package/dist/agents/decomposer.d.ts.map +1 -0
- package/dist/agents/decomposer.js +138 -0
- package/dist/agents/decomposer.js.map +1 -0
- package/dist/agents/dynamic-tools.d.ts +52 -0
- package/dist/agents/dynamic-tools.d.ts.map +1 -0
- package/dist/agents/dynamic-tools.js +395 -0
- package/dist/agents/dynamic-tools.js.map +1 -0
- package/dist/agents/index.d.ts +29 -0
- package/dist/agents/index.d.ts.map +1 -0
- package/dist/agents/index.js +29 -0
- package/dist/agents/index.js.map +1 -0
- package/dist/agents/installer.d.ts +39 -0
- package/dist/agents/installer.d.ts.map +1 -0
- package/dist/agents/installer.js +205 -0
- package/dist/agents/installer.js.map +1 -0
- package/dist/{agterm → agents}/orchestrator.d.ts +7 -2
- package/dist/agents/orchestrator.d.ts.map +1 -0
- package/dist/{agterm → agents}/orchestrator.js +22 -2
- package/dist/agents/orchestrator.js.map +1 -0
- package/dist/agents/sdk-backend.d.ts +63 -0
- package/dist/agents/sdk-backend.d.ts.map +1 -0
- package/dist/agents/sdk-backend.js +489 -0
- package/dist/agents/sdk-backend.js.map +1 -0
- package/dist/agents/swarm-types.d.ts +83 -0
- package/dist/agents/swarm-types.d.ts.map +1 -0
- package/dist/agents/swarm-types.js +20 -0
- package/dist/agents/swarm-types.js.map +1 -0
- package/dist/agents/swarm.d.ts +74 -0
- package/dist/agents/swarm.d.ts.map +1 -0
- package/dist/agents/swarm.js +307 -0
- package/dist/agents/swarm.js.map +1 -0
- package/dist/{agterm → agents}/tools.d.ts +7 -5
- package/dist/agents/tools.d.ts.map +1 -0
- package/dist/agents/tools.js +776 -0
- package/dist/agents/tools.js.map +1 -0
- package/dist/{agterm → agents}/types.d.ts +14 -2
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/{agterm → agents}/types.js +2 -2
- package/dist/agents/types.js.map +1 -0
- package/dist/api-server.d.ts +26 -0
- package/dist/api-server.d.ts.map +1 -0
- package/dist/api-server.js +230 -0
- package/dist/api-server.js.map +1 -0
- package/dist/auto-checkpoint.d.ts +35 -0
- package/dist/auto-checkpoint.d.ts.map +1 -0
- package/dist/auto-checkpoint.js +143 -0
- package/dist/auto-checkpoint.js.map +1 -0
- package/dist/auto-compressor.d.ts +44 -0
- package/dist/auto-compressor.d.ts.map +1 -0
- package/dist/auto-compressor.js +145 -0
- package/dist/auto-compressor.js.map +1 -0
- package/dist/background-jobs.d.ts +45 -0
- package/dist/background-jobs.d.ts.map +1 -0
- package/dist/background-jobs.js +122 -0
- package/dist/background-jobs.js.map +1 -0
- package/dist/bin.d.ts +6 -2
- package/dist/bin.d.ts.map +1 -1
- package/dist/bin.js +127 -24
- package/dist/bin.js.map +1 -1
- package/dist/checkpoint.d.ts +49 -0
- package/dist/checkpoint.d.ts.map +1 -0
- package/dist/checkpoint.js +219 -0
- package/dist/checkpoint.js.map +1 -0
- package/dist/circuit-breaker/breaker.d.ts +80 -0
- package/dist/circuit-breaker/breaker.d.ts.map +1 -0
- package/dist/circuit-breaker/breaker.js +408 -0
- package/dist/circuit-breaker/breaker.js.map +1 -0
- package/dist/circuit-breaker/defaults.d.ts +8 -0
- package/dist/circuit-breaker/defaults.d.ts.map +1 -0
- package/dist/circuit-breaker/defaults.js +35 -0
- package/dist/circuit-breaker/defaults.js.map +1 -0
- package/dist/circuit-breaker/index.d.ts +9 -0
- package/dist/circuit-breaker/index.d.ts.map +1 -0
- package/dist/circuit-breaker/index.js +8 -0
- package/dist/circuit-breaker/index.js.map +1 -0
- package/dist/circuit-breaker/types.d.ts +77 -0
- package/dist/circuit-breaker/types.d.ts.map +1 -0
- package/dist/circuit-breaker/types.js +8 -0
- package/dist/circuit-breaker/types.js.map +1 -0
- package/dist/circuit-breaker.d.ts +8 -0
- package/dist/circuit-breaker.d.ts.map +1 -0
- package/dist/circuit-breaker.js +7 -0
- package/dist/circuit-breaker.js.map +1 -0
- package/dist/cli/agent.d.ts +9 -0
- package/dist/cli/agent.d.ts.map +1 -0
- package/dist/cli/agent.js +262 -0
- package/dist/cli/agent.js.map +1 -0
- package/dist/cli/commands.d.ts +12 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/{cli.js → cli/commands.js} +285 -422
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +222 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/types.d.ts +30 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +20 -0
- package/dist/cli/types.js.map +1 -0
- package/dist/companions.d.ts +54 -0
- package/dist/companions.d.ts.map +1 -0
- package/dist/companions.js +440 -0
- package/dist/companions.js.map +1 -0
- package/dist/config.d.ts +23 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +95 -22
- package/dist/config.js.map +1 -1
- package/dist/diff.d.ts +27 -0
- package/dist/diff.d.ts.map +1 -1
- package/dist/diff.js +415 -10
- package/dist/diff.js.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +20 -11
- package/dist/errors.js.map +1 -1
- package/dist/git-status.d.ts +23 -0
- package/dist/git-status.d.ts.map +1 -0
- package/dist/git-status.js +92 -0
- package/dist/git-status.js.map +1 -0
- package/dist/headless.d.ts +25 -0
- package/dist/headless.d.ts.map +1 -0
- package/dist/headless.js +182 -0
- package/dist/headless.js.map +1 -0
- package/dist/hud/api.d.ts +35 -0
- package/dist/hud/api.d.ts.map +1 -0
- package/dist/hud/api.js +448 -0
- package/dist/hud/api.js.map +1 -0
- package/dist/hud/palettes.d.ts +9 -0
- package/dist/hud/palettes.d.ts.map +1 -0
- package/dist/hud/palettes.js +280 -0
- package/dist/hud/palettes.js.map +1 -0
- package/dist/hud/skins.d.ts +12 -0
- package/dist/hud/skins.d.ts.map +1 -0
- package/dist/hud/skins.js +365 -0
- package/dist/hud/skins.js.map +1 -0
- package/dist/hud/theme-packs/api.d.ts +51 -0
- package/dist/hud/theme-packs/api.d.ts.map +1 -0
- package/dist/hud/theme-packs/api.js +145 -0
- package/dist/hud/theme-packs/api.js.map +1 -0
- package/dist/hud/theme-packs/index.d.ts +18 -0
- package/dist/hud/theme-packs/index.d.ts.map +1 -0
- package/dist/hud/theme-packs/index.js +38 -0
- package/dist/hud/theme-packs/index.js.map +1 -0
- package/dist/hud/theme-packs/types.d.ts +29 -0
- package/dist/hud/theme-packs/types.d.ts.map +1 -0
- package/dist/hud/theme-packs/types.js +9 -0
- package/dist/hud/theme-packs/types.js.map +1 -0
- package/dist/hud/types.d.ts +182 -0
- package/dist/hud/types.d.ts.map +1 -0
- package/dist/hud/types.js +7 -0
- package/dist/hud/types.js.map +1 -0
- package/dist/idle-eviction.d.ts +34 -0
- package/dist/idle-eviction.d.ts.map +1 -0
- package/dist/idle-eviction.js +78 -0
- package/dist/idle-eviction.js.map +1 -0
- package/dist/index.d.ts +9 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -3
- package/dist/index.js.map +1 -1
- package/dist/iteration-ledger.d.ts +105 -0
- package/dist/iteration-ledger.d.ts.map +1 -0
- package/dist/iteration-ledger.js +237 -0
- package/dist/iteration-ledger.js.map +1 -0
- package/dist/markdown.d.ts.map +1 -1
- package/dist/markdown.js +1 -27
- package/dist/markdown.js.map +1 -1
- package/dist/mcp.d.ts +35 -0
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +291 -7
- package/dist/mcp.js.map +1 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +12 -2
- package/dist/memory.js.map +1 -1
- package/dist/model-detection.d.ts +5 -0
- package/dist/model-detection.d.ts.map +1 -1
- package/dist/model-detection.js +278 -10
- package/dist/model-detection.js.map +1 -1
- package/dist/model-router.d.ts.map +1 -1
- package/dist/model-router.js +33 -11
- package/dist/model-router.js.map +1 -1
- package/dist/plugins.d.ts +8 -0
- package/dist/plugins.d.ts.map +1 -1
- package/dist/plugins.js +97 -6
- package/dist/plugins.js.map +1 -1
- package/dist/providers/anthropic.d.ts +10 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +221 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/bedrock.d.ts +17 -0
- package/dist/providers/bedrock.d.ts.map +1 -0
- package/dist/providers/bedrock.js +574 -0
- package/dist/providers/bedrock.js.map +1 -0
- package/dist/providers/compat.d.ts +13 -0
- package/dist/providers/compat.d.ts.map +1 -0
- package/dist/providers/compat.js +202 -0
- package/dist/providers/compat.js.map +1 -0
- package/dist/providers/google.d.ts +10 -0
- package/dist/providers/google.d.ts.map +1 -0
- package/dist/providers/google.js +203 -0
- package/dist/providers/google.js.map +1 -0
- package/dist/providers/index.d.ts +23 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +145 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/ollama.d.ts +17 -0
- package/dist/providers/ollama.d.ts.map +1 -0
- package/dist/providers/ollama.js +289 -0
- package/dist/providers/ollama.js.map +1 -0
- package/dist/providers/openai.d.ts +121 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +485 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/types.d.ts +63 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +164 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/sandbox-native.d.ts +59 -0
- package/dist/sandbox-native.d.ts.map +1 -0
- package/dist/sandbox-native.js +292 -0
- package/dist/sandbox-native.js.map +1 -0
- package/dist/sandbox.d.ts +2 -2
- package/dist/sandbox.d.ts.map +1 -1
- package/dist/sandbox.js +59 -13
- package/dist/sandbox.js.map +1 -1
- package/dist/scope.d.ts +3 -1
- package/dist/scope.d.ts.map +1 -1
- package/dist/scope.js +13 -1
- package/dist/scope.js.map +1 -1
- package/dist/session-timeout.d.ts +31 -0
- package/dist/session-timeout.d.ts.map +1 -0
- package/dist/session-timeout.js +100 -0
- package/dist/session-timeout.js.map +1 -0
- package/dist/setup.d.ts.map +1 -1
- package/dist/setup.js +29 -17
- package/dist/setup.js.map +1 -1
- package/dist/smart-router.d.ts +73 -0
- package/dist/smart-router.d.ts.map +1 -0
- package/dist/smart-router.js +332 -0
- package/dist/smart-router.js.map +1 -0
- package/dist/storage.d.ts +19 -0
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +164 -1
- package/dist/storage.js.map +1 -1
- package/dist/streaming.d.ts +4 -0
- package/dist/streaming.d.ts.map +1 -1
- package/dist/streaming.js +12 -0
- package/dist/streaming.js.map +1 -1
- package/dist/styles.d.ts +32 -0
- package/dist/styles.d.ts.map +1 -1
- package/dist/styles.js +91 -0
- package/dist/styles.js.map +1 -1
- package/dist/summarization.d.ts +1 -1
- package/dist/summarization.js +4 -4
- package/dist/summarization.js.map +1 -1
- package/dist/terminal-image.d.ts +115 -0
- package/dist/terminal-image.d.ts.map +1 -0
- package/dist/terminal-image.js +766 -0
- package/dist/terminal-image.js.map +1 -0
- package/dist/terminal-recording.d.ts +55 -0
- package/dist/terminal-recording.d.ts.map +1 -0
- package/dist/terminal-recording.js +182 -0
- package/dist/terminal-recording.js.map +1 -0
- package/dist/themes.d.ts +19 -35
- package/dist/themes.d.ts.map +1 -1
- package/dist/themes.js +101 -210
- package/dist/themes.js.map +1 -1
- package/dist/tmux.d.ts +35 -0
- package/dist/tmux.d.ts.map +1 -0
- package/dist/tmux.js +106 -0
- package/dist/tmux.js.map +1 -0
- package/dist/tools.d.ts +3 -3
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +587 -45
- package/dist/tools.js.map +1 -1
- package/dist/trust.d.ts +53 -0
- package/dist/trust.d.ts.map +1 -0
- package/dist/trust.js +154 -0
- package/dist/trust.js.map +1 -0
- package/dist/types.d.ts +7 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +70 -32
- package/dist/types.js.map +1 -1
- package/dist/ui/agent.d.ts +61 -0
- package/dist/ui/agent.d.ts.map +1 -0
- package/dist/ui/agent.js +768 -0
- package/dist/ui/agent.js.map +1 -0
- package/dist/ui/chat-input.d.ts +32 -0
- package/dist/ui/chat-input.d.ts.map +1 -0
- package/dist/ui/chat-input.js +355 -0
- package/dist/ui/chat-input.js.map +1 -0
- package/dist/ui/commands.d.ts +92 -0
- package/dist/ui/commands.d.ts.map +1 -0
- package/dist/ui/commands.js +3006 -0
- package/dist/ui/commands.js.map +1 -0
- package/dist/ui/completions.d.ts +22 -0
- package/dist/ui/completions.d.ts.map +1 -0
- package/dist/ui/completions.js +215 -0
- package/dist/ui/completions.js.map +1 -0
- package/dist/ui/components.d.ts +38 -0
- package/dist/ui/components.d.ts.map +1 -0
- package/dist/ui/components.js +422 -0
- package/dist/ui/components.js.map +1 -0
- package/dist/ui/context.d.ts +12 -0
- package/dist/ui/context.d.ts.map +1 -0
- package/dist/ui/context.js +102 -0
- package/dist/ui/context.js.map +1 -0
- package/dist/ui/error-boundary.d.ts +33 -0
- package/dist/ui/error-boundary.d.ts.map +1 -0
- package/dist/ui/error-boundary.js +94 -0
- package/dist/ui/error-boundary.js.map +1 -0
- package/dist/ui/frame.d.ts +13 -0
- package/dist/ui/frame.d.ts.map +1 -0
- package/dist/ui/frame.js +89 -0
- package/dist/ui/frame.js.map +1 -0
- package/dist/ui/index.d.ts +12 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +928 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/messages.d.ts +19 -0
- package/dist/ui/messages.d.ts.map +1 -0
- package/dist/ui/messages.js +181 -0
- package/dist/ui/messages.js.map +1 -0
- package/dist/ui/modals.d.ts +52 -0
- package/dist/ui/modals.d.ts.map +1 -0
- package/dist/ui/modals.js +204 -0
- package/dist/ui/modals.js.map +1 -0
- package/dist/ui/pack-picker.d.ts +12 -0
- package/dist/ui/pack-picker.d.ts.map +1 -0
- package/dist/ui/pack-picker.js +101 -0
- package/dist/ui/pack-picker.js.map +1 -0
- package/dist/ui/status-bar.d.ts +20 -0
- package/dist/ui/status-bar.d.ts.map +1 -0
- package/dist/ui/status-bar.js +41 -0
- package/dist/ui/status-bar.js.map +1 -0
- package/dist/ui/theme-picker.d.ts +24 -0
- package/dist/ui/theme-picker.d.ts.map +1 -0
- package/dist/ui/theme-picker.js +190 -0
- package/dist/ui/theme-picker.js.map +1 -0
- package/dist/ui/types.d.ts +62 -0
- package/dist/ui/types.d.ts.map +1 -0
- package/dist/ui/types.js +7 -0
- package/dist/ui/types.js.map +1 -0
- package/dist/version-check.d.ts.map +1 -1
- package/dist/version-check.js +1 -9
- package/dist/version-check.js.map +1 -1
- package/package.json +8 -3
- package/dist/agterm/agent-detection.js.map +0 -1
- package/dist/agterm/cli-backend.js.map +0 -1
- package/dist/agterm/index.d.ts +0 -12
- package/dist/agterm/index.d.ts.map +0 -1
- package/dist/agterm/index.js +0 -15
- package/dist/agterm/index.js.map +0 -1
- package/dist/agterm/orchestrator.d.ts.map +0 -1
- package/dist/agterm/orchestrator.js.map +0 -1
- package/dist/agterm/tools.d.ts.map +0 -1
- package/dist/agterm/tools.js +0 -278
- package/dist/agterm/tools.js.map +0 -1
- package/dist/agterm/types.d.ts.map +0 -1
- package/dist/agterm/types.js.map +0 -1
- package/dist/cli.d.ts +0 -14
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/providers.d.ts +0 -51
- package/dist/providers.d.ts.map +0 -1
- package/dist/providers.js +0 -1146
- package/dist/providers.js.map +0 -1
- package/dist/ui-cli.d.ts +0 -17
- package/dist/ui-cli.d.ts.map +0 -1
- package/dist/ui-cli.js +0 -3730
- package/dist/ui-cli.js.map +0 -1
|
@@ -0,0 +1,766 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Terminal Image & Banner Rendering
|
|
3
|
+
*
|
|
4
|
+
* Provides terminal capability detection and text-based banner rendering
|
|
5
|
+
* with ANSI colors. No external image processing dependencies required.
|
|
6
|
+
*
|
|
7
|
+
* Extracted from scripts/image-poc.mjs detection logic, adapted for
|
|
8
|
+
* integration with the HUD skin system.
|
|
9
|
+
*
|
|
10
|
+
* @see scripts/image-poc.mjs - Full image rendering POC (requires sharp)
|
|
11
|
+
*/
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Terminal Capability Detection
|
|
14
|
+
// ============================================================================
|
|
15
|
+
/**
|
|
16
|
+
* Detect the best image rendering mode for the current terminal.
|
|
17
|
+
*
|
|
18
|
+
* Detection rules (in priority order):
|
|
19
|
+
* 1. ITERM_SESSION_ID / LC_TERMINAL=iTerm2 / TERM_PROGRAM=iTerm.app|WezTerm -> iterm2
|
|
20
|
+
* 2. KITTY_PID / TERM=xterm-kitty / GHOSTTY_RESOURCES_DIR -> kitty
|
|
21
|
+
* 3. COLORTERM=truecolor|24bit -> halfblock
|
|
22
|
+
* 4. Otherwise -> ascii
|
|
23
|
+
*/
|
|
24
|
+
export function detectBestMode() {
|
|
25
|
+
const env = process.env;
|
|
26
|
+
// iTerm2 protocol support
|
|
27
|
+
if (env.ITERM_SESSION_ID ||
|
|
28
|
+
env.LC_TERMINAL === 'iTerm2' ||
|
|
29
|
+
env.TERM_PROGRAM === 'iTerm.app' ||
|
|
30
|
+
env.TERM_PROGRAM === 'WezTerm') {
|
|
31
|
+
return 'iterm2';
|
|
32
|
+
}
|
|
33
|
+
// Kitty graphics protocol
|
|
34
|
+
if (env.KITTY_PID ||
|
|
35
|
+
env.TERM === 'xterm-kitty' ||
|
|
36
|
+
env.GHOSTTY_RESOURCES_DIR) {
|
|
37
|
+
return 'kitty';
|
|
38
|
+
}
|
|
39
|
+
// Truecolor support -> half-block rendering
|
|
40
|
+
if (env.COLORTERM === 'truecolor' || env.COLORTERM === '24bit') {
|
|
41
|
+
return 'halfblock';
|
|
42
|
+
}
|
|
43
|
+
// Fallback to ASCII
|
|
44
|
+
return 'ascii';
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Returns terminal image capabilities summary.
|
|
48
|
+
*/
|
|
49
|
+
export function getTerminalImageInfo() {
|
|
50
|
+
const env = process.env;
|
|
51
|
+
return {
|
|
52
|
+
mode: detectBestMode(),
|
|
53
|
+
truecolor: env.COLORTERM === 'truecolor' || env.COLORTERM === '24bit',
|
|
54
|
+
width: parseInt(env.COLUMNS || '', 10) || process.stdout.columns || 80,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// ============================================================================
|
|
58
|
+
// ANSI Color Helpers
|
|
59
|
+
// ============================================================================
|
|
60
|
+
const ESC = '\x1b[';
|
|
61
|
+
const RESET = `${ESC}0m`;
|
|
62
|
+
/** Apply 256-color or truecolor foreground based on hex string (#RRGGBB) */
|
|
63
|
+
export function colorFg(text, hex) {
|
|
64
|
+
const rgb = hexToRgb(hex);
|
|
65
|
+
if (!rgb)
|
|
66
|
+
return text;
|
|
67
|
+
return `${ESC}38;2;${rgb.r};${rgb.g};${rgb.b}m${text}${RESET}`;
|
|
68
|
+
}
|
|
69
|
+
/** Apply truecolor background based on hex string (#RRGGBB) */
|
|
70
|
+
export function colorBg(text, hex) {
|
|
71
|
+
const rgb = hexToRgb(hex);
|
|
72
|
+
if (!rgb)
|
|
73
|
+
return text;
|
|
74
|
+
return `${ESC}48;2;${rgb.r};${rgb.g};${rgb.b}m${text}${RESET}`;
|
|
75
|
+
}
|
|
76
|
+
/** Bold text */
|
|
77
|
+
export function bold(text) {
|
|
78
|
+
return `${ESC}1m${text}${RESET}`;
|
|
79
|
+
}
|
|
80
|
+
/** Dim text */
|
|
81
|
+
export function dim(text) {
|
|
82
|
+
return `${ESC}2m${text}${RESET}`;
|
|
83
|
+
}
|
|
84
|
+
function hexToRgb(hex) {
|
|
85
|
+
const match = hex.match(/^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/);
|
|
86
|
+
if (!match)
|
|
87
|
+
return null;
|
|
88
|
+
return {
|
|
89
|
+
r: parseInt(match[1], 16),
|
|
90
|
+
g: parseInt(match[2], 16),
|
|
91
|
+
b: parseInt(match[3], 16),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
// ============================================================================
|
|
95
|
+
// Banner Rendering
|
|
96
|
+
// ============================================================================
|
|
97
|
+
/**
|
|
98
|
+
* Render a text-based decorative banner using box-drawing characters.
|
|
99
|
+
*
|
|
100
|
+
* Uses the detected terminal mode to choose appropriate decoration level:
|
|
101
|
+
* - iterm2/kitty/halfblock: Full Unicode box drawing with color
|
|
102
|
+
* - ascii: Simple ASCII borders
|
|
103
|
+
* - none: Just the text
|
|
104
|
+
*/
|
|
105
|
+
export function renderBanner(text, mode) {
|
|
106
|
+
const m = mode ?? detectBestMode();
|
|
107
|
+
const lines = [];
|
|
108
|
+
if (m === 'none') {
|
|
109
|
+
lines.push(text);
|
|
110
|
+
return lines.join('\n');
|
|
111
|
+
}
|
|
112
|
+
const isUnicode = m !== 'ascii';
|
|
113
|
+
const h = isUnicode ? '\u2500' : '-';
|
|
114
|
+
const v = isUnicode ? '\u2502' : '|';
|
|
115
|
+
const tl = isUnicode ? '\u256D' : '+';
|
|
116
|
+
const tr = isUnicode ? '\u256E' : '+';
|
|
117
|
+
const bl = isUnicode ? '\u2570' : '+';
|
|
118
|
+
const br = isUnicode ? '\u256F' : '+';
|
|
119
|
+
const dh = isUnicode ? '\u2550' : '=';
|
|
120
|
+
const width = Math.max(text.length + 4, 40);
|
|
121
|
+
const padding = width - text.length - 2;
|
|
122
|
+
const padLeft = Math.floor(padding / 2);
|
|
123
|
+
const padRight = padding - padLeft;
|
|
124
|
+
// Top border with decorative double line
|
|
125
|
+
lines.push(` ${tl}${dh}${h.repeat(width - 2)}${dh}${tr}`);
|
|
126
|
+
// Content line
|
|
127
|
+
lines.push(` ${v}${' '.repeat(padLeft)} ${text} ${' '.repeat(padRight)}${v}`);
|
|
128
|
+
// Bottom border
|
|
129
|
+
lines.push(` ${bl}${dh}${h.repeat(width - 2)}${dh}${br}`);
|
|
130
|
+
return lines.join('\n');
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Render pre-formatted ASCII art lines with optional per-line color function.
|
|
134
|
+
*
|
|
135
|
+
* @param art - Array of pre-formatted ASCII art lines
|
|
136
|
+
* @param colorFn - Optional function to apply ANSI color to each line
|
|
137
|
+
* @returns Rendered string with newlines
|
|
138
|
+
*/
|
|
139
|
+
export function renderAsciiArt(art, colorFn) {
|
|
140
|
+
if (!art.length)
|
|
141
|
+
return '';
|
|
142
|
+
const lines = art.map((line, i) => colorFn ? colorFn(line, i) : line);
|
|
143
|
+
return lines.join('\n');
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Render a skin's banner art with full decorative frame and ANSI colors.
|
|
147
|
+
*
|
|
148
|
+
* This is the main function used by the /banner command. It takes banner art
|
|
149
|
+
* lines, wraps them in a frame appropriate for the terminal mode, and applies
|
|
150
|
+
* the given palette color.
|
|
151
|
+
*
|
|
152
|
+
* @param art - Banner art lines from skin.banner.art
|
|
153
|
+
* @param color - Hex color for the banner frame (from palette)
|
|
154
|
+
* @param tagline - Optional tagline to display below the art
|
|
155
|
+
* @param mode - Override image mode detection
|
|
156
|
+
*/
|
|
157
|
+
export function renderSkinBanner(art, color, tagline, mode) {
|
|
158
|
+
const m = mode ?? detectBestMode();
|
|
159
|
+
const lines = [];
|
|
160
|
+
const isUnicode = m !== 'ascii' && m !== 'none';
|
|
161
|
+
// Determine max width from art
|
|
162
|
+
const artWidths = art.map(line => stripAnsi(line).length);
|
|
163
|
+
const maxArtWidth = Math.max(...artWidths, 30);
|
|
164
|
+
const frameWidth = maxArtWidth + 4;
|
|
165
|
+
// Box chars
|
|
166
|
+
const h = isUnicode ? '\u2500' : '-';
|
|
167
|
+
const v = isUnicode ? '\u2502' : '|';
|
|
168
|
+
const tl = isUnicode ? '\u256D' : '+';
|
|
169
|
+
const tr = isUnicode ? '\u256E' : '+';
|
|
170
|
+
const bl = isUnicode ? '\u2570' : '+';
|
|
171
|
+
const br = isUnicode ? '\u256F' : '+';
|
|
172
|
+
const applyColor = (text) => color ? colorFg(text, color) : text;
|
|
173
|
+
// Top frame
|
|
174
|
+
lines.push(applyColor(`${tl}${h.repeat(frameWidth)}${tr}`));
|
|
175
|
+
// Empty line for spacing
|
|
176
|
+
lines.push(applyColor(`${v}${' '.repeat(frameWidth)}${v}`));
|
|
177
|
+
// Art lines (centered in frame)
|
|
178
|
+
for (const artLine of art) {
|
|
179
|
+
const visLen = stripAnsi(artLine).length;
|
|
180
|
+
const totalPad = frameWidth - visLen;
|
|
181
|
+
const padL = Math.floor(totalPad / 2);
|
|
182
|
+
const padR = totalPad - padL;
|
|
183
|
+
lines.push(applyColor(v) + ' '.repeat(padL) + artLine + ' '.repeat(padR) + applyColor(v));
|
|
184
|
+
}
|
|
185
|
+
// Empty line for spacing
|
|
186
|
+
lines.push(applyColor(`${v}${' '.repeat(frameWidth)}${v}`));
|
|
187
|
+
// Tagline if present
|
|
188
|
+
if (tagline) {
|
|
189
|
+
const tagVisLen = tagline.length;
|
|
190
|
+
const totalPad = frameWidth - tagVisLen;
|
|
191
|
+
const padL = Math.floor(totalPad / 2);
|
|
192
|
+
const padR = totalPad - padL;
|
|
193
|
+
lines.push(applyColor(v) + ' '.repeat(padL) + dim(tagline) + ' '.repeat(padR) + applyColor(v));
|
|
194
|
+
lines.push(applyColor(`${v}${' '.repeat(frameWidth)}${v}`));
|
|
195
|
+
}
|
|
196
|
+
// Bottom frame
|
|
197
|
+
lines.push(applyColor(`${bl}${h.repeat(frameWidth)}${br}`));
|
|
198
|
+
return lines.join('\n');
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Render a skin's banner using coloredArt from splash config.
|
|
202
|
+
* Each line gets its own hex color. Falls back to single-color rendering.
|
|
203
|
+
*
|
|
204
|
+
* @param coloredArt - Array of {text, color} objects
|
|
205
|
+
* @param tagline - Optional tagline
|
|
206
|
+
* @param mode - Override image mode detection
|
|
207
|
+
*/
|
|
208
|
+
export function renderColoredBanner(coloredArt, tagline, mode) {
|
|
209
|
+
const m = mode ?? detectBestMode();
|
|
210
|
+
const lines = [];
|
|
211
|
+
const isUnicode = m !== 'ascii' && m !== 'none';
|
|
212
|
+
// Determine max width from art
|
|
213
|
+
const artWidths = coloredArt.map(line => stripAnsi(line.text).length);
|
|
214
|
+
const maxArtWidth = Math.max(...artWidths, 30);
|
|
215
|
+
const frameWidth = maxArtWidth + 4;
|
|
216
|
+
const h = isUnicode ? '\u2500' : '-';
|
|
217
|
+
const v = isUnicode ? '\u2502' : '|';
|
|
218
|
+
const tl = isUnicode ? '\u256D' : '+';
|
|
219
|
+
const tr = isUnicode ? '\u256E' : '+';
|
|
220
|
+
const bl = isUnicode ? '\u2570' : '+';
|
|
221
|
+
const br = isUnicode ? '\u256F' : '+';
|
|
222
|
+
// Use the first line's color for the frame, or white
|
|
223
|
+
const frameColor = coloredArt[0]?.color;
|
|
224
|
+
const applyFrame = (text) => frameColor ? colorFg(text, frameColor) : text;
|
|
225
|
+
// Top frame
|
|
226
|
+
lines.push(applyFrame(`${tl}${h.repeat(frameWidth)}${tr}`));
|
|
227
|
+
lines.push(applyFrame(`${v}${' '.repeat(frameWidth)}${v}`));
|
|
228
|
+
// Art lines with per-line color
|
|
229
|
+
for (const { text, color } of coloredArt) {
|
|
230
|
+
const visLen = stripAnsi(text).length;
|
|
231
|
+
const totalPad = frameWidth - visLen;
|
|
232
|
+
const padL = Math.floor(totalPad / 2);
|
|
233
|
+
const padR = totalPad - padL;
|
|
234
|
+
lines.push(applyFrame(v) + ' '.repeat(padL) + colorFg(text, color) + ' '.repeat(padR) + applyFrame(v));
|
|
235
|
+
}
|
|
236
|
+
lines.push(applyFrame(`${v}${' '.repeat(frameWidth)}${v}`));
|
|
237
|
+
if (tagline) {
|
|
238
|
+
const tagVisLen = tagline.length;
|
|
239
|
+
const totalPad = frameWidth - tagVisLen;
|
|
240
|
+
const padL = Math.floor(totalPad / 2);
|
|
241
|
+
const padR = totalPad - padL;
|
|
242
|
+
lines.push(applyFrame(v) + ' '.repeat(padL) + dim(tagline) + ' '.repeat(padR) + applyFrame(v));
|
|
243
|
+
lines.push(applyFrame(`${v}${' '.repeat(frameWidth)}${v}`));
|
|
244
|
+
}
|
|
245
|
+
lines.push(applyFrame(`${bl}${h.repeat(frameWidth)}${br}`));
|
|
246
|
+
return lines.join('\n');
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Render a splash animation to stdout (pre-Ink, raw terminal output).
|
|
250
|
+
* Returns a promise that resolves when animation completes.
|
|
251
|
+
* Skippable by any keypress if process.stdin is available.
|
|
252
|
+
*/
|
|
253
|
+
export async function renderSplashAnimation(art, animation, speed = 50, color) {
|
|
254
|
+
const applyColor = (text) => color ? colorFg(text, color) : text;
|
|
255
|
+
switch (animation) {
|
|
256
|
+
case 'scan-lines': {
|
|
257
|
+
// Reveal line by line from top
|
|
258
|
+
for (const line of art) {
|
|
259
|
+
console.log(applyColor(line));
|
|
260
|
+
await delay(speed);
|
|
261
|
+
}
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
case 'typewriter': {
|
|
265
|
+
// Type each line character by character
|
|
266
|
+
for (const line of art) {
|
|
267
|
+
const colored = applyColor(line);
|
|
268
|
+
process.stdout.write(colored);
|
|
269
|
+
process.stdout.write('\n');
|
|
270
|
+
await delay(speed);
|
|
271
|
+
}
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
case 'fade-in': {
|
|
275
|
+
// Show dim first, then bright
|
|
276
|
+
const dimCode = '\x1b[2m';
|
|
277
|
+
const resetCode = '\x1b[0m';
|
|
278
|
+
// First pass: dim
|
|
279
|
+
const dimLines = [];
|
|
280
|
+
for (const line of art) {
|
|
281
|
+
const dimLine = `${dimCode}${line}${resetCode}`;
|
|
282
|
+
dimLines.push(dimLine);
|
|
283
|
+
console.log(dimLine);
|
|
284
|
+
}
|
|
285
|
+
await delay(speed * 3);
|
|
286
|
+
// Move cursor up and overwrite with bright
|
|
287
|
+
process.stdout.write(`\x1b[${art.length}A`);
|
|
288
|
+
for (const line of art) {
|
|
289
|
+
console.log(applyColor(line));
|
|
290
|
+
}
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
case 'drop-in': {
|
|
294
|
+
// Lines appear one at a time with a slight bounce effect
|
|
295
|
+
for (const line of art) {
|
|
296
|
+
console.log(applyColor(line));
|
|
297
|
+
await delay(speed);
|
|
298
|
+
}
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
function delay(ms) {
|
|
304
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Run a full-screen theme transition effect.
|
|
308
|
+
* Fills the terminal with a brief animation, then clears for the new theme.
|
|
309
|
+
* All effects are pre-Ink (raw stdout) and self-cleaning.
|
|
310
|
+
*/
|
|
311
|
+
export async function renderTransition(config) {
|
|
312
|
+
if (!process.stdout.isTTY || config.effect === 'none')
|
|
313
|
+
return;
|
|
314
|
+
const cols = process.stdout.columns || 80;
|
|
315
|
+
const rows = process.stdout.rows || 24;
|
|
316
|
+
const duration = config.duration ?? 1500;
|
|
317
|
+
const color = config.color ?? '#00FF00';
|
|
318
|
+
const colorSec = config.colorSecondary ?? '#003300';
|
|
319
|
+
// Hide cursor during animation
|
|
320
|
+
process.stdout.write('\x1b[?25l');
|
|
321
|
+
try {
|
|
322
|
+
switch (config.effect) {
|
|
323
|
+
case 'matrix-rain':
|
|
324
|
+
await matrixRain(cols, rows, duration, color, colorSec, config.chars);
|
|
325
|
+
break;
|
|
326
|
+
case 'warp-speed':
|
|
327
|
+
await warpSpeed(cols, rows, duration, color);
|
|
328
|
+
break;
|
|
329
|
+
case 'glitch':
|
|
330
|
+
await glitchEffect(cols, rows, duration, color, colorSec);
|
|
331
|
+
break;
|
|
332
|
+
case 'terminal-boot':
|
|
333
|
+
await terminalBoot(cols, rows, duration, color);
|
|
334
|
+
break;
|
|
335
|
+
case 'pixel-dissolve':
|
|
336
|
+
await pixelDissolve(cols, rows, duration, color);
|
|
337
|
+
break;
|
|
338
|
+
case 'sparkle':
|
|
339
|
+
await sparkleEffect(cols, rows, duration, color, colorSec);
|
|
340
|
+
break;
|
|
341
|
+
case 'rainbow-wave':
|
|
342
|
+
await rainbowWave(cols, rows, duration);
|
|
343
|
+
break;
|
|
344
|
+
case 'static-noise':
|
|
345
|
+
await staticNoise(cols, rows, duration);
|
|
346
|
+
break;
|
|
347
|
+
case 'fade':
|
|
348
|
+
case 'fade-in':
|
|
349
|
+
await fadeEffect(cols, rows, duration, color);
|
|
350
|
+
break;
|
|
351
|
+
case 'scan-lines':
|
|
352
|
+
await scanLinesEffect(cols, rows, duration, color, colorSec);
|
|
353
|
+
break;
|
|
354
|
+
case 'digital-rain':
|
|
355
|
+
await matrixRain(cols, rows, duration, color, colorSec, config.chars);
|
|
356
|
+
break;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
finally {
|
|
360
|
+
// Show cursor, clear screen
|
|
361
|
+
process.stdout.write('\x1b[?25h');
|
|
362
|
+
process.stdout.write('\x1b[2J\x1b[H');
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
// --- Matrix Digital Rain ---
|
|
366
|
+
async function matrixRain(cols, rows, duration, color, colorDim, chars) {
|
|
367
|
+
const defaultChars = '\u30A2\u30A4\u30A6\u30A8\u30AA\u30AB\u30AD\u30AF\u30B1\u30B3\u30B5\u30B7\u30B9\u30BB\u30BD\u30BF\u30C1\u30C4\u30C6\u30C80123456789';
|
|
368
|
+
const charArr = Array.isArray(chars) ? chars : [...(chars || defaultChars)];
|
|
369
|
+
const randChar = () => charArr[Math.floor(Math.random() * charArr.length)];
|
|
370
|
+
// Column state: each column has a "drop" position that falls
|
|
371
|
+
const drops = new Array(cols).fill(0).map(() => Math.floor(Math.random() * -rows));
|
|
372
|
+
const speeds = new Array(cols).fill(0).map(() => 0.5 + Math.random() * 1.5);
|
|
373
|
+
const frameTime = 50;
|
|
374
|
+
const frames = Math.floor(duration / frameTime);
|
|
375
|
+
// Clear screen
|
|
376
|
+
process.stdout.write('\x1b[2J\x1b[H');
|
|
377
|
+
for (let f = 0; f < frames; f++) {
|
|
378
|
+
// Build frame buffer
|
|
379
|
+
let output = '\x1b[H'; // Move to top-left
|
|
380
|
+
for (let y = 0; y < Math.min(rows - 1, 30); y++) {
|
|
381
|
+
let line = '';
|
|
382
|
+
for (let x = 0; x < Math.min(cols, 120); x += 2) { // Skip every other col for performance
|
|
383
|
+
const dropY = Math.floor(drops[x]);
|
|
384
|
+
const dist = y - dropY;
|
|
385
|
+
if (dist === 0) {
|
|
386
|
+
// Leading character — bright
|
|
387
|
+
line += colorFg(randChar(), '#FFFFFF');
|
|
388
|
+
}
|
|
389
|
+
else if (dist > 0 && dist < 8) {
|
|
390
|
+
// Trail — primary color, fading
|
|
391
|
+
const fade = 1 - (dist / 8);
|
|
392
|
+
const r = Math.floor(parseInt(color.slice(1, 3), 16) * fade);
|
|
393
|
+
const g = Math.floor(parseInt(color.slice(3, 5), 16) * fade);
|
|
394
|
+
const b = Math.floor(parseInt(color.slice(5, 7), 16) * fade);
|
|
395
|
+
const hex = `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
|
|
396
|
+
line += colorFg(randChar(), hex);
|
|
397
|
+
}
|
|
398
|
+
else if (dist > 0 && dist < 15 && Math.random() > 0.7) {
|
|
399
|
+
// Sparse background rain
|
|
400
|
+
line += colorFg(randChar(), colorDim);
|
|
401
|
+
}
|
|
402
|
+
else {
|
|
403
|
+
line += ' ';
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
output += line + '\n';
|
|
407
|
+
}
|
|
408
|
+
process.stdout.write(output);
|
|
409
|
+
// Advance drops
|
|
410
|
+
for (let x = 0; x < cols; x += 2) {
|
|
411
|
+
drops[x] += speeds[x];
|
|
412
|
+
if (drops[x] > rows + 10) {
|
|
413
|
+
drops[x] = Math.floor(Math.random() * -8);
|
|
414
|
+
speeds[x] = 0.5 + Math.random() * 1.5;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
await delay(frameTime);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
// --- Warp Speed (Star Trek style) ---
|
|
421
|
+
async function warpSpeed(cols, rows, duration, color) {
|
|
422
|
+
const frameTime = 60;
|
|
423
|
+
const frames = Math.floor(duration / frameTime);
|
|
424
|
+
const centerX = Math.floor(cols / 2);
|
|
425
|
+
const centerY = Math.floor(Math.min(rows - 1, 25) / 2);
|
|
426
|
+
const stars = [];
|
|
427
|
+
const starChars = ['.', '*', '+', '\u2022', '\u2219', '\u00B7'];
|
|
428
|
+
process.stdout.write('\x1b[2J\x1b[H');
|
|
429
|
+
for (let f = 0; f < frames; f++) {
|
|
430
|
+
// Spawn new stars from center
|
|
431
|
+
const spawnCount = Math.min(3, Math.floor(f / 3) + 1);
|
|
432
|
+
for (let i = 0; i < spawnCount; i++) {
|
|
433
|
+
const angle = Math.random() * Math.PI * 2;
|
|
434
|
+
const speed = 0.5 + Math.random() * 2;
|
|
435
|
+
stars.push({
|
|
436
|
+
x: centerX, y: centerY,
|
|
437
|
+
vx: Math.cos(angle) * speed,
|
|
438
|
+
vy: Math.sin(angle) * speed * 0.5, // Squish vertically for terminal aspect
|
|
439
|
+
char: starChars[Math.floor(Math.random() * starChars.length)],
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
// Build frame
|
|
443
|
+
const grid = Array.from({ length: Math.min(rows - 1, 25) }, () => new Array(Math.min(cols, 120)).fill(' '));
|
|
444
|
+
// Update and render stars
|
|
445
|
+
for (let i = stars.length - 1; i >= 0; i--) {
|
|
446
|
+
const s = stars[i];
|
|
447
|
+
s.x += s.vx;
|
|
448
|
+
s.y += s.vy;
|
|
449
|
+
// Accelerate as they move outward (warp stretch)
|
|
450
|
+
s.vx *= 1.08;
|
|
451
|
+
s.vy *= 1.08;
|
|
452
|
+
const sx = Math.floor(s.x);
|
|
453
|
+
const sy = Math.floor(s.y);
|
|
454
|
+
if (sx < 0 || sx >= Math.min(cols, 120) || sy < 0 || sy >= Math.min(rows - 1, 25)) {
|
|
455
|
+
stars.splice(i, 1);
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
// Streak effect — draw a line from current to previous position
|
|
459
|
+
const dist = Math.sqrt(s.vx * s.vx + s.vy * s.vy);
|
|
460
|
+
const streakChar = dist > 3 ? '\u2500' : dist > 1.5 ? '\u2022' : s.char;
|
|
461
|
+
grid[sy][sx] = streakChar;
|
|
462
|
+
}
|
|
463
|
+
let output = '\x1b[H';
|
|
464
|
+
for (const row of grid) {
|
|
465
|
+
output += colorFg(row.join(''), color) + '\n';
|
|
466
|
+
}
|
|
467
|
+
process.stdout.write(output);
|
|
468
|
+
// Keep star count manageable
|
|
469
|
+
while (stars.length > 200)
|
|
470
|
+
stars.shift();
|
|
471
|
+
await delay(frameTime);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
// --- Glitch Effect (Cyberpunk) ---
|
|
475
|
+
async function glitchEffect(cols, rows, duration, color, colorSec) {
|
|
476
|
+
const frameTime = 80;
|
|
477
|
+
const frames = Math.floor(duration / frameTime);
|
|
478
|
+
const glitchChars = '\u2588\u2593\u2592\u2591\u2580\u2584\u258C\u2590/\\|#@$%&';
|
|
479
|
+
const glitchArr = [...glitchChars];
|
|
480
|
+
const w = Math.min(cols, 120);
|
|
481
|
+
const h = Math.min(rows - 1, 25);
|
|
482
|
+
process.stdout.write('\x1b[2J\x1b[H');
|
|
483
|
+
for (let f = 0; f < frames; f++) {
|
|
484
|
+
let output = '\x1b[H';
|
|
485
|
+
const glitchIntensity = Math.sin((f / frames) * Math.PI); // Peaks in middle
|
|
486
|
+
for (let y = 0; y < h; y++) {
|
|
487
|
+
let line = '';
|
|
488
|
+
if (Math.random() < glitchIntensity * 0.4) {
|
|
489
|
+
// Glitch line — random block characters
|
|
490
|
+
const offset = Math.floor(Math.random() * 10) - 5;
|
|
491
|
+
const spaces = ' '.repeat(Math.max(0, offset));
|
|
492
|
+
for (let x = 0; x < w - Math.abs(offset); x++) {
|
|
493
|
+
if (Math.random() < glitchIntensity * 0.6) {
|
|
494
|
+
const c = Math.random() > 0.5 ? color : colorSec;
|
|
495
|
+
line += colorFg(glitchArr[Math.floor(Math.random() * glitchArr.length)], c);
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
line += ' ';
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
line = spaces + line;
|
|
502
|
+
}
|
|
503
|
+
else {
|
|
504
|
+
// Mostly empty with sparse glitch
|
|
505
|
+
for (let x = 0; x < w; x++) {
|
|
506
|
+
if (Math.random() < glitchIntensity * 0.05) {
|
|
507
|
+
line += colorFg(glitchArr[Math.floor(Math.random() * glitchArr.length)], color);
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
line += ' ';
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
output += line.slice(0, w) + '\n';
|
|
515
|
+
}
|
|
516
|
+
process.stdout.write(output);
|
|
517
|
+
await delay(frameTime);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
// --- Terminal Boot (WarGames / Retro) ---
|
|
521
|
+
async function terminalBoot(cols, rows, duration, color) {
|
|
522
|
+
const bootLines = [
|
|
523
|
+
'INITIALIZING SYSTEM...',
|
|
524
|
+
'LOADING KERNEL... OK',
|
|
525
|
+
'MEMORY CHECK... 640K OK',
|
|
526
|
+
'LOADING DRIVERS...',
|
|
527
|
+
' [OK] TERMINAL',
|
|
528
|
+
' [OK] NETWORK',
|
|
529
|
+
' [OK] AI SUBSYSTEM',
|
|
530
|
+
'',
|
|
531
|
+
'SYSTEM READY.',
|
|
532
|
+
'',
|
|
533
|
+
'WELCOME TO CALLIOPE',
|
|
534
|
+
'',
|
|
535
|
+
];
|
|
536
|
+
process.stdout.write('\x1b[2J\x1b[H');
|
|
537
|
+
const timePerLine = Math.floor(duration / bootLines.length);
|
|
538
|
+
for (const line of bootLines) {
|
|
539
|
+
if (line === '') {
|
|
540
|
+
console.log();
|
|
541
|
+
await delay(timePerLine / 2);
|
|
542
|
+
continue;
|
|
543
|
+
}
|
|
544
|
+
// Typewriter effect per character
|
|
545
|
+
const colored = colorFg(line, color);
|
|
546
|
+
process.stdout.write(colored);
|
|
547
|
+
process.stdout.write('\n');
|
|
548
|
+
await delay(timePerLine);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
// --- Pixel Dissolve ---
|
|
552
|
+
async function pixelDissolve(cols, rows, duration, color) {
|
|
553
|
+
const frameTime = 60;
|
|
554
|
+
const frames = Math.floor(duration / frameTime);
|
|
555
|
+
const w = Math.min(cols, 120);
|
|
556
|
+
const h = Math.min(rows - 1, 25);
|
|
557
|
+
const blockChars = ['\u2588', '\u2593', '\u2592', '\u2591', ' '];
|
|
558
|
+
process.stdout.write('\x1b[2J\x1b[H');
|
|
559
|
+
for (let f = 0; f < frames; f++) {
|
|
560
|
+
const progress = f / frames; // 0 → 1
|
|
561
|
+
let output = '\x1b[H';
|
|
562
|
+
for (let y = 0; y < h; y++) {
|
|
563
|
+
let line = '';
|
|
564
|
+
for (let x = 0; x < w; x++) {
|
|
565
|
+
// Dissolve from full → empty, with some randomness
|
|
566
|
+
const threshold = progress + (Math.random() * 0.3 - 0.15);
|
|
567
|
+
if (threshold < 0.3) {
|
|
568
|
+
line += colorFg('\u2588', color);
|
|
569
|
+
}
|
|
570
|
+
else if (threshold < 0.5) {
|
|
571
|
+
line += colorFg('\u2593', color);
|
|
572
|
+
}
|
|
573
|
+
else if (threshold < 0.7) {
|
|
574
|
+
line += colorFg('\u2592', color);
|
|
575
|
+
}
|
|
576
|
+
else if (threshold < 0.85) {
|
|
577
|
+
line += colorFg('\u2591', color);
|
|
578
|
+
}
|
|
579
|
+
else {
|
|
580
|
+
line += ' ';
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
output += line + '\n';
|
|
584
|
+
}
|
|
585
|
+
process.stdout.write(output);
|
|
586
|
+
await delay(frameTime);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
// --- Sparkle (Zelda / Fantasy) ---
|
|
590
|
+
async function sparkleEffect(cols, rows, duration, color, colorSec) {
|
|
591
|
+
const frameTime = 80;
|
|
592
|
+
const frames = Math.floor(duration / frameTime);
|
|
593
|
+
const sparkleChars = ['\u2728', '\u2727', '\u2726', '\u2735', '\u2734', '\u2733', '\u00B7', ' '];
|
|
594
|
+
const w = Math.min(cols, 120);
|
|
595
|
+
const h = Math.min(rows - 1, 25);
|
|
596
|
+
process.stdout.write('\x1b[2J\x1b[H');
|
|
597
|
+
for (let f = 0; f < frames; f++) {
|
|
598
|
+
const intensity = Math.sin((f / frames) * Math.PI); // Peak in middle
|
|
599
|
+
let output = '\x1b[H';
|
|
600
|
+
for (let y = 0; y < h; y++) {
|
|
601
|
+
let line = '';
|
|
602
|
+
for (let x = 0; x < w; x++) {
|
|
603
|
+
if (Math.random() < intensity * 0.15) {
|
|
604
|
+
const c = Math.random() > 0.5 ? color : colorSec;
|
|
605
|
+
line += colorFg(sparkleChars[Math.floor(Math.random() * 4)], c);
|
|
606
|
+
}
|
|
607
|
+
else if (Math.random() < intensity * 0.05) {
|
|
608
|
+
line += colorFg('\u00B7', color);
|
|
609
|
+
}
|
|
610
|
+
else {
|
|
611
|
+
line += ' ';
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
output += line + '\n';
|
|
615
|
+
}
|
|
616
|
+
process.stdout.write(output);
|
|
617
|
+
await delay(frameTime);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
// --- Rainbow Wave (Saggitaria) ---
|
|
621
|
+
async function rainbowWave(cols, rows, duration) {
|
|
622
|
+
const frameTime = 60;
|
|
623
|
+
const frames = Math.floor(duration / frameTime);
|
|
624
|
+
const w = Math.min(cols, 120);
|
|
625
|
+
const h = Math.min(rows - 1, 25);
|
|
626
|
+
const waveChars = ['\u2588', '\u2593', '\u2592', '\u2591'];
|
|
627
|
+
const colors = ['#FF6B6B', '#FFE66D', '#4ECDC4', '#45B7D1', '#96CEB4', '#FFEAA7', '#DDA0DD', '#FF6B6B'];
|
|
628
|
+
process.stdout.write('\x1b[2J\x1b[H');
|
|
629
|
+
for (let f = 0; f < frames; f++) {
|
|
630
|
+
let output = '\x1b[H';
|
|
631
|
+
const phase = (f / frames) * Math.PI * 4;
|
|
632
|
+
for (let y = 0; y < h; y++) {
|
|
633
|
+
let line = '';
|
|
634
|
+
for (let x = 0; x < w; x++) {
|
|
635
|
+
const wave = Math.sin(phase + x * 0.1 + y * 0.2);
|
|
636
|
+
const colorIdx = Math.floor(((wave + 1) / 2) * (colors.length - 1));
|
|
637
|
+
const charIdx = Math.floor(((wave + 1) / 2) * (waveChars.length - 1));
|
|
638
|
+
if (Math.abs(wave) > 0.3) {
|
|
639
|
+
line += colorFg(waveChars[charIdx], colors[colorIdx]);
|
|
640
|
+
}
|
|
641
|
+
else {
|
|
642
|
+
line += ' ';
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
output += line + '\n';
|
|
646
|
+
}
|
|
647
|
+
process.stdout.write(output);
|
|
648
|
+
await delay(frameTime);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
// --- Static Noise ---
|
|
652
|
+
async function staticNoise(cols, rows, duration) {
|
|
653
|
+
const frameTime = 50;
|
|
654
|
+
const frames = Math.floor(duration / frameTime);
|
|
655
|
+
const noiseChars = '\u2588\u2593\u2592\u2591 ';
|
|
656
|
+
const noiseArr = [...noiseChars];
|
|
657
|
+
const w = Math.min(cols, 120);
|
|
658
|
+
const h = Math.min(rows - 1, 25);
|
|
659
|
+
process.stdout.write('\x1b[2J\x1b[H');
|
|
660
|
+
for (let f = 0; f < frames; f++) {
|
|
661
|
+
const fadeOut = 1 - (f / frames); // Fade to black
|
|
662
|
+
let output = '\x1b[H';
|
|
663
|
+
for (let y = 0; y < h; y++) {
|
|
664
|
+
let line = '';
|
|
665
|
+
for (let x = 0; x < w; x++) {
|
|
666
|
+
if (Math.random() < fadeOut * 0.5) {
|
|
667
|
+
const gray = Math.floor(Math.random() * 200 * fadeOut);
|
|
668
|
+
const hex = `#${gray.toString(16).padStart(2, '0')}${gray.toString(16).padStart(2, '0')}${gray.toString(16).padStart(2, '0')}`;
|
|
669
|
+
line += colorFg(noiseArr[Math.floor(Math.random() * noiseArr.length)], hex);
|
|
670
|
+
}
|
|
671
|
+
else {
|
|
672
|
+
line += ' ';
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
output += line + '\n';
|
|
676
|
+
}
|
|
677
|
+
process.stdout.write(output);
|
|
678
|
+
await delay(frameTime);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
// --- Fade Effect ---
|
|
682
|
+
async function fadeEffect(cols, rows, duration, color) {
|
|
683
|
+
const frameTime = 60;
|
|
684
|
+
const frames = Math.floor(duration / frameTime);
|
|
685
|
+
const w = Math.min(cols, 120);
|
|
686
|
+
const h = Math.min(rows - 1, 25);
|
|
687
|
+
const blockChars = [' ', '\u2591', '\u2592', '\u2593', '\u2588', '\u2593', '\u2592', '\u2591', ' '];
|
|
688
|
+
process.stdout.write('\x1b[2J\x1b[H');
|
|
689
|
+
for (let f = 0; f < frames; f++) {
|
|
690
|
+
const progress = f / frames;
|
|
691
|
+
// Fade in then out: peak at 0.5
|
|
692
|
+
const intensity = Math.sin(progress * Math.PI);
|
|
693
|
+
const charIdx = Math.floor(intensity * (blockChars.length - 1));
|
|
694
|
+
const ch = blockChars[charIdx];
|
|
695
|
+
// Parse base color and scale brightness by intensity
|
|
696
|
+
const r = parseInt(color.slice(1, 3), 16);
|
|
697
|
+
const g = parseInt(color.slice(3, 5), 16);
|
|
698
|
+
const b = parseInt(color.slice(5, 7), 16);
|
|
699
|
+
const cr = Math.floor(r * intensity);
|
|
700
|
+
const cg = Math.floor(g * intensity);
|
|
701
|
+
const cb = Math.floor(b * intensity);
|
|
702
|
+
const hex = `#${cr.toString(16).padStart(2, '0')}${cg.toString(16).padStart(2, '0')}${cb.toString(16).padStart(2, '0')}`;
|
|
703
|
+
let output = '\x1b[H';
|
|
704
|
+
const line = colorFg(ch.repeat(w), hex);
|
|
705
|
+
for (let y = 0; y < h; y++) {
|
|
706
|
+
output += line + '\n';
|
|
707
|
+
}
|
|
708
|
+
process.stdout.write(output);
|
|
709
|
+
await delay(frameTime);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
// --- Scan Lines Effect ---
|
|
713
|
+
async function scanLinesEffect(cols, rows, duration, color, colorSec) {
|
|
714
|
+
const frameTime = 50;
|
|
715
|
+
const frames = Math.floor(duration / frameTime);
|
|
716
|
+
const w = Math.min(cols, 120);
|
|
717
|
+
const h = Math.min(rows - 1, 25);
|
|
718
|
+
process.stdout.write('\x1b[2J\x1b[H');
|
|
719
|
+
for (let f = 0; f < frames; f++) {
|
|
720
|
+
const scanY = Math.floor((f / frames) * h * 2) % h;
|
|
721
|
+
let output = '\x1b[H';
|
|
722
|
+
for (let y = 0; y < h; y++) {
|
|
723
|
+
const dist = Math.abs(y - scanY);
|
|
724
|
+
if (dist === 0) {
|
|
725
|
+
// Bright scan line
|
|
726
|
+
output += colorFg('\u2588'.repeat(w), '#FFFFFF') + '\n';
|
|
727
|
+
}
|
|
728
|
+
else if (dist <= 2) {
|
|
729
|
+
// Near glow
|
|
730
|
+
output += colorFg('\u2593'.repeat(w), color) + '\n';
|
|
731
|
+
}
|
|
732
|
+
else if (dist <= 4) {
|
|
733
|
+
// Dim trail
|
|
734
|
+
output += colorFg('\u2591'.repeat(w), colorSec) + '\n';
|
|
735
|
+
}
|
|
736
|
+
else {
|
|
737
|
+
output += ' '.repeat(w) + '\n';
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
process.stdout.write(output);
|
|
741
|
+
await delay(frameTime);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
/**
|
|
745
|
+
* Get a human-readable label for an image mode.
|
|
746
|
+
*/
|
|
747
|
+
export function getImageModeLabel(mode) {
|
|
748
|
+
const labels = {
|
|
749
|
+
iterm2: 'iTerm2 Inline Image',
|
|
750
|
+
kitty: 'Kitty Graphics Protocol',
|
|
751
|
+
halfblock: 'Unicode Half-Block',
|
|
752
|
+
braille: 'Braille Dots',
|
|
753
|
+
ascii: 'ASCII',
|
|
754
|
+
none: 'None',
|
|
755
|
+
};
|
|
756
|
+
return labels[mode];
|
|
757
|
+
}
|
|
758
|
+
// ============================================================================
|
|
759
|
+
// Utility
|
|
760
|
+
// ============================================================================
|
|
761
|
+
/** Strip ANSI escape sequences from a string for measuring visible width */
|
|
762
|
+
function stripAnsi(str) {
|
|
763
|
+
// eslint-disable-next-line no-control-regex
|
|
764
|
+
return str.replace(/\x1b\[[0-9;]*m/g, '').replace(/\x1b[\\_\]][^\x07\x1b]*[\x07\x1b\\]?/g, '');
|
|
765
|
+
}
|
|
766
|
+
//# sourceMappingURL=terminal-image.js.map
|