Skip to content

Latest commit

 

History

History
174 lines (124 loc) · 8.17 KB

File metadata and controls

174 lines (124 loc) · 8.17 KB

LucentScreen — Projekt-Kontext für Claude

Windows-Screenshot-Tool als WPF-Anwendung in Windows PowerShell 5.1 mit Tray-Integration, globalen Hotkeys, Editor und Verlauf. Verteilung als signiertes Paket → MSI durch das Softwareverteilteam.

PS 5.1 ist einziges Target. PowerShell 7 wird nicht unterstützt (Enterprise-Hosts haben es nicht garantiert; doppelter Support kostet nur Boilerplate). Erwägen wir später erneut, wenn pwsh.exe flächendeckend ausgerollt ist. Kein PS-7-Sprachfeature nutzen: keine Ternary ? :, keine Null-Conditional ?./??, kein &&/|| in der Pipeline. $IsWindows existiert in 5.1 nicht — nie referenzieren.

Lies vor jeder Implementierung todo.md (offene Arbeitspakete) und docs/Architektur.md (Module + Datenfluss). Was bereits erledigt ist, steht in luscreen-docs/docs/entwicklung/erledigt.md (inkl. Commit-Log). Datumsformat in Logs/Tabellen: YYYYMMDD-HHMM (z.B. 20260515-1412).


Architektur (nicht verhandelbar)

Layer Pfad Darf Darf nicht
core src/core/*.psm1 Logik, GDI+, P/Invoke, Konfig, Logging PresentationFramework, XAML, Tray
ui src/ui/*.psm1 XAML laden, Fenster, Hotkey-Hook, NotifyIcon Direkte Domain-Logik (delegiert an core)
views src/views/*.xaml reine XAML-Markup-Dateien Code-Behind (Logik gehört in ui-Module)
main.ps1 src/LucentScreen.ps1 Bootstrap + Application-Loop Sonst nichts

Regel: main.ps1 ist der einzige Ort, der core und ui zusammensteckt. Module untereinander dürfen Import-Module aufrufen, aber ui darf nicht von core umgekehrt importiert werden.


STA + Single-Instance (Pflicht)

# 1. STA prüfen -- WPF und Clipboard funktionieren sonst nicht.
if ([Threading.Thread]::CurrentThread.GetApartmentState() -ne 'STA') {
    Start-Process powershell.exe -ArgumentList '-STA','-File',$PSCommandPath
    exit
}

# 2. Single-Instance via Named-Mutex (kein File-Lock)
$mutex = [System.Threading.Mutex]::new($false, 'Global\LucentScreen.SingleInstance')
if (-not $mutex.WaitOne(0, $false)) { exit }

# 3. DPI-Awareness als allererste Zeile nach Logging
[LucentScreen.Native]::SetProcessDpiAwarenessContext(-4)  # PER_MONITOR_AWARE_V2

Konventionen

  • Sprache: UI/Doku Deutsch, Code/Variablen/Logs intern Englisch, User-sichtbare Logs Deutsch.
  • Keine Claude/AI-Marker in Commits, Code-Kommentaren, Doku.
  • Keine globalen Variablen ($global:). Innerhalb Modul: $script:-Scope.
  • Funktionen die fehlschlagen können, geben strukturiertes Hashtable zurück:
    return @{ Success = $true;  Status = 'OK';    Message = ''; Path = $path }
    return @{ Success = $false; Status = 'Error'; Message = ''; Path = $null }
  • Config-Pfad: %APPDATA%\LucentScreen\config.json — NIE im Programmordner (MSI-/Per-Machine-kompatibel).
  • WPF-Disposing: NotifyIcon, Bitmap, Graphics, BitmapSource-Streams müssen .Dispose() bekommen. Faustregel: jedes New-Object mit IDisposable → try/finally.
  • Hotkey-Lifecycle: jedes RegisterHotKey braucht zwingenden UnregisterHotKey-Counterpart im Shutdown-Path.
  • PSSA-clean: Code muss tools/Invoke-PSSA.ps1 ohne Errors überstehen. Warnings tolerieren, dokumentieren. Konfig: PSScriptAnalyzerSettings.psd1 im Repo-Root.

Modulvorlage

#Requires -Version 5.1
Set-StrictMode -Version Latest

function Get-Something {
    param([string]$Param)
    # implementation
}

Export-ModuleMember -Function Get-Something

Jede Funktion ist entweder exportiert oder fängt mit _ an (privat).

PS-5.1-Tabus

Anti-Pattern PS-5.1-Lösung
$cond ? 'a' : 'b' if ($cond) { 'a' } else { 'b' }
$x?.Property if ($null -ne $x) { $x.Property }
$val ?? 'default' if ($null -eq $val) { 'default' } else { $val }
cmd1 || cmd2 (Pipeline-Chain) klassisch: separater if-Block über $LASTEXITCODE
$IsWindows lesen nicht verfügbar in 5.1 — entweder weglassen oder ($PSVersionTable.Platform -ne 'Unix')
[Type]::new(args) für out-Parameter OK; Edge-Cases mit [ref] testen
Invoke-RestMethod -SkipCertificateCheck nicht in 5.1 — [ServicePointManager]::ServerCertificateValidationCallback setzen

Entwickler-Workflow: ISE vs. VS Code

System.Windows.Application ist ein Per-AppDomain-Singleton. PowerShell ISE hostet selbst eine WPF-Application im UI-Thread — der LucentScreen-Bootstrap würde aus dem Pipeline-Thread auf das fremde Objekt zugreifen und die ISE killen. Der Host-Guard in src/LucentScreen.ps1 fängt das ab.

Empfehlung für Entwicklung: VS Code mit PowerShell-Extension. Hintergrund: VS Code selbst ist Electron (kein WPF), und die PowerShell-Extension startet einen separaten powershell.exe-PSES-Prozess für den Integrated Console. Der Prozess hat beim Start kein WPF-Application → Host-Guard greift nicht, App läuft direkt in der Editor-Session, F5/F8 funktionieren beide.

ISE-Workflow (falls nötig):

  • F5 (Run Script) funktioniert via Self-Relaunch — der Host-Guard erkennt $psISE und startet einen frischen powershell.exe -STA -File $PSCommandPath-Sub-Prozess. ISE bleibt offen, App läuft in eigener AppDomain (kein In-ISE-Debugging möglich).
  • F8 (Run Selection) funktioniert nur teilweise: $PSCommandPath ist bei F8 nicht gesetzt, daher fällt der Host-Guard auf MessageBox + return zurück. Ab 0.3.11 wird die WinForms-MessageBox (statt WPF) genutzt und return (statt exit 0) — beides nötig, damit die ISE die fremde WPF-Application der ISE nicht von einem Pipeline-Thread aus anfasst und beim Skript-Ende nicht in einen Runspace-Teardown läuft. Praxis-Erfahrung (Mai 2026): mit WPF-MessageBox + exit 0 hat die ISE trotz angezeigter Box wieder neugestartet. Für REPL-Tests einzelner Funktionen lieber VS Code benutzen.

VS-Code-Caveat: Integrated-Console-Session persistiert. Sobald die App im PSES-Prozess hochgefahren ist, lebt Application.Current weiter. Ein zweiter F5-Start würde am Application.new() mit „mehr als eine Instanz" scheitern — daher vor erneutem Test: Ctrl+Shift+P → „PowerShell: Restart Session" (oder das Restart-Icon im Integrated Console).


Test-Konventionen (Pester 5)

BeforeAll {
    Import-Module "$PSScriptRoot/../src/core/<modul>.psm1" -Force
}

AfterEach {
    Get-ChildItem $TestDrive -Recurse | Remove-Item -Recurse -Force -EA SilentlyContinue
}

# Mocking von Modul-internen Aufrufen IMMER mit -ModuleName
Mock -ModuleName <modul> Get-Foo { 'mock' }

$TestDrive ist innerhalb Describe shared — pro It einzigartige Pfade benutzen.


Run-Tasks (./run.ps1)

p   Parse-Check (AST aller *.ps1/*.psm1)
l   PSScriptAnalyzer Lint (alle Sources)
L   PSSA nur geänderte Dateien (-OnlyChangedSinceMain)
t   Pester (alle Tests)
T   Pester (einzelne Test-Datei)
a   Audit (auditor-Agent: parse+pssa+pester+arch+doc-sync)
s   App starten (-STA)
S   App stoppen
d   Zensical-Site bauen + im Standard-Browser öffnen
D   Doku-Live-Server starten (http://127.0.0.1:8000)
h   HTML-Single-Page Doku bauen (LucentScreen.docs.html)

Reports

Alle Werkzeuge schreiben Reports nach reports/<tool>/:

  • reports/pssa/pssa-report.{md,json}
  • reports/pester/pester-report.md + pester-results.xml
  • reports/parse/parse-report.md
  • reports/audit/audit-yyyy-mm-dd.md

Snapshots vor Release: reports/<tool>/history/yyyy-mm-dd_HHmm/.


Specialist-Agents

Siehe .claude/agents/*.md. Routing-Faustregel:

  • Audit → auditor (opus)
  • PS-Modul oder Pester → powershell-specialist (sonnet)
  • XAML/WPF-Layout/HwndSource → wpf-ui-specialist (sonnet)
  • Add-Type/P/Invoke/GDI+ → csharp-specialist (sonnet)
  • Multi-Monitor/Bereichs-Capture → capture-engine-specialist (sonnet)
  • MSI/Transfer-Bundle/Signing → packaging-specialist (sonnet)
  • DE-User-Doku → doc-writer (haiku)

Future-Erwägungen

  • PS 7-Support neu evaluieren, sobald pwsh.exe Standard auf den Ziel-Hosts ist (z.B. via MSI/MDM ausgerollt). Dann lassen sich Ternary/Null-Conditional/&&-Chains aktivieren, was Code etwas kürzer und besser lesbar macht. Bis dahin: 5.1 only, kein doppelter Support.