feat: add Backspace design prototype and rebrand from Opencord

- Add Backspace-design-prototype.html: finalized "Aether Drift" design
  language with warm matte surfaces and subtle frosted glass accents
- Update CLAUDE.md with DESIGN SYSTEM section and remove Discord clone references
- Rename all Opencord references to Backspace across the full codebase
- Archive outdated design experiments and Discord-specific assets
- Add science-backed accessibility fallback (prefers-reduced-transparency)
This commit is contained in:
Jannis Braun
2026-03-01 21:26:57 +01:00
parent aeb38a714a
commit e219229b63
58 changed files with 547 additions and 239 deletions
+2 -2
View File
@@ -1,5 +1,5 @@
appId: com.opencord.desktop
productName: Opencord
appId: com.backspace.desktop
productName: Backspace
directories:
output: dist-electron
files:
+1 -1
View File
@@ -1,5 +1,5 @@
{
"name": "@opencord/desktop",
"name": "@backspace/desktop",
"version": "1.0.0",
"private": true,
"main": "dist/main.js",
+5 -5
View File
@@ -14,7 +14,7 @@ let mainWindow: BrowserWindow | null = null;
let tray: Tray | null = null;
let isQuitting = false;
const SERVER_URL = process.env.OPENCORD_URL || 'http://localhost:3000';
const SERVER_URL = process.env.BACKSPACE_URL || 'http://localhost:3000';
function createWindow(): void {
mainWindow = new BrowserWindow({
@@ -22,7 +22,7 @@ function createWindow(): void {
height: 800,
minWidth: 940,
minHeight: 500,
title: 'Opencord',
title: 'Backspace',
titleBarStyle: process.platform === 'darwin' ? 'hiddenInset' : 'default',
backgroundColor: '#313338',
show: false,
@@ -61,7 +61,7 @@ function createWindow(): void {
}
function createTray(): void {
// Create a 16x16 tray icon (blue circle for Opencord)
// Create a 16x16 tray icon (blue circle for Backspace)
const size = 16;
const canvas = Buffer.alloc(size * size * 4);
const cx = size / 2;
@@ -89,7 +89,7 @@ function createTray(): void {
const contextMenu = Menu.buildFromTemplate([
{
label: 'Show Opencord',
label: 'Show Backspace',
click: () => {
mainWindow?.show();
mainWindow?.focus();
@@ -111,7 +111,7 @@ function createTray(): void {
},
]);
tray.setToolTip('Opencord');
tray.setToolTip('Backspace');
tray.setContextMenu(contextMenu);
tray.on('click', () => {
+1 -1
View File
@@ -1,6 +1,6 @@
import { contextBridge, ipcRenderer } from 'electron';
contextBridge.exposeInMainWorld('opencord', {
contextBridge.exposeInMainWorld('backspace', {
platform: process.platform,
showNotification: (title: string, body: string) => {
ipcRenderer.send('show-notification', { title, body });
+2 -2
View File
@@ -1,5 +1,5 @@
{
"name": "@opencord/server",
"name": "@backspace/server",
"version": "1.0.0",
"private": true,
"type": "module",
@@ -15,7 +15,7 @@
"@fastify/rate-limit": "^9.1.0",
"@fastify/static": "^7.0.4",
"@fastify/websocket": "^10.0.1",
"@opencord/shared": "workspace:*",
"@backspace/shared": "workspace:*",
"bcryptjs": "^2.4.3",
"better-sqlite3": "^11.3.0",
"cheerio": "^1.0.0",
+1 -1
View File
@@ -47,7 +47,7 @@ export const config = {
},
uploadDir: env('UPLOAD_DIR', resolve(__dirname, '../../../data/uploads')),
dbPath: env('DB_PATH', resolve(__dirname, '../../../data/opencord.db')),
dbPath: env('DB_PATH', resolve(__dirname, '../../../data/backspace.db')),
maxUploadSize: envInt('MAX_UPLOAD_SIZE', 104857600),
registrationOpen: envBool('REGISTRATION_OPEN', true),
} as const;
+7 -4
View File
@@ -1,5 +1,5 @@
import Database from 'better-sqlite3';
import { DEFAULT_EVERYONE_PERMISSIONS, PermissionBits, ALL_PERMISSIONS, permissionsToString } from '@opencord/shared/src/permissions.js';
import { DEFAULT_EVERYONE_PERMISSIONS, PermissionBits, ALL_PERMISSIONS, permissionsToString } from '@backspace/shared/src/permissions.js';
export function runMigrations(db: Database.Database): void {
console.log('Checking for database migrations...');
@@ -137,9 +137,12 @@ function migrateEveryoneRoles(db: Database.Database): void {
}
// Migrate existing admin members: ensure an Admin role exists and assign it
const adminMembers = db.prepare(
"SELECT server_id, user_id FROM server_members WHERE role = 'admin'"
).all() as { server_id: string; user_id: string }[];
// Only run if the old `role` column still exists on server_members
const smColumns = db.pragma('table_info(server_members)') as { name: string }[];
const hasRoleColumn = smColumns.some(c => c.name === 'role');
const adminMembers = hasRoleColumn
? db.prepare("SELECT server_id, user_id FROM server_members WHERE role = 'admin'").all() as { server_id: string; user_id: string }[]
: [];
if (adminMembers.length > 0) {
// Group by server
+4 -4
View File
@@ -2,7 +2,7 @@ import { getDb, schema } from './index.js';
import { generateSnowflake } from '../utils/snowflake.js';
import { hashPassword } from '../utils/auth.js';
import { eq } from 'drizzle-orm';
import { DEFAULT_EVERYONE_PERMISSIONS, permissionsToString } from '@opencord/shared/src/permissions.js';
import { DEFAULT_EVERYONE_PERMISSIONS, permissionsToString } from '@backspace/shared/src/permissions.js';
export async function seedDatabase(): Promise<void> {
const db = getDb();
@@ -31,9 +31,9 @@ export async function seedDatabase(): Promise<void> {
const serverId = generateSnowflake();
db.insert(schema.servers).values({
id: serverId,
name: 'Opencord',
name: 'Backspace',
ownerId: adminId,
inviteCode: 'opencord',
inviteCode: 'backspace',
createdAt: Date.now(),
}).run();
@@ -76,6 +76,6 @@ export async function seedDatabase(): Promise<void> {
}).run();
console.log('Database seeded successfully');
console.log(` Default server: Opencord (invite code: opencord)`);
console.log(` Default server: Backspace (invite code: backspace)`);
console.log(` Admin user: admin / admin123`);
}
+1 -1
View File
@@ -93,7 +93,7 @@ async function main(): Promise<void> {
try {
await app.listen({ port: config.port, host: config.host });
console.log(`Opencord server running at http://${config.host}:${config.port}`);
console.log(`Backspace server running at http://${config.host}:${config.port}`);
} catch (err) {
app.log.error(err);
process.exit(1);
+1 -1
View File
@@ -4,7 +4,7 @@ import { getDb, schema } from '../db/index.js';
import { hashPassword, verifyPassword, signJwt } from '../utils/auth.js';
import { generateSnowflake } from '../utils/snowflake.js';
import { config } from '../config.js';
import type { RegisterRequest, LoginRequest, AuthResponse, User } from '@opencord/shared';
import type { RegisterRequest, LoginRequest, AuthResponse, User } from '@backspace/shared';
function sanitizeUser(row: typeof schema.users.$inferSelect): User {
return {
+2 -2
View File
@@ -4,13 +4,13 @@ import { getDb, schema } from '../db/index.js';
import { authenticate } from '../utils/auth.js';
import { generateSnowflake } from '../utils/snowflake.js';
import { isMember, hasPermission, getChannelServerId, PermissionBits, computePermissions } from '../utils/permissions.js';
import { permissionsToString } from '@opencord/shared/src/permissions.js';
import { permissionsToString } from '@backspace/shared/src/permissions.js';
import { connectionManager } from '../ws/handler.js';
import type {
CreateChannelRequest,
UpdateChannelRequest,
Channel,
} from '@opencord/shared';
} from '@backspace/shared';
function rowToChannel(row: typeof schema.channels.$inferSelect): Channel {
return {
+1 -1
View File
@@ -16,7 +16,7 @@ import type {
PaginatedQuery,
Attachment,
Reaction,
} from '@opencord/shared';
} from '@backspace/shared';
function sanitizeUser(row: typeof schema.users.$inferSelect): User {
return {
+1 -1
View File
@@ -3,7 +3,7 @@ import { AccessToken } from 'livekit-server-sdk';
import { authenticate } from '../utils/auth.js';
import { config } from '../config.js';
import { getChannelServerId, hasPermission, isDmMember, PermissionBits } from '../utils/permissions.js';
import type { LiveKitTokenRequest, LiveKitTokenResponse } from '@opencord/shared';
import type { LiveKitTokenRequest, LiveKitTokenResponse } from '@backspace/shared';
export async function livekitRoutes(app: FastifyInstance): Promise<void> {
app.post<{ Body: LiveKitTokenRequest & { dmChannelId?: string } }>('/api/livekit/token', {
+1 -1
View File
@@ -12,7 +12,7 @@ import type {
User,
MessageWithUser,
Reaction,
} from '@opencord/shared';
} from '@backspace/shared';
function sanitizeUser(row: typeof schema.users.$inferSelect): User {
return {
+2 -2
View File
@@ -4,7 +4,7 @@ import { getDb, schema } from '../db/index.js';
import { authenticate } from '../utils/auth.js';
import { generateSnowflake } from '../utils/snowflake.js';
import { isMember, isServerOwner, hasPermission, computePermissions, PermissionBits } from '../utils/permissions.js';
import { DEFAULT_EVERYONE_PERMISSIONS, ALL_PERMISSIONS, permissionsToString } from '@opencord/shared/src/permissions.js';
import { DEFAULT_EVERYONE_PERMISSIONS, ALL_PERMISSIONS, permissionsToString } from '@backspace/shared/src/permissions.js';
import crypto from 'crypto';
import { connectionManager } from '../ws/handler.js';
import type {
@@ -18,7 +18,7 @@ import type {
MemberWithUser,
ServerWithChannelsAndMembers,
Role,
} from '@opencord/shared';
} from '@backspace/shared';
function sanitizeUser(row: typeof schema.users.$inferSelect): User {
return {
+1 -1
View File
@@ -2,7 +2,7 @@ import type { FastifyInstance } from 'fastify';
import { eq } from 'drizzle-orm';
import { getDb, schema } from '../db/index.js';
import { authenticate } from '../utils/auth.js';
import type { InstanceStreamingLimits } from '@opencord/shared';
import type { InstanceStreamingLimits } from '@backspace/shared';
const VALID_RESOLUTIONS = [540, 720, 1080];
const VALID_FRAMERATES = [30, 45, 60];
+1 -1
View File
@@ -10,7 +10,7 @@ import type {
FriendRequest,
SendFriendRequest,
UpdateFriendRequest,
} from '@opencord/shared';
} from '@backspace/shared';
function sanitizeUser(row: typeof schema.users.$inferSelect): User {
return {
+1 -1
View File
@@ -7,7 +7,7 @@ import { eq } from 'drizzle-orm';
import fs from 'fs';
import path from 'path';
import { pipeline } from 'stream/promises';
import type { Attachment } from '@opencord/shared';
import type { Attachment } from '@backspace/shared';
export async function uploadRoutes(app: FastifyInstance): Promise<void> {
// Ensure upload directory exists
+1 -1
View File
@@ -3,7 +3,7 @@ import { eq } from 'drizzle-orm';
import { getDb, schema } from '../db/index.js';
import { authenticate } from '../utils/auth.js';
import { connectionManager } from '../ws/handler.js';
import type { User, UpdateUserRequest } from '@opencord/shared';
import type { User, UpdateUserRequest } from '@backspace/shared';
function sanitizeUser(row: typeof schema.users.$inferSelect): User {
return {
+1 -1
View File
@@ -15,7 +15,7 @@ export async function utilRoutes(app: FastifyInstance): Promise<void> {
try {
const response = await fetch(url, {
headers: {
'User-Agent': 'OpencordBot/1.0',
'User-Agent': 'BackspaceBot/1.0',
},
});
+1 -1
View File
@@ -5,7 +5,7 @@ import {
ALL_PERMISSIONS,
stringToPermissions,
permissionsToString,
} from '@opencord/shared/src/permissions.js';
} from '@backspace/shared/src/permissions.js';
// Re-export for convenience
export { PermissionBits, ALL_PERMISSIONS, permissionsToString, stringToPermissions };
+1 -1
View File
@@ -5,7 +5,7 @@ import { connectionManager } from './handler.js';
import type { VoiceRoom, DmRoomMeta, ServerRoomMeta } from './handler.js';
import { isMember, getChannelServerId, isDmMember, hasPermission, PermissionBits } from '../utils/permissions.js';
import { broadcastDmMessage, getDmMessageWithUser } from '../routes/dm.js';
import type { User, MessageWithUser, Attachment, DmMessageWithUser } from '@opencord/shared';
import type { User, MessageWithUser, Attachment, DmMessageWithUser } from '@backspace/shared';
function sanitizeUser(row: typeof schema.users.$inferSelect): User {
return {
+1 -1
View File
@@ -15,7 +15,7 @@ import type {
ServerFolder,
ReadState,
ActiveCallInfo,
} from '@opencord/shared';
} from '@backspace/shared';
// SQLite's SQLITE_MAX_VARIABLE_NUMBER default is 999.
// Chunk inArray() calls to stay safely under this limit.
+1 -1
View File
@@ -1,5 +1,5 @@
{
"name": "@opencord/shared",
"name": "@backspace/shared",
"version": "1.0.0",
"private": true,
"type": "module",
+1 -1
View File
@@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Opencord</title>
<title>Backspace</title>
</head>
<body class="bg-discord-bg-primary text-discord-text-primary">
<div id="root"></div>
+2 -2
View File
@@ -1,5 +1,5 @@
{
"name": "@opencord/web",
"name": "@backspace/web",
"version": "1.0.0",
"private": true,
"type": "module",
@@ -10,7 +10,7 @@
},
"dependencies": {
"@livekit/components-react": "^2.7.4",
"@opencord/shared": "workspace:*",
"@backspace/shared": "workspace:*",
"@sapphi-red/web-noise-suppressor": "^0.3.5",
"livekit-client": "^2.9.0",
"prism-react-renderer": "^2.4.1",
+2 -2
View File
@@ -26,12 +26,12 @@ import type {
Friend,
FriendRequest,
InstanceStreamingLimits,
} from '@opencord/shared';
} from '@backspace/shared';
const BASE_URL = '/api';
function getToken(): string | null {
return localStorage.getItem('opencord_token');
return localStorage.getItem('backspace_token');
}
async function request<T>(
+1 -1
View File
@@ -22,7 +22,7 @@ export function Embed({ url }: EmbedProps) {
// Simple fetch from our new API
fetch(`/api/utils/metadata?url=${encodeURIComponent(url)}`, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('opencord_token')}`
'Authorization': `Bearer ${localStorage.getItem('backspace_token')}`
}
})
.then(res => res.json())
@@ -5,7 +5,7 @@ import { MemoryRouter } from 'react-router-dom';
import { FriendsPage } from './FriendsPage';
import { useSocialStore } from '../../stores/socialStore';
import { useServerStore } from '../../stores/serverStore';
import type { Friend, FriendRequest } from '@opencord/shared';
import type { Friend, FriendRequest } from '@backspace/shared';
// Mock the api module
vi.mock('../../api/client', () => ({
@@ -6,7 +6,7 @@ import { Avatar } from '../ui/Avatar';
import { MemberListToggleButton } from '../layout/MemberListToggleButton';
import { LoadingSpinner } from '../ui/LoadingSpinner';
import { api } from '../../api/client';
import type { Friend, FriendRequest } from '@opencord/shared';
import type { Friend, FriendRequest } from '@backspace/shared';
type Tab = 'online' | 'all' | 'pending' | 'add';
@@ -143,7 +143,7 @@ export function FriendsPage() {
return (
<div className="flex-1 p-8">
<h2 className="text-base font-bold text-discord-text-primary uppercase mb-2">Add Friend</h2>
<p className="text-sm text-discord-text-muted mb-4">You can add friends with their Opencord username.</p>
<p className="text-sm text-discord-text-muted mb-4">You can add friends with their Backspace username.</p>
<form onSubmit={handleAddFriend} className="relative mb-8">
<input
type="text"
@@ -1,5 +1,5 @@
import React, { useMemo, useRef, useEffect } from 'react';
import type { MemberWithUser } from '@opencord/shared';
import type { MemberWithUser } from '@backspace/shared';
import { Avatar } from '../ui/Avatar';
import { useServerStore } from '../../stores/serverStore';
+1 -1
View File
@@ -1,5 +1,5 @@
import React, { useState } from 'react';
import type { MessageWithUser } from '@opencord/shared';
import type { MessageWithUser } from '@backspace/shared';
import { MarkdownRenderer } from './MarkdownRenderer';
import { Avatar } from '../ui/Avatar';
import { ContextMenu } from '../ui/ContextMenu';
@@ -4,7 +4,7 @@ import { isDmChannel, useServerStore } from '../../stores/serverStore';
import { wsSend } from '../../hooks/useWebSocket';
import { api } from '../../api/client';
import { MentionPopover } from './MentionPopover';
import type { MemberWithUser } from '@opencord/shared';
import type { MemberWithUser } from '@backspace/shared';
interface MessageInputProps {
channelId: string;
@@ -6,7 +6,7 @@ import { useAuthStore } from '../../stores/authStore';
import { useSocialStore } from '../../stores/socialStore';
import { Avatar } from '../ui/Avatar';
import { LoadingSpinner } from '../ui/LoadingSpinner';
import type { MessageWithUser } from '@opencord/shared';
import type { MessageWithUser } from '@backspace/shared';
const EMPTY_MESSAGES: MessageWithUser[] = [];
@@ -2,7 +2,7 @@ import React, { useEffect, useMemo } from 'react';
import { useSocialStore } from '../../stores/socialStore';
import { useUIStore } from '../../stores/uiStore';
import { Avatar } from '../ui/Avatar';
import type { Friend } from '@opencord/shared';
import type { Friend } from '@backspace/shared';
export function ActivityPanel() {
const friends = useSocialStore((s) => s.friends);
@@ -53,12 +53,12 @@ export function AppLayout() {
// MutationObserver: neutralize rogue LiveKit <audio> elements that bypass our Web Audio pipeline.
// LiveKit can re-attach hidden <audio> elements after .detach(), causing full-volume playback
// that ignores our volume/mute controls. Any <audio> without data-opencord is immediately killed.
// that ignores our volume/mute controls. Any <audio> without data-backspace is immediately killed.
useEffect(() => {
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
mutation.addedNodes.forEach((node) => {
if (node instanceof HTMLAudioElement && !node.dataset.opencord) {
if (node instanceof HTMLAudioElement && !node.dataset.backspace) {
node.muted = true;
node.volume = 0;
node.pause();
@@ -210,7 +210,7 @@ export function AppLayout() {
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
</svg>
<p className="text-discord-text-muted">Loading Opencord...</p>
<p className="text-discord-text-muted">Loading Backspace...</p>
</div>
</div>
);
@@ -1,5 +1,5 @@
import React, { useMemo } from 'react';
import type { MemberWithUser } from '@opencord/shared';
import type { MemberWithUser } from '@backspace/shared';
import { useServerStore } from '../../stores/serverStore';
import { useUIStore } from '../../stores/uiStore';
import { Avatar } from '../ui/Avatar';
@@ -4,7 +4,7 @@ import { Avatar } from '../ui/Avatar';
import { useUIStore } from '../../stores/uiStore';
import { useServerStore } from '../../stores/serverStore';
import { api } from '../../api/client';
import type { User } from '@opencord/shared';
import type { User } from '@backspace/shared';
export function AddDmMemberModal() {
const [query, setQuery] = useState('');
@@ -5,7 +5,7 @@ import { Avatar } from '../ui/Avatar';
import { useUIStore } from '../../stores/uiStore';
import { useServerStore } from '../../stores/serverStore';
import { api } from '../../api/client';
import type { User } from '@opencord/shared';
import type { User } from '@backspace/shared';
export function NewDmModal() {
const [query, setQuery] = useState('');
@@ -8,7 +8,7 @@ import { Avatar } from '../ui/Avatar';
import { api } from '../../api/client';
import { useNavigate } from 'react-router-dom';
import { hasPermissionBit, PermissionBits } from '../../utils/permissions';
import type { InstanceStreamingLimits } from '@opencord/shared';
import type { InstanceStreamingLimits } from '@backspace/shared';
const VALID_RESOLUTIONS = [540, 720, 1080] as const;
const VALID_FRAMERATES = [30, 45, 60] as const;
+1 -1
View File
@@ -1,5 +1,5 @@
import React from 'react';
import type { User } from '@opencord/shared';
import type { User } from '@backspace/shared';
import { useUIStore } from '../../stores/uiStore';
interface AvatarProps {
@@ -1,6 +1,6 @@
import React from 'react';
import { useNavigate } from 'react-router-dom';
import type { User } from '@opencord/shared';
import type { User } from '@backspace/shared';
import { Avatar } from '../ui/Avatar';
import { api } from '../../api/client';
import { useServerStore } from '../../stores/serverStore';
@@ -60,7 +60,7 @@ export function UserProfilePopout({ user, onClose, position }: UserProfilePopout
<div className="w-full h-[1px] bg-discord-modifier-accent mb-3" />
<div className="mb-3">
<div className="text-[12px] font-bold text-discord-text-header uppercase mb-1">Opencord Member Since</div>
<div className="text-[12px] font-bold text-discord-text-header uppercase mb-1">Backspace Member Since</div>
<div className="text-[12px] text-discord-text-normal font-medium">
{new Date(user.createdAt).toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' })}
</div>
@@ -49,7 +49,7 @@ function AudioTrackElement({
// The <audio> element is always muted — it serves only as a Chrome
// keep-alive so Chrome continues processing the WebRTC track.
// Real audio output goes through the Web Audio pipeline.
return <audio ref={audioRef} autoPlay playsInline data-opencord="keepalive" />;
return <audio ref={audioRef} autoPlay playsInline data-backspace="keepalive" />;
}
/**
+1 -1
View File
@@ -5,7 +5,7 @@ import { useChatStore } from '../stores/chatStore';
import { useVoiceStore } from '../stores/voiceStore';
import { useSocialStore } from '../stores/socialStore';
import { useSettingsStore } from '../stores/settingsStore';
import type { ServerEvent, ClientEvent, ActiveCallInfo } from '@opencord/shared';
import type { ServerEvent, ClientEvent, ActiveCallInfo } from '@backspace/shared';
let globalWs: WebSocket | null = null;
let reconnectAttempts = 0;
+6 -6
View File
@@ -1,5 +1,5 @@
import { create } from 'zustand';
import type { User } from '@opencord/shared';
import type { User } from '@backspace/shared';
import { api } from '../api/client';
import { useChatStore } from './chatStore';
import { useServerStore } from './serverStore';
@@ -21,7 +21,7 @@ interface AuthState {
}
export const useAuthStore = create<AuthState>((set, get) => ({
token: localStorage.getItem('opencord_token'),
token: localStorage.getItem('backspace_token'),
user: null,
isLoading: false,
error: null,
@@ -30,7 +30,7 @@ export const useAuthStore = create<AuthState>((set, get) => ({
set({ isLoading: true, error: null });
try {
const response = await api.auth.login({ username, password });
localStorage.setItem('opencord_token', response.token);
localStorage.setItem('backspace_token', response.token);
set({ token: response.token, user: response.user, isLoading: false });
} catch (err) {
set({ isLoading: false, error: err instanceof Error ? err.message : 'Login failed' });
@@ -42,7 +42,7 @@ export const useAuthStore = create<AuthState>((set, get) => ({
set({ isLoading: true, error: null });
try {
const response = await api.auth.register({ username, password, displayName });
localStorage.setItem('opencord_token', response.token);
localStorage.setItem('backspace_token', response.token);
set({ token: response.token, user: response.user, isLoading: false });
} catch (err) {
set({ isLoading: false, error: err instanceof Error ? err.message : 'Registration failed' });
@@ -51,7 +51,7 @@ export const useAuthStore = create<AuthState>((set, get) => ({
},
logout: () => {
localStorage.removeItem('opencord_token');
localStorage.removeItem('backspace_token');
// Clear all user-scoped state to prevent data leaking between sessions
useChatStore.getState().clearAllMessages();
useServerStore.getState().populateFromReady([], [], []);
@@ -69,7 +69,7 @@ export const useAuthStore = create<AuthState>((set, get) => ({
const user = await api.users.me();
set({ user, isLoading: false });
} catch {
localStorage.removeItem('opencord_token');
localStorage.removeItem('backspace_token');
set({ token: null, user: null, isLoading: false });
}
},
+1 -1
View File
@@ -1,5 +1,5 @@
import { create } from 'zustand';
import type { MessageWithUser, Reaction, ReadState } from '@opencord/shared';
import type { MessageWithUser, Reaction, ReadState } from '@backspace/shared';
import { api } from '../api/client';
import { wsSend } from '../hooks/useWebSocket';
import { isDmChannel, useServerStore } from './serverStore';
+1 -1
View File
@@ -1,5 +1,5 @@
import { create } from 'zustand';
import type { Server, Channel, MemberWithUser, ServerWithChannelsAndMembers, Role, ServerFolder, DmChannel, User } from '@opencord/shared';
import type { Server, Channel, MemberWithUser, ServerWithChannelsAndMembers, Role, ServerFolder, DmChannel, User } from '@backspace/shared';
import { api } from '../api/client';
interface ServerState {
+1 -1
View File
@@ -1,5 +1,5 @@
import { create } from 'zustand';
import type { InstanceStreamingLimits } from '@opencord/shared';
import type { InstanceStreamingLimits } from '@backspace/shared';
import { api } from '../api/client';
interface SettingsState {
+1 -1
View File
@@ -1,5 +1,5 @@
import { create } from 'zustand';
import type { Friend, FriendRequest, User } from '@opencord/shared';
import type { Friend, FriendRequest, User } from '@backspace/shared';
import { api } from '../api/client';
interface SocialState {
+2 -2
View File
@@ -1,6 +1,6 @@
import { create } from 'zustand';
import { persist, createJSONStorage } from 'zustand/middleware';
import type { User } from '@opencord/shared';
import type { User } from '@backspace/shared';
type ModalType =
| 'createServer'
@@ -100,7 +100,7 @@ export const useUIStore = create<UIState>()(
setPipCollapsed: (collapsed) => set({ pipCollapsed: collapsed }),
}),
{
name: 'opencord-ui-settings',
name: 'backspace-ui-settings',
storage: createJSONStorage(() => localStorage),
partialize: (state) => ({
memberListOpen: state.memberListOpen,
+1 -1
View File
@@ -323,7 +323,7 @@ export const useVoiceStore = create<VoiceState>()(
}),
}),
{
name: 'opencord-voice-settings',
name: 'backspace-voice-settings',
version: 6,
migrate: (persistedState: any, version: number) => {
if (version === 0) {
+1 -1
View File
@@ -8,4 +8,4 @@ export {
hasPermissionBit,
permissionsToString,
stringToPermissions,
} from '@opencord/shared/src/permissions';
} from '@backspace/shared/src/permissions';