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,441 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CAT UI — ui/preview_panel.py: the Web Preview pane.
|
|
3
|
+
|
|
4
|
+
Lives in the right workspace pane, switchable with the Code Editor
|
|
5
|
+
(never mounted/unmounted — only `display` flips, so navigation history,
|
|
6
|
+
URL bar text and scroll survive Code↔Preview switches):
|
|
7
|
+
|
|
8
|
+
┌──────────────────────────────────────────────────────────┐
|
|
9
|
+
│ ← → ⟳ [ http://127.0.0.1:5173/ ] 🖥 💻 📟 📱 ◫ ⏻ ⿻ │ BrowserBar
|
|
10
|
+
├──────────────────────────────────────────────────────────┤
|
|
11
|
+
│ [👁 Preview] [📋 Console] [⚠ Problems (0)] │ DevToolsBar
|
|
12
|
+
├──────────────────────────────────────────────────────────┤
|
|
13
|
+
│ ● Writing style.css │ activity strip
|
|
14
|
+
│ ✓ Preview synchronized │
|
|
15
|
+
├──────────────────────────────────────────────────────────┤
|
|
16
|
+
│ # CAT Live ← LIVE DOM outline / │ viewport
|
|
17
|
+
│ js-ran real Chromium / Console │
|
|
18
|
+
└──────────────────────────────────────────────────────────┘
|
|
19
|
+
|
|
20
|
+
Terminal-first contract: the page itself renders in genuine Chromium
|
|
21
|
+
(browser/engine.py); this panel shows its structured snapshot — title,
|
|
22
|
+
headings, links, buttons, images, console errors — plus dev server & console.
|
|
23
|
+
No external browser window is ever opened.
|
|
24
|
+
|
|
25
|
+
All colors are `$variables` resolved by CCTApp.get_css_variables(), so
|
|
26
|
+
the panel follows every theme automatically.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
if __name__ == "__main__":
|
|
30
|
+
print("This is a library file and is not meant to be run directly.")
|
|
31
|
+
import sys
|
|
32
|
+
sys.exit(1)
|
|
33
|
+
|
|
34
|
+
import os
|
|
35
|
+
import re
|
|
36
|
+
from typing import List, Optional
|
|
37
|
+
|
|
38
|
+
TEXTUAL_AVAILABLE = True
|
|
39
|
+
try:
|
|
40
|
+
from textual.containers import Horizontal, Vertical, VerticalScroll
|
|
41
|
+
from textual.widgets import Button, Input, Static
|
|
42
|
+
except Exception:
|
|
43
|
+
TEXTUAL_AVAILABLE = False
|
|
44
|
+
|
|
45
|
+
MAX_ACTIVITY_LINES = 6
|
|
46
|
+
MAX_CONSOLE_LINES = 100
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _outline_markup(snapshot):
|
|
50
|
+
"""PreviewSnapshot -> Rich markup for the DOM viewport."""
|
|
51
|
+
lines = []
|
|
52
|
+
if snapshot.title:
|
|
53
|
+
lines.append(f"[b]{snapshot.title}[/b]")
|
|
54
|
+
lines.append("")
|
|
55
|
+
for raw in snapshot.outline_lines[:200]:
|
|
56
|
+
line = str(raw)
|
|
57
|
+
esc = line.replace("[", "\\[")
|
|
58
|
+
if esc.startswith("#"):
|
|
59
|
+
lines.append(f"[b {chr(36)}accent]{esc}[/]")
|
|
60
|
+
elif esc.lstrip().startswith("↗"):
|
|
61
|
+
lines.append(f"[{chr(36)}text-muted]{esc}[/]")
|
|
62
|
+
elif "✗" in esc:
|
|
63
|
+
lines.append(f"[{chr(36)}error]{esc}[/]")
|
|
64
|
+
else:
|
|
65
|
+
lines.append(f"[{chr(36)}text]{esc}[/]")
|
|
66
|
+
return "\n".join(lines)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
if TEXTUAL_AVAILABLE:
|
|
70
|
+
|
|
71
|
+
class BrowserBar(Horizontal):
|
|
72
|
+
"""Navigation, URL input, viewport presets, and window controls."""
|
|
73
|
+
|
|
74
|
+
def __init__(self):
|
|
75
|
+
super().__init__(id="cct-browser-bar")
|
|
76
|
+
|
|
77
|
+
def compose(self):
|
|
78
|
+
yield Button("←", id="cct-bv-back", classes="cct-bv-btn", tooltip="Back")
|
|
79
|
+
yield Button("→", id="cct-bv-fwd", classes="cct-bv-btn", tooltip="Forward")
|
|
80
|
+
yield Button("⟳", id="cct-bv-reload", classes="cct-bv-btn", tooltip="Reload")
|
|
81
|
+
yield Input(value="", placeholder="http://127.0.0.1:<port>/",
|
|
82
|
+
id="cct-bv-url")
|
|
83
|
+
# Viewport presets
|
|
84
|
+
yield Button("🖥", id="cct-bv-vp-desktop", classes="cct-bv-btn -vp-active", tooltip="Desktop (100%)")
|
|
85
|
+
yield Button("💻", id="cct-bv-vp-laptop", classes="cct-bv-btn", tooltip="Laptop (1024px)")
|
|
86
|
+
yield Button("📟", id="cct-bv-vp-tablet", classes="cct-bv-btn", tooltip="Tablet (768px)")
|
|
87
|
+
yield Button("📱", id="cct-bv-vp-mobile", classes="cct-bv-btn", tooltip="Mobile (375px)")
|
|
88
|
+
# Window / Layout controls
|
|
89
|
+
yield Button("◫", id="cct-bv-split", classes="cct-bv-btn", tooltip="Split editor / preview")
|
|
90
|
+
yield Button("⏻", id="cct-bv-close", classes="cct-bv-btn cct-bv-danger", tooltip="Stop preview")
|
|
91
|
+
yield Button("⿻", id="cct-bv-fullscreen", classes="cct-bv-btn", tooltip="Fullscreen")
|
|
92
|
+
|
|
93
|
+
class DevToolsBar(Horizontal):
|
|
94
|
+
"""Developer Tools sub-navigation: Preview | Console | Problems."""
|
|
95
|
+
|
|
96
|
+
def __init__(self):
|
|
97
|
+
super().__init__(id="cct-devtools-bar")
|
|
98
|
+
|
|
99
|
+
def compose(self):
|
|
100
|
+
yield Button("👁 Preview", id="cct-dev-tab-preview", classes="cct-dev-tab -tab-active")
|
|
101
|
+
yield Button("📋 Console", id="cct-dev-tab-console", classes="cct-dev-tab")
|
|
102
|
+
yield Button("⚠ Problems", id="cct-dev-tab-problems", classes="cct-dev-tab")
|
|
103
|
+
|
|
104
|
+
class PreviewPanel(Vertical):
|
|
105
|
+
"""The WEB PREVIEW half of the right pane with developer tools and responsive viewports."""
|
|
106
|
+
|
|
107
|
+
def __init__(self, **kwargs):
|
|
108
|
+
super().__init__(id="cct-preview", **kwargs)
|
|
109
|
+
self._activity_lines = []
|
|
110
|
+
self._console_lines = []
|
|
111
|
+
self._status = ""
|
|
112
|
+
self._last_click_time = 0
|
|
113
|
+
self._last_click_target = None
|
|
114
|
+
self._active_tab = "preview" # "preview" | "console" | "problems"
|
|
115
|
+
self._active_viewport = "desktop" # "desktop" | "laptop" | "tablet" | "mobile"
|
|
116
|
+
self._last_snapshot = None
|
|
117
|
+
|
|
118
|
+
def compose(self):
|
|
119
|
+
yield BrowserBar()
|
|
120
|
+
yield DevToolsBar()
|
|
121
|
+
yield Static("", id="cct-bv-status")
|
|
122
|
+
with VerticalScroll(id="cct-bv-scroll"):
|
|
123
|
+
yield Static("", id="cct-bv-viewport")
|
|
124
|
+
yield Static("", id="cct-bv-console")
|
|
125
|
+
yield Static("", id="cct-bv-problems")
|
|
126
|
+
yield Static("", id="cct-bv-activity")
|
|
127
|
+
|
|
128
|
+
def on_mount(self):
|
|
129
|
+
self.show_status("Preview stopped — click ▷ on an HTML file or run dev server.")
|
|
130
|
+
self._switch_dev_tab("preview")
|
|
131
|
+
|
|
132
|
+
# ------------------------------------------------------ status --
|
|
133
|
+
def show_status(self, text, error=False):
|
|
134
|
+
try:
|
|
135
|
+
status = self.query_one("#cct-bv-status", Static)
|
|
136
|
+
status.update(text)
|
|
137
|
+
status.set_class(bool(error), "-error")
|
|
138
|
+
except Exception:
|
|
139
|
+
pass
|
|
140
|
+
|
|
141
|
+
def set_url(self, url):
|
|
142
|
+
try:
|
|
143
|
+
self.query_one("#cct-bv-url", Input).value = url or ""
|
|
144
|
+
except Exception:
|
|
145
|
+
pass
|
|
146
|
+
|
|
147
|
+
# ---------------------------------------------------- tabs & viewport --
|
|
148
|
+
def _switch_dev_tab(self, tab_name: str):
|
|
149
|
+
"""Switch between Preview, Console, and Problems."""
|
|
150
|
+
self._active_tab = tab_name
|
|
151
|
+
for name in ("preview", "console", "problems"):
|
|
152
|
+
btn = self.query_one(f"#cct-dev-tab-{name}", Button)
|
|
153
|
+
btn.set_class(name == tab_name, "-tab-active")
|
|
154
|
+
|
|
155
|
+
vp = self.query_one("#cct-bv-viewport", Static)
|
|
156
|
+
con = self.query_one("#cct-bv-console", Static)
|
|
157
|
+
prob = self.query_one("#cct-bv-problems", Static)
|
|
158
|
+
|
|
159
|
+
vp.display = (tab_name == "preview")
|
|
160
|
+
con.display = (tab_name == "console")
|
|
161
|
+
prob.display = (tab_name == "problems")
|
|
162
|
+
|
|
163
|
+
if tab_name == "console":
|
|
164
|
+
self._render_console()
|
|
165
|
+
elif tab_name == "problems":
|
|
166
|
+
self._render_problems()
|
|
167
|
+
|
|
168
|
+
def _switch_viewport(self, vp_name: str):
|
|
169
|
+
"""Set responsive viewport preset."""
|
|
170
|
+
self._active_viewport = vp_name
|
|
171
|
+
for name in ("desktop", "laptop", "tablet", "mobile"):
|
|
172
|
+
try:
|
|
173
|
+
btn = self.query_one(f"#cct-bv-vp-{name}", Button)
|
|
174
|
+
btn.set_class(name == vp_name, "-vp-active")
|
|
175
|
+
except Exception:
|
|
176
|
+
pass
|
|
177
|
+
|
|
178
|
+
# Apply width styling to the viewport scroll container
|
|
179
|
+
try:
|
|
180
|
+
scroll = self.query_one("#cct-bv-scroll", VerticalScroll)
|
|
181
|
+
widths = {
|
|
182
|
+
"desktop": "100%",
|
|
183
|
+
"laptop": "1024",
|
|
184
|
+
"tablet": "768",
|
|
185
|
+
"mobile": "375",
|
|
186
|
+
}
|
|
187
|
+
w = widths.get(vp_name, "100%")
|
|
188
|
+
if w == "100%":
|
|
189
|
+
scroll.styles.width = "100%"
|
|
190
|
+
scroll.styles.max_width = None
|
|
191
|
+
else:
|
|
192
|
+
scroll.styles.max_width = int(w)
|
|
193
|
+
self.show_status(f"Viewport set to {vp_name.capitalize()} ({w}px)")
|
|
194
|
+
except Exception:
|
|
195
|
+
pass
|
|
196
|
+
|
|
197
|
+
# ---------------------------------------------------- console & problems --
|
|
198
|
+
def add_console_log(self, text: str, is_error: bool = False):
|
|
199
|
+
"""Log a line from browser runtime or local dev server."""
|
|
200
|
+
prefix = "[error]✗[/]" if is_error else "[info]ℹ[/]"
|
|
201
|
+
self._console_lines.append(f"{prefix} {text}")
|
|
202
|
+
if len(self._console_lines) > MAX_CONSOLE_LINES:
|
|
203
|
+
del self._console_lines[:-MAX_CONSOLE_LINES]
|
|
204
|
+
if self._active_tab == "console":
|
|
205
|
+
self._render_console()
|
|
206
|
+
|
|
207
|
+
def _render_console(self):
|
|
208
|
+
try:
|
|
209
|
+
con = self.query_one("#cct-bv-console", Static)
|
|
210
|
+
if not self._console_lines:
|
|
211
|
+
con.update("[dim]No console output captured yet.[/dim]")
|
|
212
|
+
else:
|
|
213
|
+
con.update("\n".join(self._console_lines))
|
|
214
|
+
except Exception:
|
|
215
|
+
pass
|
|
216
|
+
|
|
217
|
+
def _render_problems(self):
|
|
218
|
+
"""Render problems from DiagnosticsManager with clickable jump links."""
|
|
219
|
+
try:
|
|
220
|
+
prob = self.query_one("#cct-bv-problems", Static)
|
|
221
|
+
from ..preview.diagnostics import get_diagnostics_manager
|
|
222
|
+
diags = get_diagnostics_manager().list_diagnostics()
|
|
223
|
+
|
|
224
|
+
# Update the tab label with error count
|
|
225
|
+
err_count = len([d for d in diags if d.severity == "error"])
|
|
226
|
+
prob_btn = self.query_one("#cct-dev-tab-problems", Button)
|
|
227
|
+
prob_btn.label = f"⚠ Problems ({err_count})" if err_count else "⚠ Problems"
|
|
228
|
+
|
|
229
|
+
if not diags:
|
|
230
|
+
prob.update("[dim]✓ No problems detected in preview or workspace.[/dim]")
|
|
231
|
+
return
|
|
232
|
+
|
|
233
|
+
lines = [f"[b]Diagnostics ({len(diags)} items):[/b]\n"]
|
|
234
|
+
for i, d in enumerate(diags):
|
|
235
|
+
color = "red" if d.severity == "error" else ("yellow" if d.severity == "warning" else "cyan")
|
|
236
|
+
loc = ""
|
|
237
|
+
if d.file:
|
|
238
|
+
fn = os.path.basename(d.file)
|
|
239
|
+
pos = f":{d.line}" if d.line is not None else ""
|
|
240
|
+
if d.column is not None:
|
|
241
|
+
pos += f":{d.column}"
|
|
242
|
+
loc = f" [{color}][{fn}{pos}][/]"
|
|
243
|
+
lines.append(f"[bold {color}][{d.severity.upper()}][/] {d.message}{loc}")
|
|
244
|
+
if d.file and d.line:
|
|
245
|
+
lines.append(f" [dim cyan]→ Press or click to jump: {d.file}:{d.line}[/dim cyan]")
|
|
246
|
+
lines.append("")
|
|
247
|
+
|
|
248
|
+
prob.update("\n".join(lines))
|
|
249
|
+
except Exception:
|
|
250
|
+
pass
|
|
251
|
+
|
|
252
|
+
# ---------------------------------------------------- viewport --
|
|
253
|
+
def show_snapshot(self, snapshot):
|
|
254
|
+
self._last_snapshot = snapshot
|
|
255
|
+
try:
|
|
256
|
+
viewport = self.query_one("#cct-bv-viewport", Static)
|
|
257
|
+
scroll = self.query_one("#cct-bv-scroll", VerticalScroll)
|
|
258
|
+
except Exception:
|
|
259
|
+
return
|
|
260
|
+
if snapshot is None:
|
|
261
|
+
return
|
|
262
|
+
self.set_url(snapshot.url)
|
|
263
|
+
|
|
264
|
+
# Ingest JS errors into diagnostics and console
|
|
265
|
+
if snapshot.js_errors:
|
|
266
|
+
from ..preview.diagnostics import get_diagnostics_manager
|
|
267
|
+
dm = get_diagnostics_manager()
|
|
268
|
+
for err in snapshot.js_errors:
|
|
269
|
+
err_str = str(err)
|
|
270
|
+
self.add_console_log(err_str, is_error=True)
|
|
271
|
+
dm.add(severity="error", message=err_str, source="browser")
|
|
272
|
+
self._render_problems()
|
|
273
|
+
|
|
274
|
+
if not snapshot.ok and snapshot.error:
|
|
275
|
+
self.show_status(f"WEB PREVIEW ERROR — {snapshot.error}",
|
|
276
|
+
error=True)
|
|
277
|
+
err_lines = list(snapshot.js_errors[-4:]) or \
|
|
278
|
+
["The site failed to load. Check the URL or files."]
|
|
279
|
+
body = "\n".join(str(e) for e in err_lines)
|
|
280
|
+
viewport.update(
|
|
281
|
+
f"[b {chr(36)}error]WEB PREVIEW ERROR[/]\n\n{body}\n\n"
|
|
282
|
+
f"[{chr(36)}text-faint]Chat / Files / Editor / AI remain "
|
|
283
|
+
f"unaffected.[/]")
|
|
284
|
+
else:
|
|
285
|
+
js_err = ""
|
|
286
|
+
if snapshot.js_errors:
|
|
287
|
+
last = str(snapshot.js_errors[-1])[:120]
|
|
288
|
+
js_err = f"\n[{chr(36)}warning]PREVIEW CONSOLE {last}[/]"
|
|
289
|
+
self.show_status(
|
|
290
|
+
f"● Live — {snapshot.summary_line}{js_err}")
|
|
291
|
+
viewport.update(_outline_markup(snapshot))
|
|
292
|
+
|
|
293
|
+
if self._active_tab == "preview":
|
|
294
|
+
scroll.scroll_home(animate=False)
|
|
295
|
+
|
|
296
|
+
def show_unavailable(self, detail):
|
|
297
|
+
try:
|
|
298
|
+
viewport = self.query_one("#cct-bv-viewport", Static)
|
|
299
|
+
except Exception:
|
|
300
|
+
return
|
|
301
|
+
self.show_status("Browser engine unavailable.", error=True)
|
|
302
|
+
viewport.update(
|
|
303
|
+
f"[b {chr(36)}warning]BROWSER ENGINE UNAVAILABLE[/]\n\n"
|
|
304
|
+
f"{detail}\n\n"
|
|
305
|
+
f"[{chr(36)}text-faint]Everything else in CAT keeps working: "
|
|
306
|
+
f"chat, files, editor and the AI agent are unaffected.[/]")
|
|
307
|
+
|
|
308
|
+
# ----------------------------------------------------- activity --
|
|
309
|
+
def push_activity(self, text, done=False):
|
|
310
|
+
"""One line of CAT BUILD ACTIVITY (spec section 15). Kept in
|
|
311
|
+
a bounded ring so rapid AI writes can't flood the pane."""
|
|
312
|
+
mark = "✓" if done else "●"
|
|
313
|
+
self._activity_lines.append(f"{mark} {text}")
|
|
314
|
+
del self._activity_lines[:-MAX_ACTIVITY_LINES:]
|
|
315
|
+
try:
|
|
316
|
+
widget = self.query_one("#cct-bv-activity", Static)
|
|
317
|
+
body = "\n".join(self._activity_lines)
|
|
318
|
+
widget.update(body)
|
|
319
|
+
widget.set_class(True, "-has-lines") if body else None
|
|
320
|
+
except Exception:
|
|
321
|
+
pass
|
|
322
|
+
|
|
323
|
+
def clear_activity(self):
|
|
324
|
+
self._activity_lines = []
|
|
325
|
+
try:
|
|
326
|
+
self.query_one("#cct-bv-activity", Static).update("")
|
|
327
|
+
except Exception:
|
|
328
|
+
pass
|
|
329
|
+
|
|
330
|
+
# ------------------------------------------------------- events --
|
|
331
|
+
def on_button_pressed(self, event):
|
|
332
|
+
bid = event.button.id
|
|
333
|
+
app = getattr(self, "app", None)
|
|
334
|
+
if app is None:
|
|
335
|
+
return
|
|
336
|
+
event.stop()
|
|
337
|
+
|
|
338
|
+
# DevTools tabs
|
|
339
|
+
if bid == "cct-dev-tab-preview":
|
|
340
|
+
self._switch_dev_tab("preview")
|
|
341
|
+
return
|
|
342
|
+
elif bid == "cct-dev-tab-console":
|
|
343
|
+
self._switch_dev_tab("console")
|
|
344
|
+
return
|
|
345
|
+
elif bid == "cct-dev-tab-problems":
|
|
346
|
+
self._switch_dev_tab("problems")
|
|
347
|
+
return
|
|
348
|
+
|
|
349
|
+
# Viewport presets
|
|
350
|
+
if bid == "cct-bv-vp-desktop":
|
|
351
|
+
self._switch_viewport("desktop")
|
|
352
|
+
return
|
|
353
|
+
elif bid == "cct-bv-vp-laptop":
|
|
354
|
+
self._switch_viewport("laptop")
|
|
355
|
+
return
|
|
356
|
+
elif bid == "cct-bv-vp-tablet":
|
|
357
|
+
self._switch_viewport("tablet")
|
|
358
|
+
return
|
|
359
|
+
elif bid == "cct-bv-vp-mobile":
|
|
360
|
+
self._switch_viewport("mobile")
|
|
361
|
+
return
|
|
362
|
+
|
|
363
|
+
# Navigation & Window controls
|
|
364
|
+
if bid == "cct-bv-back":
|
|
365
|
+
app.action_preview_back()
|
|
366
|
+
elif bid == "cct-bv-fwd":
|
|
367
|
+
app.action_preview_forward()
|
|
368
|
+
elif bid == "cct-bv-reload":
|
|
369
|
+
app.action_preview_reload()
|
|
370
|
+
elif bid == "cct-bv-split":
|
|
371
|
+
try:
|
|
372
|
+
from ..browser import WorkspaceMode
|
|
373
|
+
shell = app.query_one("#cct-workspace")
|
|
374
|
+
if shell.mode is WorkspaceMode.SPLIT:
|
|
375
|
+
shell.set_right_mode(WorkspaceMode.PREVIEW)
|
|
376
|
+
else:
|
|
377
|
+
shell.set_right_mode(WorkspaceMode.SPLIT)
|
|
378
|
+
except Exception:
|
|
379
|
+
pass
|
|
380
|
+
elif bid == "cct-bv-close":
|
|
381
|
+
app.action_preview_close()
|
|
382
|
+
elif bid == "cct-bv-fullscreen":
|
|
383
|
+
app.action_toggle_right_pane_fullscreen()
|
|
384
|
+
|
|
385
|
+
def on_input_submitted(self, event):
|
|
386
|
+
"""Enter in the URL bar navigates (local-first resolution)."""
|
|
387
|
+
app = getattr(self, "app", None)
|
|
388
|
+
if app is None:
|
|
389
|
+
return
|
|
390
|
+
event.stop()
|
|
391
|
+
app.preview_navigate_user(event.value)
|
|
392
|
+
|
|
393
|
+
def on_click(self, event):
|
|
394
|
+
"""Gestures & jumping to source from problems list."""
|
|
395
|
+
import time as _t
|
|
396
|
+
try:
|
|
397
|
+
widget = event.widget
|
|
398
|
+
widget_id = getattr(widget, "id", "")
|
|
399
|
+
|
|
400
|
+
# Check if clicking inside Problems to jump to error
|
|
401
|
+
if self._active_tab == "problems":
|
|
402
|
+
from ..preview.diagnostics import get_diagnostics_manager
|
|
403
|
+
diags = [d for d in get_diagnostics_manager().list_diagnostics() if d.file]
|
|
404
|
+
if diags and hasattr(self.app, "open_file_at"):
|
|
405
|
+
# Jump to the first diagnostic location with a line number
|
|
406
|
+
first_loc = next((d for d in diags if d.file and d.line), None)
|
|
407
|
+
if first_loc:
|
|
408
|
+
self.app.open_file_at(first_loc.file, first_loc.line, first_loc.column or 1)
|
|
409
|
+
event.stop()
|
|
410
|
+
return
|
|
411
|
+
|
|
412
|
+
# Don't interfere with button clicks
|
|
413
|
+
if widget_id and widget_id.startswith("cct-bv-") or widget_id.startswith("cct-dev-"):
|
|
414
|
+
return
|
|
415
|
+
|
|
416
|
+
now = _t.time()
|
|
417
|
+
if now - self._last_click_time < 0.35 and self._last_click_target == "preview":
|
|
418
|
+
self._last_click_time = 0
|
|
419
|
+
self._last_click_target = None
|
|
420
|
+
try:
|
|
421
|
+
from ..gestures.manager import handle_gesture
|
|
422
|
+
if handle_gesture("double_click", "preview", app=getattr(self, "app", None), context={}):
|
|
423
|
+
event.stop()
|
|
424
|
+
return
|
|
425
|
+
except Exception:
|
|
426
|
+
pass
|
|
427
|
+
try:
|
|
428
|
+
self.app.action_toggle_right_pane_fullscreen()
|
|
429
|
+
event.stop()
|
|
430
|
+
except Exception:
|
|
431
|
+
pass
|
|
432
|
+
else:
|
|
433
|
+
self._last_click_time = now
|
|
434
|
+
self._last_click_target = "preview"
|
|
435
|
+
except Exception:
|
|
436
|
+
pass
|
|
437
|
+
|
|
438
|
+
else:
|
|
439
|
+
PreviewPanel = None
|
|
440
|
+
BrowserBar = None
|
|
441
|
+
DevToolsBar = None
|