Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 12 additions & 48 deletions .cursor/index.mdc
Original file line number Diff line number Diff line change
@@ -1,54 +1,18 @@
---
description: Project Manifest and Global Architecture
alwaysApply: true
description: Open the canonical repository guidance and relevant specialist rules on demand
alwaysApply: false
---

# StoryCraft Studio — Manifest
# Cursor context index

**Zweck:** Offline-first Schreib- und Erzählwerkzeug (v1.3+): Dashboard, Manuskript, Szenenboard, Plot Board, Charaktergraph, Version Control, KI-Copilot — **kein Ghostwriting-Fokus**. Daten lokal; Privacy-first; Cloud-KI nur nutzergetriggert.
`AGENTS.md` is the canonical repository-wide rule source. Load it for repository work, then
load only the matching `.cursor/rules/*.mdc` rule and nearest nested `CLAUDE.md` for the paths
being changed. Follow the explicit cross-directory mappings in the root `CLAUDE.md` when a
caller is governed by a specialist guide elsewhere.

**Frontend:** React 19, Vite 8, TypeScript 6 (`strict`, `exactOptionalPropertyTypes`, restrike Index/Switch). Alias `@` → Repo-Root. **pnpm 10** Workspace (`packages/*`); Node ≥22. Optional **Turbo** (`*:turbo`-Skripte).
Useful deep references:

**State:** Redux Toolkit + `redux-undo` (Projekt/Undo); **Zustand** für transientes UI (`app/transientUiStore.ts` — z. B. Command-Palette). Listener-Middleware für Auto-Save/Side-Effects.

**Persistenz:** Dual IndexedDB (`dbService.ts`: State vs. Data, Migration, LZ-String, AES-GCM für Keys); **`storageService` → `storageBackend`** (Web/Tauri, Binder-Blobs, typisiertes Envelope). Kein serverseitiges ORM.

**KI (Hybrid):** `@google/genai` + **Vercel AI SDK** (`ai`, `@ai-sdk/*`); Orchestrierung `services/ai/` (Strangler) + Legacy `aiProviderService.ts` / `geminiService.ts`. **RAG assembly:** `services/ragPromptAssembly.ts` (Writer, Plot Board, Consistency pattern). DuckDB `rag_chunks.embedding` (384-dim). Lokal: Ollama, WebLLM/ONNX/Transformers (`packages/ai-core`). Thunks: `features/project/thunks/`.

**Desktop:** Tauri 2 (`src-tauri`); `@tauri-apps/api` im Vite-Bundle **external**; FS/Dialog nur über Services — UI-Atoms ohne direkte Tauri-Imports.

**Kollaboration:** Yjs + `y-webrtc` (`collaborationService.ts`); Signaling: `settings.collaboration.webrtcSignalingUrls`; CSP `connect-src` für benutzerdefinierte `wss:`/`https:`.

**UI:** Tailwind 4 (`@tailwindcss/vite`), Tokens optional `@domain/ui`; Primitive `components/ui/*`. Storybook 10 + **addon-a11y**. Biome 2 (`--error-on-warnings`), `lineWidth` 100.

**PWA:** `vite-plugin-pwa`, `injectManifest`, `public/sw.js` (Version-Sync via `scripts/sync-sw-version.mjs`).

**i18n:** Quelle `locales/<lang>/*.json` (de, en, fr, es, it); Runtime **`public/locales/<lang>/bundle.json`** via `pnpm run i18n:bundle` / `i18n:check` + `content:guard`. Keine hardcodierten UI-Strings.

**Tests:** Vitest 4 (jsdom, `tests/setup.ts`, `maxWorkers: 1`); Coverage-Schwellen **63/55/54/62** % (lines/branches/functions/statements). E2E Playwright **CI-first** (`CI=true`). Stryker informativ. Siehe [`docs/CI.md`](../docs/CI.md).

**Qualität (lokal schnell):** `pnpm run lint`, `typecheck`, `i18n:check`; optional `pnpm exec vitest run` **ohne** `--coverage`. Schwere Gates (E2E, LHCI, Coverage) = GitHub Actions.

**Barrierefreiheit:** `LiveRegionProvider` / `useAnnounce()`; Settings-Presets + Zod (`accessibilitySchema.ts`); [`docs/ACCESSIBILITY.md`](../docs/ACCESSIBILITY.md); E2E [`tests/e2e/a11y.spec.ts`](../tests/e2e/a11y.spec.ts).

**Command Center:** `services/commands/` → Palette; `CommandExecutorContext`; Shortcuts `useGlobalKeyboardShortcuts` + `services/keyboard/`; Hilfe `helpCatalog.ts` + `helpDocRetrieval.ts` + `tryActionId`; Settings-Guide-Kategorie `experimental` für 12 Feature-Flags.

**v1.10:** Mobile-Nav (Scene Board), Branches ≥55 %, Help-Artikel (Plot Board v2, Hybrid RAG, Tauri). v1.9: Lazy cold-start (`duckdbListenerLoader`, deferred `aiApi`), Help/Settings-Hub, Tauri menu/window-state/updater — [`docs/SPRINT-V1.10.md`](../docs/SPRINT-V1.10.md).

**Pfad-Konventionen:** `app/`, `components/`, `features/*`, `hooks/`, `services/`, `packages/*`, `types.ts`, `tests/`.

## Cursor-Regelkatalog

| Datei | Wann |
|-------|------|
| [`.cursorrules`](../.cursorrules) | QNBS-Masterprompt, Kommentar-Konvention |
| [`000-cursor-rules.mdc`](rules/000-cursor-rules.mdc) | Neue/geänderte `.mdc` |
| [`001-core-security.mdc`](rules/001-core-security.mdc) | Secrets, Logs, CSP |
| [`100-ai-providers-and-storage.mdc`](rules/100-ai-providers-and-storage.mdc) | KI, IDB, Storage |
| [`150-i18n-and-content.mdc`](rules/150-i18n-and-content.mdc) | Locales, Copy, Templates |
| [`200-architecture-limits.mdc`](rules/200-architecture-limits.mdc) | KISS/DRY, Dateigrößen |
| [`300-ui-components.mdc`](rules/300-ui-components.mdc) | React UI, A11y |
| [`800-testing-standards.mdc`](rules/800-testing-standards.mdc) | Vitest, E2E, CI-first |
| [`850-mcp-and-prd.mdc`](rules/850-mcp-and-prd.mdc) | Roadmap, MCP, Doku-Hub |

**Maintainer-Doku:** [`docs/BEST-PRACTICES.md`](../docs/BEST-PRACTICES.md), [`AUDIT.md`](../AUDIT.md), [`ROADMAP.md`](../ROADMAP.md). Nach größeren Code-Änderungen optional `pnpm run graphify:update`.
- `README.md` / `CONTRIBUTING.md` — product and setup
- `docs/CI.md` / `docs/PR-CI-MERGE-WORKFLOW.md` — CI and PR operations
- `docs/native/CORE-MIGRATION-LEDGER.md` — native direction
- `docs/graphify.md` / `docs/codegraph.md` — on-demand architecture investigation
14 changes: 8 additions & 6 deletions .cursor/rules/000-cursor-rules.mdc
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
---
description: ALWAYS use when asked to CREATE A RULE or UPDATE A RULE or when taught a lesson to RETAIN AS NEW RULE
globs: ~
alwaysApply: false
---

Expand All @@ -22,8 +21,11 @@ alwaysApply: false
- `300–399` UI
- `800–899` Testing/Workflows
- Frontmatter **`description`:** ATO (Action, Trigger, Outcome), **< 120 Zeichen**.
- **`globs`:** kommagetrennt, **keine Leerzeichen**, keine Anführungszeichen — z. B. `globs: services/**/*.ts,features/**/*.ts`.
- **`alwaysApply: true`** nur für [`.cursor/index.mdc`](../index.mdc) (Manifest). Fachregeln: `false` + passende `globs`.
- **`globs`:** optional, comma-separated patterns with **no spaces** or quotation marks — e.g.
`globs: services/**/*.ts,features/**/*.ts`. Omit it for description-based Apply Intelligently
rules; use real patterns for path-specific rules.
- An always-applied repository rule is not needed. Use `alwaysApply: false` with precise globs or
a useful description.
- **≤ 200 Zeilen** pro Datei; Code nicht duplizieren — verweise auf kanonische Pfade (`@services/logger.ts`).
- Pflicht: `<example>` und `<example type="invalid">`.
- Regeln mit **Plain-Text-Editor** schreiben (MDC-UI-Bugs vermeiden).
Expand All @@ -33,8 +35,7 @@ alwaysApply: false
```yaml
---
description: <ATO unter 120 Zeichen>
globs: <Pfadmuster oder ~ für manuell>
alwaysApply: <true nur Manifest>
alwaysApply: false
---
```

Expand All @@ -49,5 +50,6 @@ Hybrid-KI-Regel: `101-hybrid-ai-orchestration.mdc`, `globs: services/ai/**/*.ts,
</example>

<example type="invalid">
`globs: *.ts, *.tsx` (Leerzeichen); 300-Zeilen-Regel mit kopiertem Redux-Reducer; `alwaysApply: true` auf jeder Fachregel.
`globs: **/*` für jede Fachregel; copied architecture encyclopedias; or an always-applied
specialist rule.
</example>
7 changes: 4 additions & 3 deletions .cursor/rules/001-core-security.mdc
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
description: Protect secrets,logs,and CSP when touching any project file
globs: **/*
description: Protect secrets, logging, CSP, and privacy boundaries on executable and security-sensitive paths
globs: .mcp/proforge-mcp-server/**/*.ts,app/**/*.ts,app/**/*.tsx,api/**/*.ts,components/**/*.ts,components/**/*.tsx,config/**/*,features/**/*.ts,features/**/*.tsx,functions/**/*.ts,hooks/useLanguageToolCheck.ts,hooks/useWorldScriptAI.ts,hooks/useFactoryReset.ts,hooks/useIdbUnlockStartupGuard.ts,hooks/useSettingsView.ts,App.tsx,index.tsx,register-sw.ts,index.html,nginx.conf,package.json,packages/**/*.ts,packages/**/*.tsx,public/_headers,public/sw.js,scripts/**/*.mjs,scripts/**/*.ts,services/**/*.ts,services/**/*.tsx,vercel.json,vite.config.ts,workers/**/*,wrangler.toml,.github/**/*,src-tauri/**/*,crates/**/*
alwaysApply: false
---

# Core Security & Betrieb
# Core security red lines

## Secrets & Konfiguration

Expand All @@ -16,6 +16,7 @@ alwaysApply: false
## Tauri / CSP / Integrationen

- Neue externe Endpunkte (KI, LanguageTool, Signaling): `src-tauri` **CSP** `connect-src` prüfen/erweitern — nicht nur Web-`fetch`.
- CSP-Surfaces werden durch `scripts/sync-csp.mjs` synchronisiert; Änderungen müssen alle von diesem Script definierten Deployment-Flächen konsistent halten.
- LanguageTool und ähnliche Dienste: nur **nutzerkonfigurierte URL** + Privacy-Gating (`integrations.languageTool*`).
- Community-Templates: Zod + `pnpm run content:guard` — keine eingebetteten Secrets oder `eval`-ähnliche Payloads.

Expand Down
11 changes: 6 additions & 5 deletions .cursor/rules/100-ai-providers-and-storage.mdc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
description: Apply hybrid AI,IndexedDB,and storage patterns when editing services or features
globs: services/**/*.ts,features/**/*.ts,packages/ai-core/**/*.ts
description: Apply hybrid AI, persistence, and privacy patterns on AI and storage boundaries
globs: app/listenerMiddleware.ts,app/persistenceCoordinator.ts,app/persistedStateFlush.ts,components/ApiKeySection.tsx,components/BinderPanel.tsx,components/CharacterView.tsx,components/ManuscriptResearchSplit.tsx,components/WelcomePortal.tsx,components/WorldView.tsx,components/dashboard/BackupQuickActionsCard.tsx,components/settings/AiProviderCard.tsx,components/settings/GeneralSections.tsx,components/settings/OpenRouterSection.tsx,components/settings/EncryptionRecoveryModal.tsx,components/settings/IdbUnlockModal.tsx,components/settings/IdbUnlockModalGate.tsx,components/settings/PassphraseModal.tsx,components/settings/PrivacySection.tsx,features/lora/loraThunks.ts,features/project/projectSchemaVersion.ts,features/project/projectSchemaVersionShadow.ts,features/project/thunks/**/*.ts,features/settings/settingsSlice.ts,features/writer/writerSlice.ts,hooks/useCharacterInterviewsView.ts,hooks/useCharacterView.ts,hooks/useFactoryReset.ts,hooks/useIdbUnlockStartupGuard.ts,hooks/useSettingsView.ts,hooks/useWorldView.ts,hooks/useWorldScriptAI.ts,App.tsx,index.tsx,services/ai/**/*.ts,services/aiProviderService.ts,services/appBootstrap.ts,services/cloudSync/**/*.ts,services/codexService.ts,services/crossProjectIndexService.ts,services/crossProjectSearchService.ts,services/dbInitialization.ts,services/dbMigration.ts,services/dbService.ts,services/diagnostics/logSinks.ts,services/factoryResetService.ts,services/fs/**/*.ts,services/geminiService.ts,services/keyboard/shortcutActions.ts,services/libraryBackupService.ts,services/localAiFacade.ts,services/workerBusManager.ts,services/localFirst/**/*.ts,services/localRag*.ts,services/lora/**/*.ts,services/loraAdapterService.ts,services/pluginRegistry.ts,services/projectDocument.ts,services/projectImportSchema.ts,services/proForge/adapters/nodeProForgeCapability.ts,services/proForge/proForgeHistoryStore.ts,services/proForge/proForgeMemoryBank.ts,services/proForge/proForgeOrchestrator.ts,services/rag*.ts,services/sceneRevisionService.ts,services/settingsExchange.ts,services/startupRecovery.tsx,services/startupRecoveryPolicy.ts,services/storage/**/*.ts,services/storageBackend.ts,services/storageService.ts,services/duckdb/**/*.ts,services/logger.ts,services/collaborationService.ts,workers/v2/webllm.worker.ts,packages/ai-core/**/*.ts,packages/collab-transport/**/*
alwaysApply: false
---

Expand All @@ -10,15 +10,16 @@ alwaysApply: false

- Einstieg **`services/ai/index.ts`**: Strangler neben Legacy; Writer-Streaming → **`writer`-Slice**, Manuskript erst nach **Accept**.
- Netzwerk-KI: **`aiProviderService.ts`**, **`geminiService.ts`** — keine parallele SDK-Init in Komponenten.
- Vercel AI SDK (`ai`, `@ai-sdk/google`, `@ai-sdk/openai`): neue Pfade über `services/ai/` und Hooks (`useStoryCraftAI` o. ä.), nicht doppelte Retry-Logik.
- Vercel AI SDK (`ai`, `@ai-sdk/google`, `@ai-sdk/openai`): new paths belong in `services/ai/` and `hooks/useWorldScriptAI.ts`; do not duplicate retry logic.
- **`assertCloudAiAllowed`:** Cloud nur wenn Policy/Settings es erlauben.

## Hybrid & lokale Inferenz

- Presets: Ollama/LM Studio/vLLM via `settings.advancedAi`; Cloud-Router über `openAiCompatibleBaseUrl` + Key-Slot.
- Fallback-Kette in `aiProviderService` für Legacy-Thunks respektieren.
- WebLLM/GPU: **`gpuResourceManager`**, Tab-Leader (`packages/ai-core` / BroadcastChannel) — kein zweiter schwerer Inferenz-Tab.
- Lokales RAG/Embeddings: bestehende Speicher (`saveRagVectors`), chunked yields; `@xenova/transformers` nur über etablierte Pfade.
- WebLLM offload follows ADR 0005: keep the dedicated worker-first pool, capability-based main-thread fallback, and progress bridge; do not couple it to `enableWorkerBusV2` or move model loading back to the UI thread.
- Lokales RAG/Embeddings: bestehende Speicher (`saveRagVectors`), chunked yields; `@huggingface/transformers` nur über etablierte Pfade.

## Google GenAI

Expand All @@ -29,7 +30,7 @@ alwaysApply: false
- Zugriff über `dbService.ts`, `storageService.ts`, **`storageBackend.ts`** (`saveEnvelopeFromProjectData` — kein roher Cast im Listener).
- Record-Keys/Migrationen nicht willkürlich umbenennen; Scene-Revisions → `sceneRevisionService.ts` (eigener Store).
- `checkStorageHealth()` in `services/dbInitialization.ts` — liefert `StorageHealth`; wird beim App-Start ausgeführt (Low-Storage-Toast).
- Kollaboration: nur **eine** CRDT-Schicht (`collaborationService.ts` + Yjs). RTCDataChannel-Verschlüsselung läuft über den y-webrtc-Patch (`patches/y-webrtc@10.3.0.patch`) — nicht erneut implementieren.
- Kollaboration: nur **eine** CRDT-Schicht (`collaborationService.ts` + Yjs). RTCDataChannel-Verschlüsselung lebt in `packages/collab-transport`; Änderungen folgen `VENDOR-FORKS.md` und `pnpm run verify:vendor` — keine parallele Schicht implementieren.

## Privacy

Expand Down
15 changes: 8 additions & 7 deletions .cursor/rules/150-i18n-and-content.mdc
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
---
description: Enforce i18n key parity and content guard when editing locales or copy
globs: locales/**/*.json,public/locales/**/*,community-templates/**/*,scripts/build-i18n.mjs,scripts/check-i18n-keys.mjs,scripts/content-guard.mjs
globs: i18n/locales.ts,contexts/I18nContext.tsx,locales/**/*.json,public/locales/**/*,community-templates/**/*,scripts/build-i18n.mjs,scripts/check-i18n-keys.mjs,scripts/content-guard.mjs,scripts/i18n-locales.mjs,scripts/i18n-quality-report.mjs,scripts/i18n-status-dashboard.ts,scripts/check-doc-metrics.mjs,scripts/bulk-translate-locales.mjs,services/i18n/**/*.ts,services/i18nBootstrap.ts,services/projectI18nRepair.ts,services/help/helpCatalog.ts,services/help/helpSearch.ts,tests/unit/i18n/localesRegistry.test.ts
alwaysApply: false
---

# i18n & Content

## Schlüssel & Bundles

- Quelle: **`locales/<lang>/*.json`** (de, en, fr, es, it) — **alle fünf** Locales bei neuen Keys.
- Runtime: **`public/locales/<lang>/bundle.json`** via `node scripts/build-i18n.mjs` (läuft in `predev` / `i18n:check`).
- Source: **`locales/<lang>/*.json`** for every locale returned by the canonical registries (`i18n/locales.ts` and `scripts/i18n-locales.mjs`); add every new key to every locale tree.
- Registry authority: keep the typed locale SSOT, filesystem bridge, and their integrity test aligned; do not duplicate a locale roster or count here.
- Runtime: **`public/locales/<lang>/bundle.json`** via `node scripts/build-i18n.mjs` (also used by `i18n:check`).
- CI: **`pnpm run i18n:check`** (Key-Parität + Bundle + `content:guard`).
- UI-Text nur als **Übersetzungsschlüssel** (Dot-Notation), nie hardcodiert in TSX — außer Dev-Debug.

Expand All @@ -21,19 +22,19 @@ alwaysApply: false
## Community & Help

- Templates: kanonisches **Englisch** in `community-templates/index.json` + Spiegel unter `public/community-templates/`; Zod in `fetchCommunityTemplates`.
- Help-Struktur: **`services/help/helpCatalog.ts`** (nicht `help.categories` in JSON); Texte in `locales/<lang>/help.json`. Neue Artikel: Katalog + alle 5 Locales (siehe `scripts/help-extra-keys.json`, `scripts/help-locales-es-fr-it.json`).
- Help structure: **`services/help/helpCatalog.ts`** (not `help.categories` in JSON); text lives in `locales/<lang>/help.json`. New articles require the catalog plus every locale tree.
Comment thread
qnbs marked this conversation as resolved.
Comment thread
qnbs marked this conversation as resolved.
- Help-Artikel: **`tryActionId`** für Palette/Navigation; Suche über `helpSearch.ts`; Demo-Feedback über Toasts, nicht `alert`.

## PR-Checkliste

1. Keys in allen Locales
1. Keys in every locale tree defined by the canonical registries
2. `pnpm run i18n:check` lokal (Quick-Tier)
3. Keine Secrets/PII in JSON-Beispielen

<example>
Neuer Settings-String: Key in `locales/en/settings.json` + de/fr/es/it; `i18n:check` grün; Help-Link mit `tryActionId: 'nav-settings'`.
New Settings copy: key in every `locales/*/settings.json`, rebuild bundles, and keep `i18n:check` green.
</example>

<example type="invalid">
Nur `en` befüllt; UI zeigt rohe Keys weil `bundle.json` nicht gebaut; deutscher Fließtext direkt in `components/Dashboard.tsx`.
Only `en` updated; runtime bundles not rebuilt; or user-visible text hardcoded in a component.
</example>
18 changes: 9 additions & 9 deletions .cursor/rules/200-architecture-limits.mdc
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
---
description: Keep files small and reuse logic via services,hooks,and feature slices
globs: **/*
globs: App.tsx,types.ts,types/**/*.ts,app/**/*.ts,components/**/*.tsx,contexts/**/*.ts,contexts/**/*.tsx,features/**/*.ts,hooks/**/*.ts,services/**/*.ts,packages/**/*.ts,packages/**/*.tsx,vite.config.ts
alwaysApply: false
---

# Architektur: KISS, DRY, Grenzen
# Architecture limits

## Prinzipien

- **KISS:** Einfachste Lösung mit `strict` Types und testbaren Seams.
- **DRY:** Logik in `services/`, `hooks/`, `features/*/thunks` — nicht in Views duplizieren.

## View-Muster (StoryCraft)
## View pattern (WorldScript Studio)

- **Component + Hook + Context:** Rendering in `components/*View.tsx`, Logik in `hooks/use*View.ts`, Kontext für Kindbaum.
- **Component + Hook + Context:** rendering in `components/*View.tsx`, logic in `hooks/use*View.ts`, context for child trees.
- **Redux** für persistierten App-State; **Zustand** nur transient (`app/transientUiStore.ts`).
- **Undo:** `redux-undo` am Projekt-Slice; Plot-Board-**Viewport** lokal — Verbindungen/Subplots im `projectSlice` wenn undo-relevant.
- Schwere Views: `React.lazy` in `App.tsx`; Plot-Board-Subchunks, ForceGraph, Collaboration lazy; `listenerMiddleware` + `aiApi` dynamic import für DuckDB/RAG/Provider.
- **ProForge Pipeline** (`services/proForge/`, `features/proForge/`, `components/proForge/`): 8-stage Agentic-Pipeline hinter `enableProForge`-Flag. Orchestrator nie direkt in Komponenten instantiieren — über `hooks/useProForgeOrchestrator.ts`.
- **Voice** (`services/voice/`): Abstract-Engine-Pattern; `VoiceCommandService` als Singleton hinter `enableVoiceSupport`-Flag.
- Neue schwere optionale Chunks müssen in `vite.config.ts` gemeinsam in `manualChunks` und PWA-`globIgnores` gepflegt werden; siehe ADR 0002, damit sie nicht service-worker-vorinstalliert werden.
- ProForge orchestration stays behind `hooks/useProForgeOrchestrator.ts`; do not instantiate it in components.
- Voice uses the existing engine abstractions and `VoiceCommandService`; honor its feature/settings gates.

## Dateigrößen

Expand All @@ -32,9 +33,8 @@ alwaysApply: false

## Feature Flags

- Experimentelles über `features/featureFlags/featureFlagsSlice.ts` (19 Flags) — nicht verstreute `if (true)`-Hacks.
- Standard **ein**: `enableCodexAutoTracking`, `enableCrossProjectSearch`, `enablePlotBoardV2`. Alle anderen: aus.
- Major-Features (Voice, ProForge, DuckDB, LoRA, CloudSync, PluginSystem) müssen per Flag ein-/ausgeschaltet werden.
- Experimental behavior belongs in `features/featureFlags/featureFlagsSlice.ts`; do not use scattered `if (true)` hacks.
- Read current flag defaults from that slice; do not duplicate a stale flag count or default table in rules.

<example>
Manuskript >700 Zeilen: `ManuscriptInspector.tsx` + `hooks/useManuscript.ts`; Plot-Board-Canvas eigene Datei unter `components/plotBoard/`.
Expand Down
Loading
Loading