Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 1 addition & 10 deletions dimos/utils/cli/human/humancli.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,16 +112,7 @@ def on_mount(self) -> None:
# Focus on input
self.input_widget.focus()

# Display ASCII art banner
ascii_art = """
██████╗ ██╗███╗ ███╗███████╗███╗ ██╗███████╗██╗ ██████╗ ███╗ ██╗ █████╗ ██╗
██╔══██╗██║████╗ ████║██╔════╝████╗ ██║██╔════╝██║██╔═══██╗████╗ ██║██╔══██╗██║
██║ ██║██║██╔████╔██║█████╗ ██╔██╗ ██║███████╗██║██║ ██║██╔██╗ ██║███████║██║
██║ ██║██║██║╚██╔╝██║██╔══╝ ██║╚██╗██║╚════██║██║██║ ██║██║╚██╗██║██╔══██║██║
██████╔╝██║██║ ╚═╝ ██║███████╗██║ ╚████║███████║██║╚██████╔╝██║ ╚████║██║ ██║███████╗
╚═════╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝
"""
self.chat_log.write(f"[{theme.ACCENT}]{ascii_art}[/{theme.ACCENT}]")
self.chat_log.write(f"[{theme.ACCENT}]{theme.ascii_logo}[/{theme.ACCENT}]")

# Welcome message
self._add_system_message("Connected to DimOS Agent Interface")
Expand Down
179 changes: 179 additions & 0 deletions dimos/utils/cli/human/humanclianim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
#!/usr/bin/env python3
# Copyright 2025 Dimensional Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import random
import sys
import threading
import time

from terminaltexteffects import Color

from dimos.utils.cli import theme

# Global to store the imported main function
_humancli_main = None
_import_complete = threading.Event()

print(theme.ACCENT)


def import_cli_in_background():
"""Import the heavy CLI modules in the background"""
global _humancli_main
try:
from dimos.utils.cli.human.humancli import main as humancli_main

_humancli_main = humancli_main
except Exception as e:
print(f"Failed to import CLI: {e}")
finally:
_import_complete.set()


def get_effect_config(effect_name):
"""Get hardcoded configuration for a specific effect"""
# Hardcoded configs for each effect
global_config = {
"final_gradient_stops": [Color(theme.ACCENT)],
}

configs = {
"randomsequence": {
"speed": 0.075,
},
"slide": {"direction": "left", "movement_speed": 1.5},
"sweep": {"direction": "left"},
"print": {
"print_speed": 10,
"print_head_return_speed": 10,
"final_gradient_stops": [Color(theme.ACCENT)],
},
"pour": {"pour_speed": 9},
"matrix": {"rain_symbols": "01", "rain_fall_speed_range": (4, 7)},
"decrypt": {"typing_speed": 5, "decryption_speed": 3},
"burn": {"fire_chars": "█", "flame_color": "ffffff"},
"expand": {"expand_direction": "center"},
"scattered": {"movement_speed": 0.5},
"beams": {"movement_speed": 0.5, "beam_delay": 0},
"middleout": {"center_movement_speed": 3, "full_movement_speed": 0.5},
"rain": {
"rain_symbols": "░▒▓█",
"rain_fall_speed_range": (5, 10),
},
"highlight": {"highlight_brightness": 3},
}

return {**configs.get(effect_name, {}), **global_config}


def run_banner_animation():
"""Run the ASCII banner animation before launching Textual"""

# Check if we should animate
random_anim = ["scattered", "print", "expand", "slide", "rain"]
animation_style = os.environ.get("DIMOS_BANNER_ANIMATION", random.choice(random_anim)).lower()

if animation_style == "none":
return # Skip animation
from terminaltexteffects.effects.effect_beams import Beams
from terminaltexteffects.effects.effect_burn import Burn
from terminaltexteffects.effects.effect_colorshift import ColorShift
from terminaltexteffects.effects.effect_decrypt import Decrypt
from terminaltexteffects.effects.effect_expand import Expand
from terminaltexteffects.effects.effect_highlight import Highlight
from terminaltexteffects.effects.effect_matrix import Matrix
from terminaltexteffects.effects.effect_middleout import MiddleOut
from terminaltexteffects.effects.effect_overflow import Overflow
from terminaltexteffects.effects.effect_pour import Pour
from terminaltexteffects.effects.effect_print import Print
from terminaltexteffects.effects.effect_rain import Rain
from terminaltexteffects.effects.effect_random_sequence import RandomSequence
from terminaltexteffects.effects.effect_scattered import Scattered
from terminaltexteffects.effects.effect_slide import Slide
from terminaltexteffects.effects.effect_sweep import Sweep

# The DIMENSIONAL ASCII art
ascii_art = "\n" + theme.ascii_logo.replace("\n", "\n ")
# Choose effect based on style
effect_map = {
"slide": Slide,
"sweep": Sweep,
"print": Print,
"pour": Pour,
"burn": Burn,
"matrix": Matrix,
"rain": Rain,
"scattered": Scattered,
"expand": Expand,
"decrypt": Decrypt,
"overflow": Overflow,
"randomsequence": RandomSequence,
"beams": Beams,
"middleout": MiddleOut,
"highlight": Highlight,
}

EffectClass = effect_map.get(animation_style, Slide)

# Clear screen before starting animation
print("\033[2J\033[H", end="", flush=True)

# Get effect configuration
effect_config = get_effect_config(animation_style)

# Create and run the effect with config
effect = EffectClass(ascii_art)
for key, value in effect_config.items():
setattr(effect.effect_config, key, value)

# Run the animation - terminal.print() handles all screen management
with effect.terminal_output() as terminal:
for frame in effect:
terminal.print(frame)

# Brief pause to see the final frame
time.sleep(0.5)

# Clear screen for Textual to take over
print("\033[2J\033[H", end="")


def main():
"""Main entry point - run animation then launch the real CLI"""

# Start importing CLI in background (this is slow)
import_thread = threading.Thread(target=import_cli_in_background, daemon=True)
import_thread.start()

# Run the animation while imports happen (if not in web mode)
if not (len(sys.argv) > 1 and sys.argv[1] == "web"):
run_banner_animation()

# Wait for import to complete
_import_complete.wait(timeout=10) # Max 10 seconds wait

# Launch the real CLI
if _humancli_main:
_humancli_main()
else:
# Fallback if threaded import failed
from dimos.utils.cli.human.humancli import main as humancli_main

humancli_main()


if __name__ == "__main__":
main()
9 changes: 9 additions & 0 deletions dimos/utils/cli/theme.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,12 @@ def get(name: str, default: str = "#ffffff") -> str:
ERROR = COLORS.get("error", "#ff0000")
WARNING = COLORS.get("warning", "#ffcc00")
INFO = COLORS.get("info", "#00eeee")

ascii_logo = """
▇▇▇▇▇▇╗ ▇▇╗▇▇▇╗ ▇▇▇╗▇▇▇▇▇▇▇╗▇▇▇╗ ▇▇╗▇▇▇▇▇▇▇╗▇▇╗ ▇▇▇▇▇▇╗ ▇▇▇╗ ▇▇╗ ▇▇▇▇▇╗ ▇▇╗
▇▇╔══▇▇╗▇▇║▇▇▇▇╗ ▇▇▇▇║▇▇╔════╝▇▇▇▇╗ ▇▇║▇▇╔════╝▇▇║▇▇╔═══▇▇╗▇▇▇▇╗ ▇▇║▇▇╔══▇▇╗▇▇║
▇▇║ ▇▇║▇▇║▇▇╔▇▇▇▇╔▇▇║▇▇▇▇▇╗ ▇▇╔▇▇╗ ▇▇║▇▇▇▇▇▇▇╗▇▇║▇▇║ ▇▇║▇▇╔▇▇╗ ▇▇║▇▇▇▇▇▇▇║▇▇║
▇▇║ ▇▇║▇▇║▇▇║╚▇▇╔╝▇▇║▇▇╔══╝ ▇▇║╚▇▇╗▇▇║╚════▇▇║▇▇║▇▇║ ▇▇║▇▇║╚▇▇╗▇▇║▇▇╔══▇▇║▇▇║
▇▇▇▇▇▇╔╝▇▇║▇▇║ ╚═╝ ▇▇║▇▇▇▇▇▇▇╗▇▇║ ╚▇▇▇▇║▇▇▇▇▇▇▇║▇▇║╚▇▇▇▇▇▇╔╝▇▇║ ╚▇▇▇▇║▇▇║ ▇▇║▇▇▇▇▇▇▇╗
╚═════╝ ╚═╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝
"""
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ lcmspy = "dimos.utils.cli.lcmspy.run_lcmspy:main"
foxglove-bridge = "dimos.utils.cli.foxglove_bridge.run_foxglove_bridge:main"
skillspy = "dimos.utils.cli.skillspy.skillspy:main"
agentspy = "dimos.utils.cli.agentspy.agentspy:main"
human-cli = "dimos.utils.cli.human.humancli:main"
humancli = "dimos.utils.cli.human.humanclianim:main"
dimos-robot = "dimos.robot.cli.dimos_robot:main"

[project.optional-dependencies]
Expand Down Expand Up @@ -189,6 +189,7 @@ dev = [
"pytest-timeout==2.4.0",
"textual==3.7.1",
"requests-mock==1.12.1",
"terminaltexteffects==0.12.2",

# Types
"lxml-stubs>=0.5.1,<1",
Expand Down
Loading