FeatureBeta-Programm application backend (public form → Brevo email)
New public, no-auth endpoint `POST /api/public/beta/apply` accepts the xaio-beta-landing form (name, email, company, role, usecase[], vision, experience, hours, referral, consent), captures IP + user-agent, sends a Brevo confirmation mail to the applicant and an admin notification to BETA_ADMIN_EMAIL (falls back to ADMIN_EMAIL). Stores all submissions for the beta-cohort selection process.
FeaturePer-user project favorites — collaborators favorite without affecting others
The global `projects.is_favorite` column was replaced by a `project_favorites` join table keyed by (user_id, project_id). Each collaborator now has their own favorite list on shared projects — toggling a star no longer flips it for the entire team. Migration V076 moves existing owner favorites over and drops the legacy column. The update_project endpoint accepts owner OR collaborator role for the toggle.
Infrastructurecloud-local: management Flyway migrations now run on start
The cloud-local startup script previously skipped management-DB migrations, leaving developers on outdated local schemas (the "column resource_size does not exist" delete-error reproduced this). The orchestrator script now runs Flyway against `xaio_management` on every cloud-local start.
FeaturePer-message kebab popover with Bearbeitungszeit, AI Tasks and Geänderte Dateien
Every assistant message now has a 3-dot kebab on its action row. The popover shows two stat-pills (Clock + duration, Sparkles + AI Tasks used with the unit label muted), a "· N FILES" eyebrow, and a per-file diff list when the turn modified files. Each file row shows a TS/JS/PY/CSS language badge in neutral gray, the path, a 5-block proportional add/del bar, and +N -N counters; click the row to expand the unified diff inline. Empty popovers (no duration, no AI tasks, no files) hide the kebab entirely. Portal-rendered so it escapes the chat container overflow. Backend persists turn duration_ms and ai_tasks_used onto the iter row for survive-reload, and a new commit_sha field links each turn to its autocommit so per-file diffs are fetched via the existing /git/commits/{sha}/diff endpoint.
FeatureTelegram-style date separators in chat
Messages from different days now sit beneath a centered locale-aware pill ("May 1" / "1. Mai", year only when it differs from this year). On scroll, a sticky floating pill appears at the top with the date of the message under the viewport edge; it fades 1.2s after scroll stops and hides automatically when a static separator with the same date is already visible.
FeatureAuto-draining message queue with pause + drag-reorder
Sending a new message while a turn is still streaming queues it instead of bouncing the input. The queue header sits above the input as a collapsed pill with [count] [pause] [chevron]; expand reveals the full list with a drag-handle per item, an "as next"/"als nächstes" badge on item #0, and per-item remove. When the active turn ends, the head of the queue is automatically dispatched as the next turn — unless the user paused the queue. Resume after pause also drains. State lives in the chat panel so multiple ActionBar instances on the same message stay in sync.
FeatureQuiet welcome screen — Inter 500 + Instrument Serif accent, no dashed drop-zone
New "Was willst du bauen?" / "What do you want to build?" hero in Inter 500 with the last word ("bauen?" / "build?") rendered in Instrument Serif italic — the brand moment. Responsive sizes 52/40/32px with letter-spacing -1.6/-1.2/0px across breakpoints, line-height 1.05, color #111. Subtitle 15/1.5 muted #6b7280: "Ein Prompt." / "A prompt." (was "1 Prompt"). The dashed drop-zone box was removed; its function moved to a small 32×32 [+] icon button inside a single 52px footer with keyboard hint and ink-pill build button. A drag-onto-card overlay with "Drop files here" in Instrument Serif italic kicks in only during an actual drag. Keyboard hint and model picker only appear once the prompt has enough content to actually build.
FeatureReal Brevo-backed friend invitations with Airbuild-styled email
The Invite-Friends modal now sends real transactional invitation emails through Brevo instead of opening a mailto. New POST /api/auth/referral/invite accepts a list of emails plus a language hint (de/en), validates + dedupes, fetches the sender's referral URL and dispatches a hand-styled HTML email per recipient: warm off-white card on a subtle gradient, +25 hero with tabular-nums, "AI Tasks" in Instrument Serif italic blue accent, ink CTA, 3-step block, footer disclaimer. Per-address result (sent / failed / invalid) is surfaced in the modal feedback line. Soft cap of 20 invitations per request. Counter switched from completed_referrals to total_referrals so the user sees real signups (label: "angemeldet" / "signed up", was misleadingly "eingeladen" / "invited").
FeatureVersion Control reskin — Airbuild branch glyph, timeline rail, light-mode diff
Versionsverwaltung modal now wears the full Airbuild vocabulary: custom branch glyph instead of the GitHub icon, "verbunden" pill with green sync dot, "REPO" eyebrow with derived owner/repo slug + sync indicator, section headers per spec (chevron + icon + title + mono count-pill), "aktiv" chip in accent blue for the current branch, 28×24 white icon-buttons (Pull/Refresh/Merge/Push/PR/Checkout/Delete), 1px timeline rail with 10×10 commit nodes (accent-blue for HEAD), commit rows with mono short-hash + 13.5/500 title + author·time and a HEAD pill, file diff cards with +N / −N stats and "· Zeilen N–M" hunk headers, and a 4-col code grid (34/34/16/1fr). DiffViewer is now light-mode aware: same surface in dark, soft warm tones in light.
FeatureMediathek redesign — 1080×720 modal with search, segmented tabs, neutral pill chrome
Media Library is now a centered 1080×720 dialog (radius 16, soft Airbuild shadow). Top bar carries a folder-icon-box, "Medienbibliothek" title in Geist 14/600 plus a mono "· N FILES · 292 KB" sublabel, a 220px search field with ⌘K hint pill, a "Von URL" ghost button, and an ink primary "Hochladen" pill. Tabs are segmented with a mono 2-digit counter; an empty tab dims to muted-soft. Asset grid uses repeat(auto-fill, minmax(180,1fr)) with 14px gap and translateY(-1px) on hover. Pills (public / private / generated) switched from saturated solids to neutral mono-outline. New footer carries an LIMITS eyebrow with the size limits and a STRG+V kbd hint. ⌘K focuses the search field; search filters the active tab live.
FeatureTeam modal redesign — owner mono-tag, role select, two-step removal
Team panel rebuilt as a 680px modal per spec. Header carries a Users-icon bubble, "Team" title and a mono · N MEMBERS sublabel. The inline-invite row accepts comma-separated email addresses (split + dedupe + RFC-light validate + parallel POST), reports per-address success / failure inline. Member rows show a 36px avatar (or accent-blue initial) plus name/email/"DU" tag; the Owner gets a non-clickable INHABER mono-tag with crown, everyone else gets a 28px-tall RoleSelect popover (Bearbeiten / Nur ansehen with role descriptions, a divider, and a red "Aus Team entfernen"). Removal goes through a confirm dialog with explicit pre-checks (no projectUuid, missing permission, owner-protect, self-protect) and surfaced HTTP errors — no more silent no-ops. RoleSelect popover is portal-rendered so the modal's overflow doesn't clip it.
FeatureSecrets in assistant messages are now masked with click-to-copy
When the model answers with an API key, password, database URL, bearer token or any other recognised secret pattern, it now renders inline as the same amber Shield-icon pill we use in user input — masked to first6...last3 with the env-var name in parentheses. Click copies the original value to the clipboard with a 1.2s "Kopiert" confirmation. Hooked into every text-bearing markdown tag (p, li, h1-h6, strong, em, blockquote, td/th, inline code, link text) plus the plain-text branch. Block <pre><code> stays untouched so multi-line snippets remain readable.
FeatureSidebar shows the current user in the team avatar stack
The team avatar stack in the sidebar used to filter out the current user. It now includes you — moved to the front of the list so your avatar renders rightmost in the cascading stack (closest to the Team button, top of the painting pile). Fallback initials use solid accent-blue background with a white initial instead of the previous near-transparent treatment. Bottom divider in the footer also matches the top divider's mx-2.5 inset.
FeatureEdit/Copy/Revert action row on user messages
User messages now carry a hover-revealed thin-icon row (Edit, Copy, Revert). Hover-only by default so the chat stays calm; full keyboard navigation supported. Combined with the kebab popover on assistant messages this gives every turn a consistent, lightweight action vocabulary on both sides.
FeatureRecently opened projects in the sidebar
Sidebar now lists the last 5 opened projects under the Resources section (when expanded), with rectangular thumbnails (no border / padding) and a FolderOpen fallback. The list is sourced from a client-side localStorage map (xaio:lastOpened, max 50 entries, recency-sorted) and refreshes when you switch projects. The currently open project is filtered out so the list always shows alternatives.
FeatureGenerated images now appear inline in chat messages
When the AI generates images via the generate_image tool, they now render in a small horizontal wrap-list directly inside the chat message — both in project view and dev view. Dev view places them at the bottom of the message above the timestamp, alongside the action bar.
FeatureTab badges no longer get covered by sibling tab backgrounds
The error / info badges on the workspace tabs (Backend, Preview, Inbox, Runs) used to overflow into the next tab's area and disappear behind its solid background. They now sit at z-20 with a 2px halo ring (white in light, ink in dark) so they're always readable, regardless of which tab is active. pointer-events: none so they don't intercept clicks meant for the tab itself.
FeatureLocale-aware AI Tasks number formatting
AI Task counts now respect the document language: 1.48 in English, 1,48 in German. Token counts and detailed cost breakdowns are now hidden in prod and stage environments — they're only useful for development debugging.
FixReferral pending count fixed (Python `not` was dropping the WHERE filter)
`not UserReferral.referrer_bonus_granted` was being evaluated by Python at query-build time, coercing the column object to a literal True/False and silently dropping the WHERE clause. Switched to `.is_(False)` so SQLAlchemy emits the correct `WHERE referrer_bonus_granted = false`. Affects pending_referrals (used to come back as 0 or full total depending on coercion, never the actual pending set) and grant_referrer_reward (the lookup that decides whether a referrer is owed +25 AI Tasks could miss the real pending row).
FeatureWorkspace runner — Vite config + index.html re-patched on every wake
When dev-server file-sync overwrites the runner's vite.config.ts (which carries the per-workspace base path), Vite serves wrong asset URLs and CloudFront returns HTML for missing /src/main.tsx. The runner's start() now re-patches vite.config and index.html every time, even when "already running". Extracted helpers _ensure_vite_config and _ensure_index_html_patched. Also: workspaces with dead nodes are now marked STOPPED instead of deleted to prevent data loss when runners are recycled.