diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000..b5632e5
Binary files /dev/null and b/.DS_Store differ
diff --git a/__mac-cluade-app-builder/.DS_Store b/__mac-cluade-app-builder/.DS_Store
new file mode 100644
index 0000000..4ea6420
Binary files /dev/null and b/__mac-cluade-app-builder/.DS_Store differ
diff --git a/__mac-cluade-app-builder/.gitignore b/__mac-cluade-app-builder/.gitignore
new file mode 100644
index 0000000..163a629
--- /dev/null
+++ b/__mac-cluade-app-builder/.gitignore
@@ -0,0 +1,2 @@
+.build/
+*.app/
diff --git a/__mac-cluade-app-builder/Package.resolved b/__mac-cluade-app-builder/Package.resolved
new file mode 100644
index 0000000..5f89513
--- /dev/null
+++ b/__mac-cluade-app-builder/Package.resolved
@@ -0,0 +1,23 @@
+{
+ "pins" : [
+ {
+ "identity" : "swift-argument-parser",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/apple/swift-argument-parser",
+ "state" : {
+ "revision" : "6a52f3251125d74daf04fcbd5e6f08a75d074382",
+ "version" : "1.8.2"
+ }
+ },
+ {
+ "identity" : "swiftterm",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/migueldeicaza/SwiftTerm.git",
+ "state" : {
+ "branch" : "main",
+ "revision" : "24a68bcadc479d945c7ca32f21ac0a8ab895c690"
+ }
+ }
+ ],
+ "version" : 2
+}
diff --git a/__mac-cluade-app-builder/Package.swift b/__mac-cluade-app-builder/Package.swift
new file mode 100644
index 0000000..7cd16a9
--- /dev/null
+++ b/__mac-cluade-app-builder/Package.swift
@@ -0,0 +1,20 @@
+// swift-tools-version:5.9
+import PackageDescription
+
+let package = Package(
+ name: "MiniTerm",
+ platforms: [
+ .macOS(.v13)
+ ],
+ dependencies: [
+ // Pin to a tag instead of `branch` if you prefer reproducible builds,
+ // e.g. .package(url: "...", from: "1.2.0")
+ .package(url: "https://github.com/migueldeicaza/SwiftTerm.git", branch: "main")
+ ],
+ targets: [
+ .executableTarget(
+ name: "MiniTerm",
+ dependencies: ["SwiftTerm"]
+ )
+ ]
+)
diff --git a/__mac-cluade-app-builder/README.md b/__mac-cluade-app-builder/README.md
new file mode 100644
index 0000000..962adff
--- /dev/null
+++ b/__mac-cluade-app-builder/README.md
@@ -0,0 +1,99 @@
+# MiniTerm — winzige eigenständige Terminal-Apps für macOS
+
+Ein minimaler, auf **SwiftTerm** basierender Terminal-Emulator. Sinn der Sache:
+jede erzeugte `.app` ist eine **eigene macOS-App mit eigener Bundle-ID** — also
+**eigenes Dock-Icon und eigener ⌘-Tab-Eintrag**. Genau das, was sich unter macOS
+mit `--class` (wie bei Alacritty/Ghostty unter Linux) *nicht* lösen lässt.
+
+Jede App startet in einem konfigurierbaren **Arbeitsverzeichnis** und führt einen
+konfigurierbaren **Befehl** aus (z. B. `claude`). Für Claude Code reicht das
+locker — SwiftTerm ist ein vollwertiger xterm-256color-kompatibler Emulator.
+
+## Voraussetzungen
+
+- macOS 13+ und **Xcode** bzw. die Command Line Tools (`xcode-select --install`)
+- Swift (kommt mit Xcode)
+
+## Schnellstart
+
+```bash
+cd MiniTerm
+chmod +x make-app.sh
+
+./make-app.sh \
+ --name "Claude · Projekt A" \
+ --bundle-id com.deinname.claude.projekta \
+ --workdir ~/code/projekt-a \
+ --command "claude"
+```
+
+Beim ersten Lauf wird das Binary einmal gebaut (`swift build -c release`,
+lädt SwiftTerm). Danach liegt die App in `~/Applications/Claude · Projekt A.app`
+— per Doppelklick starten, Dock-Icon erscheint als eigene App.
+
+## Mehrere Apps (eigene Icons/Configs)
+
+Einfach pro App einmal aufrufen. Das Binary wird nur einmal kompiliert und
+wiederverwendet:
+
+```bash
+./make-app.sh --name "Claude · Webshop" --bundle-id com.deinname.claude.webshop \
+ --workdir ~/code/webshop --command "claude" --icon ./icons/shop.icns
+
+./make-app.sh --name "Claude · Infra" --bundle-id com.deinname.claude.infra \
+ --workdir ~/code/infra --command "claude" --icon ./icons/infra.icns
+
+./make-app.sh --name "Scratch Shell" --bundle-id com.deinname.scratch \
+ --workdir ~ --command "" # leer = einfache Shell
+```
+
+## Optionen
+
+| Flag | Pflicht | Bedeutung |
+|----------------|---------|--------------------------------------------------------|
+| `--name` | ja | Anzeigename + Dateiname der App |
+| `--bundle-id` | ja | Eindeutige Bundle-ID (gibt die eigene App-Identität) |
+| `--workdir` | nein | Startverzeichnis (`~` wird expandiert; leer = Home) |
+| `--command` | nein | Befehl beim Start (leer = nur eine Login-Shell) |
+| `--font-size` | nein | Schriftgröße (Default 13) |
+| `--font` | nein | Schriftfamilie (Default Menlo; Fallback System-Mono) |
+| `--icon` | nein | PNG/JPG (wird automatisch konvertiert) oder `.icns` |
+| `--out` | nein | Zielordner (Default `~/Applications`) |
+
+## Icon aus einem PNG
+
+`--icon` nimmt direkt ein **PNG** (oder JPG) — das Skript erzeugt daraus
+automatisch alle nötigen Größen und das `.icns` (via `sips`/`iconutil`):
+
+```bash
+./make-app.sh --name "Claude · Infra" --bundle-id com.deinname.claude.infra \
+ --workdir ~/code/infra --command "claude" --icon ~/Downloads/infra.png
+```
+
+Am besten ein **quadratisches** Bild nehmen, sonst verzerrt `sips` es. Eine
+fertige `.icns`-Datei kannst du weiterhin auch direkt angeben.
+
+## Wie die Konfiguration funktioniert
+
+Das kompilierte Binary ist für alle Apps identisch. Die Unterschiede stecken in
+der `Info.plist` jedes Bundles (Schlüssel `MTCommand`, `MTWorkingDirectory`,
+`MTWindowTitle`, `MTFontSize`, `MTFontName`). Das Programm liest sie beim Start aus
+`Bundle.main`. So genügt ein Build für beliebig viele Apps.
+
+## Hinweise
+
+- Der Befehl läuft immer über eine **Login-Shell** (`zsh -l`), damit PATH stimmt
+ (Homebrew, `~/.local/bin` usw.) und `claude` gefunden wird.
+- Nach Beenden des Befehls fällt das Fenster in eine normale Shell zurück
+ (`exec zsh -l`), schließt sich also nicht sofort.
+- Die App ist **ad-hoc signiert**. Beim ersten Start ggf. Rechtsklick → „Öffnen",
+ falls Gatekeeper meckert.
+- SwiftTerm-Version: im `Package.swift` auf `branch: "main"`. Für reproduzierbare
+ Builds dort auf einen festen Tag (`from: "..."`) umstellen.
+
+## Tastatur
+
+- **Enter** sendet wie gewohnt einen Wagenrücklauf (`\r`) → Eingabe absenden.
+- **Shift+Enter** sendet einen Zeilenumbruch (`\n`) → für mehrzeilige Eingaben,
+ z. B. in Claude Code. Gilt auch für Enter auf dem Ziffernblock.
+- ⌘C / ⌘V funktionieren über das Edit-Menü.
diff --git a/__mac-cluade-app-builder/Sources/MiniTerm/main.swift b/__mac-cluade-app-builder/Sources/MiniTerm/main.swift
new file mode 100644
index 0000000..b13759e
--- /dev/null
+++ b/__mac-cluade-app-builder/Sources/MiniTerm/main.swift
@@ -0,0 +1,213 @@
+import AppKit
+import SwiftTerm
+
+// MARK: - Configuration
+//
+// Every value is read from the app bundle's Info.plist, so a single compiled
+// binary can power many different .app bundles (each with its own command,
+// working directory, title and icon). See make-app.sh.
+//
+// MTCommand String Command to run, e.g. "claude". Empty = plain shell.
+// MTWorkingDirectory String Directory to start in. "~" is expanded. Empty = home.
+// MTFontSize Number Font point size (default 13).
+// MTFontName String Font family name (default "Menlo"). Falls back to
+// the system monospaced font if the name is missing.
+// MTWindowTitle String Window title (default = CFBundleName).
+//
+struct AppConfig {
+ let command: String
+ let workingDirectory: String?
+ let fontSize: CGFloat
+ let fontName: String
+ let windowTitle: String
+
+ static func load() -> AppConfig {
+ let info = Bundle.main.infoDictionary ?? [:]
+
+ let cmd = ((info["MTCommand"] as? String) ?? "")
+ .trimmingCharacters(in: .whitespacesAndNewlines)
+
+ var workdir: String? = nil
+ if let raw = info["MTWorkingDirectory"] as? String,
+ !raw.trimmingCharacters(in: .whitespaces).isEmpty {
+ workdir = (raw as NSString).expandingTildeInPath
+ }
+
+ let size = (info["MTFontSize"] as? NSNumber)?.doubleValue ?? 13.0
+
+ let fontName = ((info["MTFontName"] as? String) ?? "")
+ .trimmingCharacters(in: .whitespacesAndNewlines)
+
+ let title = (info["MTWindowTitle"] as? String)
+ ?? (info["CFBundleName"] as? String)
+ ?? "Terminal"
+
+ return AppConfig(command: cmd,
+ workingDirectory: workdir,
+ fontSize: CGFloat(size),
+ fontName: fontName.isEmpty ? "Menlo" : fontName,
+ windowTitle: title)
+ }
+}
+
+// MARK: - App delegate
+final class AppDelegate: NSObject, NSApplicationDelegate, LocalProcessTerminalViewDelegate {
+ private var window: NSWindow!
+ private var terminalView: LocalProcessTerminalView!
+ private var keyMonitor: Any?
+ private let config = AppConfig.load()
+
+ func applicationDidFinishLaunching(_ notification: Notification) {
+ let frame = NSRect(x: 0, y: 0, width: 900, height: 560)
+
+ terminalView = LocalProcessTerminalView(frame: frame)
+ terminalView.processDelegate = self
+ terminalView.font = resolvedFont()
+
+ installKeyMonitor()
+
+ window = NSWindow(
+ contentRect: frame,
+ styleMask: [.titled, .closable, .miniaturizable, .resizable],
+ backing: .buffered,
+ defer: false)
+ window.title = config.windowTitle
+ window.contentView = terminalView
+ window.setFrameAutosaveName("MiniTermMain")
+ window.center()
+ window.makeKeyAndOrderFront(nil)
+ window.makeFirstResponder(terminalView)
+
+ setupMenu()
+ launchProcess()
+ }
+
+ private func launchProcess() {
+ let shell = userShell()
+
+ // We always go through a LOGIN shell (-l) so PATH is set up exactly like
+ // in a normal terminal (Homebrew, ~/.local/bin, etc.). Without this,
+ // tools like `claude` are often "command not found".
+ let args: [String]
+ if config.command.isEmpty {
+ args = ["-l"]
+ } else {
+ // Run the requested command, then hand control to an interactive
+ // login shell so the window stays open after the command exits.
+ args = ["-l", "-c", "\(config.command); exec \(shell) -l"]
+ }
+
+ let env = Terminal.getEnvironmentVariables(termName: "xterm-256color", trueColor: true)
+
+ terminalView.startProcess(
+ executable: shell,
+ args: args,
+ environment: env,
+ execName: nil,
+ currentDirectory: config.workingDirectory)
+ }
+
+ // Intercept Shift+Return (and Shift+Enter on the numpad) before it reaches
+ // the terminal and send a line feed (\n) instead of the normal carriage
+ // return (\r). This lets CLIs like Claude Code insert a newline in
+ // multi-line input while plain Enter still submits. We use a local event
+ // monitor because SwiftTerm's keyDown(with:) is `public`, not `open`, so it
+ // can't be overridden from outside the module.
+ private func installKeyMonitor() {
+ keyMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) { [weak self] event in
+ guard let self = self else { return event }
+ // Only act when our window is focused and the keyboard focus is in
+ // the terminal (the view itself or one of its subviews).
+ guard self.window.isKeyWindow else { return event }
+ if let responder = self.window.firstResponder as? NSView,
+ !(responder === self.terminalView || responder.isDescendant(of: self.terminalView)) {
+ return event
+ }
+
+ let isReturn = (event.keyCode == 36 || event.keyCode == 76)
+ let mods = event.modifierFlags.intersection(.deviceIndependentFlagsMask)
+ if isReturn && mods == .shift {
+ self.terminalView.send(txt: "\n")
+ return nil // consume the event
+ }
+ return event
+ }
+ }
+
+ // Resolve the configured font, falling back gracefully so the app never
+ // launches with a broken/empty font if the name doesn't exist on the system.
+ private func resolvedFont() -> NSFont {
+ if let f = NSFont(name: config.fontName, size: config.fontSize) {
+ return f
+ }
+ if let menlo = NSFont(name: "Menlo", size: config.fontSize) {
+ return menlo
+ }
+ return NSFont.monospacedSystemFont(ofSize: config.fontSize, weight: .regular)
+ }
+
+ private func userShell() -> String {
+ if let shell = ProcessInfo.processInfo.environment["SHELL"], !shell.isEmpty {
+ return shell
+ }
+ return "/bin/zsh"
+ }
+
+ private func setupMenu() {
+ let mainMenu = NSMenu()
+
+ // Application menu
+ let appItem = NSMenuItem()
+ mainMenu.addItem(appItem)
+ let appMenu = NSMenu()
+ let name = config.windowTitle
+ appMenu.addItem(withTitle: "Hide \(name)",
+ action: #selector(NSApplication.hide(_:)),
+ keyEquivalent: "h")
+ appMenu.addItem(NSMenuItem.separator())
+ appMenu.addItem(withTitle: "Quit \(name)",
+ action: #selector(NSApplication.terminate(_:)),
+ keyEquivalent: "q")
+ appItem.submenu = appMenu
+
+ // Edit menu (enables ⌘C / ⌘V in the terminal)
+ let editItem = NSMenuItem()
+ mainMenu.addItem(editItem)
+ let editMenu = NSMenu(title: "Edit")
+ editMenu.addItem(withTitle: "Copy",
+ action: #selector(NSText.copy(_:)),
+ keyEquivalent: "c")
+ editMenu.addItem(withTitle: "Paste",
+ action: #selector(NSText.paste(_:)),
+ keyEquivalent: "v")
+ editItem.submenu = editMenu
+
+ NSApp.mainMenu = mainMenu
+ }
+
+ // MARK: LocalProcessTerminalViewDelegate
+ func sizeChanged(source: LocalProcessTerminalView, newCols: Int, newRows: Int) {}
+
+ func setTerminalTitle(source: LocalProcessTerminalView, title: String) {
+ // Keep a stable window title. Uncomment to follow the shell's title:
+ // window.title = title.isEmpty ? config.windowTitle : title
+ }
+
+ func hostCurrentDirectoryUpdate(source: TerminalView, directory: String?) {}
+
+ func processTerminated(source: TerminalView, exitCode: Int32?) {
+ NSApp.terminate(nil)
+ }
+
+ func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
+ return true
+ }
+}
+
+// MARK: - Entry point
+let app = NSApplication.shared
+app.setActivationPolicy(.regular) // a real Dock app, not a background process
+let delegate = AppDelegate()
+app.delegate = delegate
+app.activate(ignoringOtherApps: true)
+app.run()
diff --git a/__mac-cluade-app-builder/icons/download.jpeg b/__mac-cluade-app-builder/icons/download.jpeg
new file mode 100755
index 0000000..c8bb397
Binary files /dev/null and b/__mac-cluade-app-builder/icons/download.jpeg differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/128x128/apps/chrome-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png b/__mac-cluade-app-builder/icons/hicolor/128x128/apps/chrome-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png
new file mode 100755
index 0000000..1ccce91
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/128x128/apps/chrome-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/128x128/apps/chrome-eoficlgicibekocmfdomjbfnjmehnhcd-Default.png b/__mac-cluade-app-builder/icons/hicolor/128x128/apps/chrome-eoficlgicibekocmfdomjbfnjmehnhcd-Default.png
new file mode 100755
index 0000000..6ee0178
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/128x128/apps/chrome-eoficlgicibekocmfdomjbfnjmehnhcd-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/128x128/apps/chrome-lneibfepblpbmpahmjfemmjggkkmdjnj-Default.png b/__mac-cluade-app-builder/icons/hicolor/128x128/apps/chrome-lneibfepblpbmpahmjfemmjggkkmdjnj-Default.png
new file mode 100755
index 0000000..214c81e
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/128x128/apps/chrome-lneibfepblpbmpahmjfemmjggkkmdjnj-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/128x128/apps/chrome-oakokndjledcjeafhnccifkkikobnidc-Default.png b/__mac-cluade-app-builder/icons/hicolor/128x128/apps/chrome-oakokndjledcjeafhnccifkkikobnidc-Default.png
new file mode 100755
index 0000000..c660e75
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/128x128/apps/chrome-oakokndjledcjeafhnccifkkikobnidc-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/128x128/apps/chrome-pfjjjhloipcienfopbicmcjmlkilooii-Default.png b/__mac-cluade-app-builder/icons/hicolor/128x128/apps/chrome-pfjjjhloipcienfopbicmcjmlkilooii-Default.png
new file mode 100755
index 0000000..8ffcfc0
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/128x128/apps/chrome-pfjjjhloipcienfopbicmcjmlkilooii-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/128x128/apps/msedge-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png b/__mac-cluade-app-builder/icons/hicolor/128x128/apps/msedge-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png
new file mode 100755
index 0000000..4f8c0ea
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/128x128/apps/msedge-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/16x16/apps/C06E_winhlp32.0.png b/__mac-cluade-app-builder/icons/hicolor/16x16/apps/C06E_winhlp32.0.png
new file mode 100755
index 0000000..f8ab918
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/16x16/apps/C06E_winhlp32.0.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/16x16/apps/D15F_hh.0.png b/__mac-cluade-app-builder/icons/hicolor/16x16/apps/D15F_hh.0.png
new file mode 100755
index 0000000..f8ab918
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/16x16/apps/D15F_hh.0.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/16x16/apps/D23E_msiexec.0.png b/__mac-cluade-app-builder/icons/hicolor/16x16/apps/D23E_msiexec.0.png
new file mode 100755
index 0000000..49c5579
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/16x16/apps/D23E_msiexec.0.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/16x16/apps/chrome-oakokndjledcjeafhnccifkkikobnidc-Default.png b/__mac-cluade-app-builder/icons/hicolor/16x16/apps/chrome-oakokndjledcjeafhnccifkkikobnidc-Default.png
new file mode 100755
index 0000000..2ca1d2b
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/16x16/apps/chrome-oakokndjledcjeafhnccifkkikobnidc-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/256x256/apps/C06E_winhlp32.0.png b/__mac-cluade-app-builder/icons/hicolor/256x256/apps/C06E_winhlp32.0.png
new file mode 100755
index 0000000..6e72db2
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/256x256/apps/C06E_winhlp32.0.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/256x256/apps/D15F_hh.0.png b/__mac-cluade-app-builder/icons/hicolor/256x256/apps/D15F_hh.0.png
new file mode 100755
index 0000000..c5206d6
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/256x256/apps/D15F_hh.0.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/256x256/apps/D23E_msiexec.0.png b/__mac-cluade-app-builder/icons/hicolor/256x256/apps/D23E_msiexec.0.png
new file mode 100755
index 0000000..7af8169
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/256x256/apps/D23E_msiexec.0.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/256x256/apps/chrome-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png b/__mac-cluade-app-builder/icons/hicolor/256x256/apps/chrome-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png
new file mode 100755
index 0000000..8d7fd7f
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/256x256/apps/chrome-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/256x256/apps/chrome-eoficlgicibekocmfdomjbfnjmehnhcd-Default.png b/__mac-cluade-app-builder/icons/hicolor/256x256/apps/chrome-eoficlgicibekocmfdomjbfnjmehnhcd-Default.png
new file mode 100755
index 0000000..dcdd240
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/256x256/apps/chrome-eoficlgicibekocmfdomjbfnjmehnhcd-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/256x256/apps/chrome-lneibfepblpbmpahmjfemmjggkkmdjnj-Default.png b/__mac-cluade-app-builder/icons/hicolor/256x256/apps/chrome-lneibfepblpbmpahmjfemmjggkkmdjnj-Default.png
new file mode 100755
index 0000000..c017683
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/256x256/apps/chrome-lneibfepblpbmpahmjfemmjggkkmdjnj-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/256x256/apps/chrome-oakokndjledcjeafhnccifkkikobnidc-Default.png b/__mac-cluade-app-builder/icons/hicolor/256x256/apps/chrome-oakokndjledcjeafhnccifkkikobnidc-Default.png
new file mode 100755
index 0000000..5a82bc0
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/256x256/apps/chrome-oakokndjledcjeafhnccifkkikobnidc-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/256x256/apps/chrome-pfjjjhloipcienfopbicmcjmlkilooii-Default.png b/__mac-cluade-app-builder/icons/hicolor/256x256/apps/chrome-pfjjjhloipcienfopbicmcjmlkilooii-Default.png
new file mode 100755
index 0000000..307244e
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/256x256/apps/chrome-pfjjjhloipcienfopbicmcjmlkilooii-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/256x256/apps/msedge-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png b/__mac-cluade-app-builder/icons/hicolor/256x256/apps/msedge-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png
new file mode 100755
index 0000000..660836b
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/256x256/apps/msedge-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/32x32/apps/C06E_winhlp32.0.png b/__mac-cluade-app-builder/icons/hicolor/32x32/apps/C06E_winhlp32.0.png
new file mode 100755
index 0000000..5b64f1e
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/32x32/apps/C06E_winhlp32.0.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/32x32/apps/D15F_hh.0.png b/__mac-cluade-app-builder/icons/hicolor/32x32/apps/D15F_hh.0.png
new file mode 100755
index 0000000..d5ee64a
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/32x32/apps/D15F_hh.0.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/32x32/apps/D23E_msiexec.0.png b/__mac-cluade-app-builder/icons/hicolor/32x32/apps/D23E_msiexec.0.png
new file mode 100755
index 0000000..4348519
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/32x32/apps/D23E_msiexec.0.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/32x32/apps/chrome-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png b/__mac-cluade-app-builder/icons/hicolor/32x32/apps/chrome-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png
new file mode 100755
index 0000000..a1773b3
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/32x32/apps/chrome-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/32x32/apps/chrome-eoficlgicibekocmfdomjbfnjmehnhcd-Default.png b/__mac-cluade-app-builder/icons/hicolor/32x32/apps/chrome-eoficlgicibekocmfdomjbfnjmehnhcd-Default.png
new file mode 100755
index 0000000..f067b9c
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/32x32/apps/chrome-eoficlgicibekocmfdomjbfnjmehnhcd-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/32x32/apps/chrome-lneibfepblpbmpahmjfemmjggkkmdjnj-Default.png b/__mac-cluade-app-builder/icons/hicolor/32x32/apps/chrome-lneibfepblpbmpahmjfemmjggkkmdjnj-Default.png
new file mode 100755
index 0000000..9f95337
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/32x32/apps/chrome-lneibfepblpbmpahmjfemmjggkkmdjnj-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/32x32/apps/chrome-oakokndjledcjeafhnccifkkikobnidc-Default.png b/__mac-cluade-app-builder/icons/hicolor/32x32/apps/chrome-oakokndjledcjeafhnccifkkikobnidc-Default.png
new file mode 100755
index 0000000..3031a4e
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/32x32/apps/chrome-oakokndjledcjeafhnccifkkikobnidc-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/32x32/apps/chrome-pfjjjhloipcienfopbicmcjmlkilooii-Default.png b/__mac-cluade-app-builder/icons/hicolor/32x32/apps/chrome-pfjjjhloipcienfopbicmcjmlkilooii-Default.png
new file mode 100755
index 0000000..aa13741
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/32x32/apps/chrome-pfjjjhloipcienfopbicmcjmlkilooii-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/32x32/apps/msedge-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png b/__mac-cluade-app-builder/icons/hicolor/32x32/apps/msedge-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png
new file mode 100755
index 0000000..a1773b3
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/32x32/apps/msedge-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/48x48/apps/C06E_winhlp32.0.png b/__mac-cluade-app-builder/icons/hicolor/48x48/apps/C06E_winhlp32.0.png
new file mode 100755
index 0000000..489d60a
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/48x48/apps/C06E_winhlp32.0.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/48x48/apps/D15F_hh.0.png b/__mac-cluade-app-builder/icons/hicolor/48x48/apps/D15F_hh.0.png
new file mode 100755
index 0000000..3e6ef35
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/48x48/apps/D15F_hh.0.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/48x48/apps/D23E_msiexec.0.png b/__mac-cluade-app-builder/icons/hicolor/48x48/apps/D23E_msiexec.0.png
new file mode 100755
index 0000000..536f361
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/48x48/apps/D23E_msiexec.0.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/48x48/apps/chrome-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png b/__mac-cluade-app-builder/icons/hicolor/48x48/apps/chrome-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png
new file mode 100755
index 0000000..9c8b281
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/48x48/apps/chrome-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/48x48/apps/chrome-eoficlgicibekocmfdomjbfnjmehnhcd-Default.png b/__mac-cluade-app-builder/icons/hicolor/48x48/apps/chrome-eoficlgicibekocmfdomjbfnjmehnhcd-Default.png
new file mode 100755
index 0000000..1d94c31
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/48x48/apps/chrome-eoficlgicibekocmfdomjbfnjmehnhcd-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/48x48/apps/chrome-lneibfepblpbmpahmjfemmjggkkmdjnj-Default.png b/__mac-cluade-app-builder/icons/hicolor/48x48/apps/chrome-lneibfepblpbmpahmjfemmjggkkmdjnj-Default.png
new file mode 100755
index 0000000..ea57e05
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/48x48/apps/chrome-lneibfepblpbmpahmjfemmjggkkmdjnj-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/48x48/apps/chrome-oakokndjledcjeafhnccifkkikobnidc-Default.png b/__mac-cluade-app-builder/icons/hicolor/48x48/apps/chrome-oakokndjledcjeafhnccifkkikobnidc-Default.png
new file mode 100755
index 0000000..190d84d
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/48x48/apps/chrome-oakokndjledcjeafhnccifkkikobnidc-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/48x48/apps/chrome-pfjjjhloipcienfopbicmcjmlkilooii-Default.png b/__mac-cluade-app-builder/icons/hicolor/48x48/apps/chrome-pfjjjhloipcienfopbicmcjmlkilooii-Default.png
new file mode 100755
index 0000000..ae4b7d4
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/48x48/apps/chrome-pfjjjhloipcienfopbicmcjmlkilooii-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/48x48/apps/msedge-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png b/__mac-cluade-app-builder/icons/hicolor/48x48/apps/msedge-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png
new file mode 100755
index 0000000..9c8b281
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/48x48/apps/msedge-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/512x512/apps/chrome-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png b/__mac-cluade-app-builder/icons/hicolor/512x512/apps/chrome-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png
new file mode 100755
index 0000000..5db10bd
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/512x512/apps/chrome-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/512x512/apps/chrome-eoficlgicibekocmfdomjbfnjmehnhcd-Default.png b/__mac-cluade-app-builder/icons/hicolor/512x512/apps/chrome-eoficlgicibekocmfdomjbfnjmehnhcd-Default.png
new file mode 100755
index 0000000..d5ffeeb
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/512x512/apps/chrome-eoficlgicibekocmfdomjbfnjmehnhcd-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/512x512/apps/chrome-lneibfepblpbmpahmjfemmjggkkmdjnj-Default.png b/__mac-cluade-app-builder/icons/hicolor/512x512/apps/chrome-lneibfepblpbmpahmjfemmjggkkmdjnj-Default.png
new file mode 100755
index 0000000..a059e2f
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/512x512/apps/chrome-lneibfepblpbmpahmjfemmjggkkmdjnj-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/hicolor/512x512/apps/msedge-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png b/__mac-cluade-app-builder/icons/hicolor/512x512/apps/msedge-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png
new file mode 100755
index 0000000..6cdb7dc
Binary files /dev/null and b/__mac-cluade-app-builder/icons/hicolor/512x512/apps/msedge-cifhbcnohmdccbgoicgdjpfamggdegmo-Default.png differ
diff --git a/__mac-cluade-app-builder/icons/limbach.png b/__mac-cluade-app-builder/icons/limbach.png
new file mode 100755
index 0000000..bba815d
Binary files /dev/null and b/__mac-cluade-app-builder/icons/limbach.png differ
diff --git a/__mac-cluade-app-builder/icons/robot.png b/__mac-cluade-app-builder/icons/robot.png
new file mode 100755
index 0000000..c2d864b
Binary files /dev/null and b/__mac-cluade-app-builder/icons/robot.png differ
diff --git a/__mac-cluade-app-builder/icons/wittgenstein.png b/__mac-cluade-app-builder/icons/wittgenstein.png
new file mode 100755
index 0000000..fc810bf
Binary files /dev/null and b/__mac-cluade-app-builder/icons/wittgenstein.png differ
diff --git a/__mac-cluade-app-builder/icons/wuerth-ft.png b/__mac-cluade-app-builder/icons/wuerth-ft.png
new file mode 100755
index 0000000..60d48e6
Binary files /dev/null and b/__mac-cluade-app-builder/icons/wuerth-ft.png differ
diff --git a/__mac-cluade-app-builder/icons/wuerth.png b/__mac-cluade-app-builder/icons/wuerth.png
new file mode 100755
index 0000000..95ae414
Binary files /dev/null and b/__mac-cluade-app-builder/icons/wuerth.png differ
diff --git a/__mac-cluade-app-builder/make-app.sh b/__mac-cluade-app-builder/make-app.sh
new file mode 100755
index 0000000..45b1157
--- /dev/null
+++ b/__mac-cluade-app-builder/make-app.sh
@@ -0,0 +1,149 @@
+#!/usr/bin/env bash
+#
+# make-app.sh — build a standalone macOS .app from the MiniTerm binary.
+#
+# Each generated .app has its OWN bundle id, so macOS treats it as a separate
+# application: separate Dock icon, separate ⌘-Tab entry. Run this script once
+# per app you want.
+#
+# Usage:
+# ./make-app.sh \
+# --name "Claude · Projekt A" \
+# --bundle-id com.yourname.claude.projekta \
+# --workdir ~/code/project-a \
+# --command "claude" \
+# [--font-size 13] \
+# [--font "JetBrains Mono"] \
+# [--icon ./icons/projekta.png] \ # .png/.jpg (auto-converted) or .icns
+# [--out ~/Applications]
+#
+# Build the binary fresh? The script runs `swift build -c release` automatically
+# the first time and reuses the result for further apps.
+
+set -euo pipefail
+
+# ---- defaults -------------------------------------------------------------
+NAME=""
+BUNDLE_ID=""
+WORKDIR=""
+COMMAND=""
+FONT_SIZE="13"
+FONT_NAME="Menlo"
+ICON=""
+OUTDIR="$HOME/Applications"
+
+# ---- parse args -----------------------------------------------------------
+while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --name) NAME="$2"; shift 2 ;;
+ --bundle-id) BUNDLE_ID="$2"; shift 2 ;;
+ --workdir) WORKDIR="$2"; shift 2 ;;
+ --command) COMMAND="$2"; shift 2 ;;
+ --font-size) FONT_SIZE="$2"; shift 2 ;;
+ --font) FONT_NAME="$2"; shift 2 ;;
+ --icon) ICON="$2"; shift 2 ;;
+ --out) OUTDIR="$2"; shift 2 ;;
+ -h|--help)
+ grep '^#' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
+ *) echo "Unknown option: $1" >&2; exit 1 ;;
+ esac
+done
+
+if [[ -z "$NAME" || -z "$BUNDLE_ID" ]]; then
+ echo "Error: --name and --bundle-id are required." >&2
+ echo "Run './make-app.sh --help' for usage." >&2
+ exit 1
+fi
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+cd "$SCRIPT_DIR"
+
+# ---- build binary once ----------------------------------------------------
+BIN=".build/release/MiniTerm"
+if [[ ! -f "$BIN" ]]; then
+ echo "==> Building MiniTerm (release)…"
+ swift build -c release
+fi
+
+# expand ~ in workdir
+WORKDIR="${WORKDIR/#\~/$HOME}"
+
+# ---- assemble the .app bundle ---------------------------------------------
+APP="$OUTDIR/$NAME.app"
+echo "==> Creating $APP"
+rm -rf "$APP"
+mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Resources"
+
+cp "$BIN" "$APP/Contents/MacOS/MiniTerm"
+chmod +x "$APP/Contents/MacOS/MiniTerm"
+
+# Build an .icns from a square source image (PNG/JPG/…) using macOS tools.
+make_icns_from_image() {
+ local src="$1" dest="$2"
+ local iconset
+ iconset="$(mktemp -d)/icon.iconset"
+ mkdir -p "$iconset"
+ # All sizes Apple expects in an .iconset
+ local sizes=(16 32 128 256 512)
+ for s in "${sizes[@]}"; do
+ sips -z "$s" "$s" "$src" --out "$iconset/icon_${s}x${s}.png" >/dev/null
+ sips -z "$((s*2))" "$((s*2))" "$src" --out "$iconset/icon_${s}x${s}@2x.png" >/dev/null
+ done
+ iconutil -c icns "$iconset" -o "$dest"
+ rm -rf "$(dirname "$iconset")"
+}
+
+ICON_KEY=""
+if [[ -n "$ICON" ]]; then
+ if [[ ! -f "$ICON" ]]; then
+ echo " (warning: --icon file not found: $ICON; skipping icon)" >&2
+ elif [[ "$ICON" == *.icns ]]; then
+ cp "$ICON" "$APP/Contents/Resources/AppIcon.icns"
+ ICON_KEY="CFBundleIconFileAppIcon"
+ elif command -v sips >/dev/null 2>&1 && command -v iconutil >/dev/null 2>&1; then
+ echo "==> Converting $ICON to .icns…"
+ if make_icns_from_image "$ICON" "$APP/Contents/Resources/AppIcon.icns"; then
+ ICON_KEY="CFBundleIconFileAppIcon"
+ else
+ echo " (warning: icon conversion failed; building without icon)" >&2
+ fi
+ else
+ echo " (warning: sips/iconutil not available; pass a .icns file instead)" >&2
+ fi
+fi
+
+cat > "$APP/Contents/Info.plist" <
+
+
+
+ CFBundleName${NAME}
+ CFBundleDisplayName${NAME}
+ CFBundleExecutableMiniTerm
+ CFBundleIdentifier${BUNDLE_ID}
+ CFBundlePackageTypeAPPL
+ CFBundleVersion1
+ CFBundleShortVersionString1.0
+ LSMinimumSystemVersion13.0
+ NSHighResolutionCapable
+ ${ICON_KEY}
+ MTCommand${COMMAND}
+ MTWorkingDirectory${WORKDIR}
+ MTWindowTitle${NAME}
+ MTFontSize${FONT_SIZE}
+ MTFontName${FONT_NAME}
+
+
+PLIST
+
+# ---- ad-hoc codesign (helps on Apple Silicon / Gatekeeper) ----------------
+if command -v codesign >/dev/null 2>&1; then
+ codesign --force --deep --sign - "$APP" >/dev/null 2>&1 \
+ && echo "==> Ad-hoc signed." \
+ || echo " (codesign skipped/failed — app still runs)"
+fi
+
+echo "==> Done: $APP"
+echo " Command : ${COMMAND:-}"
+echo " Workdir : ${WORKDIR:-}"
+echo " Font : ${FONT_NAME} ${FONT_SIZE}pt"
diff --git a/__mac-cluade-app-builder/wittgenstein.png b/__mac-cluade-app-builder/wittgenstein.png
new file mode 100755
index 0000000..fc810bf
Binary files /dev/null and b/__mac-cluade-app-builder/wittgenstein.png differ
diff --git a/limbach/.~lock.Workshop_LIS.pptx# b/limbach/.~lock.Workshop_LIS.pptx#
deleted file mode 100644
index 93432cc..0000000
--- a/limbach/.~lock.Workshop_LIS.pptx#
+++ /dev/null
@@ -1 +0,0 @@
-,marcuh,mach12,12.06.2026 09:55,file:///home/marcuh/.config/libreoffice/4;
\ No newline at end of file
diff --git a/limbach/Folie2.pptx b/limbach/Folie2.pptx
index 3ffe790..00bd175 100644
Binary files a/limbach/Folie2.pptx and b/limbach/Folie2.pptx differ
diff --git a/limbach/Folie3.pptx b/limbach/Folie3.pptx
index cd383d8..35afb03 100644
Binary files a/limbach/Folie3.pptx and b/limbach/Folie3.pptx differ
diff --git a/limbach/Folie3b.pptx b/limbach/Folie3b.pptx
new file mode 100644
index 0000000..8051a42
Binary files /dev/null and b/limbach/Folie3b.pptx differ
diff --git a/limbach/Folie4.pptx b/limbach/Folie4.pptx
index 9d3016b..f280531 100644
Binary files a/limbach/Folie4.pptx and b/limbach/Folie4.pptx differ
diff --git a/limbach/Vault_Impuls_Workshop.pptx b/limbach/Vault_Impuls_Workshop.pptx
index c4904a2..43d1cb8 100644
Binary files a/limbach/Vault_Impuls_Workshop.pptx and b/limbach/Vault_Impuls_Workshop.pptx differ
diff --git a/limbach/Workshop_LIS.pptx b/limbach/Workshop_LIS.pptx
index 4a23b99..7342471 100644
Binary files a/limbach/Workshop_LIS.pptx and b/limbach/Workshop_LIS.pptx differ
diff --git a/limbach/adesso_style.py b/limbach/adesso_style.py
new file mode 100644
index 0000000..dbfe85f
--- /dev/null
+++ b/limbach/adesso_style.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python3
+"""Gemeinsames adesso-Styling für die Workshop-Folien.
+Teal->Blau-Verlauf, Fira Sans, weiße Shapes + Coral-Akzente,
+Header (Pill + Titel) und schlanke Fußzeile (Seitenzahl + adesso)."""
+
+from pptx.util import Inches, Pt
+from pptx.dml.color import RGBColor
+from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
+from pptx.enum.shapes import MSO_SHAPE
+from pptx.oxml.ns import qn
+
+# ---- adesso Palette --------------------------------------------------------
+WHITE = RGBColor(0xFF, 0xFF, 0xFF)
+INK = RGBColor(0x0E, 0x2A, 0x47) # tiefes Navy – Text auf hellen Boxen / dunkle Füllungen
+BLUE = RGBColor(0x00, 0x6E, 0xC7)
+TEAL = RGBColor(0x28, 0xDC, 0xAA)
+GREEN = RGBColor(0x76, 0xC8, 0x00)
+CORAL = RGBColor(0xFF, 0x98, 0x68)
+PINK = RGBColor(0xF5, 0x66, 0xBA)
+TAUPE = RGBColor(0x88, 0x7D, 0x75)
+LIGHT = RGBColor(0xD7, 0xE6, 0xF4) # helle, gedämpfte Schrift/Linien auf dem Verlauf
+MUTE = RGBColor(0x6B, 0x77, 0x85) # mittleres Grau – Text auf weißen Flächen
+RECF = RGBColor(0xE6, 0xEF, 0xF8) # helle Kästchen in weißen Boxen
+
+F = "Fira Sans"
+FC = "Fira Sans Condensed"
+
+
+def alpha(shape, opacity_pct):
+ """Deckkraft (0-100) auf die Solid-Füllung setzen."""
+ srgb = shape._element.spPr.find(qn('a:solidFill')).find(qn('a:srgbClr'))
+ srgb.append(srgb.makeelement(qn('a:alpha'), {'val': str(int(opacity_pct * 1000))}))
+
+
+def line_alpha(shape, opacity_pct):
+ ln = shape._element.spPr.find(qn('a:ln'))
+ srgb = ln.find(qn('a:solidFill')).find(qn('a:srgbClr'))
+ srgb.append(srgb.makeelement(qn('a:alpha'), {'val': str(int(opacity_pct * 1000))}))
+
+
+def paint_background(s, width, height):
+ """Teal->Blau-Verlauf als Folienhintergrund."""
+ bgr = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, width, height)
+ bgr.line.fill.background(); bgr.shadow.inherit = False
+ bgr.fill.gradient()
+ try:
+ bgr.fill.gradient_angle = 90.0
+ except Exception:
+ pass
+ st = bgr.fill.gradient_stops
+ st[0].position = 0.0; st[0].color.rgb = TEAL
+ st[1].position = 1.0; st[1].color.rgb = BLUE
+ return bgr
+
+
+def chrome(s, title, page):
+ """Header (Pill + Titel) + schlanke Fußzeile (Seitenzahl + adesso)."""
+ pill = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE,
+ Inches(0.55), Inches(0.46), Inches(0.5), Inches(0.26))
+ pill.fill.background(); pill.line.color.rgb = WHITE; pill.line.width = Pt(1.5)
+ pill.shadow.inherit = False
+ try: pill.adjustments[0] = 0.5
+ except Exception: pass
+
+ def _t(x, y, w, h, t, size, color, bold=False, align=PP_ALIGN.LEFT, font=FC):
+ tb = s.shapes.add_textbox(x, y, w, h); tf = tb.text_frame
+ tf.word_wrap = True; tf.margin_left = tf.margin_right = Pt(0)
+ tf.margin_top = tf.margin_bottom = Pt(0)
+ p = tf.paragraphs[0]; p.alignment = align
+ r = p.add_run(); r.text = t
+ r.font.size = Pt(size); r.font.bold = bold; r.font.name = font; r.font.color.rgb = color
+
+ _t(Inches(1.25), Inches(0.34), Inches(11), Inches(0.7), title, 30, WHITE, bold=True)
+ _t(Inches(0.55), Inches(7.06), Inches(1.0), Inches(0.3), str(page), 11, LIGHT)
+ _t(Inches(10.95), Inches(6.99), Inches(1.85), Inches(0.4), "adesso", 17, WHITE,
+ bold=True, align=PP_ALIGN.RIGHT)
diff --git a/limbach/build_deck.py b/limbach/build_deck.py
index ba10c17..d9743d3 100644
--- a/limbach/build_deck.py
+++ b/limbach/build_deck.py
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
"""Workshop-Folien 'Vault – Technischer Impuls': ruhig, visuell, wenig Text."""
+import os
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
@@ -240,6 +241,6 @@ box(s, last_x + Inches(0.75), ey - Inches(0.05), Inches(2.7), Inches(0.95),
caption(s, "Der Zustand entsteht aus den Ereignissen – Mehrfaches inklusive.")
-out = "/home/marcuh/claude-projects/limbach/Vault_Impuls_Workshop.pptx"
+out = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Vault_Impuls_Workshop.pptx")
prs.save(out)
print("saved", out)
diff --git a/limbach/folie2.py b/limbach/folie2.py
index 9b5b1ca..28f6e8f 100644
--- a/limbach/folie2.py
+++ b/limbach/folie2.py
@@ -1,31 +1,21 @@
#!/usr/bin/env python3
-"""Folie 2 – Grenzen der Vault-Integration. Kette: Auftrag -> altes LIS ->
-Schattenbuchhaltung/Metadaten -> Vault (am Ende). Der Layer wird zum SPOT."""
+"""Folie 2 – Grenzen der Vault-Integration (adesso-Styling).
+Kette: Auftrag -> Auftragsannahme -> Schattenbuchhaltung (SPOT) -> Vault ->
+Zusammenführung."""
+import os
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.enum.shapes import MSO_SHAPE
-
-INK = RGBColor(0x2A, 0x2F, 0x3A)
-NAVY = RGBColor(0x1F, 0x3A, 0x5F)
-ACCENT = RGBColor(0xC0, 0x49, 0x2F)
-MUTE = RGBColor(0x8A, 0x93, 0xA0)
-LINEC = RGBColor(0xC3, 0xC9, 0xD1)
-FRAME = RGBColor(0xF4, 0xF6, 0xF8)
-TINT_R = RGBColor(0xF6, 0xE5, 0xDF)
-BG = RGBColor(0xFF, 0xFF, 0xFF)
-WHITE = RGBColor(0xFF, 0xFF, 0xFF)
-F = "Calibri"
+from adesso_style import (WHITE, INK, CORAL, LIGHT, RECF, F, FC,
+ alpha, line_alpha, paint_background, chrome)
prs = Presentation()
prs.slide_width = Inches(13.333)
prs.slide_height = Inches(7.5)
s = prs.slides.add_slide(prs.slide_layouts[6])
-bgr = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, prs.slide_width, prs.slide_height)
-bgr.fill.solid(); bgr.fill.fore_color.rgb = BG; bgr.line.fill.background()
-bgr.shadow.inherit = False
def shp(kind, x, y, w, h, fill=None, line=None, lw=Pt(1), rot=0):
@@ -39,62 +29,62 @@ def shp(kind, x, y, w, h, fill=None, line=None, lw=Pt(1), rot=0):
return sh
-def boxtext(sh, t, size=14, color=INK, bold=False):
+def boxtext(sh, t, size=14, color=INK, bold=False, font=F):
tf = sh.text_frame; tf.word_wrap = True
tf.vertical_anchor = MSO_ANCHOR.MIDDLE
tf.margin_left = tf.margin_right = Pt(4); tf.margin_top = tf.margin_bottom = Pt(1)
p = tf.paragraphs[0]; p.alignment = PP_ALIGN.CENTER
r = p.add_run(); r.text = t
- r.font.size = Pt(size); r.font.bold = bold; r.font.name = F; r.font.color.rgb = color
+ r.font.size = Pt(size); r.font.bold = bold; r.font.name = font; r.font.color.rgb = color
def txt(x, y, w, h, t, size=14, color=INK, bold=False, italic=False,
- align=PP_ALIGN.LEFT, spc=None, lead=1.1, anchor=MSO_ANCHOR.TOP):
+ align=PP_ALIGN.LEFT, spc=None, lead=1.1, anchor=MSO_ANCHOR.TOP, font=F):
tb = s.shapes.add_textbox(x, y, w, h); tf = tb.text_frame
tf.word_wrap = True; tf.vertical_anchor = anchor
tf.margin_left = tf.margin_right = tf.margin_top = tf.margin_bottom = Pt(0)
p = tf.paragraphs[0]; p.alignment = align; p.line_spacing = lead
r = p.add_run(); r.text = t
r.font.size = Pt(size); r.font.bold = bold; r.font.italic = italic
- r.font.name = F; r.font.color.rgb = color
+ r.font.name = font; r.font.color.rgb = color
if spc: r.font._rPr.set('spc', str(spc))
return tb
-# ---- Kopf ------------------------------------------------------------------
-txt(Inches(0.95), Inches(0.32), Inches(8), Inches(0.35), "INTEGRATION",
- size=12.5, color=ACCENT, bold=True, spc=240)
-txt(Inches(0.95), Inches(0.62), Inches(11.5), Inches(0.85), "Grenzen der Vault-Integration?",
- size=31, color=NAVY, bold=True)
-shp(MSO_SHAPE.RECTANGLE, Inches(0.95), Inches(1.34), Inches(0.6), Pt(4), fill=ACCENT)
+paint_background(s, prs.slide_width, prs.slide_height)
+chrome(s, "Grenzen der Vault-Integration?", 2)
# ---- Linke Spalte ----------------------------------------------------------
def claim(y, t, bold=False, sub=None):
- shp(MSO_SHAPE.RECTANGLE, Inches(0.95), y + Inches(0.08), Inches(0.14), Inches(0.14), fill=ACCENT)
- txt(Inches(1.28), y, Inches(4.5), Inches(0.4), t, size=16.5, color=INK, bold=bold)
+ shp(MSO_SHAPE.RECTANGLE, Inches(0.95), y + Inches(0.08), Inches(0.14), Inches(0.14), fill=CORAL)
+ txt(Inches(1.28), y, Inches(4.5), Inches(0.4), t, size=16.5, color=WHITE, bold=bold)
if sub:
txt(Inches(1.28), y + Inches(0.34), Inches(4.5), Inches(0.32), sub,
- size=12.5, color=MUTE, italic=True)
+ size=12.5, color=LIGHT, italic=True)
+
claim(Inches(2.2), "Middleware kapselt den Rand – den Kern nicht", bold=True)
-claim(Inches(2.95), "1:n geht nur als Schattenmodell",
+claim(Inches(2.95), "Mischaufträge nur als Schattenmodell",
sub="aufwändig · fragil · fehleranfällig")
claim(Inches(3.95), "die Fachwahrheit verlässt den Kern")
-shp(MSO_SHAPE.RECTANGLE, Inches(0.95), Inches(4.95), Inches(0.5), Pt(3.5), fill=ACCENT)
+shp(MSO_SHAPE.RECTANGLE, Inches(0.95), Inches(4.95), Inches(0.5), Pt(3.5), fill=CORAL)
txt(Inches(0.95), Inches(5.12), Inches(4.85), Inches(1.1),
"Die Schattenbuchhaltung wird zum Single Point of Truth – nicht Vault.",
- size=19, color=NAVY, bold=True, lead=1.08)
+ size=19, color=WHITE, bold=True, lead=1.08, font=FC)
txt(Inches(0.95), Inches(6.45), Inches(4.85), Inches(0.4),
- "Genau die Rolle, die Vault halten müsste.", size=13.5, color=MUTE, italic=True)
+ "Genau die Rolle, die Vault halten müsste.", size=13.5, color=LIGHT, italic=True)
# ===========================================================================
-# RECHTS: Kette Auftrag -> altes LIS -> Layer (SPOT) -> Vault (Ende)
+# RECHTS: Kette Auftrag -> Auftragsannahme -> SPOT -> Vault -> Zusammenführung
# ===========================================================================
-fx, fy, fw, fh = Inches(6.25), Inches(2.4), Inches(5.9), Inches(4.55)
-shp(MSO_SHAPE.ROUNDED_RECTANGLE, fx, fy, fw, fh, fill=FRAME, line=LINEC, lw=Pt(1.5))
-txt(fx + Inches(0.3), fy + Inches(0.14), fw - Inches(0.6), Inches(0.3),
- "Auftragsweg – hin und zurück durch den Layer", size=12.5, color=NAVY, bold=True)
+fx, fy, fw, fh = Inches(6.25), Inches(2.1), Inches(5.9), Inches(4.72)
+panel = shp(MSO_SHAPE.ROUNDED_RECTANGLE, fx, fy, fw, fh, fill=WHITE)
+alpha(panel, 10)
+panel.line.color.rgb = WHITE; panel.line.width = Pt(1)
+line_alpha(panel, 35)
+txt(fx + Inches(0.3), fy + Inches(0.12), fw - Inches(0.6), Inches(0.3),
+ "Auftragsweg – hin und zurück durch den Layer", size=12.5, color=WHITE, bold=True)
cxc = fx + Inches(2.05)
bw = Inches(2.7)
@@ -102,60 +92,83 @@ bx = cxc - bw / 2
def down(y, label=None):
- shp(MSO_SHAPE.DOWN_ARROW, cxc - Inches(0.11), y, Inches(0.22), Inches(0.26), fill=MUTE)
+ shp(MSO_SHAPE.DOWN_ARROW, cxc - Inches(0.11), y, Inches(0.22), Inches(0.22), fill=LIGHT)
if label:
- txt(cxc + Inches(0.22), y - Inches(0.02), Inches(1.6), Inches(0.3), label,
- size=11, color=MUTE, italic=True, anchor=MSO_ANCHOR.MIDDLE)
+ txt(cxc + Inches(0.22), y - Inches(0.02), Inches(1.7), Inches(0.3), label,
+ size=11, color=LIGHT, italic=True, anchor=MSO_ANCHOR.MIDDLE)
# 1) Auftrag
-b = shp(MSO_SHAPE.ROUNDED_RECTANGLE, bx, fy + Inches(0.52), bw, Inches(0.48), fill=BG, line=LINEC)
-boxtext(b, "Auftrag · Mischauftrag (1:n)", size=12.5, color=INK, bold=True)
-down(fy + Inches(1.02))
+b = shp(MSO_SHAPE.ROUNDED_RECTANGLE, bx, fy + Inches(0.44), bw, Inches(0.40), fill=WHITE)
+boxtext(b, "Auftrag · Mischauftrag", size=12.5, color=INK, bold=True)
+down(fy + Inches(0.86))
-# 2) Altes LIS
-b = shp(MSO_SHAPE.ROUNDED_RECTANGLE, bx, fy + Inches(1.3), bw, Inches(0.5), fill=NAVY)
-boxtext(b, "Altes LIS · nimmt 1:n an", size=13, color=WHITE, bold=True)
-down(fy + Inches(1.82), "Magic")
+# 2) Auftragsannahme (ersetzt altes LIS)
+b = shp(MSO_SHAPE.ROUNDED_RECTANGLE, bx, fy + Inches(1.06), bw, Inches(0.40), fill=WHITE)
+boxtext(b, "Auftragsannahme", size=13, color=INK, bold=True)
+down(fy + Inches(1.48), "Magic")
-# 3) Schattenbuchhaltung = SPOT
-b = shp(MSO_SHAPE.ROUNDED_RECTANGLE, bx - Inches(0.15), fy + Inches(2.1), bw + Inches(0.3),
- Inches(0.78), fill=TINT_R, line=ACCENT, lw=Pt(2))
-txt(bx - Inches(0.15), fy + Inches(2.18), bw + Inches(0.3), Inches(0.32),
- "Schattenbuchhaltung / Metadaten", size=13, color=ACCENT, bold=True, align=PP_ALIGN.CENTER)
-txt(bx - Inches(0.15), fy + Inches(2.52), bw + Inches(0.3), Inches(0.3), "Mapping 1 ↔ 3",
- size=12, color=INK, align=PP_ALIGN.CENTER)
-# aktiver SPOT-Stern
-shp(MSO_SHAPE.STAR_5_POINT, fx + fw - Inches(0.78), fy + Inches(2.18), Inches(0.5), Inches(0.5),
- fill=ACCENT)
-txt(fx + fw - Inches(2.55), fy + Inches(2.7), Inches(1.75), Inches(0.28), "Single Point of Truth",
- size=10.5, color=ACCENT, bold=True, align=PP_ALIGN.RIGHT)
-down(fy + Inches(2.9), "1 → 3, Vault-kompatibel")
+# 3) Schattenbuchhaltung = Rahmen um den Vault-Kasten
+frx, fry = bx - Inches(0.22), fy + Inches(1.72)
+frw, frh = bw + Inches(0.50), Inches(2.22)
+frame = shp(MSO_SHAPE.ROUNDED_RECTANGLE, frx, fry, frw, frh,
+ fill=RGBColor(0xFF, 0xE3, 0x9B))
+alpha(frame, 22)
+frame.line.color.rgb = CORAL; frame.line.width = Pt(2)
-# 4) Vault am Ende
-b = shp(MSO_SHAPE.ROUNDED_RECTANGLE, bx, fy + Inches(3.18), bw, Inches(0.82), fill=NAVY)
-txt(bx, fy + Inches(3.24), bw, Inches(0.3), "Vault · 1:1 · Speicher",
- size=13, color=WHITE, bold=True, align=PP_ALIGN.CENTER)
+# Überschrift + Positions-Mapping (oben im Rahmen)
+txt(frx, fy + Inches(1.78), frw, Inches(0.28), "Schattenbuchhaltung",
+ size=13, color=CORAL, bold=True, align=PP_ALIGN.CENTER)
+txt(frx, fy + Inches(2.07), frw, Inches(0.24), "Positions-Mapping",
+ size=11.5, color=WHITE, align=PP_ALIGN.CENTER)
+
+# aktiver SPOT-Stern (rechts neben dem Rahmen)
+shp(MSO_SHAPE.STAR_5_POINT, Inches(10.75), fy + Inches(1.80), Inches(0.5), Inches(0.5), fill=CORAL)
+txt(Inches(9.95), fy + Inches(2.32), Inches(2.1), Inches(0.28), "Single Point of Truth",
+ size=10.5, color=CORAL, bold=True, align=PP_ALIGN.CENTER)
+down(fy + Inches(2.33))
+
+# 4) Vault speichert (aufgesplittet) – im Rahmen
+b = shp(MSO_SHAPE.ROUNDED_RECTANGLE, bx, fy + Inches(2.50), bw, Inches(0.70), fill=WHITE)
+txt(bx, fy + Inches(2.55), bw, Inches(0.3), "Vault · 1:1 · Speicher",
+ size=13, color=INK, bold=True, align=PP_ALIGN.CENTER)
for k in range(3):
shp(MSO_SHAPE.ROUNDED_RECTANGLE, cxc - Inches(1.08) + k * Inches(0.78),
- fy + Inches(3.58), Inches(0.62), Inches(0.3), fill=RGBColor(0x34, 0x4E, 0x70))
- txt(cxc - Inches(1.08) + k * Inches(0.78), fy + Inches(3.58), Inches(0.62), Inches(0.3),
- "Rec", size=9.5, color=WHITE, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE)
-# Geist-Stern (durchgestrichen) bei Vault
-gx = fx + fw - Inches(0.74)
-shp(MSO_SHAPE.STAR_5_POINT, gx, fy + Inches(3.32), Inches(0.44), Inches(0.44),
- fill=BG, line=LINEC, lw=Pt(1.25))
-shp(MSO_SHAPE.RECTANGLE, gx - Inches(0.02), fy + Inches(3.52), Inches(0.48), Pt(2),
- fill=ACCENT, rot=20)
-txt(fx + fw - Inches(2.45), fy + Inches(3.3), Inches(1.65), Inches(0.4),
- "müsste hier liegen", size=10, color=MUTE, align=PP_ALIGN.RIGHT)
+ fy + Inches(2.87), Inches(0.62), Inches(0.3), fill=RECF)
+ txt(cxc - Inches(1.08) + k * Inches(0.78), fy + Inches(2.87), Inches(0.62), Inches(0.3),
+ "Rec", size=9.5, color=INK, align=PP_ALIGN.CENTER, anchor=MSO_ANCHOR.MIDDLE)
+# Geist-Stern (durchgestrichen) – die Wahrheit müsste hier liegen
+gx = Inches(10.79)
+gs = shp(MSO_SHAPE.STAR_5_POINT, gx, fy + Inches(2.55), Inches(0.42), Inches(0.42))
+gs.fill.background(); gs.line.color.rgb = LIGHT; gs.line.width = Pt(1.25)
+shp(MSO_SHAPE.RECTANGLE, gx - Inches(0.02), fy + Inches(2.73), Inches(0.46), Pt(2),
+ fill=CORAL, rot=20)
+txt(Inches(9.95), fy + Inches(3.02), Inches(2.1), Inches(0.3), "müsste hier liegen",
+ size=10, color=LIGHT, align=PP_ALIGN.CENTER)
+down(fy + Inches(3.22))
-# Rückweg (Lesen) – Pfeil links nach oben
-shp(MSO_SHAPE.UP_ARROW, fx + Inches(0.34), fy + Inches(1.45), Inches(0.26), Inches(2.45),
- fill=RGBColor(0xD8, 0xCB, 0xC6))
-txt(fx + Inches(0.1), fy + Inches(3.95), Inches(1.55), Inches(0.5),
- "Lesen: zurück, wieder LIS-kompatibel", size=9.5, color=ACCENT, align=PP_ALIGN.LEFT, lead=1.0)
+# 5) Daten zusammenführen (unten im Rahmen)
+txt(frx, fy + Inches(3.42), frw, Inches(0.28), "Daten Zusammenführen",
+ size=12.5, color=WHITE, bold=True, align=PP_ALIGN.CENTER)
-out = "/home/marcuh/claude-projects/limbach/Folie2.pptx"
+# 6) Weiterverarbeitung – grauer Pfeil + weißer Kasten unter dem Rahmen
+shp(MSO_SHAPE.DOWN_ARROW, cxc - Inches(0.11), fy + Inches(4.00), Inches(0.22), Inches(0.22),
+ fill=RGBColor(0x8C, 0x8C, 0x8C))
+b = shp(MSO_SHAPE.ROUNDED_RECTANGLE, bx, fy + Inches(4.26), bw, Inches(0.40), fill=WHITE)
+boxtext(b, "Weiterverarbeitung", size=12.5, color=INK, bold=True)
+
+# Rückweg (Lesen) – Pfeil links nach oben, Label vertikal daneben
+ua = shp(MSO_SHAPE.UP_ARROW, fx + Inches(0.08), fy + Inches(1.55), Inches(0.26), Inches(2.30),
+ fill=LIGHT)
+alpha(ua, 55)
+lt = s.shapes.add_textbox(fx - Inches(0.95), fy + Inches(2.40), Inches(2.2), Inches(0.3))
+ltf = lt.text_frame; ltf.word_wrap = False
+ltf.margin_left = ltf.margin_right = ltf.margin_top = ltf.margin_bottom = Pt(0)
+lp = ltf.paragraphs[0]; lp.alignment = PP_ALIGN.CENTER
+lr = lp.add_run(); lr.text = "Lesen: zurück durch den Layer"
+lr.font.size = Pt(9.5); lr.font.name = F; lr.font.color.rgb = CORAL
+lt.rotation = 270
+
+out = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Folie2.pptx")
prs.save(out)
print("saved", out)
diff --git a/limbach/folie3.py b/limbach/folie3.py
index 7dc3df9..829bc44 100644
--- a/limbach/folie3.py
+++ b/limbach/folie3.py
@@ -2,35 +2,32 @@
"""Folie 3 – LIS mit Umsystemen (Adapter, links/rechts) und andockenden Modulen
(unten), unter einer Steuerungs-/Governance-Schicht; weitere LIS angedeutet."""
+import os
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.enum.shapes import MSO_SHAPE
+from adesso_style import (WHITE, INK, BLUE, TEAL, GREEN, CORAL, LIGHT, MUTE, RECF,
+ F, FC, paint_background, chrome)
-INK = RGBColor(0x2A, 0x2F, 0x3A)
-NAVY = RGBColor(0x1F, 0x3A, 0x5F)
-ACCENT = RGBColor(0xC0, 0x49, 0x2F)
-TEAL = RGBColor(0x2F, 0x8F, 0x83)
-AMBER = RGBColor(0xA9, 0x73, 0x22)
-MUTE = RGBColor(0x8A, 0x93, 0xA0)
-LINEC = RGBColor(0xC3, 0xC9, 0xD1)
-HINT = RGBColor(0xEE, 0xF1, 0xF4)
-HINTL = RGBColor(0xCB, 0xD2, 0xDA)
-TINT_T = RGBColor(0xDD, 0xEC, 0xEA)
-TINT_A = RGBColor(0xF3, 0xE8, 0xD2)
-UMS = RGBColor(0xEC, 0xEE, 0xF1)
-BG = RGBColor(0xFF, 0xFF, 0xFF)
-WHITE = RGBColor(0xFF, 0xFF, 0xFF)
-F = "Calibri"
+# Rollen-Mapping auf die adesso-Palette
+NAVY = INK # dunkle Strukturelemente (Bar, LIS-Kern)
+ACCENT = CORAL # Adapter-Linien
+AMBER = GREEN # zweites Modul
+LINEC = LIGHT # Verbinder / feine Linien
+HINT = RGBColor(0xE6, 0xEF, 0xF8) # helle "weitere LIS"-Karten
+HINTL = RGBColor(0xC4, 0xD6, 0xE8)
+TINT_T = WHITE # Modul-Boxen weiß
+TINT_A = WHITE
+UMS = WHITE # Umsystem-Boxen weiß
+BG = WHITE
prs = Presentation()
prs.slide_width = Inches(13.333)
prs.slide_height = Inches(7.5)
s = prs.slides.add_slide(prs.slide_layouts[6])
-bgr = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, prs.slide_width, prs.slide_height)
-bgr.fill.solid(); bgr.fill.fore_color.rgb = BG; bgr.line.fill.background()
-bgr.shadow.inherit = False
+paint_background(s, prs.slide_width, prs.slide_height)
def shp(kind, x, y, w, h, fill=None, line=None, lw=Pt(1), rot=0):
@@ -75,11 +72,7 @@ def vline(x, y, h, color=LINEC, weight=Pt(1.4)):
# ---- Kopf ------------------------------------------------------------------
-txt(Inches(0.95), Inches(0.32), Inches(8), Inches(0.35), "ARCHITEKTUR",
- size=12.5, color=ACCENT, bold=True, spc=240)
-txt(Inches(0.95), Inches(0.62), Inches(11), Inches(0.85), "Offen – und gesteuert",
- size=31, color=NAVY, bold=True)
-shp(MSO_SHAPE.RECTANGLE, Inches(0.95), Inches(1.34), Inches(0.6), Pt(4), fill=ACCENT)
+chrome(s, "Offen – und gesteuert", 3)
x0, x1 = Inches(0.95), Inches(12.38)
@@ -144,6 +137,14 @@ core_y, core_h = Inches(4.3), Inches(1.2)
core_cx = core_x + core_w / 2
core_by = core_y + core_h
+# Adapter-Schicht: echte Schicht als Hülle um den Kern (Trennung)
+lay_pad_x, lay_pad_t = Inches(0.24), Inches(0.30)
+lay_x = core_x - lay_pad_x
+lay_w = core_w + lay_pad_x * 2
+lay_y = core_y - lay_pad_t
+lay_h = core_h + lay_pad_t
+lay_right = lay_x + lay_w
+
def adapter(xc, yc):
shp(MSO_SHAPE.ROUNDED_RECTANGLE, xc - Inches(0.10), yc - Inches(0.11),
@@ -185,8 +186,12 @@ def icon_dfu(ix, iy): # DFÜ
fill=ACCENT)
-# Governance -> LIS
-vline(core_cx, gov_y + Inches(0.07), core_y - (gov_y + Inches(0.07)), color=LINEC, weight=Pt(1))
+g = Inches(0.10) # Abstand Adapter-Linie <-> LIS
+th = Inches(0.08) # Dicke der roten Adapter-Linie
+
+# Governance -> obere Adapter-Linie am Kern
+vline(core_cx, gov_y + Inches(0.07), (core_y - g - th) - (gov_y + Inches(0.07)),
+ color=LINEC, weight=Pt(1))
uw, uh = Inches(1.55), Inches(0.6)
@@ -198,37 +203,50 @@ def ums_box(bx, yc, name, icon_fn):
size=12, color=INK, align=PP_ALIGN.LEFT)
-# Umsysteme links
+# Umsysteme links -> Adapter-Schichten am linken Kernrand
lx = Inches(0.8)
ums_box(lx, Inches(4.6), "Anlieferung", icon_truck)
ums_box(lx, Inches(5.2), "DFÜ", icon_dfu)
for yc in (Inches(4.6), Inches(5.2)):
- hline(lx + uw, yc, core_x - (lx + uw))
- adapter(core_x - Inches(0.16), yc)
+ hline(lx + uw, yc, (core_x - g - th) - (lx + uw))
-# Umsystem rechts
+# Umsystem rechts -> Adapter-Linie am rechten Kernrand
gx = Inches(6.65)
ums_box(gx, Inches(4.9), "Geräte", icon_device)
-hline(core_x + core_w, Inches(4.9), gx - (core_x + core_w))
-adapter(core_x + core_w + Inches(0.16), Inches(4.9))
+hline(core_x + core_w + g + th, Inches(4.9), gx - (core_x + core_w + g + th))
-# LIS (kompakt, solide)
+# ---- LIS-Kern -------------------------------------------------------------
shp(MSO_SHAPE.ROUNDED_RECTANGLE, core_x, core_y, core_w, core_h, fill=NAVY)
sh = shp(MSO_SHAPE.ROUNDED_RECTANGLE, core_x, core_y, core_w, core_h, fill=NAVY)
boxtext(sh, "LIS", size=22, color=WHITE, bold=True)
-# Module unten (andockend)
+
+# ---- 5 Adapter-Linien – je Schnittstelle eine, mit Abstand zum Kern -------
+def rbar_v(outer_x, yc, h=Inches(0.55)): # vertikal, linker/rechter Rand
+ shp(MSO_SHAPE.RECTANGLE, outer_x, yc - h / 2, th, h, fill=ACCENT)
+
+
+def rbar_h(xc, outer_y, w): # horizontal, oben/unten
+ shp(MSO_SHAPE.RECTANGLE, xc - w / 2, outer_y, w, th, fill=ACCENT)
+
+
+rbar_v(core_x - g - th, Inches(4.6)) # 1) Anlieferung
+rbar_v(core_x - g - th, Inches(5.2)) # 2) DFÜ
+rbar_h(core_cx, core_y - g - th, Inches(1.15)) # 3) nach oben (Steuerung)
+rbar_v(core_x + core_w + g, Inches(4.9)) # 4) Geräte
+rbar_h(core_cx, core_by + g, core_w - Inches(0.2)) # 5) Fachliche Verarbeitung
+
+# Fachliche Verarbeitung (unten): Module an der unteren Adapter-Linie
mw, mh = Inches(1.7), Inches(0.6)
my = Inches(6.05)
for nm, fill, tc, mcx in [("Hochdurchsatz", TINT_T, TEAL, Inches(3.85)),
("Spezial", TINT_A, AMBER, Inches(5.85))]:
- vline(mcx, core_by, my - core_by, color=LINEC, weight=Pt(1))
- dock(mcx, core_by + (my - core_by) / 2)
- sh = shp(MSO_SHAPE.ROUNDED_RECTANGLE, mcx - mw / 2, my, mw, mh, fill=fill, line=tc, lw=Pt(1.25))
- boxtext(sh, nm, size=12.5, color=tc, bold=True)
+ vline(mcx, core_by + g + th, my - (core_by + g + th), color=LINEC, weight=Pt(1))
+ sh = shp(MSO_SHAPE.ROUNDED_RECTANGLE, mcx - mw / 2, my, mw, mh, fill=fill, line=tc, lw=Pt(1.5))
+ boxtext(sh, nm, size=12.5, color=INK, bold=True)
-txt(Inches(2.9), my + mh + Inches(0.12), Inches(4.0), Inches(0.3), "eigene Module",
- size=12, color=MUTE, bold=True, align=PP_ALIGN.CENTER)
+txt(Inches(2.9), my + mh + Inches(0.12), Inches(4.0), Inches(0.3), "Fachliche Verarbeitung",
+ size=12, color=LIGHT, bold=True, align=PP_ALIGN.CENTER)
# ===========================================================================
@@ -252,17 +270,14 @@ mini_lis(Inches(10.0), Inches(4.75), faded=False)
vline(Inches(10.0) + hw_ / 2, gov_y + Inches(0.07), Inches(4.75) - (gov_y + Inches(0.07)),
color=LINEC, weight=Pt(1))
txt(Inches(9.9), Inches(4.35) + hh_ + Inches(0.2), Inches(2.4), Inches(0.35), "weitere LIS",
- size=13, color=MUTE, align=PP_ALIGN.CENTER)
+ size=13, color=LIGHT, align=PP_ALIGN.CENTER)
# ---- Mini-Legende ----------------------------------------------------------
ly = Inches(7.05)
-adapter(Inches(7.35), ly)
-txt(Inches(7.55), ly - Inches(0.16), Inches(2.1), Inches(0.32), "Adapter (Umsysteme)",
- size=11.5, color=MUTE)
-dock(Inches(9.65), ly)
-txt(Inches(9.85), ly - Inches(0.16), Inches(2.1), Inches(0.32), "Andocken (Module)",
- size=11.5, color=MUTE)
+shp(MSO_SHAPE.RECTANGLE, Inches(7.55), ly - Inches(0.11), th, Inches(0.22), fill=ACCENT)
+txt(Inches(7.8), ly - Inches(0.16), Inches(3.2), Inches(0.32),
+ "Adapter-Schicht – je Schnittstelle eine", size=11.5, color=LIGHT)
-out = "/home/marcuh/claude-projects/limbach/Folie3.pptx"
+out = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Folie3.pptx")
prs.save(out)
print("saved", out)
diff --git a/limbach/folie3b.py b/limbach/folie3b.py
new file mode 100644
index 0000000..2ba128c
--- /dev/null
+++ b/limbach/folie3b.py
@@ -0,0 +1,180 @@
+#!/usr/bin/env python3
+"""Folie 4 (eingehängt nach Folie 3) – Architektur der Modularisierung:
+Modularer Monolith vs. Microservices, anschaulich im Containerumfeld.
+Links: 1 Container, Module mit In-Process-Calls, 1 geteilte DB.
+Rechts: N Container, Services über Netzwerk, DB je Service.
+Unten: kompakte Gegenüberstellung der wesentlichen Unterschiede."""
+
+import os
+from pptx import Presentation
+from pptx.util import Inches, Pt
+from pptx.dml.color import RGBColor
+from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
+from pptx.enum.shapes import MSO_SHAPE
+from adesso_style import (WHITE, INK, BLUE, TEAL, GREEN, CORAL, LIGHT, MUTE, RECF,
+ F, FC, paint_background, chrome)
+
+NAVY = INK
+ACCENT = CORAL
+LINEC = LIGHT
+
+prs = Presentation()
+prs.slide_width = Inches(13.333)
+prs.slide_height = Inches(7.5)
+s = prs.slides.add_slide(prs.slide_layouts[6])
+paint_background(s, prs.slide_width, prs.slide_height)
+
+
+def shp(kind, x, y, w, h, fill=None, line=None, lw=Pt(1), rot=0):
+ sh = s.shapes.add_shape(kind, x, y, w, h)
+ if fill is None: sh.fill.background()
+ else: sh.fill.solid(); sh.fill.fore_color.rgb = fill
+ if line is None: sh.line.fill.background()
+ else: sh.line.color.rgb = line; sh.line.width = lw
+ if rot: sh.rotation = rot
+ sh.shadow.inherit = False
+ return sh
+
+
+def boxtext(sh, t, size=14, color=INK, bold=False):
+ tf = sh.text_frame; tf.word_wrap = True
+ tf.vertical_anchor = MSO_ANCHOR.MIDDLE
+ tf.margin_left = tf.margin_right = Pt(3); tf.margin_top = tf.margin_bottom = Pt(1)
+ p = tf.paragraphs[0]; p.alignment = PP_ALIGN.CENTER
+ r = p.add_run(); r.text = t
+ r.font.size = Pt(size); r.font.bold = bold; r.font.name = F; r.font.color.rgb = color
+
+
+def txt(x, y, w, h, t, size=14, color=INK, bold=False, italic=False,
+ align=PP_ALIGN.LEFT, spc=None):
+ tb = s.shapes.add_textbox(x, y, w, h); tf = tb.text_frame
+ tf.word_wrap = True; tf.vertical_anchor = MSO_ANCHOR.MIDDLE
+ tf.margin_left = tf.margin_right = tf.margin_top = tf.margin_bottom = Pt(0)
+ p = tf.paragraphs[0]; p.alignment = align
+ r = p.add_run(); r.text = t
+ r.font.size = Pt(size); r.font.bold = bold; r.font.italic = italic
+ r.font.name = F; r.font.color.rgb = color
+ if spc: r.font._rPr.set('spc', str(spc))
+ return tb
+
+
+def vline(x, y, h, color=LINEC, weight=Pt(1.4)):
+ shp(MSO_SHAPE.RECTANGLE, x - weight / 2, y, weight, h, fill=color)
+
+
+def hline(x, y, w, color=LINEC, weight=Pt(1.4)):
+ shp(MSO_SHAPE.RECTANGLE, x, y - weight / 2, w, weight, fill=color)
+
+
+# ---- Kopf + Kernaussage ----------------------------------------------------
+chrome(s, "Architektur der Modularisierung", 4)
+txt(Inches(1.25), Inches(0.98), Inches(11.6), Inches(0.34),
+ "Gleiche fachlichen Schnitte – der Unterschied ist die Laufzeit- und Deployment-Grenze.",
+ size=14, color=LIGHT, italic=True)
+
+DOT = [TEAL, GREEN, BLUE] # gleiche Module/Services beidseitig farblich verankert
+LET = ["A", "B", "C"]
+
+# ===========================================================================
+# Spalten-Titel
+# ===========================================================================
+cL = Inches(3.6) # Mittelpunkt linke Spalte
+cR = Inches(9.75) # Mittelpunkt rechte Spalte
+
+txt(Inches(0.95), Inches(1.5), Inches(5.3), Inches(0.32), "Modularer Monolith",
+ size=18, color=WHITE, bold=True, align=PP_ALIGN.CENTER)
+txt(Inches(0.95), Inches(1.84), Inches(5.3), Inches(0.26), "1 Deployable · 1 Prozess",
+ size=11.5, color=LIGHT, align=PP_ALIGN.CENTER)
+
+txt(Inches(7.0), Inches(1.5), Inches(5.5), Inches(0.32), "Microservices",
+ size=18, color=WHITE, bold=True, align=PP_ALIGN.CENTER)
+txt(Inches(7.0), Inches(1.84), Inches(5.5), Inches(0.26), "N Deployables · N Prozesse",
+ size=11.5, color=LIGHT, align=PP_ALIGN.CENTER)
+
+# ===========================================================================
+# LINKS – Modularer Monolith: 1 Container, Module, geteilte DB
+# ===========================================================================
+cx_x, cx_w, cx_y, cx_h = Inches(0.95), Inches(5.3), Inches(2.2), Inches(1.6)
+shp(MSO_SHAPE.ROUNDED_RECTANGLE, cx_x, cx_y, cx_w, cx_h, line=WHITE, lw=Pt(1.5))
+txt(cx_x + Inches(0.15), cx_y + Inches(0.05), Inches(2.0), Inches(0.26), "1 Container",
+ size=11, color=LIGHT, bold=True)
+
+mw, mh, my = Inches(1.35), Inches(0.8), Inches(2.62)
+mcx = [Inches(2.05), Inches(3.6), Inches(5.15)]
+for i, mx in enumerate(mcx):
+ sh = shp(MSO_SHAPE.ROUNDED_RECTANGLE, mx - mw / 2, my, mw, mh, fill=WHITE, line=LINEC, lw=Pt(1))
+ boxtext(sh, "Modul " + LET[i], size=13, color=INK, bold=True)
+ shp(MSO_SHAPE.OVAL, mx - Inches(0.07), my + Inches(0.12), Inches(0.14), Inches(0.14), fill=DOT[i])
+# In-Process-Pfeile zwischen den Modulen
+for ax in (Inches(2.725), Inches(4.275)):
+ shp(MSO_SHAPE.RIGHT_ARROW, ax, my + mh / 2 - Inches(0.09), Inches(0.2), Inches(0.18), fill=ACCENT)
+txt(cx_x, cx_y + cx_h - Inches(0.34), cx_w, Inches(0.26), "In-Process-Aufrufe · Nanosekunden",
+ size=11, color=LIGHT, align=PP_ALIGN.CENTER)
+
+# geteilte Datenbank
+db_w, db_h, db_y = Inches(1.5), Inches(0.95), Inches(4.0)
+vline(cL, cx_y + cx_h, db_y - (cx_y + cx_h), color=LINEC, weight=Pt(1.4))
+sh = shp(MSO_SHAPE.CAN, cL - db_w / 2, db_y, db_w, db_h, fill=WHITE, line=LINEC, lw=Pt(1))
+boxtext(sh, "DB", size=14, color=INK, bold=True)
+txt(cx_x, db_y + db_h + Inches(0.06), cx_w, Inches(0.28), "1 geteilte Datenbank",
+ size=12.5, color=WHITE, bold=True, align=PP_ALIGN.CENTER)
+
+# ===========================================================================
+# RECHTS – Microservices: Netzwerk-Bus, N Container, DB je Service
+# ===========================================================================
+bar_x, bar_w, bar_y, bar_h = Inches(7.1), Inches(5.3), Inches(2.2), Inches(0.26)
+bar = shp(MSO_SHAPE.ROUNDED_RECTANGLE, bar_x, bar_y, bar_w, bar_h, fill=NAVY)
+boxtext(bar, "Netzwerk-Grenze · HTTP / gRPC / Events", size=11, color=WHITE, bold=True)
+
+scx = [Inches(7.95), Inches(9.75), Inches(11.55)]
+sc_w, sc_h, sc_y = Inches(1.5), Inches(1.2), Inches(2.62)
+sdb_w, sdb_h, sdb_y = Inches(0.85), Inches(0.7), Inches(3.97)
+for i, cx in enumerate(scx):
+ vline(cx, bar_y + bar_h, sc_y - (bar_y + bar_h), color=LINEC, weight=Pt(1.1))
+ shp(MSO_SHAPE.ROUNDED_RECTANGLE, cx - sc_w / 2, sc_y, sc_w, sc_h, line=WHITE, lw=Pt(1.5))
+ sh = shp(MSO_SHAPE.ROUNDED_RECTANGLE, cx - Inches(0.55), sc_y + Inches(0.3),
+ Inches(1.1), Inches(0.62), fill=WHITE, line=LINEC, lw=Pt(1))
+ boxtext(sh, "Svc " + LET[i], size=12.5, color=INK, bold=True)
+ shp(MSO_SHAPE.OVAL, cx - Inches(0.06), sc_y + Inches(0.4), Inches(0.12), Inches(0.12), fill=DOT[i])
+ # eigene DB je Service
+ vline(cx, sc_y + sc_h, sdb_y - (sc_y + sc_h), color=LINEC, weight=Pt(1.1))
+ sh = shp(MSO_SHAPE.CAN, cx - sdb_w / 2, sdb_y, sdb_w, sdb_h, fill=WHITE, line=LINEC, lw=Pt(1))
+ boxtext(sh, "DB", size=11, color=INK, bold=True)
+txt(bar_x, sdb_y + sdb_h + Inches(0.06), bar_w, Inches(0.28), "Datenbank je Service",
+ size=12.5, color=WHITE, bold=True, align=PP_ALIGN.CENTER)
+
+# ===========================================================================
+# Trenner + vs.
+# ===========================================================================
+vline(Inches(6.65), Inches(1.55), Inches(3.5), color=LINEC, weight=Pt(1))
+vs = shp(MSO_SHAPE.OVAL, Inches(6.65) - Inches(0.31), Inches(3.05), Inches(0.62), Inches(0.46),
+ line=WHITE, lw=Pt(1.5))
+vs.fill.background()
+boxtext(vs, "vs.", size=13, color=WHITE, bold=True)
+
+# ===========================================================================
+# Gegenüberstellung – wesentliche Unterschiede
+# ===========================================================================
+rows = [
+ ("Skalierung", "als Ganzes (mehr Replicas)", "je Service gezielt"),
+ ("Datenhaltung", "1 geteilte DB", "DB je Service"),
+ ("Deployment", "gemeinsam, 1 Pipeline", "unabhängig je Service"),
+ ("Konsistenz", "ACID, lokal", "eventual / Saga"),
+]
+ry = Inches(5.5)
+rh = Inches(0.33)
+chip_w = Inches(1.95)
+for i, (dim, mono, ms) in enumerate(rows):
+ y = ry + rh * i
+ chip = shp(MSO_SHAPE.ROUNDED_RECTANGLE, Inches(6.65) - chip_w / 2, y, chip_w, Inches(0.28),
+ line=LINEC, lw=Pt(0.75))
+ chip.fill.background()
+ boxtext(chip, dim, size=11, color=LIGHT, bold=True)
+ txt(Inches(2.5), y - Inches(0.02), Inches(3.1), Inches(0.32), mono,
+ size=12, color=WHITE, align=PP_ALIGN.RIGHT)
+ txt(Inches(7.72), y - Inches(0.02), Inches(3.1), Inches(0.32), ms,
+ size=12, color=WHITE, align=PP_ALIGN.LEFT)
+
+out = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Folie3b.pptx")
+prs.save(out)
+print("saved", out)
diff --git a/limbach/folie4.py b/limbach/folie4.py
index 35998ea..a090e9c 100644
--- a/limbach/folie4.py
+++ b/limbach/folie4.py
@@ -1,36 +1,33 @@
#!/usr/bin/env python3
-"""Folie 4 – Eventstream als Quelle; Aussagen links, Lupen rechts, Strecke unten."""
+"""Folie 4 – Eventstream als Quelle; Aussagen links, Lupen rechts, Strecke unten
+(adesso-Styling)."""
+import os
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.enum.shapes import MSO_SHAPE
+from adesso_style import (WHITE, INK, BLUE, TEAL, GREEN, CORAL, LIGHT, RECF, F, FC,
+ paint_background, chrome)
-INK = RGBColor(0x2A, 0x2F, 0x3A)
-NAVY = RGBColor(0x1F, 0x3A, 0x5F)
-ACCENT = RGBColor(0xC0, 0x49, 0x2F)
-TEAL = RGBColor(0x2F, 0x8F, 0x83)
-AMBER = RGBColor(0xC8, 0x8A, 0x3A)
-MUTE = RGBColor(0x8A, 0x93, 0xA0)
-GLASS = RGBColor(0xF2, 0xF5, 0xF8)
-BELT = RGBColor(0xD7, 0xDC, 0xE2)
-DARK = RGBColor(0x33, 0x39, 0x44)
-LINEC = RGBColor(0xC3, 0xC9, 0xD1)
-THIN = RGBColor(0xDD, 0xE2, 0xE8)
-STREAM = RGBColor(0x24, 0x37, 0x52)
-BG = RGBColor(0xFF, 0xFF, 0xFF)
-WHITE = RGBColor(0xFF, 0xFF, 0xFF)
-F = "Calibri"
+# Rollen-Mapping auf die adesso-Palette
+NAVY = INK # dunkle Füllungen (Lupe, Maschine)
+ACCENT = CORAL
+AMBER = GREEN
+GLASS = RECF
+BELT = RECF
+DARK = INK
+LINEC = LIGHT
+THIN = LIGHT
+STREAM = INK
+MID = RGBColor(0x34, 0x4E, 0x70)
prs = Presentation()
prs.slide_width = Inches(13.333)
prs.slide_height = Inches(7.5)
SW, SH = prs.slide_width, prs.slide_height
s = prs.slides.add_slide(prs.slide_layouts[6])
-bgr = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, SW, SH)
-bgr.fill.solid(); bgr.fill.fore_color.rgb = BG; bgr.line.fill.background()
-bgr.shadow.inherit = False
def shp(kind, x, y, w, h, fill=None, line=None, lw=Pt(1), rot=0):
@@ -44,25 +41,21 @@ def shp(kind, x, y, w, h, fill=None, line=None, lw=Pt(1), rot=0):
return sh
-def txt(x, y, w, h, t, size=16, color=INK, bold=False, italic=False,
- align=PP_ALIGN.LEFT, spc=None):
+def txt(x, y, w, h, t, size=16, color=WHITE, bold=False, italic=False,
+ align=PP_ALIGN.LEFT, spc=None, font=F):
tb = s.shapes.add_textbox(x, y, w, h); tf = tb.text_frame
tf.word_wrap = True; tf.vertical_anchor = MSO_ANCHOR.MIDDLE
tf.margin_left = tf.margin_right = tf.margin_top = tf.margin_bottom = Pt(0)
p = tf.paragraphs[0]; p.alignment = align
r = p.add_run(); r.text = t
r.font.size = Pt(size); r.font.bold = bold; r.font.italic = italic
- r.font.name = F; r.font.color.rgb = color
+ r.font.name = font; r.font.color.rgb = color
if spc: r.font._rPr.set('spc', str(spc))
return tb
-# ---- Kopf (ganz oben) ------------------------------------------------------
-txt(Inches(0.95), Inches(0.32), Inches(8), Inches(0.35), "MODELL",
- size=12.5, color=ACCENT, bold=True, spc=240)
-txt(Inches(0.95), Inches(0.62), Inches(11), Inches(0.85), "Zustand oder Ereignis",
- size=31, color=NAVY, bold=True)
-shp(MSO_SHAPE.RECTANGLE, Inches(0.95), Inches(1.34), Inches(0.6), Pt(4), fill=ACCENT)
+paint_background(s, SW, SH)
+chrome(s, "Zustand oder Ereignis", 5)
x0, x1 = Inches(1.0), Inches(12.33)
beltw = x1 - x0
@@ -77,25 +70,23 @@ cy = Inches(1.95)
for i, c in enumerate(claims):
y = cy + Inches(0.6) * i
shp(MSO_SHAPE.RECTANGLE, Inches(0.95), y + Inches(0.1), Inches(0.14), Inches(0.14), fill=ACCENT)
- bold = (i == 0)
- txt(Inches(1.28), y, Inches(4.6), Inches(0.45), c,
- size=16.5, color=INK, bold=bold)
+ txt(Inches(1.28), y, Inches(4.6), Inches(0.45), c, size=16.5, color=WHITE, bold=(i == 0))
# ===========================================================================
-# LUPEN (Projektionen) – nach rechts gerückt
+# LUPEN (Projektionen)
# ===========================================================================
def lupe(cx, label, label_w):
- cy = Inches(2.45)
+ cyy = Inches(2.45)
d = Inches(0.9)
- shp(MSO_SHAPE.OVAL, cx - d / 2, cy - d / 2, d, d, fill=WHITE, line=NAVY, lw=Pt(4))
- shp(MSO_SHAPE.OVAL, cx - d / 2 + Inches(0.13), cy - d / 2 + Inches(0.12),
- Inches(0.17), Inches(0.13), fill=RGBColor(0xED,0xF1,0xF5))
+ shp(MSO_SHAPE.OVAL, cx - d / 2, cyy - d / 2, d, d, fill=WHITE, line=NAVY, lw=Pt(4))
+ shp(MSO_SHAPE.OVAL, cx - d / 2 + Inches(0.13), cyy - d / 2 + Inches(0.12),
+ Inches(0.17), Inches(0.13), fill=RECF)
hw, hh = Inches(0.48), Inches(0.15)
- hcx, hcy = cx + d * 0.52, cy + d * 0.52
+ hcx, hcy = cx + d * 0.52, cyy + d * 0.52
shp(MSO_SHAPE.ROUNDED_RECTANGLE, hcx - hw / 2, hcy - hh / 2, hw, hh, fill=NAVY, rot=45)
- txt(cx - label_w / 2, cy + d / 2 + Inches(0.16), label_w, Inches(0.6),
- label, size=14, color=NAVY, bold=True, align=PP_ALIGN.CENTER)
- return cy + d / 2
+ txt(cx - label_w / 2, cyy + d / 2 + Inches(0.16), label_w, Inches(0.6),
+ label, size=14, color=WHITE, bold=True, align=PP_ALIGN.CENTER)
+ return cyy + d / 2
lupes = [
@@ -133,7 +124,7 @@ mx, mw = Inches(5.45), Inches(2.45)
my, mh = Inches(5.2), Inches(1.5)
shp(MSO_SHAPE.ROUNDED_RECTANGLE, mx, my, mw, mh, fill=NAVY)
shp(MSO_SHAPE.ROUNDED_RECTANGLE, mx + Inches(0.3), my + Inches(0.22),
- mw - Inches(0.6), Inches(0.32), fill=RGBColor(0x34,0x4E,0x70))
+ mw - Inches(0.6), Inches(0.32), fill=MID)
shp(MSO_SHAPE.OVAL, mx + Inches(0.35), my + Inches(0.74), Inches(0.15), Inches(0.15), fill=TEAL)
shp(MSO_SHAPE.OVAL, mx + Inches(0.6), my + Inches(0.74), Inches(0.15), Inches(0.15), fill=AMBER)
shp(MSO_SHAPE.RECTANGLE, mx - Inches(0.02), belt_top - Inches(0.02),
@@ -165,6 +156,6 @@ for xv in pre_x + post_x:
shp(MSO_SHAPE.OVAL, Inches(xv) - Inches(0.05), acx - Inches(0.05),
Inches(0.1), Inches(0.1), fill=AMBER)
-out = "/home/marcuh/claude-projects/limbach/Folie4.pptx"
+out = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Folie4.pptx")
prs.save(out)
print("saved", out)
diff --git a/limbach/memory/MEMORY.md b/limbach/memory/MEMORY.md
index b2cb63e..fbb5730 100644
--- a/limbach/memory/MEMORY.md
+++ b/limbach/memory/MEMORY.md
@@ -1 +1,3 @@
- [Limbach Workshop / Outline](limbach-workshop-outline.md) — Wissensaufbau in Outline für Workshop 15./16.06.2026
+- [adesso Styling](adesso-styling.md) — Corporate-Template-Quelle & Design-Tokens für Folien 2–4 (adesso_style.py)
+- [Verbotene Wörter](verbotene-woerter.md) — „Pointe", „der Hit", „der Clou" & reißerische Wörter nie verwenden
diff --git a/limbach/memory/adesso-styling.md b/limbach/memory/adesso-styling.md
new file mode 100644
index 0000000..2e32147
--- /dev/null
+++ b/limbach/memory/adesso-styling.md
@@ -0,0 +1,18 @@
+---
+name: adesso-styling
+description: Corporate-Template-Quelle und Design-Tokens für das adesso-Styling der Workshop-Folien
+metadata:
+ node_type: memory
+ type: reference
+ originSessionId: 38920718-90c7-440b-acfe-8a06dc15263b
+---
+
+Die Workshop-Folien (Folie 2–4) wurden auf das **adesso-Corporate-Template** umgestylt.
+
+- **Referenz-Deck:** `/Users/marcus.hinz/Documents/schnzz.pptx` (adesso-Masterdeck, 96 Folien; LibreOffice exportiert nur die ~19 sichtbaren).
+- **Design-Tokens** (extrahiert aus dem Theme): Fonts **Fira Sans Condensed** (Titel) / **Fira Sans** (Body); Palette Blau `#006EC7`, Purpur `#461EBE`, Grün `#76C800`, Teal `#28DCAA`, Coral `#FF9868`, Pink `#F566BA`, Taupe `#887D75`.
+- **Look:** Teal→Blau-Verlauf, weiße Shapes + Text, Coral-Akzente; Header = Pill-Marker + Titel; schlanke Fußzeile = Seitenzahl + „adesso".
+
+Implementiert im geteilten Modul `adesso_style.py` (von `folie2/3/4.py` importiert). Logo wird als Text „adesso" gesetzt (echtes Asset `image2.svg` im Template ist weiß-auf-weiß ohne Alpha → nicht direkt nutzbar).
+
+Siehe [[limbach-workshop-outline]].
diff --git a/limbach/memory/verbotene-woerter.md b/limbach/memory/verbotene-woerter.md
new file mode 100644
index 0000000..02688fc
--- /dev/null
+++ b/limbach/memory/verbotene-woerter.md
@@ -0,0 +1,17 @@
+---
+name: verbotene-woerter
+description: Wörter, die in Folien/Texten für den User niemals verwendet werden dürfen
+metadata:
+ type: feedback
+---
+
+Reißerische / werbliche „Aha"-Wörter sind verboten – weder in Folieninhalten noch in der Konversation. Konkret untersagt:
+
+- **„Pointe"**
+- **„der Hit"**
+- **„der Clou"**
+- sinngleiche Effekthascherei (z. B. „der Knaller", „das Highlight", „der Wow-Effekt", „die Sensation")
+
+**Why:** Ausdrückliche, mit Nachdruck formulierte Vorgabe des Users; passt nicht zum sachlichen Ton.
+
+**How to apply:** Komplett vermeiden. Stattdessen nüchtern: „Kernaussage", „das Entscheidende", „Fazit", „der zentrale Vorteil", „der wesentliche Unterschied". Liste bei weiteren Hinweisen ergänzen.
diff --git a/limbach/merge.py b/limbach/merge.py
index dda889a..5f56f20 100644
--- a/limbach/merge.py
+++ b/limbach/merge.py
@@ -2,13 +2,14 @@
"""Fügt Folie2, Folie3, Folie4 zu einer Datei zusammen (Reihenfolge 2, 3, 4)."""
import copy
+import os
from pptx import Presentation
-base = "/home/marcuh/claude-projects/limbach/"
+base = os.path.dirname(os.path.abspath(__file__)) + "/"
dst = Presentation(base + "Folie2.pptx") # Seite 1 = Folie 2
blank = dst.slide_layouts[6]
-for fname in ("Folie3.pptx", "Folie4.pptx"): # Seiten 2 und 3
+for fname in ("Folie3.pptx", "Folie3b.pptx", "Folie4.pptx"): # Seiten 2, 3, 4, 5
src = Presentation(base + fname)
new_slide = dst.slides.add_slide(blank)
for shape in src.slides[0].shapes: