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
16 changes: 0 additions & 16 deletions .github/workflows/cross-platform-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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)


Expand Down
16 changes: 14 additions & 2 deletions core/avatar_tray.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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)
Expand All @@ -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():
Expand Down
2 changes: 1 addition & 1 deletion core/canvas_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion core/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
22 changes: 22 additions & 0 deletions docs/CREATIVE_CANVAS_PAUSED.md
Original file line number Diff line number Diff line change
@@ -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.
16 changes: 3 additions & 13 deletions install_mac.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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."
Expand Down
33 changes: 3 additions & 30 deletions install_windows.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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."
}
Expand All @@ -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) {
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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) {
Expand Down
24 changes: 24 additions & 0 deletions tests/test_avatar_tray.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
40 changes: 40 additions & 0 deletions tests/test_canvas_paused.py
Original file line number Diff line number Diff line change
@@ -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
8 changes: 4 additions & 4 deletions tests/test_install_mac.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand Down
2 changes: 1 addition & 1 deletion tests/test_trinity_bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]


Expand Down
6 changes: 3 additions & 3 deletions tests/test_windows_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand Down
Loading
Loading