Commit Graph
46 Commits
Author SHA1 Message Date
Jannis Braun 3790386a5f feat: real-time user profile updates and propagate avatarColor to all Avatar callsites
Broadcast user_updated events over WebSocket when profile fields change,
updating members, DM participants, friends, and cached messages in real time.
Widen useVoiceParticipantMeta to return the full user object and add a
standalone avatarColor prop to Avatar so all ~16 callsites now resolve
the user's chosen gradient color instead of falling back to hash-based colors.
2026-03-11 01:12:12 +01:00
Jannis Braun ce63c5ed36 feat: real-time SPEAK permission enforcement in voice channels
Permission changes now take effect immediately without requiring
disconnect/reconnect. Modeled as "permission mute" parallel to
server mute — server recomputes SPEAK for all voice participants
on role/override changes and broadcasts state via WebSocket.
Includes amber UI indicators and mic toggle blocking.
2026-03-10 14:50:01 +01:00
Jannis Braun 4d9454382c refactor: unify settings into tabbed modal with UI polish
Split monolithic UserSettings into AccountPanel, VoicePanel, ConnectionsPanel,
and InstancePanel tabs. Remove standalone InstanceSettings modal. Add reusable
Toggle component fixing size deformation, color inconsistency (green→purple),
and flex-shrink issues. Fix custom status clearing by always sending the field
to the server. Wrap Log Out button in glass bubble. Add subtle card depth with
borders.
2026-03-10 03:57:01 +01:00
Jannis Braun f3b3c249ca fix: dynamically size channel list padding from floating panel height
Replace static pb-[140px] with ResizeObserver-driven padding so the
last channel is never hidden behind the voice control panel.
2026-03-10 00:49:54 +01:00
Jannis Braun cfd24c5d58 fix: show mute slash icon when server-deafened and render badges from effective state
Add isServerDeafened to mic icon slash conditions in VoiceControlBar and
ChannelSidebar. Restructure VoiceUser badge rendering to gate on effective
state (intent OR server enforcement) instead of only LiveKit participant
intent, fixing missing badges in the voice grid for server-muted users.
2026-03-09 23:26:51 +01:00
Jannis Braun 651d7edf9d fix: guard voice controls against toggling intent while server muted/deafened 2026-03-09 23:15:47 +01:00
Jannis Braun 907f8285cd fix: decouple voice intent from server enforcement to prevent involuntary unmute
When a moderator lifted a server mute/deafen, the client was involuntarily
turning on the user's microphone because isMuted/isDeafened conflated user
intent with server enforcement. Now intent (isMuted/isDeafened) is never
mutated by server events. Effective state (intent || serverEnforcement) is
computed at broadcast and hardware time via centralized helpers.
2026-03-09 22:25:56 +01:00
Jannis Braun db1909d785 fix: resolve federated identity checks for server mute/deafen pipeline
- The client now dynamically resolves the user's federated identity via `getMyUserIdForOrigin` when evaluating incoming `voice_server_muted` and `voice_server_deafened` events. Previously, the client incorrectly compared the remote event's federated `userId` against the local `authStore` home `userId`, causing federated users to silently drop restriction events.
- Client-side mic/deafen toggles (`toggleMic`, `toggleDeafen`) now accurately evaluate the user's origin-specific ID against the restriction sets, preventing federated users from bypassing locks.
- UI state selectors (`VoiceControlBar`, `ChannelSidebar`) now compute `myOriginId` to correctly render the yellow server-lockdown indicators for cross-instance users.
2026-03-09 19:54:15 +01:00
Jannis Braun 6a12fe2024 fix: rearchitect server mute/deafen pipeline to scope restrictions by spaceId
- Replaces global `userId` tracking with `spaceId:userId` composite keys across both backend and frontend, fixing the issue where server-muting a user in one space bled into others.
- Modifies client-side `ready` event hydration to merge voice states per-origin instead of completely overwriting the store, preventing federated connections from wiping out home instance mutes.
- Excludes server voice restrictions from `zustand/persist` so stale client caches don't override the server's authority on reload.
- Fixes React component reactivity by using reactive store selections for `spaceId` instead of imperative `getState()` calls, ensuring UI lockdown indicators accurately reflect the initial websocket handshake.
2026-03-09 19:44:37 +01:00
Jannis Braun f6cdfe993f fix: server-deafen now shows amber mute icon across all UI surfaces
When a moderator server-deafens a user, the implied mute is server-imposed
and should display amber (not red) everywhere. Updated VoiceControlBar,
ChannelSidebar, VoiceChannel, and VoiceUser to check isServerDeafened
alongside isServerMuted for amber color and cursor-not-allowed state.

Also includes smart mute/deafen toggle logic (Discord-style coupling),
server-side enforcement of mute/deafen bypass, and cleanup of server
voice state on user departure.
2026-03-09 17:12:00 +01:00
Jannis Braun e2c18ad2b0 feat: bans system, voice moderation, and federated space settings fixes
Add ban/unban functionality with BansPanel in space settings, voice
moderation context menu (mute/deafen/disconnect), and fix federated
space settings panels to use origin-aware API client. Show domain
indicators for federated members in MembersPanel.
2026-03-09 15:56:46 +01:00
Jannis Braun 450df432b2 feat: dedicated Instance Settings modal with admin controls
Move instance-level administration out of Space Settings into its own
modal. Adds admin UI for instance name, registration toggle, and
discovery toggle. Streaming limits panel relocated from SpaceSettings.

- Add InstanceAdminSettings type and GET/PATCH /api/settings/instance
- Add registration_open DB column (nullable, env var fallback)
- Auth registration and instance info now check DB override
- New InstanceSettings modal with General and Streaming tabs
- Admin shield button in UserAreaPanel (visible to admins only)
- Remove Streaming tab from SpaceSettings
2026-03-09 03:52:01 +01:00
Jannis Braun fc06e25731 refactor: rename Server → Space across entire codebase
Renames all domain terminology from "Server" to "Space" throughout the
application — database schema, API routes, shared types, stores, components,
and UI strings. Files renamed: ServerSidebar → SpaceSidebar, CreateServer →
CreateSpace, JoinServer → JoinSpace, ServerSettings → SpaceSettings,
serverStore → spaceStore, routes/servers → routes/spaces.
2026-03-08 20:08:24 +01:00
Jannis Braun b642989e6e fix: give Explore its own /explore route so PiP navigates back to voice correctly
The Explore page shared /channels/@me with Friends, differentiated by a
showExplore UI flag. When PiP navigated to a voice channel, the stale
flag caused MainContent to render text chat instead of the voice grid.
Replace the flag with a dedicated /explore route so the URL is the
single source of truth.
2026-03-05 01:29:16 +01:00
Jannis Braun 5e4a5c2ee9 fix: clean up cross-instance voice state when switching federated servers
Send explicit voice_leave to the old instance when joining voice on a
different origin, preventing stale voice state from showing the user in
two channels. Also make WS reconnect voice re-registration origin-aware
so remote reconnects properly restore voice state.
2026-03-04 20:57:15 +01:00
Jannis Braun 65e9ee5203 fix: prevent duplicate DMs at creation time, clean up corrupted read states
Replace the unreliable client-side DM dedup loop in populateFromReady with
a creation-time guard (findExistingDmForUser) that checks all instances
before opening a new DM. Guards added to FriendsPage, NewDmModal, and
UserProfilePopout.

Also fixes: corrupted read_states from temp_ optimistic message IDs (server
migration + client-side validation), federation-aware closeDm/addDmMember
API routing, isSelf-based DM member filtering in sidebar/header, and WS
event error isolation.
2026-03-04 20:04:15 +01:00
Jannis Braun 6e44a4ef2f fix: DM avatar color and reactions in federation + explore/server discovery
- Fix DM welcome header avatar using home identity for correct gradient color
- Register DM channel IDs in channelOriginMap so federated DM operations
  (reactions, messages, typing) route to the correct instance
- Pass origin when creating DM channels from friends list and WS events
- Add server discovery/explore page with public server listings
- Add server visibility and description fields
2026-03-04 18:02:43 +01:00
Jannis Braun dca9c4dc83 feat: stateless federated identity resolver + federation UX improvements
Add identity.ts with isSelf() and resolveDisplayIdentity() — pure
stateless functions that detect replicated-self using the immutable
(username, homeInstance) composite key. No store lookups, no data
mutation. Fixes wrong avatar gradient and missing edit/delete on
own messages in remote channels.

Also includes: optimistic message dedup fix for cross-instance
messages (content-only matching), federation toast notifications,
Username component with @domain display, invite parser, and
deploy script simplification.
2026-03-04 03:37:49 +01:00
Jannis Braun 758c6a7b5b feat: normalize remote asset URLs and route UI actions by instance origin
Phase 5 of multi-instance federation. Adds a two-layer fix:

Layer 1 — Data ingestion normalization: Remote instance user avatars,
server icons, and attachment filenames are rewritten to absolute URLs
when entering the app (via WebSocket events or API responses), so all
downstream components render them correctly without changes.

Layer 2 — Outbound action routing: wsSend calls (voice join/leave/status,
typing) and file uploads in UI components now route through the correct
instance based on the active channel's origin.
2026-03-03 00:28:24 +01:00
Jannis Braun 37dc73231d refactor: convert color system to RGB channels for native Tailwind opacity
Root cause fix for all broken CSS variable opacity modifiers. Converted
every hex CSS variable to space-separated RGB channels (e.g. #fda4af →
253 164 175) and updated Tailwind config to use rgb(var(...) / <alpha-value>).
This makes bg-accent-rose/10, border-border-hard/50, etc. work natively
everywhere — no more currentColor fallback. Reverted all previous explicit
rgba() workarounds back to clean Tailwind syntax. Also fixed the voice
connection wifi icon SVG (was bottom-left aligned, now centered).
2026-03-02 17:46:21 +01:00
Jannis Braun 0e235fdf1c fix: DM reactions + eradicate broken CSS variable opacity modifiers
Server-side: handleReactionAdd/Remove now check dm_messages table when
message isn't found in server messages, enabling DM emoji reactions
via the same unified client event path.

Client-side: replaced all remaining broken opacity modifiers on CSS
variables (border-border-soft/50, bg-accent-rose/10, bg-surface-*/N,
etc.) with explicit rgba values. Tailwind can't decompose var() for
opacity, causing fallback to currentColor. Fixes 16 files across
error boxes, table borders, surface tints, and input separators.
2026-03-02 17:36:33 +01:00
Jannis Braun 7608afccc4 fix: panel separator borders and reaction spacing to match prototype
Replace @apply border-none with @apply border-0 on global * selector.
border-none sets border-style: none, which silently kills all Tailwind
border utilities (they only set width/color, relying on preflight's
border-style: solid). border-0 sets border-width: 0 instead, preserving
the solid style so border-r/border-l/border-b utilities render correctly.

- Add border-r border-border-hard to ChannelSidebar (both DM and server views)
- Add border-l border-border-hard to MemberSidebar and ActivityPanel
- Replace shadow-header with border-b border-border-hard on sidebar headers
- Remove duplicate mt-1 on reaction container (parent gap-1 already provides 4px)
2026-03-02 16:51:17 +01:00
Jannis Braun cdee450cf5 fix: channel sidebar and reaction bubbles to match Aether Drift prototype
Channel sidebar: subtle bg-surface-elevated highlight with rounded-[6px],
active channel left pill, rose unread dot on right, smaller #/voice icons
(18px), 10px horizontal padding, 24px voice user avatars at 36px indent.

Reaction bubbles: inline styles for background/border colors to bypass
Tailwind opacity modifier failures on hex CSS variables. Both mine (mint
tint) and non-mine states now render visible pill containers reliably.
2026-03-02 14:45:47 +01:00
Jannis Braun dcd4ef0011 feat: Aether Drift UI polish — popout redesign, sidebar cleanup, status dot fix
- Redesign UserProfilePopout with glass material, viewport clamping, flow-based
  avatar layout, and subtle send-message button
- Server sidebar: downsize icons to 40px (matching prototype), remove nonfunctional
  Explore button, add separator before action buttons, replace Tooltip with native
  title, remove hover boxShadow artifacts
- Avatar status dot: fixed 12px size with semi-transparent border for universal
  cutout effect matching the design prototype
- MemberListToggleButton: distinct three-person icon (was duplicate of Add Friends)
- FriendsPage: fix Add Friend button contrast (dark text on mint background)
- ChannelSidebar: rename Nitro/Shop to Coming Soon placeholders
2026-03-02 03:21:12 +01:00
Jannis Braun ddeb72101e feat: dynamic PiP collision avoidance, reaction pill redesign, channel dedup fix
PiP positioning:
- Replace hardcoded layout constants with dynamic DOM measurement system
- Obstacle elements declare themselves via data-pip-obstacle="left|bottom"
- getPipBounds() queries actual element rects at runtime via getBoundingClientRect
- MutationObserver + ResizeObserver re-clamp PiP when obstacles appear/resize
- PiP reappears after close when navigating away from voice channel

Reactions:
- Align reaction pills with Aether Drift prototype
- Own-reaction accent: purple → mint (bg, border, count color)
- Default pills: subtle white overlay bg + border-soft border
- Count text: 12px/semibold/txt-secondary per prototype spec

Bug fix:
- Deduplicate channel insertion in createChannel store action
- Prevents double-add race between REST response and WS broadcast
2026-03-02 02:09:52 +01:00
Jannis Braun f79575ffa7 fix: consistent avatar gradient colors across all 17 call sites
Avatar fallback gradients were hashed by display name alone when no
user prop was passed, causing the same person to appear in different
colors across messages, replies, member lists, voice panels, etc.

Added userId prop to Avatar and wired it through all 17 call sites
so the gradient always hashes by user ID.
2026-03-02 01:16:58 +01:00
Jannis Braun 95fc6f0693 feat: Phase 7 Aether Drift — typography, spacing & detail alignment
Align all component typography to the design prototype's exact measurements:
- Message author: 16px bold → 15px semibold
- Message text: 16px → 15px, line-height 1.5
- Timestamps: 12px → 11px
- Category headers: 12px bold → 11px medium, #484854 color
- Member/activity names: 15px → 13.5px with tighter line-height
- Member activity text: 12px → 11px
- Section headers: 12px → 10.5px
- Channel header names: 15px with -0.02em tracking
- Channel topic: 12px → 13px
- Header padding: 16px → 20px
- User area avatar: 32px → 34px
2026-03-02 01:05:42 +01:00
Jannis Braun 79252fe54a feat: Phase 1 Aether Drift — mobile-first grid layout and token migration
Replace Discord flex layout with CSS Grid on desktop (312px sidebar +
1fr main) and overlay drawer on mobile. Migrate all discord-* class
references to Aether Drift design tokens across 15 files (~200 refs).

Layout: ServerSidebar becomes a fixed glass strip (md:glass-strip),
ChannelSidebar gets pl-[72px] offset, bottom bar is a glass-bubble
(z-105), MessageInput floats as absolute glass pill on desktop (z-110).
MemberSidebar/ActivityPanel hidden on mobile via hidden md:block.

Z-index stack: MobileNav backdrop z-35, sidebar z-40, glass strip z-100,
bottom bar z-105, input bubble z-110, hamburger z-120, profile popout
z-145, device panels z-150, modals z-200.
2026-03-01 22:46:27 +01:00
Jannis Braun 76b8a43be2 fix: enforce channel-level RBAC across WS broadcasts, REST endpoints, and frontend reactivity
Wire the bitwise permission engine end-to-end:

- Add sendToChannel() to ConnectionManager, filtering WS recipients by VIEW_CHANNEL
- Convert 6 channel-scoped events (messages, typing, reactions) from sendToServer to sendToChannel
- Add broadcastOverrideChange() to push channel_updated/channel_deleted per-user on override mutations
- Bridge legacy server_members.role TEXT to member_roles junction table on PATCH
- Add pushReadyPayload() to force re-sync frontend store after role changes
- Filter channels by VIEW_CHANNEL in GET /api/servers/:id to prevent initial load data leak
- Pre-compute viewers before CASCADE delete on channel_deleted
- Fix frontend channel event handlers to upsert/cleanup channelToServerMap and channelPermissions
- Add ChannelSettingsModal with Private Channel toggle and gear icon in ChannelSidebar
2026-02-24 06:10:11 +01:00
Jannis Braun 8030c89c6c feat: bitwise RBAC engine with channel-level permission overrides
Replace string-based role checks (role === 'admin') with a bitwise BigInt
permission system. Adds computePermissions() resolution engine following
Discord's model: @everyone base → role union → admin shortcut → channel
overrides (role deny/allow → member deny/allow). Ready payload now filters
channels by VIEW_CHANNEL and attaches per-user myPermissions to each
server and channel. Includes channel_overrides table, @everyone role
auto-creation, migration for existing servers, and override CRUD API.
2026-02-24 05:08:59 +01:00
Jannis Braun 900eb67869 feat: add group DM support (3-10 members)
Unlock group DMs by removing the 2-member assumption across the stack.
No schema migration needed — dm_members junction table already supports
N members. Includes dedup bug fix, add/leave member endpoints, late-join
call support, group-aware sidebar rendering, and AddDmMemberModal.
2026-02-23 22:39:06 +01:00
Jannis Braun 628d417723 fix: restore VoiceControls and chat split-view in DM calls
VoiceControls was gated on server-only `currentVoiceChannelId`, now also
triggers on `activeDmCall`. Added VoiceChatPanel to DM call view matching
the server voice channel pattern.
2026-02-23 21:49:06 +01:00
Jannis Braun 5e34b39b78 fix: broadcast camera & screen share status via WebSocket for sidebar visibility
Camera and LIVE badges in the channel sidebar were only visible to users
who had joined the same LiveKit room. Widen the voice_status WS event
from {isMuted, isDeafened} to {isMuted, isDeafened, isCameraOn, isScreenSharing}
so all server members see camera/screenshare indicators without joining voice.
2026-02-23 20:07:42 +01:00
Jannis Braun ccf2001047 feat: RNNoise WASM AudioWorklet for ML-based noise suppression (Phase 3)
Inject @sapphi-red/web-noise-suppressor into AudioManager's input pipeline
as a toggleable AudioWorkletNode. The worklet is loaded lazily on first
enable, then kept alive — toggling bypasses by rewiring the graph without
destroying the WASM instance. Browser NS is forced off when RNNoise is
active to avoid double-processing. InputGain forced to stereo up-mix to
prevent mono-left-only output from the worklet. Also wires Phase 2 output
device routing (setSinkId) into AppLayout/ChannelSidebar.
2026-02-23 00:28:29 +01:00
Jannis Braun 6b8fd44a3a feat: real-time sync, notification & social system overhaul
- Add WS events: dm_channel_created, dm_channel_closed, friend_removed,
  channel_created/updated/deleted, server_updated
- Fix first-ever DM: broadcast dm_channel_created to recipient
- Add DELETE /api/dm/:id for closing DMs with re-open support
- Wire Close DM button in sidebar
- Fix dm_message_created for unknown channels (safety net)
- Broadcast friend_removed on friend deletion
- Sound system: track realtimeMessageEvents separately from API loads,
  play notification for messages in all channels, not just current
- Optimistic updates: send/edit/delete messages appear instantly with
  rollback on failure, temp message deduplication on WS echo
- Channel CRUD broadcasts to all server members
- Server update broadcast on PATCH
- Server join registers user in connectionManager immediately
- Reconnect: only reload current channel, preserve other channel caches
- Activity panel, right panel, member list toggle components
2026-02-22 22:15:11 +01:00
Jannis Braun 95217f0343 Overhaul audio architecture for robust cross-browser reliability 2026-02-19 22:13:48 +01:00
Jannis Braun 5ba64d2aea Fix double audio, Chrome reload silence, and restore input gain functionality 2026-02-19 19:43:41 +01:00
Jannis Braun 0da4a530d6 feat: voice status visibility + sidebar persistence fixes
- Add WebSocket voice_status/voice_status_update events so mute/deafen
  icons are visible in the sidebar without joining the voice channel
- Server tracks voiceUserStates and includes them in the ready payload
- Re-register voice channel on WebSocket reconnect to prevent sidebar
  users from disappearing after idle timeout
- Re-broadcast deafen state to late joiners via LiveKit data channel
- Fix black grid tile when video stops (enabled-flag guards)
- Remove duplicate mute/deafen from VoiceControls (replaced with
  Video Quality + Noise Suppression)
- Fix missing users in sidebar voice list (identity matching + fallback)
2026-02-19 07:58:50 +01:00
Jannis Braun 503e483b82 feat: Unified floating bottom panel — voice controls + user area in one card
Restructure VoiceControls and UserAreaPanel into a single fixed-positioned
floating card that spans both server and channel sidebars. Panel expands
when voice is connected and contracts to just user area when not.
2026-02-19 07:10:31 +01:00
Jannis Braun 1f813395e2 feat: Discord Dark theme overhaul — darker palette, floating controls, UI polish
Shift entire color palette from Discord's Ash theme to the darker Dark theme.
Redesign VoiceControlBar as floating auto-show pill, add gradient Join Voice
screen, move invite icon to server header, add voice channel user status
badges, update auth pages with subtle gradient backgrounds, and sweep all
hardcoded hex colors across 14 files to match the new darker palette.
2026-02-19 06:49:50 +01:00
Jannis Braun 7ae3e8c687 feat: Optimize WebRTC pipeline for 60fps screen sharing
- Implemented 'Overdrive' logic to force high bitrates on Chrome
- Fixed 'Auto' preset to default to stable 720p60
- Added persistent 'Triple-Kick' hammer to prevent bitrate throttling
- Fixed sidebar connection status sync
- Added comprehensive diagnostic logger
2026-02-19 03:34:20 +01:00
Jannis Braun 435d12e5b8 feat: unread indicators + DM bug fixes + data-driven isDmChannel
- Fix stale message cache: add force param to loadMessages, clearAllMessages action
- Fix reload race condition: URL-based isDmChannel fallback before WS ready
- Add read_states DB table for persistent unread tracking
- Add channel_ack WS event (client→server→echo) with BigInt comparison
- Wire up unread state in chatStore (readStates, unreadChannels, ackChannel)
- Auto-ack channels on MessageList view (200ms debounced)
- Unread pill indicators on server icons in ServerSidebar
- Bold text + white dot on unread channels/DMs in ChannelSidebar
- Replace all showDms reads with data-driven isDmChannel() across 8 files
- Design system, UI polish, and component fixes from previous sessions
2026-02-18 20:48:48 +01:00
Jannis Braun 7168149d98 feat: DM infrastructure overhaul + volume sliders + LiveKit dynamic URL
- Add DM edit/delete endpoints (REST + WebSocket)
- Add DM typing indicators with real-time broadcast
- Fix volume sliders to control LiveKit mic/speaker
- Fix Send Message button on user profile popout
- Add New DM modal with user search
- Fix message pagination (SQL cursor instead of in-memory)
- Guard optional attachments on DM messages
- Dynamic LiveKit URL from request Host header
- DM sidebar auto-sorts by most recent message
2026-02-18 17:29:39 +01:00
Jannis Braun 4f65ea9c86 fix: resolve LiveKit voice/video stability issues
- Guard Disconnected/ConnectionStateChanged event handlers against stale rooms:
  old room events no longer nuke the new room's state (root cause of buttons
  failing, mute getting stuck, DUPLICATE_IDENTITY cascades)
- Reset media state (isMuted/isCameraOn/isScreenSharing) on connect to prevent
  desync after reconnects
- Add voiceStates to WS ready payload so users see who's in voice on page load
- Wire VoiceControls buttons to check getActiveRoom() before SDK calls
- Guard ChannelSidebar against re-joining the same voice channel
- Switch LIVEKIT_URL to wss://nova.ddns.net/livekit for HTTPS secure context
  (required for getUserMedia in Safari)
2026-02-18 07:52:25 +01:00
Jannis Braun 5ef502f2e3 fix: repair invite links, social features, messaging + Discord UI overhaul
Phase 1 - Feature Repair:
- Fix member kick/leave: add missing db.delete() call in servers.ts
- Stabilize invite codes: return existing code instead of regenerating
- Fix user search: use LIKE instead of exact match in social.ts
- Wire DM button on FriendsPage to create/navigate to DM channels
- Add cancel outgoing friend request (DELETE endpoint + frontend)
- Add accept/decline friend request actions with WS real-time events
- Fix replyToId persistence in message creation
- Hydrate reactions and replyTo in message queries
- Add joinByCode to API client and serverStore
- Add friend_request_received/accepted WebSocket events

Phase 2 - Discord UI Overhaul:
- Remove stray borders between layout columns
- Replace shadow-sm with shadow-header on content headers
- Replace all bg-gray-*/text-gray-* with Discord color tokens
- Ensure flat color contrast (#1E1F22, #2B2D31, #313338)

Testing:
- Set up vitest + @testing-library/react + jsdom
- Add 17 tests across InviteModal, JoinServer, FriendsPage (all passing)
- Fix vite resolve.extensions to prefer .tsx over stale .js files
2026-02-18 05:34:45 +01:00
Jannis Braun 4fd17084a5 chore: Initial commit of Opencord base state 2026-02-18 02:49:21 +01:00