cct-cli 0.7.9.0__py3-none-any.whl
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.
- calc_terminal/__init__.py +14 -0
- calc_terminal/__main__.py +14 -0
- calc_terminal/activity.py +1334 -0
- calc_terminal/agent.py +3387 -0
- calc_terminal/agent_runtime.py +519 -0
- calc_terminal/ai_context.py +447 -0
- calc_terminal/ai_modes.py +752 -0
- calc_terminal/ai_personalization.py +286 -0
- calc_terminal/ai_preview_feedback.py +213 -0
- calc_terminal/aicore.py +2572 -0
- calc_terminal/anim.py +367 -0
- calc_terminal/app.py +3685 -0
- calc_terminal/art.py +639 -0
- calc_terminal/atomsim.py +368 -0
- calc_terminal/attachments.py +743 -0
- calc_terminal/benchmark_system.py +414 -0
- calc_terminal/browser/__init__.py +36 -0
- calc_terminal/browser/browser_state.py +346 -0
- calc_terminal/browser/devserver.py +176 -0
- calc_terminal/browser/engine.py +494 -0
- calc_terminal/browser/navigation.py +84 -0
- calc_terminal/browser/preview.py +429 -0
- calc_terminal/browser/preview_entry.py +95 -0
- calc_terminal/browser/project_detector.py +144 -0
- calc_terminal/browser/server.py +449 -0
- calc_terminal/browser/state.py +75 -0
- calc_terminal/browser/watcher.py +99 -0
- calc_terminal/browser_gui/__init__.py +1 -0
- calc_terminal/browser_gui/__main__.py +3 -0
- calc_terminal/browser_gui/launcher.py +173 -0
- calc_terminal/browser_gui/playwright_browser.py +117 -0
- calc_terminal/browser_gui/qt_browser.py +1501 -0
- calc_terminal/browser_gui/webview_browser.py +57 -0
- calc_terminal/capabilities/__init__.py +35 -0
- calc_terminal/capabilities/adapters/__init__.py +33 -0
- calc_terminal/capabilities/adapters/bioinformatics.py +204 -0
- calc_terminal/capabilities/adapters/browser_adapter.py +205 -0
- calc_terminal/capabilities/adapters/filesystem.py +206 -0
- calc_terminal/capabilities/adapters/git_adapter.py +202 -0
- calc_terminal/capabilities/adapters/jupyter_adapter.py +138 -0
- calc_terminal/capabilities/adapters/ml_frameworks.py +158 -0
- calc_terminal/capabilities/adapters/platforms.py +200 -0
- calc_terminal/capabilities/adapters/python_exec.py +93 -0
- calc_terminal/capabilities/adapters/quantum_adapter.py +150 -0
- calc_terminal/capabilities/adapters/scientific_comp.py +123 -0
- calc_terminal/capabilities/adapters/structural_bio.py +161 -0
- calc_terminal/capabilities/adapters/terminal.py +99 -0
- calc_terminal/capabilities/bus.py +178 -0
- calc_terminal/capabilities/discovery.py +207 -0
- calc_terminal/capabilities/schema.py +221 -0
- calc_terminal/cat.ico +0 -0
- calc_terminal/cat_browser.py +2018 -0
- calc_terminal/chat_store.py +703 -0
- calc_terminal/cli.py +1178 -0
- calc_terminal/code_editor.py +640 -0
- calc_terminal/collaboration.py +723 -0
- calc_terminal/commands_data.py +139 -0
- calc_terminal/compatibility_engine.py +352 -0
- calc_terminal/compute/__init__.py +31 -0
- calc_terminal/compute/fabric.py +350 -0
- calc_terminal/config.py +227 -0
- calc_terminal/core/__init__.py +41 -0
- calc_terminal/core/checkpoint.py +156 -0
- calc_terminal/core/input/__init__.py +45 -0
- calc_terminal/core/mode_registry.py +300 -0
- calc_terminal/core/project_graph.py +172 -0
- calc_terminal/core/recovery.py +129 -0
- calc_terminal/core/security_layer.py +112 -0
- calc_terminal/core/task_graph.py +202 -0
- calc_terminal/core/unified_runtime.py +184 -0
- calc_terminal/core/verification.py +257 -0
- calc_terminal/customization.py +1566 -0
- calc_terminal/derivations.py +153 -0
- calc_terminal/device_control.py +263 -0
- calc_terminal/diagnostics/__init__.py +27 -0
- calc_terminal/diagnostics/doctor_engine.py +382 -0
- calc_terminal/diagnostics/self_test.py +247 -0
- calc_terminal/doctor.py +519 -0
- calc_terminal/easter_eggs.py +274 -0
- calc_terminal/editor/__init__.py +1 -0
- calc_terminal/editor/actions.py +263 -0
- calc_terminal/editor/commands.py +160 -0
- calc_terminal/editor/shortcuts.py +226 -0
- calc_terminal/engine.py +259 -0
- calc_terminal/errors.py +120 -0
- calc_terminal/event_stream.py +146 -0
- calc_terminal/eventbus.py +133 -0
- calc_terminal/extensions.py +733 -0
- calc_terminal/fallback_cli.py +1321 -0
- calc_terminal/first_run.py +265 -0
- calc_terminal/fomoji_auth.py +1043 -0
- calc_terminal/formulas.py +82 -0
- calc_terminal/fs_cache.py +121 -0
- calc_terminal/fs_watcher.py +277 -0
- calc_terminal/game.py +193 -0
- calc_terminal/gen1.py +5 -0
- calc_terminal/generators.py +245 -0
- calc_terminal/gestures/__init__.py +42 -0
- calc_terminal/gestures/bindings.py +175 -0
- calc_terminal/gestures/manager.py +477 -0
- calc_terminal/goodbye.py +363 -0
- calc_terminal/gpu3d.py +290 -0
- calc_terminal/graphs.py +358 -0
- calc_terminal/hardware_analyzer.py +440 -0
- calc_terminal/host/__init__.py +30 -0
- calc_terminal/host/browser_manager.py +187 -0
- calc_terminal/host/desktop.py +1386 -0
- calc_terminal/host/launcher.py +395 -0
- calc_terminal/host/terminal.py +279 -0
- calc_terminal/identity.py +216 -0
- calc_terminal/input/__init__.py +54 -0
- calc_terminal/input/capabilities.py +258 -0
- calc_terminal/input/focus.py +87 -0
- calc_terminal/input/gestures.py +64 -0
- calc_terminal/input/pointer.py +114 -0
- calc_terminal/input/touch.py +345 -0
- calc_terminal/keys.py +84 -0
- calc_terminal/live_automation.py +165 -0
- calc_terminal/mathtext.py +433 -0
- calc_terminal/mcp.py +386 -0
- calc_terminal/memory.py +337 -0
- calc_terminal/memory_v2.py +479 -0
- calc_terminal/metrics.py +333 -0
- calc_terminal/mode_detection.py +146 -0
- calc_terminal/model.py +2431 -0
- calc_terminal/model_router.py +665 -0
- calc_terminal/models/__init__.py +0 -0
- calc_terminal/models/active_state.py +187 -0
- calc_terminal/models/dynamic_registry.py +584 -0
- calc_terminal/models/manager.py +781 -0
- calc_terminal/models/model_metadata.json +3526 -0
- calc_terminal/models/profiles.py +194 -0
- calc_terminal/models/registry.py +265 -0
- calc_terminal/models/schema.py +197 -0
- calc_terminal/models/validator.py +287 -0
- calc_terminal/models/verification_engine.py +368 -0
- calc_terminal/native_picker.py +215 -0
- calc_terminal/ollama_catalog.py +279 -0
- calc_terminal/ollama_download.py +233 -0
- calc_terminal/orchestrator.py +304 -0
- calc_terminal/package_research.py +322 -0
- calc_terminal/packages.py +1024 -0
- calc_terminal/pc_specs.py +116 -0
- calc_terminal/permissions.py +334 -0
- calc_terminal/pet.py +106 -0
- calc_terminal/pipeline.py +505 -0
- calc_terminal/platform/__init__.py +491 -0
- calc_terminal/platform/desktop.py +491 -0
- calc_terminal/platform/web.py +781 -0
- calc_terminal/preview/__init__.py +1 -0
- calc_terminal/preview/dev_server.py +303 -0
- calc_terminal/preview/diagnostics.py +131 -0
- calc_terminal/preview/live_reload.py +66 -0
- calc_terminal/preview/manager.py +129 -0
- calc_terminal/project_stats.py +209 -0
- calc_terminal/projects.py +328 -0
- calc_terminal/providers/__init__.py +0 -0
- calc_terminal/providers/adapters/__init__.py +80 -0
- calc_terminal/providers/adapters/anthropic_adapter.py +127 -0
- calc_terminal/providers/adapters/base.py +106 -0
- calc_terminal/providers/adapters/chinese_adapters.py +420 -0
- calc_terminal/providers/adapters/gemini_adapter.py +101 -0
- calc_terminal/providers/adapters/ollama_adapter.py +83 -0
- calc_terminal/providers/adapters/openai_adapter.py +159 -0
- calc_terminal/providers/adapters/other_adapters.py +246 -0
- calc_terminal/providers/anthropic_provider.py +172 -0
- calc_terminal/providers/auto_update.py +416 -0
- calc_terminal/providers/base_provider.py +105 -0
- calc_terminal/providers/discovery_manager.py +207 -0
- calc_terminal/providers/gemini_provider.py +178 -0
- calc_terminal/providers/lifecycle.py +767 -0
- calc_terminal/providers/ollama_adapter.py +707 -0
- calc_terminal/providers/openai_provider.py +254 -0
- calc_terminal/providers/provider_manager.py +1827 -0
- calc_terminal/providers/providers.json +4075 -0
- calc_terminal/reactionsim.py +279 -0
- calc_terminal/registry.py +337 -0
- calc_terminal/report.py +162 -0
- calc_terminal/research/__init__.py +45 -0
- calc_terminal/research/artifact_intel.py +126 -0
- calc_terminal/research/data_lineage.py +123 -0
- calc_terminal/research/experiment_ledger.py +303 -0
- calc_terminal/research/reproducibility.py +131 -0
- calc_terminal/resilience/__init__.py +47 -0
- calc_terminal/resilience/agent_state.py +121 -0
- calc_terminal/resilience/capability_matcher.py +174 -0
- calc_terminal/resilience/circuit_breaker.py +158 -0
- calc_terminal/resilience/failover_engine.py +230 -0
- calc_terminal/resilience/health_monitor.py +192 -0
- calc_terminal/resilience/ollama_adapter.py +125 -0
- calc_terminal/resilience/orchestrator.py +312 -0
- calc_terminal/resilience/types.py +134 -0
- calc_terminal/sandbox.py +98 -0
- calc_terminal/scires.py +558 -0
- calc_terminal/security_scanner.py +126 -0
- calc_terminal/session.py +294 -0
- calc_terminal/sim3d.py +206 -0
- calc_terminal/solver.py +276 -0
- calc_terminal/sound.py +127 -0
- calc_terminal/task_reports.py +287 -0
- calc_terminal/terminal_host.py +201 -0
- calc_terminal/terminal_identity.py +411 -0
- calc_terminal/test_ai_mode_reliability.py +344 -0
- calc_terminal/test_browser.py +368 -0
- calc_terminal/test_code_editor_upgrade.py +485 -0
- calc_terminal/test_customization.py +1148 -0
- calc_terminal/test_customization_ui.py +612 -0
- calc_terminal/test_dynamic_registry.py +304 -0
- calc_terminal/test_extensions.py +436 -0
- calc_terminal/test_overhaul.py +557 -0
- calc_terminal/test_project_detect.py +255 -0
- calc_terminal/test_root_cause_fix.py +527 -0
- calc_terminal/test_stability.py +532 -0
- calc_terminal/test_terminal_identity.py +132 -0
- calc_terminal/test_v079_speed.py +460 -0
- calc_terminal/theme.py +1107 -0
- calc_terminal/timeline.py +139 -0
- calc_terminal/todos.py +246 -0
- calc_terminal/tool_call_normalizer.py +419 -0
- calc_terminal/tui.py +104 -0
- calc_terminal/ui/__init__.py +8 -0
- calc_terminal/ui/activity_panel.py +231 -0
- calc_terminal/ui/activity_stream_panel.py +238 -0
- calc_terminal/ui/animations.py +122 -0
- calc_terminal/ui/app.py +7271 -0
- calc_terminal/ui/attach_panel.py +597 -0
- calc_terminal/ui/attachments.py +424 -0
- calc_terminal/ui/backup_panel.py +810 -0
- calc_terminal/ui/browser_shell.py +887 -0
- calc_terminal/ui/cat_agent.py +357 -0
- calc_terminal/ui/chats_panel.py +899 -0
- calc_terminal/ui/command_palette.py +125 -0
- calc_terminal/ui/command_palette_modal.py +166 -0
- calc_terminal/ui/composer.py +1141 -0
- calc_terminal/ui/context_menu.py +197 -0
- calc_terminal/ui/conversation.py +1435 -0
- calc_terminal/ui/customization_panel.py +1229 -0
- calc_terminal/ui/dashboard.py +404 -0
- calc_terminal/ui/design_system.py +557 -0
- calc_terminal/ui/diff_panel.py +213 -0
- calc_terminal/ui/editor.py +2102 -0
- calc_terminal/ui/empty_state.py +302 -0
- calc_terminal/ui/events.py +487 -0
- calc_terminal/ui/extensions_panel.py +815 -0
- calc_terminal/ui/footer.py +166 -0
- calc_terminal/ui/gestures_panel.py +383 -0
- calc_terminal/ui/goodbye_screen.py +100 -0
- calc_terminal/ui/header.py +1034 -0
- calc_terminal/ui/help_panel.py +254 -0
- calc_terminal/ui/live_activities.py +914 -0
- calc_terminal/ui/mcp_panel.py +570 -0
- calc_terminal/ui/memory_center.py +524 -0
- calc_terminal/ui/mode_colors_panel.py +525 -0
- calc_terminal/ui/nav_screens.py +747 -0
- calc_terminal/ui/ollama_panel.py +536 -0
- calc_terminal/ui/palette.py +221 -0
- calc_terminal/ui/permission_panel.py +269 -0
- calc_terminal/ui/personalization_panel.py +517 -0
- calc_terminal/ui/personalize_center.py +1568 -0
- calc_terminal/ui/preview_panel.py +441 -0
- calc_terminal/ui/resizers.py +402 -0
- calc_terminal/ui/sidebar.py +1285 -0
- calc_terminal/ui/statusbar.py +168 -0
- calc_terminal/ui/theme_css.py +1396 -0
- calc_terminal/ui/thinking.py +226 -0
- calc_terminal/ui/timeline_panel.py +102 -0
- calc_terminal/ui/todo_panel.py +193 -0
- calc_terminal/ui/viewport.py +136 -0
- calc_terminal/ui/vision_panel.py +489 -0
- calc_terminal/ui/welcome_modal.py +343 -0
- calc_terminal/ui/widgets.py +160 -0
- calc_terminal/ui/workspace.py +831 -0
- calc_terminal/viewers/__init__.py +1 -0
- calc_terminal/viewers/document_viewer.py +252 -0
- calc_terminal/viewers/image_viewer.py +241 -0
- calc_terminal/viewers/pdf_viewer.py +203 -0
- calc_terminal/viewers/presentation_viewer.py +164 -0
- calc_terminal/viewers/registry.py +120 -0
- calc_terminal/viewers/spreadsheet_viewer.py +204 -0
- calc_terminal/vision/__init__.py +89 -0
- calc_terminal/vision/analysis.py +194 -0
- calc_terminal/vision/annotations.py +297 -0
- calc_terminal/vision/capture.py +171 -0
- calc_terminal/vision/context.py +231 -0
- calc_terminal/vision/correlation.py +169 -0
- calc_terminal/vision/cursor.py +258 -0
- calc_terminal/vision/events.py +66 -0
- calc_terminal/vision/frame_pipeline.py +259 -0
- calc_terminal/vision/priority.py +218 -0
- calc_terminal/vision/provider.py +180 -0
- calc_terminal/vision/safety.py +149 -0
- calc_terminal/vision/session.py +281 -0
- calc_terminal/vision/verify.py +162 -0
- calc_terminal/vision.py +514 -0
- calc_terminal/vscode_integration.py +113 -0
- calc_terminal/web/__init__.py +8 -0
- calc_terminal/web/cat_runtime.py +710 -0
- calc_terminal/web/server.py +2891 -0
- calc_terminal/web/static/css/app.css +3152 -0
- calc_terminal/web/static/icons/badge-72.png +0 -0
- calc_terminal/web/static/icons/cat.ico +0 -0
- calc_terminal/web/static/icons/icon-128.png +0 -0
- calc_terminal/web/static/icons/icon-144.png +0 -0
- calc_terminal/web/static/icons/icon-152.png +0 -0
- calc_terminal/web/static/icons/icon-192.png +0 -0
- calc_terminal/web/static/icons/icon-384.png +0 -0
- calc_terminal/web/static/icons/icon-512.png +0 -0
- calc_terminal/web/static/icons/icon-72.png +0 -0
- calc_terminal/web/static/icons/icon-96.png +0 -0
- calc_terminal/web/static/icons/icon.svg +34 -0
- calc_terminal/web/static/icons/new-project.png +0 -0
- calc_terminal/web/static/icons/open-project.png +0 -0
- calc_terminal/web/static/index.html +734 -0
- calc_terminal/web/static/js/app.js +2403 -0
- calc_terminal/web/static/manifest.json +88 -0
- calc_terminal/web/static/sw.js +230 -0
- calc_terminal/workflow_engine.py +769 -0
- calc_terminal/workspace.py +593 -0
- calc_terminal/workspace_index.py +385 -0
- cct_cli-0.7.9.0.dist-info/METADATA +210 -0
- cct_cli-0.7.9.0.dist-info/RECORD +325 -0
- cct_cli-0.7.9.0.dist-info/WHEEL +5 -0
- cct_cli-0.7.9.0.dist-info/entry_points.txt +4 -0
- cct_cli-0.7.9.0.dist-info/licenses/LICENSE +21 -0
- cct_cli-0.7.9.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CAT v0.7.9.0 — ui/empty_state.py: the chat-pane startup centerpiece.
|
|
3
|
+
|
|
4
|
+
Shown by ConversationView's existing welcome slot whenever the main
|
|
5
|
+
chat has no conversation in it, and removed the moment real turns
|
|
6
|
+
arrive (the same hide_welcome() chokepoint that always governed this
|
|
7
|
+
slot — so the art can never leak into history, prompts or copies).
|
|
8
|
+
|
|
9
|
+
┌─────────────── chat pane only ───────────────┐
|
|
10
|
+
│ │
|
|
11
|
+
│ ██████╗ █████╗ ████████╗ │
|
|
12
|
+
│ ██╔════╝██╔══██╗╚══██╔══╝ │
|
|
13
|
+
│ ██║ ███████║ ██║ │
|
|
14
|
+
│ ██║ ██╔══██║ ██║ │
|
|
15
|
+
│ ╚██████╗██║ ██║ ██║ │
|
|
16
|
+
│ ╚═════╝╚═╝ ╚═╝ ╚═╝ │
|
|
17
|
+
│ │
|
|
18
|
+
│ CAT v0.7.9.0 │
|
|
19
|
+
│ Chemistry • Coding • Intelligence │
|
|
20
|
+
│ ● CAT Notebook ready │
|
|
21
|
+
│ Create · Analyze · Build · Debug · Research│
|
|
22
|
+
│ │
|
|
23
|
+
└──────────────────────────────────────────────┘
|
|
24
|
+
|
|
25
|
+
Hard rules:
|
|
26
|
+
|
|
27
|
+
* This is an EMPTY-STATE COMPONENT, never a message: it is not added to
|
|
28
|
+
session turns, never sent to the model, and is removed before the
|
|
29
|
+
first real bubble mounts.
|
|
30
|
+
* Centered inside the CHAT PANE ONLY — it lives inside
|
|
31
|
+
ConversationView, which is already inside WorkspaceShell's main
|
|
32
|
+
column, so explorer width / composer height / window resizes can
|
|
33
|
+
never make it overlap anything (the v0.7.9.0 layout contract holds).
|
|
34
|
+
* Mode-aware: the ready line reflects the ACTUAL active mode
|
|
35
|
+
(ai_modes.current_mode()) via set_mode().
|
|
36
|
+
* Responsive: picks full / compact / text-only word art from the live
|
|
37
|
+
widget width (on_resize), so a narrow chat pane never overflows.
|
|
38
|
+
* Subtle one-shot entrance animation (fade + slight rise) and an
|
|
39
|
+
optional slow pulse on the ready dot only — the word art itself never
|
|
40
|
+
moves continuously.
|
|
41
|
+
|
|
42
|
+
The animated CAT bot (ui/cat_agent.py) is a DIFFERENT component for a
|
|
43
|
+
DIFFERENT moment: that one appears while the agent is actively working;
|
|
44
|
+
this one exists only while there is nothing in the chat at all.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
if __name__ == "__main__":
|
|
48
|
+
print("This is a library file and is not meant to be run directly.")
|
|
49
|
+
import sys
|
|
50
|
+
sys.exit(1)
|
|
51
|
+
|
|
52
|
+
from . import design_system
|
|
53
|
+
|
|
54
|
+
TEXTUAL_AVAILABLE = True
|
|
55
|
+
try:
|
|
56
|
+
from textual.containers import Vertical
|
|
57
|
+
from textual.widgets import Static
|
|
58
|
+
except Exception:
|
|
59
|
+
TEXTUAL_AVAILABLE = False
|
|
60
|
+
|
|
61
|
+
# Width budgets (columns) of the two word-art variants, including their
|
|
62
|
+
# side padding — used to pick the variant that fits without overflow.
|
|
63
|
+
# Canonical values live in design_system (single art source); these
|
|
64
|
+
# aliases stay so existing importers (ui/dashboard.py) keep working.
|
|
65
|
+
_FULL_ART_MIN_WIDTH = design_system.FULL_ART_MIN_WIDTH
|
|
66
|
+
_COMPACT_ART_MIN_WIDTH = design_system.COMPACT_ART_MIN_WIDTH
|
|
67
|
+
|
|
68
|
+
if TEXTUAL_AVAILABLE:
|
|
69
|
+
|
|
70
|
+
def _full_art():
|
|
71
|
+
"""The app's own CAT block letters — delegated to
|
|
72
|
+
design_system (single source of truth: the classic terminal
|
|
73
|
+
boots with exactly these glyphs from calc_terminal.app.LOGO).
|
|
74
|
+
Kept under this name so existing importers keep working."""
|
|
75
|
+
return design_system.full_wordmark()
|
|
76
|
+
|
|
77
|
+
# Compact variant for narrow chat panes — same three letters,
|
|
78
|
+
# half-block construction, still unmistakably "CAT".
|
|
79
|
+
_COMPACT_ART = design_system.COMPACT_WORDMARK
|
|
80
|
+
|
|
81
|
+
def _lerp_hex(a: str, b: str, t: float) -> str:
|
|
82
|
+
"""Linear blend of two '#rrggbb' colors — powers the subtle
|
|
83
|
+
top-to-bottom gradient across the word art lines."""
|
|
84
|
+
try:
|
|
85
|
+
ar, ag, ab = int(a[1:3], 16), int(a[3:5], 16), int(a[5:7], 16)
|
|
86
|
+
br, bg, bb = int(b[1:3], 16), int(b[3:5], 16), int(b[5:7], 16)
|
|
87
|
+
return "#{:02x}{:02x}{:02x}".format(
|
|
88
|
+
round(ar + (br - ar) * t),
|
|
89
|
+
round(ag + (bg - ag) * t),
|
|
90
|
+
round(ab + (bb - ab) * t))
|
|
91
|
+
except Exception:
|
|
92
|
+
return b
|
|
93
|
+
|
|
94
|
+
def _darken(hex_color: str, amount: float) -> str:
|
|
95
|
+
"""Blend a hex color toward black by `amount` (0..1). Used only
|
|
96
|
+
by the light theme so gradient stops stay readable on white."""
|
|
97
|
+
try:
|
|
98
|
+
r = int(hex_color[1:3], 16)
|
|
99
|
+
g = int(hex_color[3:5], 16)
|
|
100
|
+
b = int(hex_color[5:7], 16)
|
|
101
|
+
f = max(0.0, min(1.0, 1.0 - amount))
|
|
102
|
+
return "#{:02x}{:02x}{:02x}".format(
|
|
103
|
+
round(r * f), round(g * f), round(b * f))
|
|
104
|
+
except Exception:
|
|
105
|
+
return hex_color
|
|
106
|
+
|
|
107
|
+
def _mode_label(mode_key=None):
|
|
108
|
+
"""'CAT <Mode>' identity from the ACTUAL mode state (never a
|
|
109
|
+
hard-coded global label)."""
|
|
110
|
+
try:
|
|
111
|
+
from .cat_agent import mode_label_for
|
|
112
|
+
from .. import ai_modes
|
|
113
|
+
return mode_label_for(mode_key or ai_modes.current_mode())
|
|
114
|
+
except Exception:
|
|
115
|
+
return "CAT"
|
|
116
|
+
|
|
117
|
+
class CATChatEmptyState(Vertical):
|
|
118
|
+
"""The startup / empty-chat centerpiece. State inputs: version,
|
|
119
|
+
mode, ready, theme — all rendered as plain Rich markup; nothing
|
|
120
|
+
here ever touches session state."""
|
|
121
|
+
|
|
122
|
+
DEFAULT_CSS = """
|
|
123
|
+
CATChatEmptyState {
|
|
124
|
+
width: 1fr;
|
|
125
|
+
height: 1fr;
|
|
126
|
+
align: center middle;
|
|
127
|
+
opacity: 0;
|
|
128
|
+
offset-y: 3;
|
|
129
|
+
}
|
|
130
|
+
CATChatEmptyState.open {
|
|
131
|
+
opacity: 1;
|
|
132
|
+
offset-y: 0;
|
|
133
|
+
}
|
|
134
|
+
CATChatEmptyState {
|
|
135
|
+
transition: opacity 500ms, offset-y 500ms;
|
|
136
|
+
}
|
|
137
|
+
CATChatEmptyState #cct-empty-art {
|
|
138
|
+
width: auto;
|
|
139
|
+
text-align: center;
|
|
140
|
+
}
|
|
141
|
+
CATChatEmptyState .cct-empty-line {
|
|
142
|
+
width: 1fr;
|
|
143
|
+
text-align: center;
|
|
144
|
+
}
|
|
145
|
+
CATChatEmptyState #cct-empty-ready {
|
|
146
|
+
transition: opacity 1300ms;
|
|
147
|
+
}
|
|
148
|
+
"""
|
|
149
|
+
|
|
150
|
+
def __init__(self, version="0.7.9.0", mode_key=None, ready=True,
|
|
151
|
+
tagline="Chemistry \u2022 Coding \u2022 Intelligence"):
|
|
152
|
+
super().__init__(id="cct-empty-state")
|
|
153
|
+
self._version = version
|
|
154
|
+
self._mode_key = mode_key
|
|
155
|
+
self._ready = bool(ready)
|
|
156
|
+
self._tagline = tagline
|
|
157
|
+
self._compact = False # re-evaluated on resize
|
|
158
|
+
|
|
159
|
+
# ------------------------------------------------------ content --
|
|
160
|
+
def _art_markup(self):
|
|
161
|
+
"""Word-art block with a restrained top-to-bottom accent
|
|
162
|
+
gradient (mode gradient stops — same family the composer
|
|
163
|
+
badge uses). Falls back through compact → text-only as the
|
|
164
|
+
pane narrows, so it can never overflow horizontally.
|
|
165
|
+
|
|
166
|
+
v0.7.9.0 light-mode audit: mode-gradient end stops are tuned
|
|
167
|
+
for dark surfaces; on white the pale ends dropped below 2:1
|
|
168
|
+
contrast and looked washed out. In LIGHT mode each line
|
|
169
|
+
color is blended ~25% toward black first, keeping the
|
|
170
|
+
gradient subtle but clearly legible."""
|
|
171
|
+
from . import theme_css
|
|
172
|
+
g_start, g_end = theme_css.gradient_hex()
|
|
173
|
+
try:
|
|
174
|
+
from .. import theme as _theme
|
|
175
|
+
if _theme.is_light():
|
|
176
|
+
g_start = _darken(g_start, 0.25)
|
|
177
|
+
g_end = _darken(g_end, 0.25)
|
|
178
|
+
except Exception:
|
|
179
|
+
pass
|
|
180
|
+
if self._compact == "text":
|
|
181
|
+
return f"[b]{g_start}CAT[/]"
|
|
182
|
+
art = _COMPACT_ART if self._compact else _full_art()
|
|
183
|
+
n = max(1, len(art) - 1)
|
|
184
|
+
lines = []
|
|
185
|
+
for i, line in enumerate(art):
|
|
186
|
+
color = _lerp_hex(g_start, g_end, i / n)
|
|
187
|
+
lines.append(f"[{color} b]{line}[/]")
|
|
188
|
+
return "\n".join(lines)
|
|
189
|
+
|
|
190
|
+
def _body_markup(self):
|
|
191
|
+
from . import theme_css
|
|
192
|
+
faint = theme_css.current_hex("text-faint")
|
|
193
|
+
muted = theme_css.current_hex("text-muted")
|
|
194
|
+
success = theme_css.current_hex("success")
|
|
195
|
+
label = _mode_label(self._mode_key)
|
|
196
|
+
status = (f"[{success}]\u25cf[/] [{muted} b]{label}[/] "
|
|
197
|
+
f"[{faint}]ready[/]") if self._ready else \
|
|
198
|
+
(f"[{faint}]\u25cb starting\u2026[/]")
|
|
199
|
+
suggestions = ("Create \u00b7 Analyze \u00b7 Build \u00b7 Debug "
|
|
200
|
+
"\u00b7 Research")
|
|
201
|
+
return {
|
|
202
|
+
"art": self._art_markup(),
|
|
203
|
+
"version": f"[{muted} b]CAT v{self._version}[/]",
|
|
204
|
+
"tagline": f"[{faint}]{self._tagline}[/]",
|
|
205
|
+
"ready": status,
|
|
206
|
+
"suggestions": f"[{faint}]{suggestions}[/]",
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
def _repaint_children(self):
|
|
210
|
+
body = self._body_markup()
|
|
211
|
+
try:
|
|
212
|
+
self.query_one("#cct-empty-art", Static).update(body["art"])
|
|
213
|
+
self.query_one("#cct-empty-version", Static).update(
|
|
214
|
+
body["version"])
|
|
215
|
+
self.query_one("#cct-empty-tagline", Static).update(
|
|
216
|
+
body["tagline"])
|
|
217
|
+
self.query_one("#cct-empty-ready", Static).update(
|
|
218
|
+
body["ready"])
|
|
219
|
+
self.query_one("#cct-empty-suggest", Static).update(
|
|
220
|
+
body["suggestions"])
|
|
221
|
+
except Exception:
|
|
222
|
+
pass
|
|
223
|
+
|
|
224
|
+
# ------------------------------------------------------- events --
|
|
225
|
+
def compose(self):
|
|
226
|
+
body = self._body_markup()
|
|
227
|
+
yield Static(body["art"], id="cct-empty-art")
|
|
228
|
+
yield Static(body["version"], id="cct-empty-version",
|
|
229
|
+
classes="cct-empty-line")
|
|
230
|
+
yield Static("", id="cct-empty-gap1", classes="cct-empty-line")
|
|
231
|
+
yield Static(body["tagline"], id="cct-empty-tagline",
|
|
232
|
+
classes="cct-empty-line")
|
|
233
|
+
yield Static("", id="cct-empty-gap2", classes="cct-empty-line")
|
|
234
|
+
yield Static(body["ready"], id="cct-empty-ready",
|
|
235
|
+
classes="cct-empty-line")
|
|
236
|
+
yield Static(body["suggestions"], id="cct-empty-suggest",
|
|
237
|
+
classes="cct-empty-line")
|
|
238
|
+
|
|
239
|
+
def on_mount(self):
|
|
240
|
+
self.call_after_refresh(self._fit_width)
|
|
241
|
+
# Subtle entrance: fade in + slight upward settle (one-shot).
|
|
242
|
+
# Implemented with a class-toggle transition instead of a
|
|
243
|
+
# CSS @keyframes animation — an infinite/long animation can
|
|
244
|
+
# hold Textual's "wait for screen" busy state, while this
|
|
245
|
+
# settles once and is done.
|
|
246
|
+
self.call_later(self.add_class, "open")
|
|
247
|
+
# Optional light status effect: slow pulse on the ready dot
|
|
248
|
+
# ONLY (never the word art). A gentle timer-driven opacity
|
|
249
|
+
# oscillation, smoothed by the CSS transition above.
|
|
250
|
+
if self._ready:
|
|
251
|
+
self._pulse_up = False
|
|
252
|
+
# Tracked (not discarded): the pulse must die with the
|
|
253
|
+
# widget instead of ticking forever after unmount.
|
|
254
|
+
self._pulse_timer = self.set_interval(1.3, self._pulse_ready_dot)
|
|
255
|
+
else:
|
|
256
|
+
self._pulse_timer = None
|
|
257
|
+
|
|
258
|
+
def on_unmount(self):
|
|
259
|
+
design_system.stop_timer(getattr(self, "_pulse_timer", None))
|
|
260
|
+
self._pulse_timer = None
|
|
261
|
+
|
|
262
|
+
def _pulse_ready_dot(self):
|
|
263
|
+
try:
|
|
264
|
+
dot = self.query_one("#cct-empty-ready", Static)
|
|
265
|
+
dot.styles.opacity = 1.0 if not self._pulse_up else 0.45
|
|
266
|
+
self._pulse_up = not self._pulse_up
|
|
267
|
+
except Exception:
|
|
268
|
+
pass
|
|
269
|
+
|
|
270
|
+
def on_resize(self, event):
|
|
271
|
+
self._fit_width()
|
|
272
|
+
|
|
273
|
+
def _fit_width(self):
|
|
274
|
+
"""Responsive rule: pick the largest art variant that fits
|
|
275
|
+
the LIVE chat-pane width (never overflow horizontally)."""
|
|
276
|
+
try:
|
|
277
|
+
w = self.size.width or self.outer_size.width or 0
|
|
278
|
+
except Exception:
|
|
279
|
+
w = 0
|
|
280
|
+
if w >= _FULL_ART_MIN_WIDTH:
|
|
281
|
+
compact = False
|
|
282
|
+
elif w >= _COMPACT_ART_MIN_WIDTH:
|
|
283
|
+
compact = True
|
|
284
|
+
else:
|
|
285
|
+
compact = "text"
|
|
286
|
+
if compact != self._compact:
|
|
287
|
+
self._compact = compact
|
|
288
|
+
self._repaint_children()
|
|
289
|
+
|
|
290
|
+
# ------------------------------------------------- public state --
|
|
291
|
+
def set_mode(self, mode_key):
|
|
292
|
+
"""Called with the ACTUAL current mode whenever it changes —
|
|
293
|
+
the ready line always names the real persona."""
|
|
294
|
+
self._mode_key = mode_key
|
|
295
|
+
self._repaint_children()
|
|
296
|
+
|
|
297
|
+
def repaint_theme(self):
|
|
298
|
+
"""Re-read theme colors (called after /theme switches)."""
|
|
299
|
+
self._repaint_children()
|
|
300
|
+
|
|
301
|
+
else:
|
|
302
|
+
CATChatEmptyState = None
|