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
+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.