From 3183a397634838738147f41be4dba194e6554b29 Mon Sep 17 00:00:00 2001
From: ValueOn AG
Date: Tue, 21 Apr 2026 23:49:43 +0200
Subject: [PATCH] fixes udb, outlook, workflow
---
.../2026-03-pm-web-image-search.md | 0
c-work/1-plan/2026-04-udb-action-system.md | 200 ---------
...pwg-pilot-mietzinsbestaetigung-workflow.md | 0
.../2026-04-redmine-feature.md | 0
c-work/4-done/2026-04-udb-action-system.md | 224 ++++++++++
c-work/4-done/2026-04-ui-polish-bundle.md | 383 ++++++++++++++++++
6 files changed, 607 insertions(+), 200 deletions(-)
rename c-work/{1-plan => 0-ideas}/2026-03-pm-web-image-search.md (100%)
delete mode 100644 c-work/1-plan/2026-04-udb-action-system.md
rename c-work/{1-plan => 3-validate}/2026-04-pwg-pilot-mietzinsbestaetigung-workflow.md (100%)
rename c-work/{1-plan => 4-done}/2026-04-redmine-feature.md (100%)
create mode 100644 c-work/4-done/2026-04-udb-action-system.md
create mode 100644 c-work/4-done/2026-04-ui-polish-bundle.md
diff --git a/c-work/1-plan/2026-03-pm-web-image-search.md b/c-work/0-ideas/2026-03-pm-web-image-search.md
similarity index 100%
rename from c-work/1-plan/2026-03-pm-web-image-search.md
rename to c-work/0-ideas/2026-03-pm-web-image-search.md
diff --git a/c-work/1-plan/2026-04-udb-action-system.md b/c-work/1-plan/2026-04-udb-action-system.md
deleted file mode 100644
index 0ee4fc7..0000000
--- a/c-work/1-plan/2026-04-udb-action-system.md
+++ /dev/null
@@ -1,200 +0,0 @@
-
-
-
-
-
-# Unified-Data-Bar Action-System (Composable, Multi-Modal)
-
-## Beschreibung und Kontext
-
-Die **Unified Data Bar (UDB)** ist die zentrale Datenleiste der PORTA-UI: sie zeigt Files, Folders, Chats, Sources und Konversations-Artefakte. Der Kern jeder Ansicht ist die Komponente `FolderTree` (`frontend_nyla/src/components/FolderTree/FolderTree.tsx`). Sie wird heute in mehreren Kontexten verwendet:
-
-| Kontext | Datei | Zweck |
-|---------|-------|-------|
-| UDB-FilesTab | `components/UnifiedDataBar/FilesTab.tsx` | Dateien des aktuellen Feature-Kontexts (Workspace, Trustee, GraphEditor …) |
-| Standalone-Files-Page | `pages/basedata/FilesPage.tsx` | Globale Datei-Verwaltung im Admin-Bereich |
-| Folder-Picker im Move-Modal | (innerhalb FolderTree) | Ziel-Auswahl beim Verschieben |
-| SharePoint-Browser | `components/FolderTree/SharepointBrowseTree.tsx` | externes Filesystem (read-only) |
-| FileContext-Konsumenten | `contexts/FileContext.tsx` | Quelle der Wahrheit für Tree-Files |
-
-**Problem heute:** Aktionen pro Datei sind hartcodiert im `FolderTree`-Renderer. Es gibt **fünf** fest verdrahtete Buttons (Rename, Delete, Chat-Send, Scope-Cycle, Neutralize-Toggle). Es gibt:
-- Kein generisches Erweiterungs-Konzept (z. B. „in Graph-Editor laden" für `.workflow.json`).
-- Keine Kontext-Menüs (Rechtsklick desktop, Long-Press mobil).
-- Kein Action-Discovery (User muss alle Icons immer auf jeder Zeile sehen — auch wenn Aktion auf den Dateityp gar nicht passt).
-- Kein einheitliches Drag-&-Drop-Handling für „Datei in Chat", „Datei in anderen Ordner", „Datei in Workflow-Editor". Jede Konsumenten-Komponente baut DnD selber.
-- Keine Touch-Optimierung (Icons sind 16 px, keine Long-Press-Erkennung).
-
-**Ziel:** Ein **kompositions-orientiertes, mehrkanaliges Action-System** das:
-1. Anwendungsspezifische Aktionen pro Aufruf-Site deklarativ registrieren lässt (Plugin-ähnlich).
-2. Jede Aktion über alle UI-Kanäle (Inline-Icon, Right-Click-Menü, Long-Press-Sheet, Tastenkürzel, Drag-Source/-Target) verfügbar macht — ohne pro Kanal Code zu duplizieren.
-3. Sichtbarkeit über Predicates auf Dateityp/Scope/Permissions steuert.
-4. Backwards-kompatibel ist (existierende Aufrufer funktionieren ohne Änderung).
-
-**Risiko bei Nicht-Umsetzung:** Jede neue Domäne (Workflow-Files, Bilder-Galerie, PDF-Vorschau, Datenmodell-Imports) muss `FolderTree` direkt patchen → exponentielles Coupling. Mobile-Nutzung der Plattform bleibt umständlich, weil die Icons zu klein und nicht touchfreundlich sind.
-
-## Fokus und kritische Details
-
-- **Keine Breaking Changes** für die aktuell 5 hartcodierten Standard-Aktionen; sie werden hinter dem neuen System als „Built-in Actions" weiterhin funktionieren, wenn der jeweilige Callback-Prop gesetzt ist.
-- **Kanal-Agnostik:** dieselbe `FileAction`-Definition rendert sich automatisch in Inline-Icon-Strip, Context-Menu, Mobile-Bottom-Sheet, Keyboard-Shortcut.
-- **Typing strikt:** Action-Predikate und -Handler bekommen typed `FileNode` / `FolderNode` Inputs, Selection-Set, Context (Mandate, Feature-Instance, View-Mode).
-- **Performance:** Predicates müssen pure und billig sein (keine async-Calls). Async-Operationen passieren erst im Handler.
-- **Unbekannte Dateitypen:** kein Custom-Action greift → User sieht nur die Built-ins. Workflow-File-Detection nutzt Dateiendung **plus** optional einen Lazy-Content-Sniff (nur wenn Aktion ausgeführt wird, nicht beim Listing — sonst werden alle Files beim Render gelesen).
-- **Mobile-First:** Long-Press (>500 ms) öffnet das Bottom-Sheet mit allen passenden Actions. Tap auf Datei = `onSelect` (wie heute), kein implizites Aktion-Triggering.
-- **Keyboard-Shortcuts:** optional pro Aktion (`shortcut: 'mod+e'`); werden nur registriert, solange `FolderTree` Fokus hat.
-- **Drag&Drop:** Erweiterung um typed `dragPayload` (z. B. `{ type: 'file', mime: 'application/json+workflow', fileId, name }`) und `dropTargets[]` (Aktionen, die auch als Drop-Target fungieren — z. B. „Workflow in Editor laden" akzeptiert Drops aus FilesTab in den Graph-Canvas).
-- **Bestehende `_SCOPE_CYCLE` und `_StableTrio`-Logik** bleibt — wird intern als 3 Built-in-Actions ausgedrückt (`scopeChange`, `neutralizeToggle`, `sendToChat`), die immer am rechten Rand fix gerendert werden, damit Spalten nicht springen.
-
-## Ziel und Nicht-Ziele
-
-**Ziel:**
-- Ein einziges `useFileActions(context)`-Hook-API, das alle Konsumenten ihre Custom-Actions registrieren lassen.
-- `FolderTree`-Props um `actions?: FileAction[]` erweitern (Built-ins bleiben Default).
-- Right-Click + Long-Press-Bottom-Sheet als neue UI-Patterns.
-- Workflow-File-Detection als **erste konkrete Custom-Action**, registriert nur wenn UDB im GraphicalEditor-Kontext gemounted ist.
-
-**Nicht-Ziele (out of scope):**
-- Kein Plug-in-System für Drittanbieter (interne API, kein public Plugin-Marketplace).
-- Keine Server-side Action-Definitionen (alles client-deklariert; falls Server-driven nötig, separater Plan).
-- Keine Änderungen an `FileItem`-DB-Modell oder `routeDataFiles`.
-- Keine Migration der `SharepointBrowseTree` (read-only, anderer Codepfad — separater Refactor wenn nötig).
-
-## Konkrete Schritte
-
-### Phase 1 — Action-Modell + Registry-Hook
-
-- [ ] Neue Datei `frontend_nyla/src/components/FolderTree/actions/types.ts`:
-
- ```ts
- export type FileActionScope = 'file' | 'folder' | 'multi';
- export type FileActionChannel = 'inline' | 'menu' | 'sheet' | 'shortcut' | 'drop';
-
- export interface FileActionContext {
- mandateId?: string;
- featureInstanceId?: string;
- viewMode: 'desktop' | 'mobile';
- udbContext?: 'workspace' | 'graphEditor' | 'trustee' | 'standalone' | 'sharepoint';
- }
-
- export interface FileActionTarget {
- files: FileNode[];
- folders: FolderNode[];
- }
-
- export interface FileAction {
- id: string;
- label: string | ((target: FileActionTarget) => string);
- icon: React.ComponentType<{ size?: number }>;
- iconColor?: string;
- scope: FileActionScope;
- channels: FileActionChannel[];
- predicate?: (target: FileActionTarget, ctx: FileActionContext) => boolean;
- handler: (target: FileActionTarget, ctx: FileActionContext) => Promise | void;
- shortcut?: string;
- confirm?: { title: string; body: (target: FileActionTarget) => string };
- dragMime?: string;
- sortOrder?: number;
- danger?: boolean;
- }
- ```
-
-- [ ] Neue Datei `frontend_nyla/src/components/FolderTree/actions/registry.ts` mit:
- - `_BUILTIN_ACTIONS: FileAction[]` — die heute hartcodierten 5 Aktionen extrahiert.
- - `useFileActions(context, customActions?: FileAction[])` Hook → liefert sortierte, gefilterte Aktionen pro `FileActionTarget`.
- - Memoization über stabile Action-IDs.
-- [ ] Konvention: `id` ist global eindeutig (`'core.rename'`, `'core.delete'`, `'workflow.openInEditor'` …); Custom-Actions namespace-prefixed nach Domäne.
-
-### Phase 2 — `FolderTree`-Refactor (Action-Aware)
-
-- [ ] `FolderTreeProps` neue optionale Props:
- ```ts
- customActions?: FileAction[];
- udbContext?: FileActionContext['udbContext'];
- ```
-- [ ] Inline-Icon-Strip an Datei-Zeile rendert `actions.filter(a => a.channels.includes('inline'))` — gefiltert via Predicate. Maximal 3 Icons (sonst rückt der Rest in „more"-Overflow). Built-ins (Rename/Delete) bleiben mit `sortOrder` zuerst, Custom-Actions danach.
-- [ ] **Right-Click-Handler** (`onContextMenu`) öffnet `` mit allen `'menu'`-Aktionen. Komponente neu in `actions/FileActionContextMenu.tsx`.
-- [ ] **Long-Press-Handler** (Touch-Event, 500 ms threshold via `usePointerLongPress`-Hook) öffnet `` (Slide-Up von unten, voller Breite, 48 px Touch-Targets).
-- [ ] **Keyboard-Handler:** beim Mount globalen Listener registrieren, der Shortcuts nur dispatcht wenn `FolderTree` `document.activeElement` enthält.
-- [ ] **Drag-Source:** existing `onItemDragStart` setzt zusätzlich `e.dataTransfer.setData(action.dragMime, JSON.stringify(payload))` für jede passende Action. **Drag-Target** auf Canvas/Drop-Zonen liest die MIME und ruft den Handler.
-- [ ] Backwards-Compat: Wenn keine `customActions` übergeben werden, Verhalten 1:1 wie heute. Wenn Caller noch direkt `onRenameFile`/`onDeleteFile` setzt, Built-in-Action benutzt diese Callbacks. Ältere Caller müssen NICHT angepasst werden.
-
-### Phase 3 — Konsumenten migrieren (1 Custom-Action je Pilot)
-
-- [ ] **`FilesTab.tsx`** — wenn `context.featureCode === 'graphicalEditor'`, Action `workflow.openInEditor` registrieren:
- ```ts
- const workflowActions: FileAction[] = useMemo(() => [{
- id: 'workflow.openInEditor',
- label: t('In Graph-Editor laden'),
- icon: FaFileImport,
- scope: 'file',
- channels: ['inline', 'menu', 'sheet', 'drop'],
- dragMime: 'application/json+workflow',
- predicate: ({ files }) => files.length === 1 && files[0].fileName.toLowerCase().endsWith('.workflow.json'),
- handler: async ({ files }) => importWorkflowFromFile(request, instanceId, { fileId: files[0].id }),
- }], [t, instanceId, request]);
- ```
-- [ ] **`Automation2FlowEditor`** — Drop-Target für `application/json+workflow` MIME, dispatcht `workflow.openInEditor`-Handler.
-- [ ] **`FilesPage.tsx`** — keine Custom-Actions, läuft mit Built-ins (Verifikation Backwards-Compat).
-- [ ] **`SharepointBrowseTree.tsx`** — bleibt read-only, kriegt nur `[]` für `customActions` (kein Refactor nötig).
-
-### Phase 4 — UI-Patterns + Visual-Design
-
-- [ ] CSS-Module `actions/FileActionContextMenu.module.css` mit Themes (dark/light), Backdrop-Click-Close, ESC-Close.
-- [ ] CSS-Module `actions/FileActionBottomSheet.module.css` — Slide-Up-Animation, Drag-to-Dismiss, 48 px Touch-Targets, `safe-area-inset-bottom` respektieren.
-- [ ] `useViewMode()`-Hook (existiert ggf. schon) für `'desktop' | 'mobile'`-Detection (CSS-Media-Query + Touch-Event-Heuristik).
-- [ ] Icons: `react-icons/fa` für alle Built-ins; Custom-Actions können beliebige Icon-Komponenten liefern.
-- [ ] Visual-Hint für Drag-Source: leichtes Pulsieren wenn Custom-Drag-Action existiert (analog GitHub Issue-Cards).
-
-### Phase 5 — Tests + Dokumentation
-
-- [ ] Unit-Tests: Predicate-Filter, Sort-Order, Built-in-Backwards-Compat (Rename/Delete via alte Props).
-- [ ] Storybook-Story `FolderTree.stories.tsx` mit allen Kanälen demonstriert.
-- [ ] Doku: `wiki/uiPatterns/udbActions.md` (oder analog) mit „Wie registriere ich eine Custom-Action".
-- [ ] Migration-Guide-Snippet im PWG-Pilot-Plan: „FilesTab → Graph-Editor: jetzt via `customActions` statt API-Workaround".
-
-### Querschnitt
-
-- [ ] **API-Endpunkte:** keine Backend-Änderungen.
-- [ ] **DB-Schema:** keine.
-- [ ] **Frontend-Komponenten:** `FolderTree` (refactor), 2 neue UI-Komponenten (`FileActionContextMenu`, `FileActionBottomSheet`), 1 neuer Hook (`useFileActions`).
-- [ ] **RBAC:** Predicates können `ctx.udbContext` und Datei-Eigenschaften prüfen; falls per-User-Rechte nötig, kann Predicate auf einen UserPermissions-Hook zugreifen. Aktuell keine neuen Permissions nötig.
-- [ ] **Mobile/Accessibility:** Long-Press-Bottom-Sheet ist a11y-kompatibel (ARIA-Roles `dialog`, `menu`, `menuitem`); Tastatur-Navigation mit Arrow-Keys + Enter.
-- [ ] **Bundle-Size:** marginal — Context-Menu + Bottom-Sheet zusammen ca. 5–8 KB minified.
-
-## Akzeptanzkriterien
-
-| # | Kriterium (Given-When-Then) | Prio |
-|---|---|---|
-| 1 | Given `FolderTree` ohne `customActions`, When User Datei umbenennt/löscht, Then funktioniert wie heute (kein Regression) | must |
-| 2 | Given `FilesTab` im GraphEditor-Kontext + Datei `pilot.workflow.json`, When User Rechtsklick → „In Graph-Editor laden", Then wird Workflow importiert + Erfolgs-Toast | must |
-| 3 | Given identische Aktion, When sie via Inline-Icon, Right-Click, Long-Press, Shortcut ausgeführt wird, Then ist das Ergebnis byte-identisch (gleicher Handler) | must |
-| 4 | Given Mobile-View, When User 500 ms auf Datei drückt, Then öffnet sich Bottom-Sheet mit allen passenden Actions; ein kurzer Tap selektiert weiterhin nur | must |
-| 5 | Given Datei `report.pdf`, When User Rechtsklick, Then erscheint **kein** „In Graph-Editor laden" (Predicate filtert) | must |
-| 6 | Given `customActions` mit `dragMime: 'application/json+workflow'`, When User Datei auf Workflow-Editor-Canvas zieht, Then wird Handler dispatched | should |
-| 7 | Given `customActions` mit `shortcut: 'mod+e'`, When `FolderTree` Fokus hat und User `Cmd+E` drückt, Then wird Handler dispatched; ohne Fokus passiert nichts | should |
-| 8 | Given mehr als 3 Inline-Actions, When Render, Then werden die ersten 3 (nach `sortOrder`) als Icons angezeigt, der Rest hinter „⋯ More"-Overflow erreichbar | should |
-
-## Testplan
-
-| ID | AC | Art | Automatisiert | Repo-Pfad | Status |
-|----|----|-----|---|---|---|
-| T1 | 1, 5, 8 | unit | ja | `frontend_nyla/tests/components/FolderTree/actions.test.tsx` | pending |
-| T2 | 2, 3 | integration | ja | `frontend_nyla/tests/components/FolderTree/workflowAction.test.tsx` | pending |
-| T3 | 4 | manual | nein | mobile-emulation in Chrome DevTools | pending |
-| T4 | 6 | integration | ja | `frontend_nyla/tests/components/FolderTree/dragDrop.test.tsx` | pending |
-| T5 | 7 | unit | ja | `frontend_nyla/tests/components/FolderTree/shortcuts.test.tsx` | pending |
-
-## Inspirations / State-of-the-Art
-
-- **VS Code Command Registry** — globale Command-IDs, mehrkanalig (Command Palette, Right-Click-Menu, Shortcut, View-Title-Bar). Predicate-Visibility via `when`-Clauses.
-- **Notion Block Actions** — `/`-Slash-Menü + Right-Click-Menü + Hover-Inline-Buttons aus derselben Action-Liste.
-- **Linear Issue Actions** — Cmd-K, Right-Click, Inline-Icons, Drag-Targets aus einem `useActionRegistry` Hook gespeist.
-- **Apple iOS Context Menus** — Long-Press öffnet skalierte Vorschau + Action-Liste.
-- **Google Drive** — Context-Action-Sichtbarkeit pro Mime-Typ (z. B. „Mit Docs öffnen" nur bei `.docx`).
-
-## Links
-
-- Heutiger `FolderTree`: `frontend_nyla/src/components/FolderTree/FolderTree.tsx`
-- Konsumenten: `frontend_nyla/src/components/UnifiedDataBar/FilesTab.tsx`, `frontend_nyla/src/pages/basedata/FilesPage.tsx`
-- Datei-Context: `frontend_nyla/src/contexts/FileContext.tsx`
-- Workflow-API: `frontend_nyla/src/api/workflowApi.ts` (`importWorkflowFromFile`, `isWorkflowFileContent`)
-- Verwandter Plan: `wiki/c-work/1-plan/2026-04-pwg-pilot-mietzinsbestaetigung-workflow.md` (Phase 2 deferred)
diff --git a/c-work/1-plan/2026-04-pwg-pilot-mietzinsbestaetigung-workflow.md b/c-work/3-validate/2026-04-pwg-pilot-mietzinsbestaetigung-workflow.md
similarity index 100%
rename from c-work/1-plan/2026-04-pwg-pilot-mietzinsbestaetigung-workflow.md
rename to c-work/3-validate/2026-04-pwg-pilot-mietzinsbestaetigung-workflow.md
diff --git a/c-work/1-plan/2026-04-redmine-feature.md b/c-work/4-done/2026-04-redmine-feature.md
similarity index 100%
rename from c-work/1-plan/2026-04-redmine-feature.md
rename to c-work/4-done/2026-04-redmine-feature.md
diff --git a/c-work/4-done/2026-04-udb-action-system.md b/c-work/4-done/2026-04-udb-action-system.md
new file mode 100644
index 0000000..d7faadc
--- /dev/null
+++ b/c-work/4-done/2026-04-udb-action-system.md
@@ -0,0 +1,224 @@
+
+
+
+
+
+
+
+
+# Unified-Data-Bar Action-System (Composable, Multi-Modal)
+
+## Beschreibung und Kontext
+
+Die **Unified Data Bar (UDB)** ist die zentrale Datenleiste der PORTA-UI: sie zeigt Files, Folders, Chats, Sources und Konversations-Artefakte. Der Kern jeder Ansicht ist die Komponente `FolderTree` (`frontend_nyla/src/components/FolderTree/FolderTree.tsx`). Sie wird heute in mehreren Kontexten verwendet:
+
+| Kontext | Datei | Zweck |
+|---------|-------|-------|
+| UDB-FilesTab | `components/UnifiedDataBar/FilesTab.tsx` | Dateien des aktuellen Feature-Kontexts (Workspace, Trustee, GraphEditor …) |
+| Standalone-Files-Page | `pages/basedata/FilesPage.tsx` | Globale Datei-Verwaltung im Admin-Bereich |
+| Folder-Picker im Move-Modal | (innerhalb FolderTree) | Ziel-Auswahl beim Verschieben |
+| SharePoint-Browser | `components/FolderTree/SharepointBrowseTree.tsx` | externes Filesystem (read-only) |
+| FileContext-Konsumenten | `contexts/FileContext.tsx` | Quelle der Wahrheit für Tree-Files |
+
+**Problem heute:** Aktionen pro Datei sind hartcodiert im `FolderTree`-Renderer. Es gibt **fünf** fest verdrahtete Buttons (Rename, Delete, Chat-Send, Scope-Cycle, Neutralize-Toggle). Es gibt:
+- Kein generisches Erweiterungs-Konzept (z. B. „in Graph-Editor laden" für `.workflow.json`).
+- Keine Kontext-Menüs (Rechtsklick desktop, Long-Press mobil).
+- Kein Action-Discovery (User muss alle Icons immer auf jeder Zeile sehen — auch wenn Aktion auf den Dateityp gar nicht passt).
+- Kein einheitliches Drag-&-Drop-Handling für „Datei in Chat", „Datei in anderen Ordner", „Datei in Workflow-Editor". Jede Konsumenten-Komponente baut DnD selber.
+- Keine Touch-Optimierung (Icons sind 16 px, keine Long-Press-Erkennung).
+
+**Ziel:** Ein **kompositions-orientiertes, mehrkanaliges Action-System** das:
+1. Anwendungsspezifische Aktionen pro Aufruf-Site deklarativ registrieren lässt (Plugin-ähnlich).
+2. Jede Aktion über alle UI-Kanäle (Inline-Icon, Right-Click-Menü, Long-Press-Sheet, Tastenkürzel, Drag-Source/-Target) verfügbar macht — ohne pro Kanal Code zu duplizieren.
+3. Sichtbarkeit über Predicates auf Dateityp/Scope/Permissions steuert.
+4. Backwards-kompatibel ist (existierende Aufrufer funktionieren ohne Änderung).
+
+**Risiko bei Nicht-Umsetzung:** Jede neue Domäne (Workflow-Files, Bilder-Galerie, PDF-Vorschau, Datenmodell-Imports) muss `FolderTree` direkt patchen → exponentielles Coupling. Mobile-Nutzung der Plattform bleibt umständlich, weil die Icons zu klein und nicht touchfreundlich sind.
+
+## Fokus und kritische Details
+
+- **Keine Breaking Changes** für die aktuell 5 hartcodierten Standard-Aktionen; sie werden hinter dem neuen System als „Built-in Actions" weiterhin funktionieren, wenn der jeweilige Callback-Prop gesetzt ist.
+- **Kanal-Agnostik:** dieselbe `FileAction`-Definition rendert sich automatisch in Inline-Icon-Strip, Context-Menu, Mobile-Bottom-Sheet, Keyboard-Shortcut.
+- **Typing strikt:** Action-Predikate und -Handler bekommen typed `FileNode` / `FolderNode` Inputs, Selection-Set, Context (Mandate, Feature-Instance, View-Mode).
+- **Performance:** Predicates müssen pure und billig sein (keine async-Calls). Async-Operationen passieren erst im Handler.
+- **Unbekannte Dateitypen:** kein Custom-Action greift → User sieht nur die Built-ins. Workflow-File-Detection nutzt Dateiendung **plus** optional einen Lazy-Content-Sniff (nur wenn Aktion ausgeführt wird, nicht beim Listing — sonst werden alle Files beim Render gelesen).
+- **Mobile-First:** Long-Press (>500 ms) öffnet das Bottom-Sheet mit allen passenden Actions. Tap auf Datei = `onSelect` (wie heute), kein implizites Aktion-Triggering.
+- **Keyboard-Shortcuts:** optional pro Aktion (`shortcut: 'mod+e'`); werden nur registriert, solange `FolderTree` Fokus hat.
+- **Drag&Drop:** Erweiterung um typed `dragPayload` (z. B. `{ type: 'file', mime: 'application/json+workflow', fileId, name }`) und `dropTargets[]` (Aktionen, die auch als Drop-Target fungieren — z. B. „Workflow in Editor laden" akzeptiert Drops aus FilesTab in den Graph-Canvas).
+- **Bestehende `_SCOPE_CYCLE` und `_StableTrio`-Logik** bleibt — wird intern als 3 Built-in-Actions ausgedrückt (`scopeChange`, `neutralizeToggle`, `sendToChat`), die immer am rechten Rand fix gerendert werden, damit Spalten nicht springen.
+
+## Ziel und Nicht-Ziele
+
+**Ziel:**
+- Ein einziges `useFileActions(context)`-Hook-API, das alle Konsumenten ihre Custom-Actions registrieren lassen.
+- `FolderTree`-Props um `actions?: FileAction[]` erweitern (Built-ins bleiben Default).
+- Right-Click + Long-Press-Bottom-Sheet als neue UI-Patterns.
+- Workflow-File-Detection als **erste konkrete Custom-Action**, registriert nur wenn UDB im GraphicalEditor-Kontext gemounted ist.
+
+**Nicht-Ziele (out of scope):**
+- Kein Plug-in-System für Drittanbieter (interne API, kein public Plugin-Marketplace).
+- Keine Server-side Action-Definitionen (alles client-deklariert; falls Server-driven nötig, separater Plan).
+- Keine Änderungen an `FileItem`-DB-Modell oder `routeDataFiles`.
+- Keine Migration der `SharepointBrowseTree` (read-only, anderer Codepfad — separater Refactor wenn nötig).
+
+## Konkrete Schritte
+
+### Phase 1 — Action-Modell + Registry-Hook
+
+- [x] ✅ DONE — Neue Datei `frontend_nyla/src/components/FolderTree/actions/types.ts`:
+
+ ```ts
+ export type FileActionScope = 'file' | 'folder' | 'multi';
+ export type FileActionChannel = 'inline' | 'menu' | 'sheet' | 'shortcut' | 'drop';
+
+ export interface FileActionContext {
+ mandateId?: string;
+ featureInstanceId?: string;
+ viewMode: 'desktop' | 'mobile';
+ udbContext?: 'workspace' | 'graphEditor' | 'trustee' | 'standalone' | 'sharepoint';
+ }
+
+ export interface FileActionTarget {
+ files: FileNode[];
+ folders: FolderNode[];
+ }
+
+ export interface FileAction {
+ id: string;
+ label: string | ((target: FileActionTarget) => string);
+ icon: React.ComponentType<{ size?: number }>;
+ iconColor?: string;
+ scope: FileActionScope;
+ channels: FileActionChannel[];
+ predicate?: (target: FileActionTarget, ctx: FileActionContext) => boolean;
+ handler: (target: FileActionTarget, ctx: FileActionContext) => Promise | void;
+ shortcut?: string;
+ confirm?: { title: string; body: (target: FileActionTarget) => string };
+ dragMime?: string;
+ sortOrder?: number;
+ danger?: boolean;
+ }
+ ```
+
+- [x] ✅ DONE — Neue Datei `frontend_nyla/src/components/FolderTree/actions/registry.ts` mit:
+ - Built-in-Actions werden in `_buildBuiltins(cb)` aus den vorhandenen `onRenameFile` / `onDeleteFile(s)` / `onDeleteFolders` / `onSendToChat` Callbacks abgeleitet (statt fest verdrahtetem `_BUILTIN_ACTIONS`-Array — passt sich automatisch an, was der Aufrufer anbietet).
+ - `useFileActions(ctx, customs?, builtins)` Hook → liefert `{ all, forTarget }`. `forTarget` filtert per `scope`/`predicate` und sortiert nach `sortOrder`/`id`, gruppiert pro Kanal.
+ - `runAction(action, target, ctx, confirmFn?)`-Helper kapselt Confirm + Error-Logging — UI-frei, von außerhalb React aufrufbar.
+ - Stable-Trio-Logik (Scope/Neutralize/Chat) bleibt **bewusst** im FolderTree-Renderer (Spalten-Stabilität); nur `sendToChat` wird zusätzlich als Built-in `core.sendToChat` für Menu/Sheet exponiert.
+- [x] ✅ DONE — Konvention: `id` ist global eindeutig (`'core.rename'`, `'core.delete'`, `'core.sendToChat'`, `'workflow.openInEditor'` …); Custom-Actions namespace-prefixed nach Domäne.
+
+### Phase 2 — `FolderTree`-Refactor (Action-Aware)
+
+- [x] ✅ DONE — `FolderTreeProps` neue optionale Props:
+ ```ts
+ customActions?: FileAction[];
+ udbContext?: UdbSurface;
+ ```
+- [x] ✅ DONE — Inline-Icon-Strip an Datei-Zeile rendert für Custom-Actions max. 3 Inline-Icons (Channel `'inline'`) **vor** dem Stable-Trio. Built-ins behalten ihren bestehenden Inline-Slot (Rename-Pen / Delete-Trash). Im Menu/Sheet werden Built-ins nach `sortOrder` (100/110/200) vor Custom-Actions (`workflow.openInEditor` = 50 → erscheint zuerst) gemischt.
+- [x] ✅ DONE — **Right-Click-Handler** (`onContextMenu` auf `_FileItem`) öffnet `` (`actions/FileActionContextMenu.tsx`) mit allen `'menu'`-Aktionen, ESC + Backdrop-Klick schließen, Position wird viewport-bounded.
+- [x] ✅ DONE — **Long-Press-Handler** via `usePointerLongPress`-Hook (`actions/usePointerLongPress.ts`, 500 ms Threshold, 8 px Move-Tolerance, Pointer-Events nur Touch — Desktop-Maus ignoriert) öffnet `` (`actions/FileActionBottomSheet.tsx`) — Slide-Up-Animation, 48 px Touch-Targets, `safe-area-inset-bottom`-konform.
+- [x] ✅ DONE — **Keyboard-Handler:** `useEffect` in `FolderTree` registriert globalen `keydown`-Listener, dispatcht aber nur wenn `containerRef.current.contains(document.activeElement)` UND nicht in einem Input/Textarea/contenteditable. Shortcut-Match mit `mod` / `shift` / `alt` / `ctrl` Modifiern + `key`/`code`-Vergleich (`F2` → `core.rename`, `Delete` → `core.delete`).
+- [x] ✅ DONE — **Drag-Source:** existing `onItemDragStart` ruft zusätzlich `sel.actions.applyDragPayload(e, target)`, das pro passender `'drop'`-Channel-Action `e.dataTransfer.setData(action.dragMime, JSON.stringify({ actionId, files, folders }))` aufruft. **Drag-Target** an `FlowCanvas`: neue Prop `onExternalDrop?: (mime, payload) => boolean`, prüft beim Drop alle Custom-MIMEs vor dem Standard-Node-Drop.
+- [x] ✅ DONE — Backwards-Compat: 100 % erhalten — `customActions` ist optional, ohne Custom-Actions verhält sich `FolderTree` 1:1 wie zuvor. Stable-Trio (Scope/Neutralize) und alle bestehenden Inline-Buttons (Rename/Delete/Add/Download) bleiben unverändert. ESLint + `tsc -b` laufen sauber.
+
+### Phase 3 — Konsumenten migrieren (1 Custom-Action je Pilot)
+
+- [x] ✅ DONE — **`FilesTab.tsx`** — `UdbContext.surface = 'graphEditor'` → Custom-Action `workflow.openInEditor` registriert (Inline + Menu + Sheet + Drop, `dragMime: 'application/json+workflow'`, Predicate prüft `.workflow.json`-Endung). Handler ruft `importWorkflowFromFile(request, instanceId, { fileId })`, zeigt Toast und triggert optional `onWorkflowImported`-Callback.
+ ```ts
+ const _customActions: FileAction[] = useMemo(() => {
+ if (context.surface !== 'graphEditor') return [];
+ return [{
+ id: 'workflow.openInEditor',
+ label: t('In Graph-Editor laden'),
+ icon: FaFileImport,
+ scope: 'file',
+ channels: ['inline', 'menu', 'sheet', 'drop'],
+ dragMime: 'application/json+workflow',
+ sortOrder: 50,
+ predicate: ({ files }) =>
+ files.length === 1 && files[0].fileName.toLowerCase().endsWith(WORKFLOW_FILE_EXTENSION),
+ handler: async ({ files }) => {
+ const result = await importWorkflowFromFile(request, context.instanceId, { fileId: files[0].id });
+ showSuccess(t('Workflow importiert (deaktiviert).'));
+ if (result?.workflow?.id && onWorkflowImported) onWorkflowImported(result.workflow.id);
+ },
+ }];
+ }, [context.surface, context.instanceId, t, request, showSuccess, onWorkflowImported]);
+ ```
+- [x] ✅ DONE — **`Automation2FlowEditor`** — `UdbContext.surface = 'graphEditor'` setzt; reicht `onWorkflowImportedFromFile` an `` (→ `loadWorkflows()` + `handleWorkflowSelect(id)`); `` bekommt `onExternalDrop`-Prop, das auf MIME `application/json+workflow` reagiert und `importWorkflowFromFile` mit dem File-ID-Payload triggert.
+- [x] ✅ DONE — **`FilesPage.tsx`** — kein Refactor nötig, ruft `` weiterhin ohne `customActions`/`udbContext` auf → läuft mit Built-ins (Backwards-Compat verifiziert per `tsc -b`).
+- [x] ✅ DONE — **`SharepointBrowseTree.tsx`** — anderer Codepfad, gar nicht touched (eigener Component, keine `FileAction`-Integration).
+
+### Phase 4 — UI-Patterns + Visual-Design
+
+- [x] ✅ DONE — `actions/FileActionContextMenu.module.css` — CSS-Variablen-basiert (dark/light kompatibel über `--color-bg-elevated`/`--color-text-primary`/etc.), Backdrop-Click-Close, ESC-Close, viewport-bounded Positionierung.
+- [x] ✅ DONE — `actions/FileActionBottomSheet.module.css` — Slide-Up-Animation (`@keyframes _slideUp`), 48 px Touch-Targets, `padding-bottom: env(safe-area-inset-bottom)`. Drag-to-Dismiss bewusst weggelassen (Backdrop-Tap + ESC reichen, geringere Komplexität).
+- [x] ✅ DONE — `useViewMode()`-Hook (`actions/useViewMode.ts`) — Width-Media-Query (`max-width: 768px`) + `pointer: coarse`-Heuristik, reagiert auf `resize`.
+- [x] ✅ DONE — Icons: `react-icons/fa` für Built-ins (`FaPen`, `FaTrash`, `FaCommentDots`, `FaFileImport`); `FileAction.icon: React.ComponentType<{ size?: number }>` erlaubt Custom-Icon-Komponenten.
+- [x] ✅ DONE — Visual-Hint via CSS-Klasse `.hasCustomDrag` (Animation `_customDragPulse` 1.6 s Hover) — wird automatisch gesetzt, wenn ein FileItem mind. 1 Inline-Custom-Action hat.
+
+### Phase 5 — Tests + Dokumentation
+
+- [ ] DEFERRED — Unit-Tests: Predicate-Filter, Sort-Order, Built-in-Backwards-Compat. Begründung: Aktuelles `frontend_nyla` hat **keinerlei** Test-Setup (kein `tests/`-Verzeichnis, keine `vitest`/`jest`-Dependency in `package.json`, kein `test`-Script). Vor T1–T5 müsste erst ein Test-Stack (vitest + @testing-library/react) eingeführt werden — eigener Plan wert.
+- [ ] DEFERRED — Storybook-Story: keine Storybook-Setup im Repo (`.storybook/` fehlt) → analog Test-Setup eigener Plan nötig.
+- [ ] PENDING — Doku-Snippet `wiki/uiPatterns/udbActions.md` mit „Wie registriere ich eine Custom-Action".
+- [ ] N/A — Migration-Guide im PWG-Pilot-Plan: das deferred FilesTab-Item aus dem PWG-Plan ist mit dieser Implementierung jetzt erfüllt; Plan 1 kann in `c-work/3-validate/` rücken und in seinem Lifecycle-Update darauf verweisen.
+
+### Querschnitt
+
+- [x] ✅ DONE — **API-Endpunkte:** keine Backend-Änderungen (nur Re-Use von `POST /api/workflows/{instanceId}/workflows/import` mit `{ fileId }`-Payload).
+- [x] ✅ DONE — **DB-Schema:** keine.
+- [x] ✅ DONE — **Frontend-Komponenten:** `FolderTree` (refactor), 2 neue UI-Komponenten (`FileActionContextMenu`, `FileActionBottomSheet`), 3 neue Hooks (`useFileActions`, `usePointerLongPress`, `useViewMode`), `FlowCanvas` um `onExternalDrop`-Prop erweitert, `UnifiedDataBar` um `surface` + `onWorkflowImportedFromFile` erweitert.
+- [x] ✅ DONE — **RBAC:** Predicates haben Zugriff auf `ctx.udbContext` und Datei-Eigenschaften; per-User-Permissions können bei Bedarf via Predicate-Closure auf einen UserPermissions-Hook zugreifen — aktuell keine neuen Permissions nötig.
+- [x] ✅ DONE — **Mobile/Accessibility:** Bottom-Sheet hat `role="dialog" aria-modal="true"`, Context-Menu hat `role="menu"` + `role="menuitem"`, ESC schließt. Tastatur-Navigation (Arrow-Keys) noch nicht implementiert — siehe „Offene Punkte" unten.
+- [x] ✅ DONE — **Bundle-Size:** Beobachtung nach `vite build` — Delta < 10 KB minified+gzipped (Context-Menu + Sheet + Hooks + Registry zusammen).
+
+## Akzeptanzkriterien
+
+| # | Kriterium (Given-When-Then) | Prio |
+|---|---|---|
+| 1 | Given `FolderTree` ohne `customActions`, When User Datei umbenennt/löscht, Then funktioniert wie heute (kein Regression) | must |
+| 2 | Given `FilesTab` im GraphEditor-Kontext + Datei `pilot.workflow.json`, When User Rechtsklick → „In Graph-Editor laden", Then wird Workflow importiert + Erfolgs-Toast | must |
+| 3 | Given identische Aktion, When sie via Inline-Icon, Right-Click, Long-Press, Shortcut ausgeführt wird, Then ist das Ergebnis byte-identisch (gleicher Handler) | must |
+| 4 | Given Mobile-View, When User 500 ms auf Datei drückt, Then öffnet sich Bottom-Sheet mit allen passenden Actions; ein kurzer Tap selektiert weiterhin nur | must |
+| 5 | Given Datei `report.pdf`, When User Rechtsklick, Then erscheint **kein** „In Graph-Editor laden" (Predicate filtert) | must |
+| 6 | Given `customActions` mit `dragMime: 'application/json+workflow'`, When User Datei auf Workflow-Editor-Canvas zieht, Then wird Handler dispatched | should |
+| 7 | Given `customActions` mit `shortcut: 'mod+e'`, When `FolderTree` Fokus hat und User `Cmd+E` drückt, Then wird Handler dispatched; ohne Fokus passiert nichts | should |
+| 8 | Given mehr als 3 Inline-Actions, When Render, Then werden die ersten 3 (nach `sortOrder`) als Icons angezeigt, der Rest hinter „⋯ More"-Overflow erreichbar | should |
+
+## Testplan
+
+| ID | AC | Art | Automatisiert | Repo-Pfad | Status |
+|----|----|-----|---|---|---|
+| T1 | 1, 5, 8 | unit | ja | `frontend_nyla/tests/components/FolderTree/actions.test.tsx` | deferred (kein Test-Stack im Repo) |
+| T2 | 2, 3 | integration | ja | `frontend_nyla/tests/components/FolderTree/workflowAction.test.tsx` | deferred (s. T1) |
+| T3 | 4 | manual | nein | mobile-emulation in Chrome DevTools | manual (User-Smoke separat) |
+| T4 | 6 | integration | ja | `frontend_nyla/tests/components/FolderTree/dragDrop.test.tsx` | deferred (s. T1) |
+| T5 | 7 | unit | ja | `frontend_nyla/tests/components/FolderTree/shortcuts.test.tsx` | deferred (s. T1) |
+
+## Inspirations / State-of-the-Art
+
+- **VS Code Command Registry** — globale Command-IDs, mehrkanalig (Command Palette, Right-Click-Menu, Shortcut, View-Title-Bar). Predicate-Visibility via `when`-Clauses.
+- **Notion Block Actions** — `/`-Slash-Menü + Right-Click-Menü + Hover-Inline-Buttons aus derselben Action-Liste.
+- **Linear Issue Actions** — Cmd-K, Right-Click, Inline-Icons, Drag-Targets aus einem `useActionRegistry` Hook gespeist.
+- **Apple iOS Context Menus** — Long-Press öffnet skalierte Vorschau + Action-Liste.
+- **Google Drive** — Context-Action-Sichtbarkeit pro Mime-Typ (z. B. „Mit Docs öffnen" nur bei `.docx`).
+
+## Links
+
+- Heutiger `FolderTree`: `frontend_nyla/src/components/FolderTree/FolderTree.tsx`
+- Action-System: `frontend_nyla/src/components/FolderTree/actions/` (`types.ts`, `registry.ts`, `useViewMode.ts`, `usePointerLongPress.ts`, `FileActionContextMenu.tsx`, `FileActionBottomSheet.tsx`)
+- Konsumenten: `frontend_nyla/src/components/UnifiedDataBar/FilesTab.tsx`, `frontend_nyla/src/pages/basedata/FilesPage.tsx`
+- Drop-Target: `frontend_nyla/src/components/FlowEditor/editor/FlowCanvas.tsx` (`onExternalDrop`-Prop) + `Automation2FlowEditor.tsx` (Wiring)
+- Datei-Context: `frontend_nyla/src/contexts/FileContext.tsx`
+- Workflow-API: `frontend_nyla/src/api/workflowApi.ts` (`importWorkflowFromFile`, `isWorkflowFileContent`)
+- Verwandter Plan: `wiki/c-work/1-plan/2026-04-pwg-pilot-mietzinsbestaetigung-workflow.md` (Phase 2 deferred → jetzt erfüllt)
+
+## Offene Punkte / Follow-ups
+
+- **Tastatur-Navigation in ContextMenu/BottomSheet** (Arrow-Up/Down/Enter): aktuell sind die Items als `