Commit Graph
215 Commits
Author SHA1 Message Date
Jannis Braun 2d7395ba64 feat: add top tab bar to Instance settings panel
Renders tab pills (General, Streaming, Storage, Users) at the
top of the content area, matching Discord's Family Center pattern.
Works alongside the sidebar sub-links — both navigate the same tabs.
2026-03-22 04:34:02 +01:00
Jannis Braun 9317ccb3a0 feat: add filter row with instance, role, date, and sort to UsersPanel 2026-03-22 04:26:00 +01:00
Jannis Braun 4f143bb289 fix: add missing Instance sub-panel titles and overhaul reset password UX
- Add <h2> title headings to General, Streaming, Storage, Users panels
- Reset password now shows confirmation dialog before executing
- Temp password displays inline below the user row (not at top of list)
- Temp password has copy button and dismiss action
2026-03-22 04:04:46 +01:00
Jannis Braun 437873c823 refactor: Instance settings back to separate pages with sidebar sub-links
The continuous scroll was wrong for Instance — its sub-panels are
fundamentally different page types (user table, storage tool, bitrate
matrix, toggles). Revert to conditional rendering.

The hook now supports two modes:
- Scroll mode (default): smooth scroll + IntersectionObserver spy
- Tab mode (onNavigate callback): sidebar sub-links switch tabs

Instance uses tab mode. The scroll-spy infrastructure remains for
future panels that benefit from it (e.g. Account sections).

Also reverts GeneralPanel to sticky save bar since it's on its own
page again with no overlap risk.
2026-03-22 03:52:26 +01:00
Jannis Braun ca9b971c29 fix: improve Instance settings section spacing and save UX
- Section headings: text-lg with descriptions, dividers spaced my-10
- GeneralPanel: convert to auto-save (toggles save immediately,
  text fields save on blur) — no more hidden save bar
- StreamingPanel: restore sticky save bar (complex multi-field form
  needs explicit save, and it's the only sticky bar now)
2026-03-22 03:43:23 +01:00
Jannis Braun fbf83cf8f4 fix: prevent infinite re-render loop in settings sections
- Memoize context value in SettingsSectionsProvider
- Use ctxRef pattern in hook to avoid depending on ctx object
  in useLayoutEffect deps (ctx object changes on every render,
  causing effect → setState → re-render → effect loop)
2026-03-22 03:32:43 +01:00
Jannis Braun 9210fb6616 fix: use stable SECTIONS array to prevent unnecessary re-renders
Remove `as const` + spread pattern that created a new array on
every render, triggering useLayoutEffect re-registration. Use
typed SettingsSection[] constant instead.
2026-03-22 03:25:50 +01:00
Jannis Braun d96d2d4dd1 fix: convert Instance save bars from sticky to inline for sectioned layout 2026-03-22 03:20:50 +01:00
Jannis Braun 61a0bff4a3 feat: refactor InstancePanel from tabs to sectioned single page 2026-03-22 03:19:36 +01:00
Jannis Braun f94958354a feat: integrate section sub-links into UserSettings sidebar 2026-03-22 03:18:28 +01:00
Jannis Braun 346fceeb74 feat: add SettingsSectionsContext for sidebar section communication 2026-03-22 03:14:59 +01:00
Jannis Braun 9812e13a0e fix: center settings content within content area
Content was left-aligned in the content pane, making it appear
off-center relative to the full modal. Add mx-auto to center
the max-w-[640px] wrapper within the content area, matching
Discord's centering behavior.
2026-03-22 02:33:05 +01:00
Jannis Braun 1f3d10842d fix: correct mobileView initial state and add SpaceSettings tab reset
- UserSettings: change mobileView default from 'content' to 'tabs'
- SpaceSettings: add useEffect to reset tab and mobileView on modal open
2026-03-22 02:26:15 +01:00
Jannis Braun e87cab58bf feat: add panel title headings to all SpaceSettings panels 2026-03-22 02:20:51 +01:00
Jannis Braun b3cb84c6a2 feat: rebuild SpaceSettings with expanded layout and grouped sidebar 2026-03-22 02:19:15 +01:00
Jannis Braun d54de16ec8 feat: add panel title headings to all UserSettings panels 2026-03-22 02:17:38 +01:00
Jannis Braun 3b06818614 feat: rebuild UserSettings with expanded layout and grouped sidebar 2026-03-22 02:15:27 +01:00
Jannis Braun 4c4bd8dec6 fix: add editable input alongside slider step presets for custom values 2026-03-22 01:44:24 +01:00
Jannis Braun 46f17cf172 fix: replace slider step number input with pill presets 2026-03-22 01:29:40 +01:00
Jannis Braun eff3210f29 fix: make slider step a compact inline row instead of prominent section 2026-03-22 01:25:03 +01:00
Jannis Braun 30cbf58770 fix: stack bitrate slider and input vertically for responsive layout 2026-03-22 01:19:28 +01:00
Jannis Braun f3ff07aa6d feat: add bitrate matrix editor with scale macro and uncapped max inputs 2026-03-22 00:52:40 +01:00
Jannis Braun 6122c7829f fix: replace regex-based isNewlyCreated with explicit isNew prop
Pass isNew from parent state instead of guessing from the role name
pattern. Set true on create/copy, cleared on back/click-to-edit.
2026-03-22 00:17:14 +01:00
Jannis Braun b8c8e39dfd fix: use existing success banner style for role created indicator
Match the status-online pattern already used for "Role saved" in the
same file, instead of introducing a one-off accent-primary variant.
2026-03-22 00:12:33 +01:00
Jannis Braun 0bd87c4ebf fix: auto-focus name input and show creation indicator in role editor
After creating or copying a role, the name input is now auto-focused
with text selected so the user immediately starts typing. A subtle
"Role created" banner appears until the user makes any change.
2026-03-22 00:05:17 +01:00
Jannis Braun da82ba6d94 fix: add key prop to RoleEditView to reset state when switching roles
RoleEditView used useState(role.name) for draft state, but React
reuses the component instance when editingRoleId changes, ignoring
the new initial value. Adding key={editingRoleId} forces a remount,
re-initializing all draft state (name, color, permissions) correctly.
2026-03-21 23:43:00 +01:00
Jannis Braun b4b22faf55 feat: auto-increment role names to avoid uniqueness conflicts on create and copy 2026-03-21 23:31:35 +01:00
Jannis Braun a9a676bc70 feat: expand admin streaming pills, add high-end resource warning
Replaces hardcoded 3-resolution/3-framerate arrays with ALL_RESOLUTIONS
and STANDARD_FRAMERATES from shared constants (540p–Native, 30–120 fps).
Adds amber warning banner when high-end options (≥1440p or ≥75 fps) are
enabled, and fixes toggleResolution to handle 'native' with correct sort.
2026-03-21 23:30:46 +01:00
Jannis Braun 57b192533d feat: add Copy Role button to duplicate roles with permissions 2026-03-21 23:30:12 +01:00
Jannis Braun 3fe9f774a1 feat: add client-side role name uniqueness validation with 409 handling 2026-03-21 23:26:06 +01:00
Jannis Braun cc7d1c191c feat: add CategorySettingsModal with overview and permissions tabs 2026-03-21 22:08:47 +01:00
Jannis Braun 892164f3a5 refactor: use extracted PermissionsEditor in ChannelSettingsModal 2026-03-21 18:35:38 +01:00
Jannis Braun 2593f7075c feat(desktop): add version display and check-for-updates to account settings 2026-03-21 02:32:19 +01:00
Jannis Braun 9e4878c48f fix: move showActivity toggle from Account to Privacy settings panel 2026-03-21 02:13:32 +01:00
Jannis Braun 1f4e149ee1 feat: add frontend Rich Presence — activityStore, ActivityCard, sidebar upgrades, settings toggle
- Task 7: Create activityStore (Zustand) with Map<userId, Activity[]>, showActivity toggle,
  debounced pushActivities, and reset on logout
- Task 8: Wire WS integration — presence_update propagates activities to store,
  ready payload initializes userActivities and showActivity (via setState to avoid side effects)
- Task 9: Create ActivityCard component with compact/full modes, type-colored labels,
  elapsed time display, asset images, and fallback to customStatus
- Task 10: Upgrade ActivityPanel to three groups — active friends (full cards, no header),
  online friends (compact), offline friends — using getPrimaryActivity for grouping
- Task 11: Upgrade MemberSidebar to show compact ActivityCards instead of raw customStatus
- Task 12: Add showActivity toggle in AccountPanel settings (Activity Status section)
  with API persistence and store sync
2026-03-21 01:55:17 +01:00
Jannis Braun 913b4c7365 feat(mobile): add mobileStyle to all modals (sheet for forms, fullscreen for settings) 2026-03-17 15:46:28 +01:00
Jannis Braun ac7d3cb71b feat: add start-at-boot setting and fix tray icon to use app logo
Add auto-launch settings (start at boot + start minimized) to the
Desktop section in Account settings, using Electron's built-in
setLoginItemSettings API across macOS, Windows, and Linux.

Replace the fallback colored-circle tray icon with proper B logo assets:
template images for macOS (adapts to light/dark menu bar) and colored
icons for Windows/Linux. Fix BGRA channel order bug in fallback generator
and move tray icons to resources/ so they're packaged into the app.
2026-03-17 02:28:38 +01:00
Jannis Braun 95ec3ff952 feat: Electron screen share picker, instance selector, and system audio loopback
- Custom screen share picker for Electron (ScreenSharePicker.tsx) with
  Aether Drift design: glass-bubble footer, adaptive grid, pill tabs,
  border-based selection (avoids overflow clipping), hover brightness
- Instance URL picker so Electron connects to any Backspace server
- System audio loopback on Windows/Linux via desktopCapturer callback
- macOS: video-only callback (OS blocks system audio capture)
- IPC bridge for screen source enumeration and selection
- Purge stale service worker caches on Electron launch
2026-03-16 03:50:07 +01:00
Jannis Braun 8ae3ffc912 feat: Electron desktop app — hardening, IPC bridge, and dev launch fixes
- Dev/prod URL auto-detection (Vite 5173 in dev, server 3000 in prod)
- Typed IPC bridge via preload (notifications, badge, window controls, updates, deep links)
- Native OS notifications via NotificationController with window focus suppression
- Auto-update via electron-updater with UpdateToast UI
- Deep linking (backspace:// protocol) for macOS and Windows/Linux
- Window state persistence (position, size, maximize across restarts)
- Tray icon with graceful fallback when icon asset missing
- Suppress PWA service worker polling/reloads inside Electron
- Platform detection layer (isElectron, getElectronAPI)
- Root workspace scripts (dev:desktop, build:desktop)
- Document BACKSPACE_URL and BACKSPACE_UPDATE_URL env vars
2026-03-16 00:10:47 +01:00
Jannis Braun 2e6fa3cdc6 feat: optimize profile image sizes, silent PWA updates, storage cleanup fixes
- Resize avatars/icons to 256px and banners to 1280px (client crop + server safety net)
- Add server-side resizeProfileImage() for federation/API uploads without crop modal
- Fix unconstrained crop on RegisterPage and CreateSpace (was missing maxOutputDimension)
- PWA: switch to autoUpdate with skipWaiting/clientsClaim for seamless deploys
- Storage janitor: exclude profile images from unlinked cleanup, delete stale thumbnails
- Add deleteAttachmentByFilename() to clean orphaned attachment records for profile images
- Migration: one-time cleanup of stale profile image attachment records
- GeneralPanel: wrap in <form> to prevent implicit submission
2026-03-15 19:16:48 +01:00
Jannis Braun 4d230711fc feat: launch readiness — PWA, API hardening, memory leak fixes, sticker removal
- Add PWA infrastructure: vite-plugin-pwa, manifest, service worker,
  SW update prompt component, placeholder icons, Apple meta tags
- Harden API client: 401 auto-logout, AbortController timeouts
  (30s standard, 120s uploads), onUnauthorized callback
- Fix memory leaks: clear voice user status on leave, clean up all
  Maps (channelToSpaceMap, permissions, etc.) on removeSpace
- Upgrade error boundary to Aether Drift design with Try Again button,
  collapsible stack trace, and componentDidCatch logging
- Configure desktop icon paths in electron-builder.yml
- Remove sticker feature (server routes, schema, types, UI components)
- Fix Docker build: use **/node_modules in .dockerignore to prevent
  COPY from clobbering pnpm-installed workspace dependencies
- Add vite-env.d.ts declarations for noise suppressor wasm imports
- Exclude test files from tsc build via tsconfig
2026-03-15 15:41:22 +01:00
Jannis Braun 3de6e4a668 feat: GIF search (Klipy), stickers, emoji picker, and bug fixes
- Add GIF search powered by Klipy API with correct response mapping
  (file.sm/hd tiers, not flat files structure)
- Add sticker system: packs, upload with auto-downscale, send in messages
- Add tabbed InputPopover with emoji, GIF, and sticker pickers
- Fix GIF API key migration race condition (column-add loop vs rename)
- Fix masked API key corruption on settings save (server + client guards)
- Fix sticker packs 403 (reversed isMember parameter order)
- Fix emoji picker not filling popover width (perLine 8→9, CSS 100%)
- Add error logging for Klipy API failures
2026-03-15 02:04:37 +01:00
Jannis Braun 3a266e07ed fix: allow bare filenames in avatar/banner validation, fix password min length
isValidAssetUrl() was rejecting bare filenames (e.g. "1234567890.webp") which
is the established convention the frontend sends. Now accepts bare filenames
while still blocking path traversal and unsafe schemes.

Also updates client-side password validation to match server's 8-char minimum.
2026-03-15 00:11:10 +01:00
Jannis Braun 7c544c1ff4 feat: security hardening, DB indexes, token revocation, and input validation
- SSRF protection: DNS resolution + private IP blocking on metadata fetcher
- Upload security: CSP/X-Frame-Options headers, SVG forced download, nosniff
- Auth hardening: JWT secret min length, password min 8 chars, token revocation via password_changed_at
- Attachment ownership verification before linking to messages
- Message length limit (4000 chars) enforced on client and server
- Asset URL validation on avatar/banner updates
- Federation instance validation (domain regex, origin scheme, length limits)
- DB indexes on all FK columns for query performance
- Migrations: nullable moderator columns, dm_messages reply_to FK constraint
- File cleanup on avatar/banner replacement and space deletion
- Fastify trustProxy, AbortController on fetches, typing map size cap
2026-03-15 00:06:15 +01:00
Jannis Braun 836f0acef6 feat: standardize input styling with tier system, add depth and admin features
- Define 4 input tier CSS classes (input-standard, input-search, input-embedded, input-danger)
  in globals.css, migrating ~50 inputs across ~28 component files to use them
- Add subtle border and inset shadow to solid input tiers for resting-state visibility
- Fix focus ring clipping in settings panel scroll container
- Fix phantom Tailwind tokens (border-border-primary, placeholder-txt-muted)
- Add admin user management panel, storage management, and account deletion utilities
2026-03-14 13:49:32 +01:00
Jannis Braun b194cc1915 feat: discover people tab, privacy settings, and friend request button fix
- Add "Discover People" section to Add Friend tab with user cards, mutual counts, and inline actions
- Add discoverStore for fetching/searching discoverable users across local and federated instances
- Add PrivacyPanel to user settings with discoverability toggle
- Add is_discoverable column to users table with migration
- Fix "Send Friend Request" button vertical alignment using transform centering
2026-03-13 22:54:19 +01:00
Jannis Braun 3e97c2b0f5 feat: image optimization — client-side resize + server-side thumbnails
Avatars/banners now resize to max 512px/1920px and convert to WebP before
upload (zero server cost). Chat image uploads generate an 800px-wide WebP
thumbnail via Sharp; the feed shows the thumbnail, click opens the full-res
original. Adds lazy loading to avatars. Federation-compatible: remote
instances without this feature fall back gracefully.
2026-03-13 16:44:14 +01:00
Jannis Braun c3617de832 feat: public invite landing page with federation redirect
Make /join/:code a public route with a standalone JoinPage that shows a
space preview and handles authenticated, unauthenticated, and cross-instance
users. Adds GET /api/spaces/invite/:code/preview (no auth) endpoint,
?redirect= param support on login/register, and cleans up dead invite
handling from AppLayout and JoinSpace modal.
2026-03-13 03:04:52 +01:00
Jannis Braun 07ef49eac0 feat: channel permissions UI, DM Sans font, private channel filtering, migration fix
- Rewrite ChannelSettingsModal with full tri-state permission override UI
  for roles and members (allow/neutral/deny per permission bit)
- Switch font from Inter to self-hosted DM Sans (woff2 variable fonts)
- Add client-side VIEW_CHANNEL filtering in ChannelSidebar for private channels
- Broadcast isPrivate flag on channel override changes
- Fix voice permission bit migration: gate behind persistent flag to prevent
  repeated re-runs that stripped STREAM from @everyone roles
- Add speakingUserIds set to voice store for efficient user-level lookups
- Clear current channel view when a channel is deleted
- Move .glass-strip to @layer utilities for proper CSS specificity
- Simplify avatar initials font size to proportional formula
2026-03-13 02:47:32 +01:00
Jannis Braun 770e0490dd feat: increase perceived audio loudness — +3dB master boost, configurable SFX volume
Three changes to bring output volume closer to native apps:
- Insert masterBoost GainNode (+3dB) before the compressor/limiter
- Raise default system sound volume from 0.5 to 0.8
- Add configurable Sound Effects Volume slider (0–200%) in Voice settings
2026-03-12 23:42:11 +01:00