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: