Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
09160d6
refactor(api): unify sharepic handlers into single handler
Movm Jan 4, 2026
df1a577
feat(api): add background removal and template gallery improvements
Movm Jan 4, 2026
f74b689
feat(mobile): refactor image studio with gallery and editing features
Movm Jan 4, 2026
4ffca3e
feat(mobile): add content picker and improve generator forms
Movm Jan 4, 2026
a7281c4
feat(mobile): improve subtitle editor, profile and navigation
Movm Jan 4, 2026
45f98e8
feat(mobile): add notebooks feature and improve app structure
Movm Jan 4, 2026
b6da694
refactor(web): modularize image studio hooks and add editing features
Movm Jan 4, 2026
b3c01a9
feat(web): add canvas editor for interactive sharepic editing
Movm Jan 4, 2026
094b415
fix(web): improve common components and form handling
Movm Jan 4, 2026
614d03e
fix(web): update text generators and feature pages
Movm Jan 4, 2026
a54b244
fix(web): update hooks, stores, styles and configuration
Movm Jan 4, 2026
f55799a
feat(shared): add canvas editor and update image studio types
Movm Jan 4, 2026
ecffa2a
chore: add new assets and update dependencies
Movm Jan 4, 2026
6cf6bde
docs: add GEMINI.md for Gemini AI context
Movm Jan 4, 2026
31d2db2
feat: remove AWS/Amazon Bedrock and add IONOS fallback
Movm Jan 5, 2026
ff4a769
feat: remove Ultra Mode UI and add Telekom provider
Movm Jan 5, 2026
d613a6f
feat(api): update anthropic and telekom adapters
Movm Jan 7, 2026
ac46c91
refactor(web): migrate canvases to config-based architecture
Movm Jan 7, 2026
0971e96
feat(web): update canvas primitives and add new shared components
Movm Jan 7, 2026
2977fc6
feat(web): overhaul canvas sidebar with new sections and subsection n…
Movm Jan 7, 2026
ceccb66
feat(web): update canvas editor utilities and configurations
Movm Jan 7, 2026
f31bf0e
feat(web): update image studio flow and stock image grid layout
Movm Jan 7, 2026
882136b
feat(web): update DreizeilenCanvas and add canvas icons
Movm Jan 7, 2026
6b12384
chore: project cleanup, dependency updates, and gitignore improvements
Movm Jan 7, 2026
5e01cd5
chore: untrack .turbo directory and fix gitignore
Movm Jan 7, 2026
0c20279
feat(api): add illustration scrapers for OpenDoodles and Undraw
Movm Jan 9, 2026
ff22eca
feat(web): add OpenDoodles illustration library
Movm Jan 9, 2026
90662aa
feat(web): add Undraw illustration library
Movm Jan 9, 2026
032f1b4
feat(web): update canvas editor for illustration support
Movm Jan 9, 2026
eb0550b
feat(api): add shared media service and API routes
Movm Jan 9, 2026
4412127
feat(api): enhance sharepic generation with illustrations
Movm Jan 9, 2026
5135e08
feat(api): add template features to shared_media schema
Movm Jan 9, 2026
5e23fce
feat(api): add canvas element renderers for backend generation
Movm Jan 9, 2026
cdb3e64
feat(api): add Unsplash API integration
Movm Jan 9, 2026
4a380ee
feat(web): enhance image studio with templates and Unsplash
Movm Jan 9, 2026
a089013
chore: update pnpm lockfile
Movm Jan 9, 2026
5cd1124
refactor(web): extract BaseForm.tsx into reusable custom hooks
Movm Jan 9, 2026
70a0d4e
feat: integrate Docusaurus documentation into monorepo
Movm Jan 9, 2026
659dfea
feat: add production-ready collaborative docs platform with Hocuspocus
Movm Jan 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ apps/web/node_modules/.cache
*.pack
apps/api/local_cache/
apps/api/fastembed_cache/
.docusaurus

# Playwright MCP files
.playwright-mcp/
Expand Down Expand Up @@ -85,6 +86,7 @@ scripts/
# Documentation (local development)
docs/
!apps/api/docs/
!apps/docs/

# Backup files
backup/
Expand Down Expand Up @@ -119,3 +121,11 @@ apps/desktop/src-tauri/gen/schemas/*.json
target/
Cargo.lock
!apps/desktop/src-tauri/Cargo.lock

# Typecheck outputs
apps/web/typecheck_output.txt
apps/web/typecheck_output_full.txt

# Turbo
.turbo/
**/.turbo/
103 changes: 103 additions & 0 deletions GEMINI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Grünerator - Project Context

## Project Overview

**Grünerator** is an AI-powered content creation platform built specifically for the German Green Party (Die Grünen). It enables users to generate political content (press releases, social media posts), create sharepics, and subtitle videos with domain-specific AI knowledge.

This is a **monorepo** managed with **pnpm workspaces**, containing a full-stack application with web, mobile, and desktop clients.

### Key Technologies

* **Frontend:** React 19, Vite 7, Zustand (state management).
* **Styling:** **Standard CSS with CSS Variables**. No frameworks like Tailwind or Bootstrap are used.
* **Backend:** Node.js, Express.js (Cluster mode), PostgreSQL, Redis, Keycloak (Auth), Qdrant (Vector DB).
* **AI:** Mistral AI (primary), Anthropic Claude (via Bedrock), Flux (Images), AssemblyAI (Transcription).
* **Mobile:** React Native (Expo).
* **Desktop:** Tauri (Rust + Web Frontend).
* **Package Manager:** pnpm.

## Architecture

The system follows a multi-tier architecture designed for data sovereignty (EU hosting) and scalability.

* **API (`apps/api`):** The core backend.
* **Cluster Mode:** Uses Node.js `cluster` module for vertical scaling.
* **AI Worker Pool:** Dedicated worker threads for non-blocking AI operations.
* **Services:** Decoupled services for Auth, Database, Profiles, and specific AI tasks (Subtitler, Sharepic).
* **Data Stores:** PostgreSQL (User/Content data), Redis (Sessions/Cache), Qdrant (Vector Embeddings).
* **Web Client (`apps/web`):** The primary user interface.
* **Feature-Sliced Design:** Modular architecture.
* **Real-time:** Y.js for collaborative editing.
* **Mobile App (`apps/mobile`):** Expo-based React Native app for iOS and Android.
* **Desktop App (`apps/desktop`):** Tauri wrapper around the web frontend for offline/native capabilities.

## Directory Structure

* `apps/`
* `api/`: Backend Express server.
* `web/`: Main React frontend.
* `mobile/`: React Native (Expo) mobile app.
* `desktop/`: Tauri desktop app.
* `sites/`: Static sites/landing pages.
* `packages/`: Shared libraries (e.g., `@gruenerator/shared`).
* `services/`: Microservices, specifically `mcp` (Model Context Protocol).
* `docs/`: Documentation.

## Building and Running

The project uses `pnpm` for script management. Run these commands from the **root directory**.

### Installation
```bash
pnpm install
```

### Development
* **Web + Backend:**
```bash
pnpm dev:web # Starts frontend at localhost:5173
pnpm dev:backend # Starts backend at localhost:3000 (requires DBs running)
```
* **Mobile:**
```bash
pnpm dev:mobile # Starts Expo bundler
```
* **Desktop:**
```bash
pnpm --filter @gruenerator/desktop dev # Starts Tauri dev environment
```

### Production Build
* **Web:** `pnpm build:web`
* **Desktop:** `pnpm build:desktop`
* **Shared:** `pnpm build:shared`

### Testing
* **Root:** `npm test` (Runs tests across packages)
* **Backend Auth:** `pnpm --filter @gruenerator/api test:auth`

## Development Conventions

* **Language:** TypeScript is used across the entire stack.
* **Styling:**
* **NO Tailwind.** Use standard CSS files.
* **Variables:** Use CSS variables defined in `apps/web/src/assets/styles/common/variables.css` for colors (`--primary-600`, `--secondary-600`), spacing, and theming.
* **Structure:**
* Global: `apps/web/src/assets/styles/common/`
* Components: `apps/web/src/assets/styles/components/`
* Features: `apps/web/src/features/**/*.css`
* **Dark Mode:** Handled via `[data-theme="dark"]` and `@media (prefers-color-scheme: dark)` in `variables.css`.
* **Commits:** Follows Semantic Release/Conventional Commits (e.g., `feat:`, `fix:`, `chore:`).
* **Branching:** Create feature branches (`feature/name`) and PR to `main`.
* **State Management:** `zustand` is preferred for global state in frontend apps.
* **API Communication:** The backend exposes a REST API. Frontend uses `axios` or `tanstack-query` (implied from React context) for data fetching.

## Environment Setup

Ensure the following services are running locally or accessible:
1. **PostgreSQL** (Port 5432)
2. **Redis** (Port 6379)
3. **Keycloak** (Auth Provider)
4. **Qdrant** (Vector DB)

Copy `.env.example` to `.env` in `apps/api` and `apps/web` and configure secrets (API keys, DB credentials).
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,35 @@ npm run test:auth # Authentication tests

---

## Documentation

User-facing documentation is maintained in the `/documentation` directory using Docusaurus.

### Development
```bash
pnpm run dev:documentation # Start documentation dev server (localhost:3000)
pnpm run build:documentation # Build documentation site
```

### Documentation Structure
```
documentation/
├── docs/ # Main documentation pages
│ ├── Grundlagen/ # Basics and guides
│ ├── Profil/ # Profile and cloud features
│ ├── gruenerieren/ # Content generation features
│ ├── llm-basics/ # AI/LLM fundamentals
│ └── ueber-den-gruenerator/ # About Grünerator
├── blog/ # News and updates
├── src/ # Custom pages and components
└── static/ # Images and assets
```

### Deployment
Documentation is deployed to: https://xgwok08o0ccgo4g4cgcoksc8.services.moritz-waechter.de

---

## Roadmap

- [x] Core text generation
Expand Down
15 changes: 15 additions & 0 deletions api_typecheck_output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

> @gruenerator/api@1.0.0 typecheck /home/morit/gruenerator/apps/api
> tsc --noEmit

../../packages/shared/src/canvas-editor/index.ts(1,15): error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './types.js'?
../../packages/shared/src/canvas-editor/index.ts(2,15): error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './hooks/index.js'?
../../packages/shared/src/search/collections/index.ts(14,8): error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './types.js'?
../../packages/shared/src/search/collections/index.ts(25,8): error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './config.js'?
../../packages/shared/src/search/filters/index.ts(17,8): error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './types.js'?
../../packages/shared/src/search/filters/index.ts(30,8): error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './QdrantFilterBuilder.js'?
../../packages/shared/src/utils/index.ts(10,8): error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './textNormalization.js'?
../../packages/shared/src/utils/index.ts(18,8): error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './stringDistance.js'?
/home/morit/gruenerator/apps/api:
 ERR_PNPM_RECURSIVE_RUN_FIRST_FAIL  @gruenerator/api@1.0.0 typecheck: `tsc --noEmit`
Exit status 2
12 changes: 12 additions & 0 deletions api_typecheck_output_v2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

> @gruenerator/api@1.0.0 typecheck /home/morit/gruenerator/apps/api
> tsc --noEmit

../../packages/shared/src/canvas-editor/hooks/index.ts(1,33): error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './useCanvasLayers.js'?
../../packages/shared/src/canvas-editor/hooks/index.ts(2,68): error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './useCanvasLayers.js'?
../../packages/shared/src/canvas-editor/hooks/index.ts(4,34): error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './useCanvasHistory.js'?
../../packages/shared/src/canvas-editor/hooks/index.ts(5,70): error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './useCanvasHistory.js'?
../../packages/shared/src/utils/stringDistance.ts(8,29): error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './textNormalization.js'?
/home/morit/gruenerator/apps/api:
 ERR_PNPM_RECURSIVE_RUN_FIRST_FAIL  @gruenerator/api@1.0.0 typecheck: `tsc --noEmit`
Exit status 2
9 changes: 9 additions & 0 deletions api_typecheck_output_v3.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

> @gruenerator/api@1.0.0 typecheck /home/morit/gruenerator/apps/api
> tsc --noEmit

../../packages/shared/src/canvas-editor/hooks/useCanvasHistory.ts(7,48): error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean '../types.js'?
../../packages/shared/src/canvas-editor/hooks/useCanvasLayers.ts(7,63): error TS2835: Relative import paths need explicit file extensions in ECMAScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean '../types.js'?
/home/morit/gruenerator/apps/api:
 ERR_PNPM_RECURSIVE_RUN_FIRST_FAIL  @gruenerator/api@1.0.0 typecheck: `tsc --noEmit`
Exit status 2
4 changes: 4 additions & 0 deletions api_typecheck_output_v4.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

> @gruenerator/api@1.0.0 typecheck /home/morit/gruenerator/apps/api
> tsc --noEmit

33 changes: 30 additions & 3 deletions apps/api/agents/chat/IntentClassifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,8 +334,22 @@ REGELN:
- Bei "conversation" → NUR {"agent": "universal"}, NIEMALS mehrere Intents!
- Bei "document_query" → NUR {"agent": "universal"}
- Bei "content_creation" → passende Agents (social, sharepic, antrag, imagine, etc.)

SHAREPIC-SPEZIFISCHE REGELN (WICHTIG!):
- "zitat", "quote", "spruch", "aussage" → IMMER agent: "zitat" (NICHT dreizeilen!)
- "dreizeilen", "slogan", "drei zeilen", "3 zeilen" → agent: "dreizeilen"
- "info", "fakten", "information" → agent: "info"
- "sharepic" OHNE spezifischen Typ → agent: "sharepic_auto"
- Mit Bild + zitat → zitat_with_image
- Ohne Bild + zitat → zitat

BEISPIELE SHAREPIC:
- "Erstelle ein Zitat zum Thema Klimaschutz" → agent: "zitat" (NICHT dreizeilen!)
- "Mach einen Slogan über Windenergie" → agent: "dreizeilen"
- "Sharepic mit 3 Zeilen" → agent: "dreizeilen"
- "Quote von Annalena Baerbock" → agent: "zitat"

ANDERE REGELN:
- "bild erstellen", "generiere bild", "visualisiere", "illustriere", "flux", "ki-bild" → imagine
- Mit Bild + "transformiere"/"begrüne"/"bearbeite" → imagine (für Bildbearbeitung)
- Im Zweifel: "conversation" mit "universal" (weniger ist mehr!)
Expand All @@ -362,7 +376,7 @@ Beispiele für requestType:
- "Sharepic und Instagram Post" → content_creation, [sharepic_auto, instagram]

Antworte als JSON:
{"requestType": "conversation|document_query|content_creation", "subIntent": "summarize|translate|compare|explain|brainstorm|general", "intents": [{"agent": "...", "confidence": 0.9}]}`;
{"requestType": "conversation|document_query|content_creation", "subIntent": "summarize|translate|compare|explain|brainstorm|general", "intents": [{"agent": "...", "confidence": 0.9}]}${context.singleIntentOnly ? '\n\nWICHTIG: Gib NUR EINEN Intent zurück - den besten Match! Keine mehreren Intents.' : ''}`;

try {
console.log('[IntentClassifier] Calling AI for multi-intent classification');
Expand Down Expand Up @@ -415,8 +429,8 @@ Antworte als JSON:
parsedIntents = [parsedIntents];
}

// Validate and enrich intents
const validIntents = parsedIntents
// Validate and enrich intents - pick only the highest confidence intent for sharepic requests
let validIntents = parsedIntents
.filter(intent => intent && intent.agent && AGENT_MAPPINGS[intent.agent])
.map(intent => ({
agent: intent.agent,
Expand All @@ -428,6 +442,19 @@ Antworte als JSON:
confidence: intent.confidence || 0.8
}));

// For sharepic intents, only keep the highest confidence one to avoid multi-intent confusion
const sharepicAgents = ['zitat', 'zitat_with_image', 'dreizeilen', 'info', 'sharepic_auto', 'quote'];
const sharepicIntents = validIntents.filter(i => sharepicAgents.includes(i.agent));
if (sharepicIntents.length > 1) {
// Sort by confidence and keep only the best one
sharepicIntents.sort((a, b) => (b.confidence || 0) - (a.confidence || 0));
const bestSharepic = sharepicIntents[0];
console.log('[IntentClassifier] Multiple sharepic intents detected, keeping best:', bestSharepic.agent);
// Replace all sharepic intents with just the best one
validIntents = validIntents.filter(i => !sharepicAgents.includes(i.agent));
validIntents.push(bestSharepic);
}

if (validIntents.length === 0) {
console.warn('[IntentClassifier] No valid intents found in AI response');
return null;
Expand Down
2 changes: 2 additions & 0 deletions apps/api/agents/chat/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ export interface ChatContext {
lastAgent?: string;
/** Current topic of conversation */
topic?: string;
/** When true, AI returns only the single best intent (used by Image Studio) */
singleIntentOnly?: boolean;
}

/**
Expand Down
33 changes: 33 additions & 0 deletions apps/api/database/postgres/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,22 @@ CREATE TABLE IF NOT EXISTS saved_generators (
CREATE INDEX IF NOT EXISTS idx_saved_generators_user_id ON saved_generators(user_id);
CREATE INDEX IF NOT EXISTS idx_saved_generators_generator_id ON saved_generators(generator_id);

-- Template Likes (Users can like/favorite templates for ranking)
CREATE TABLE IF NOT EXISTS template_likes (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
template_id TEXT NOT NULL, -- Supports both UUID (user templates) and string IDs (system templates)
template_type TEXT NOT NULL DEFAULT 'system', -- 'user' | 'system' | 'file'
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,

-- Ensure user can only like a template once
UNIQUE(user_id, template_id)
);

CREATE INDEX IF NOT EXISTS idx_template_likes_user_id ON template_likes(user_id);
CREATE INDEX IF NOT EXISTS idx_template_likes_template_id ON template_likes(template_id);
CREATE INDEX IF NOT EXISTS idx_template_likes_popularity ON template_likes(template_id, created_at);

-- User Templates (Canva templates and other user templates)
CREATE TABLE IF NOT EXISTS user_templates (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
Expand Down Expand Up @@ -875,9 +891,26 @@ ALTER TABLE shared_media ADD COLUMN IF NOT EXISTS alt_text TEXT;
ALTER TABLE shared_media ADD COLUMN IF NOT EXISTS upload_source TEXT DEFAULT 'upload';
ALTER TABLE shared_media ADD COLUMN IF NOT EXISTS original_filename TEXT;

-- Template feature columns for canvas editor
ALTER TABLE shared_media ADD COLUMN IF NOT EXISTS is_template BOOLEAN DEFAULT FALSE;
ALTER TABLE shared_media ADD COLUMN IF NOT EXISTS template_visibility TEXT DEFAULT 'private'
CHECK (template_visibility IN ('private', 'unlisted', 'public'));
ALTER TABLE shared_media ADD COLUMN IF NOT EXISTS template_use_count INTEGER DEFAULT 0;
ALTER TABLE shared_media ADD COLUMN IF NOT EXISTS template_creator_name TEXT;
ALTER TABLE shared_media ADD COLUMN IF NOT EXISTS original_template_id UUID REFERENCES shared_media(id);

-- Media library indexes
CREATE INDEX IF NOT EXISTS idx_shared_media_library ON shared_media(user_id, is_library_item, created_at DESC);

-- Template indexes for efficient discovery
CREATE INDEX IF NOT EXISTS idx_shared_media_templates
ON shared_media(is_template, template_visibility, created_at DESC)
WHERE is_template = TRUE;

CREATE INDEX IF NOT EXISTS idx_shared_media_public_templates
ON shared_media(is_template, template_visibility, image_type, created_at DESC)
WHERE is_template = TRUE AND template_visibility = 'public';

-- Download tracking table for shared media
CREATE TABLE IF NOT EXISTS shared_media_downloads (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
Expand Down
Loading