From 5565da83643973e792c921bc19fe2f8cf33b53c7 Mon Sep 17 00:00:00 2001 From: MathiasEngel Date: Tue, 22 Sep 2026 13:56:49 +0200 Subject: [PATCH] Pause Creative Canvas integration and avoid native macOS tray-menu crash --- .github/workflows/cross-platform-smoke.yml | 16 ----- README.md | 5 ++ core/avatar_tray.py | 16 ++++- core/canvas_manager.py | 2 +- core/configuration.py | 2 +- docs/CREATIVE_CANVAS_PAUSED.md | 22 +++++++ install_mac.sh | 16 +---- install_windows.ps1 | 33 +---------- tests/test_avatar_tray.py | 24 ++++++++ tests/test_canvas_paused.py | 40 +++++++++++++ tests/test_install_mac.py | 8 +-- tests/test_trinity_bridge.py | 2 +- tests/test_windows_installer.py | 6 +- trinity_classic.py | 68 ---------------------- trinity_launcher.py | 11 +--- 15 files changed, 123 insertions(+), 148 deletions(-) create mode 100644 docs/CREATIVE_CANVAS_PAUSED.md create mode 100644 tests/test_canvas_paused.py diff --git a/.github/workflows/cross-platform-smoke.yml b/.github/workflows/cross-platform-smoke.yml index c2ef251..a113e3c 100644 --- a/.github/workflows/cross-platform-smoke.yml +++ b/.github/workflows/cross-platform-smoke.yml @@ -25,19 +25,11 @@ jobs: steps: - uses: actions/checkout@v4 - with: - submodules: recursive - uses: actions/setup-python@v5 with: python-version: "3.11" - - uses: actions/setup-node@v4 - with: - node-version: "20" - cache: npm - cache-dependency-path: components/TrinityCanvas/package-lock.json - - name: Install test tools run: python -m pip install pytest pytest-timeout requests numpy PySide6 @@ -61,14 +53,6 @@ jobs: test-*.log if-no-files-found: warn - - name: Build and smoke-test Trinity Canvas - working-directory: components/TrinityCanvas - run: | - npm ci - npm run typecheck - npm run build - npm run test:production - - name: Validate macOS installer syntax if: runner.os == 'macOS' run: bash -n install_mac.sh diff --git a/README.md b/README.md index 30083f4..f62db69 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,9 @@ # Trinity — Academic Personal Concierge 🧞‍♀️ + +> **22.09.2026: Creative Canvas ist auf Eis.** Kein automatischer Start, +> kein Desktop-Reiter und keine Installation/CI-Builds mehr. Bestehende Canvas-Daten +> und der historische Quellcode bleiben erhalten. Für die Agentenoberfläche wird +> [TrinityHUB](https://github.com/ProfEngel/trinity-hub) separat betrieben. [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.20286707.svg)](https://doi.org/10.5281/zenodo.20286707) diff --git a/core/avatar_tray.py b/core/avatar_tray.py index 252a514..954bd39 100644 --- a/core/avatar_tray.py +++ b/core/avatar_tray.py @@ -1,10 +1,11 @@ """Small, gently blinking Trinity eyes for the desktop menu bar.""" import random +import sys from pathlib import Path from PySide6.QtCore import QObject, QRectF, QSettings, Qt, QTimer -from PySide6.QtGui import QColor, QIcon, QPainter, QPixmap +from PySide6.QtGui import QColor, QCursor, QIcon, QPainter, QPixmap from PySide6.QtWidgets import QApplication, QMenu, QSystemTrayIcon from desktop_audio_activity import desktop_audio_active @@ -74,7 +75,12 @@ def __init__(self, window, speaker_control, settings=None, home=None): self.menu.addSeparator() self.menu.addAction("Einstellungen …", window.open_settings) self.menu.addAction("Trinity beenden", QApplication.instance().quit) - self.icon.setContextMenu(self.menu) + if sys.platform == "darwin": + # Avoid native NSMenu tracking: Qt's Cocoa bridge can ask a + # SysDefined NSEvent for clickCount and abort the whole UI. + self.icon.activated.connect(self._show_menu) + else: + self.icon.setContextMenu(self.menu) self.blink_timer = QTimer(self) self.blink_timer.setSingleShot(True) self.blink_timer.timeout.connect(self._blink) @@ -94,6 +100,12 @@ def apply_startup_mode(self): return self.window.show() + def _show_menu(self, reason): + if reason in (QSystemTrayIcon.Trigger, QSystemTrayIcon.Context): + # Leave the native status-item callback before opening a Qt popup. + position = QCursor.pos() + QTimer.singleShot(0, lambda: self.menu.popup(position)) + def minimize(self): # Never hide the only way back on desktops without a system tray. if not QSystemTrayIcon.isSystemTrayAvailable(): diff --git a/core/canvas_manager.py b/core/canvas_manager.py index f8001d7..d414e4f 100644 --- a/core/canvas_manager.py +++ b/core/canvas_manager.py @@ -65,7 +65,7 @@ def __init__(self, home: str | Path, config: dict | None = None): @property def enabled(self) -> bool: - return bool(self.settings.get("enabled", True)) + return bool(self.settings.get("enabled", False)) @property def url(self) -> str: diff --git a/core/configuration.py b/core/configuration.py index a6c1ae0..535f878 100644 --- a/core/configuration.py +++ b/core/configuration.py @@ -131,7 +131,7 @@ def default_config(platform_name=None): "default_workflow": "Flux2_Klein_T2I_API.json", }, "canvas": { - "enabled": True, + "enabled": False, "install_dir": "", "port": 8787, "host": "127.0.0.1", diff --git a/docs/CREATIVE_CANVAS_PAUSED.md b/docs/CREATIVE_CANVAS_PAUSED.md new file mode 100644 index 0000000..82629c4 --- /dev/null +++ b/docs/CREATIVE_CANVAS_PAUSED.md @@ -0,0 +1,22 @@ +# Creative Canvas pausiert – 22.09.2026 + +TrinityHUB ist die separat betriebene Agentenoberfläche. Creative Canvas ist +nicht mehr Teil des normalen Desktop-Betriebs: + +- kein Autostart, auch nicht mit einem alten `canvas.enabled: true`; +- kein Canvas-Reiter und keine Canvas-Statuskarte; +- kein Download/Build durch macOS-/Windows-Installer oder Desktop-CI; +- neue Konfigurationen deaktivieren Canvas standardmäßig. + +Quellcode, Git-Submodule-Verweis und bestehende Canvas-Daten bleiben zur späteren +Wiederaufnahme erhalten. Die alten ausdrücklich aufgerufenen `trinity canvas` +Wartungsbefehle bleiben verfügbar; sie sind kein Teil des normalen Startwegs. +Kein Repository wird gelöscht oder schreibgeschützt archiviert. HUB wird dadurch +weder neu installiert noch umkonfiguriert. Bestehende Nutzerkonfigurationen und +Sprachmodelle werden nicht migriert. + +Die MiniTrinity verwendet auf macOS ein verzögert geöffnetes Qt-Popup statt des +nativen Tray-Kontextmenüs. Dies umgeht den in lokalen Absturzberichten gefundenen +`NSEvent clickCount`/`NSMenuTrackingSession`-Abbruch. Windows behält sein natives +Tray-Menü. Automatisierte Tests prüfen die verzögerte Ausführung; die konkrete +macOS-Menüinteraktion bleibt zusätzlich praktisch zu prüfen. diff --git a/install_mac.sh b/install_mac.sh index e11f566..6fdabe7 100644 --- a/install_mac.sh +++ b/install_mac.sh @@ -138,7 +138,7 @@ fi echo "" echo "📥 Lade aktuelle Trinity-Version herunter..." if command -v git &> /dev/null; then - if ! git clone --branch main --single-branch --recurse-submodules --shallow-submodules "$REPOSITORY" "$INSTALL_DIR"; then + if ! git clone --branch main --single-branch "$REPOSITORY" "$INSTALL_DIR"; then [ "$IS_UPDATE" = true ] && mv "$ROLLBACK_DIR" "$INSTALL_DIR" echo "❌ Download fehlgeschlagen; die vorherige Installation wurde wiederhergestellt." exit 1 @@ -195,17 +195,7 @@ if ! install_dependencies; then exit 1 fi -# 6.4 Trinity Canvas als verwaltete Desktop-Komponente installieren. -echo "🎨 Installiere Trinity Canvas..." -if ! command -v node >/dev/null 2>&1 || ! command -v npm >/dev/null 2>&1; then - echo " ⚠️ Node.js/npm fehlt. Trinity läuft, Canvas kann später mit 'trinity canvas install' ergänzt werden." -elif [ -f "$CANVAS_DIR/package.json" ]; then - (cd "$CANVAS_DIR" && npm ci && npm run build) - echo " ✅ Die zu dieser Trinity-Version gehörende Canvas-Komponente ist produktionsbereit." -else - echo " ❌ Die eingebundene Canvas-Komponente fehlt. Prüfe die Git-Submodule." - exit 1 -fi +# Creative Canvas is on hold; do not fetch or build its dependencies. # 6.5 Benutzerweiten CLI-Befehl installieren CLI_BIN="$HOME/.local/bin" @@ -274,7 +264,7 @@ echo "👉 Auf deinem Schreibtisch liegt ein Verweis namens 'Trinity.app'." echo "👉 Doppelklicke einfach darauf, um Trinity zu starten." echo "👉 Du kannst sie auch in deine Dock-Leiste ziehen." echo "👉 In einem neuen Terminal steht außerdem der Befehl 'trinity' bereit." -echo "👉 Canvas startet mit Trinity und erscheint ohne Portangabe im Desktop-Reiter 'Canvas'." +echo "👉 Creative Canvas pausiert; TrinityHUB wird separat betrieben." echo "👉 Die Trinity-Werkstatt startet künftig automatisch bei deiner macOS-Anmeldung." WORKBENCH_URL="$(./venv/bin/python3 scripts/workbench_url.py 2>/dev/null || echo http://127.0.0.1:8765)" echo "👉 Im Browser erreichst du sie unter $WORKBENCH_URL/#werkstatt." diff --git a/install_windows.ps1 b/install_windows.ps1 index 83c7599..b2d0f6d 100644 --- a/install_windows.ps1 +++ b/install_windows.ps1 @@ -300,7 +300,7 @@ if ($isUpdate) { Write-Host "Lade Trinity herunter ..." $git = Get-Command "git.exe" -ErrorAction SilentlyContinue if ($git) { - & $git.Source clone --branch $Branch --single-branch --recurse-submodules --shallow-submodules $Repository $InstallDir + & $git.Source clone --branch $Branch --single-branch $Repository $InstallDir if ($LASTEXITCODE -ne 0) { throw "Git konnte Trinity nicht herunterladen." } @@ -316,15 +316,6 @@ else { Move-Item $sourceDir.FullName $InstallDir Remove-Item $zipPath, $extractPath -Recurse -Force - $canvasZipPath = Join-Path $env:TEMP "trinity-canvas-$timestamp.zip" - $canvasExtractPath = Join-Path $env:TEMP "trinity-canvas-$timestamp" - $canvasZipUrl = "https://github.com/ProfEngel/TrinityCreativeCanvas/archive/$CanvasRevision.zip" - Invoke-WebRequest -Uri $canvasZipUrl -OutFile $canvasZipPath - Expand-Archive -Path $canvasZipPath -DestinationPath $canvasExtractPath -Force - $canvasSourceDir = Get-ChildItem $canvasExtractPath -Directory | Select-Object -First 1 - New-Item -ItemType Directory -Path (Split-Path $CanvasInstallDir) -Force | Out-Null - Move-Item $canvasSourceDir.FullName $CanvasInstallDir - Remove-Item $canvasZipPath, $canvasExtractPath -Recurse -Force } if ($isUpdate) { @@ -377,25 +368,7 @@ finally { Pop-Location } -Write-Host "Installiere Trinity Canvas ..." -$node = Get-Command "node.exe" -ErrorAction SilentlyContinue -$npm = Get-Command "npm.cmd" -ErrorAction SilentlyContinue -if (-not $node -or -not $npm) { - Write-Warning "Node.js/npm fehlt. Trinity läuft; Canvas kann nach der Node.js-Installation ergänzt werden." -} -elseif (Test-Path "$CanvasInstallDir\package.json") { - Push-Location $CanvasInstallDir - try { - & $npm.Source ci - if ($LASTEXITCODE -ne 0) { throw "Canvas-Abhängigkeiten konnten nicht installiert werden." } - & $npm.Source run build - if ($LASTEXITCODE -ne 0) { throw "Trinity Canvas konnte nicht gebaut werden." } - } - finally { Pop-Location } -} -else { - throw "Die zu Trinity gehörende Canvas-Komponente fehlt." -} +# Creative Canvas is on hold; do not fetch or build its dependencies. $cliBin = "$InstallDir\bin" $cliWrapper = "$cliBin\trinity.cmd" @@ -506,7 +479,7 @@ Write-Host "Trinity liegt unter: $InstallDir" Write-Host "Trinity startet mit den in den Einstellungen gewählten Oberflächen." Write-Host "Eine zusätzliche Desktop-Verknüpfung unterdrückt das Terminal, sofern eine GUI aktiv ist." Write-Host "In einer neuen PowerShell steht außerdem der Befehl 'trinity' bereit." -Write-Host "Canvas startet mit Trinity und erscheint ohne Portangabe im Desktop-Reiter 'Canvas'." +Write-Host "Creative Canvas pausiert; TrinityHUB wird separat betrieben." Write-Host "Trinity und die Werkstatt starten künftig automatisch bei der Windows-Anmeldung." $workbenchUrl = (& $venvPython "$InstallDir\scripts\workbench_url.py").Trim() if (-not $workbenchUrl) { diff --git a/tests/test_avatar_tray.py b/tests/test_avatar_tray.py index 38ad8c8..ac41dc2 100644 --- a/tests/test_avatar_tray.py +++ b/tests/test_avatar_tray.py @@ -11,6 +11,30 @@ from trinity_app import WebEngineDragFilter +def test_macos_menu_is_deferred_without_native_context_menu(tmp_path, monkeypatch): + from core import avatar_tray + app = QApplication.instance() or QApplication([]) + monkeypatch.setattr(avatar_tray.sys, "platform", "darwin") + window = QWidget() + window.open_chat = Mock() + window.show_latest_content = Mock() + window.open_settings = Mock() + tray = AvatarTray(window, Mock(), QSettings(str(tmp_path / "avatar.ini"), QSettings.IniFormat), home=tmp_path) + assert tray.icon.contextMenu() is None + pending = [] + monkeypatch.setattr(avatar_tray.QTimer, "singleShot", lambda delay, callback: pending.append((delay, callback))) + monkeypatch.setattr(tray.menu, "popup", Mock()) + tray.icon.activated.emit(QSystemTrayIcon.Trigger) + tray.menu.popup.assert_not_called() + assert len(pending) == 1 and pending[0][0] == 0 + pending[0][1]() + tray.menu.popup.assert_called_once() + tray.icon.activated.emit(QSystemTrayIcon.MiddleClick) + assert len(pending) == 1 + tray.audio_timer.stop() + window.close() + + def test_tray_round_trip_persists_preference_and_stops_blinking(tmp_path, monkeypatch): app = QApplication.instance() or QApplication([]) window = QWidget() diff --git a/tests/test_canvas_paused.py b/tests/test_canvas_paused.py new file mode 100644 index 0000000..65315ea --- /dev/null +++ b/tests/test_canvas_paused.py @@ -0,0 +1,40 @@ +"""The retained legacy component must not participate in normal Desktop use.""" +import ast +from pathlib import Path + +from canvas_manager import CanvasManager +from configuration import default_config + + +ROOT = Path(__file__).resolve().parents[1] + + +def test_canvas_defaults_off_and_leaves_existing_data_untouched(tmp_path, monkeypatch): + assert default_config()["canvas"]["enabled"] is False + manager = CanvasManager(tmp_path, {}) + manager.data_dir.mkdir(parents=True) + data = manager.data_dir / "retained.txt" + data.write_text("keep") + monkeypatch.setattr(manager, "is_running", lambda: (_ for _ in ()).throw(AssertionError("no network probe"))) + assert not manager.enabled + assert manager.start() is None + assert data.read_text() == "keep" + + +def test_launcher_has_no_canvas_start_and_ui_has_no_canvas_tab(): + tree = ast.parse((ROOT / "trinity_launcher.py").read_text(encoding="utf-8")) + assert not any(isinstance(n, ast.Call) and isinstance(n.func, ast.Attribute) + and isinstance(n.func.value, ast.Name) and n.func.value.id == "canvas_manager" + and n.func.attr == "start" for n in ast.walk(tree)) + ui = (ROOT / "trinity_classic.py").read_text(encoding="utf-8") + assert 'addTab(canvas_tab' not in ui + assert 'self.canvas_workspace' not in ui + + +def test_installers_and_ci_do_not_fetch_or_build_canvas(): + for name in ("install_mac.sh", "install_windows.ps1", ".github/workflows/cross-platform-smoke.yml"): + source = (ROOT / name).read_text(encoding="utf-8") + assert "--recurse-submodules" not in source + assert "npm ci" not in source + assert "$canvasZipUrl" not in source + assert "Build and smoke-test Trinity Canvas" not in source diff --git a/tests/test_install_mac.py b/tests/test_install_mac.py index 2e3fbaa..32616ea 100644 --- a/tests/test_install_mac.py +++ b/tests/test_install_mac.py @@ -52,15 +52,15 @@ def test_macos_app_is_kept_outside_icloud_managed_desktop(): assert "codesign --verify" in script -def test_macos_installer_manages_canvas_outside_the_vault(): +def test_macos_installer_preserves_canvas_reference_without_installing_it(): script = INSTALLER.read_text(encoding="utf-8") submodules = (ROOT_DIR / ".gitmodules").read_text(encoding="utf-8") assert 'CANVAS_DIR="$INSTALL_DIR/components/TrinityCanvas"' in script assert "ProfEngel/TrinityCreativeCanvas.git" in submodules - assert "--recurse-submodules" in script - assert 'npm ci && npm run build' in script - assert "trinity canvas install" in script + assert "--recurse-submodules" not in script + assert 'npm ci && npm run build' not in script + assert "Creative Canvas pausiert" in script def test_macos_installer_enables_login_start_for_trinity_workbench(): diff --git a/tests/test_trinity_bridge.py b/tests/test_trinity_bridge.py index 904e5b0..8ab9a45 100644 --- a/tests/test_trinity_bridge.py +++ b/tests/test_trinity_bridge.py @@ -188,7 +188,7 @@ def test_bridge_dashboard_exposes_agent_metadata(tmp_path): assert "allowed_tools" in trinity assert "rights" in trinity assert result["canvas"]["url"] == "http://127.0.0.1:8787" - assert result["canvas"]["state"] == "not_installed" + assert result["canvas"]["state"] == "disabled" assert "message" in result["canvas"] diff --git a/tests/test_windows_installer.py b/tests/test_windows_installer.py index 8373411..a60b769 100644 --- a/tests/test_windows_installer.py +++ b/tests/test_windows_installer.py @@ -28,10 +28,10 @@ def test_windows_installer_targets_main_and_creates_both_launch_modes(): assert 'Join-Path $InstallDir "components\\TrinityCanvas"' in script assert "ProfEngel/TrinityCreativeCanvas.git" in script assert '$CanvasRevision = "21099e2d17181be2d3e0ad62210abfe1fda87cf8"' in script - assert "archive/$CanvasRevision.zip" in script + assert "archive/$CanvasRevision.zip" not in script assert "archive/refs/heads/main.zip" not in script - assert "--recurse-submodules" in script - assert "$npm.Source run build" in script + assert "--recurse-submodules" not in script + assert "$npm.Source run build" not in script def test_windows_installer_stops_running_trinity_before_replacing_update(): diff --git a/trinity_classic.py b/trinity_classic.py index a2f3a13..77ad00b 100644 --- a/trinity_classic.py +++ b/trinity_classic.py @@ -51,7 +51,6 @@ enqueue_chat_request, load_chat_events, ) -from canvas_manager import CanvasManager from memory_store import MemoryStore, render_graph_html from runtime_reset import delete_session_summary from remote_client import RemoteTrinityClient @@ -457,30 +456,6 @@ def __init__(self): web_layout.addLayout(web_toolbar) web_layout.addWidget(self.web_workspace, 1) - canvas_tab = QWidget() - canvas_layout = QVBoxLayout(canvas_tab) - canvas_layout.setContentsMargins(0, 0, 0, 0) - canvas_toolbar = QHBoxLayout() - canvas_label = QLabel("Trinity Canvas – gemeinsam mit Trinity gestartet") - canvas_label.setObjectName("section") - canvas_reload_button = QPushButton("Neu laden") - canvas_reload_button.setObjectName("subtle") - canvas_reload_button.clicked.connect(self.reload_canvas) - canvas_external_button = QPushButton("Extern öffnen") - canvas_external_button.setObjectName("subtle") - self.canvas_manager = CanvasManager(BASE_DIR) - self._canvas_status_cache = (0.0, {}) - canvas_external_button.clicked.connect(self.open_canvas_externally) - canvas_toolbar.addWidget(canvas_label, 1) - canvas_toolbar.addWidget(canvas_reload_button) - canvas_toolbar.addWidget(canvas_external_button) - self.canvas_workspace = QWebEngineView() - self._configure_web_view(self.canvas_workspace) - self.canvas_workspace.loadFinished.connect(self._canvas_load_finished) - self.reload_canvas() - canvas_layout.addLayout(canvas_toolbar) - canvas_layout.addWidget(self.canvas_workspace, 1) - agents_tab = QWidget() agents_layout = QVBoxLayout(agents_tab) agents_layout.setContentsMargins(0, 0, 0, 0) @@ -558,7 +533,6 @@ def __init__(self): self.main_tabs.addTab(daily_tab, "Talk") self.main_tabs.addTab(lecture_tab, "Vortrag") self.main_tabs.addTab(web_tab, "Web") - self.main_tabs.addTab(canvas_tab, "Canvas") self.main_tabs.addTab(agents_tab, "Agents") self.main_tabs.addTab(control_tab, "Control") self.main_tabs.addTab(chat_tab, "Chat") @@ -1544,7 +1518,6 @@ def _dashboard_snapshot(self): ] open_jobs = sum(int(record.job_open) for record in catalog) failed_jobs = sum(int(record.job_failed) for record in catalog) - canvas = self._current_canvas_status() return { "agents_total": len(catalog), "agents_active": len(active_agents), @@ -1558,7 +1531,6 @@ def _dashboard_snapshot(self): "payloads": len(payload_events), "latest_session": sessions[0].title if sessions else "", "latest_result": str(payload_events[-1].get("text") or payload_events[-1].get("source") or "")[:120] if payload_events else "", - "canvas": canvas, "top_agents": triggerable[:5], "catalog": catalog, } @@ -1644,12 +1616,6 @@ def _control_html(self, snapshot=None): "body": f"Aktuelle Session: {snapshot['latest_session'] or 'keine aktive Workspace-Session'}", "badge": "Prompts", }, - { - "icon": "▱", - "title": "Canvas", - "body": snapshot["canvas"]["message"], - "badge": snapshot["canvas"]["state"], - }, ], ) @@ -1689,40 +1655,6 @@ def open_lecture_externally(self): else: self.choose_lecture_pdf() - def _current_canvas_status(self, refresh=False): - checked_at, cached = self._canvas_status_cache - if not refresh and cached and time.monotonic() - checked_at < 2.0: - return dict(cached) - status = self.canvas_manager.status(timeout=0.2) - self._canvas_status_cache = (time.monotonic(), status) - return dict(status) - - def reload_canvas(self): - status = self._current_canvas_status(refresh=True) - if status["running"]: - self.canvas_workspace.setUrl(QUrl(status["url"])) - else: - self.canvas_workspace.setHtml( - self.canvas_manager.unavailable_page(status), - QUrl("about:blank"), - ) - - def _canvas_load_finished(self, ok): - if ok: - return - status = self._current_canvas_status(refresh=True) - self.canvas_workspace.setHtml( - self.canvas_manager.unavailable_page(status), - QUrl("about:blank"), - ) - - def open_canvas_externally(self): - status = self._current_canvas_status(refresh=True) - if status["running"]: - QDesktopServices.openUrl(QUrl(status["url"])) - return - QMessageBox.warning(self, "Trinity Canvas", status["message"]) - def open_web_address(self): url = QUrl.fromUserInput(self.web_address.text().strip()) if not url.isValid() or not url.scheme(): diff --git a/trinity_launcher.py b/trinity_launcher.py index d09a0f3..0ff42ba 100644 --- a/trinity_launcher.py +++ b/trinity_launcher.py @@ -327,15 +327,8 @@ def launch_trinity(): voice_process = None canvas_process = None canvas_manager = CanvasManager(base_dir) - if canvas_manager.enabled: - try: - canvas_process = canvas_manager.start(log_handle=canvas_log) - _log_message( - launcher_log, - "Canvas ist bereit und wird von Trinity verwaltet.", - ) - except (OSError, ValueError, subprocess.SubprocessError) as exc: - _log_message(launcher_log, f"Canvas konnte nicht gestartet werden: {exc}") + # Creative Canvas is on hold. Retain its data, but never autostart it, + # including installations whose old configuration still enables it. web_enabled = ui_modes["web"] companion_enabled = companion_config.get("enabled", False) workbench_enabled = workbench_config.get("enabled", True)