diff --git a/README.md b/README.md
index 7313f03..5850bc9 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,10 @@
-# Ditchcord
+# Zep
-Ditchcord is a self-hosted, privacy-oriented chat application built with **Svelte 5** and **SpacetimeDB**. Designed as a modern, lightweight alternative to commercial platforms, it prioritizes performance, low-latency communication, and high feature density without the bloat.
+Zep is a self-hosted, privacy-oriented chat application built with **Svelte 5** and **SpacetimeDB**. Designed as a modern, lightweight alternative to commercial platforms, it prioritizes performance, low-latency communication, and high feature density without the bloat.
## π Features
-Ditchcord offers a rich, Discord-inspired user experience backed by a highly scalable, WebAssembly-powered database architecture.
+Zep offers a rich, Discord-inspired user experience backed by a highly scalable, WebAssembly-powered database architecture.
* **Multi-Server & Channel Support:** Organize your communities with dedicated Text, Voice, and Threaded channels.
* **Blazing Fast Pagination:** Uses a sophisticated hybrid "Recent Activity Cache" to load channels instantly, with on-demand expansion for deep history scrollback.
@@ -15,7 +15,7 @@ Ditchcord offers a rich, Discord-inspired user experience backed by a highly sca
## πΊοΈ Roadmap
-We are actively developing Ditchcord. Here is what's coming next:
+We are actively developing Zep. Here is what's coming next:
* [ ] **Direct Messaging:** Private, 1-on-1 conversations outside of server contexts.
* [ ] **Mentions & Notifications:** Robust `@user` and `@role` tagging with RLS-protected notification delivery.
@@ -32,7 +32,7 @@ We are actively developing Ditchcord. Here is what's coming next:
* **Desktop:** Tauri (Planned/Configured)
### Architecture Highlights
-Ditchcord leverages **SpacetimeDB's client-side cache model** to eliminate traditional REST API bottlenecks.
+Zep leverages **SpacetimeDB's client-side cache model** to eliminate traditional REST API bottlenecks.
* **Zero-Latency Switching:** The client subscribes to a lightweight `recent_message` table for all joined servers on startup. This populates the UI instantly when switching channels.
* **Surgical Subscriptions:** The heavy `message` history table is only subscribed to on-demand when a user explicitly requests older messages, saving bandwidth and memory.
* **WASM Reducers:** All database mutations (sending messages, updating profiles) are handled by deterministic TypeScript reducers compiled to WebAssembly and executed transactionally on the server.
@@ -56,7 +56,7 @@ Ditchcord leverages **SpacetimeDB's client-side cache model** to eliminate tradi
```
3. Publish the module to your SpacetimeDB instance (e.g., `maincloud` or a local instance). *Note: The first publish creates the database.*
```bash
- spacetime publish --server maincloud ditchcord
+ spacetime publish --server maincloud zep
```
4. Generate the TypeScript bindings for the frontend:
```bash
@@ -76,7 +76,7 @@ Ditchcord leverages **SpacetimeDB's client-side cache model** to eliminate tradi
3. Configure your environment variables (create a `.env.local` file if necessary) to point to your published SpacetimeDB instance:
```env
VITE_SPACETIMEDB_HOST=wss://maincloud.spacetimedb.com
- VITE_SPACETIMEDB_DB_NAME=ditchcord
+ VITE_SPACETIMEDB_DB_NAME=zep
```
4. Run the development server:
```bash
diff --git a/index.html b/index.html
index 00285f2..4f803da 100644
--- a/index.html
+++ b/index.html
@@ -4,7 +4,7 @@
-
Ditchcord
+ Zep
diff --git a/package.json b/package.json
index c49b736..371a757 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,5 @@
{
- "name": "ditchcord",
+ "name": "zep",
"private": true,
"version": "0.0.1",
"type": "module",
diff --git a/public/vite.svg b/public/vite.svg
index e7b8dfb..7a1777e 100644
--- a/public/vite.svg
+++ b/public/vite.svg
@@ -1 +1,15 @@
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spacetimedb/package.json b/spacetimedb/package.json
index fea99e4..76ae248 100644
--- a/spacetimedb/package.json
+++ b/spacetimedb/package.json
@@ -1,5 +1,5 @@
{
- "name": "ditchcord",
+ "name": "zep",
"license": "ISC",
"type": "module",
"scripts": {
diff --git a/spacetimedb/src/index.ts b/spacetimedb/src/index.ts
index 93af439..ba4e4df 100644
--- a/spacetimedb/src/index.ts
+++ b/spacetimedb/src/index.ts
@@ -1225,7 +1225,7 @@ export const init = spacetimedb.init((ctx) => {
if (!hasServers) {
const s = ctx.db.server.insert({
id: 0n,
- name: "Ditchcord",
+ name: "Zep",
owner: undefined,
});
ctx.db.channel.insert({
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index b79314e..16d7a1c 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -713,18 +713,6 @@ dependencies = [
"syn 2.0.117",
]
-[[package]]
-name = "ditchcord"
-version = "0.1.0"
-dependencies = [
- "log",
- "serde",
- "serde_json",
- "tauri",
- "tauri-build",
- "tauri-plugin-log",
-]
-
[[package]]
name = "dlopen2"
version = "0.8.2"
@@ -5193,6 +5181,18 @@ dependencies = [
"synstructure",
]
+[[package]]
+name = "zep"
+version = "0.1.0"
+dependencies = [
+ "log",
+ "serde",
+ "serde_json",
+ "tauri",
+ "tauri-build",
+ "tauri-plugin-log",
+]
+
[[package]]
name = "zerocopy"
version = "0.8.48"
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index 09305fc..8d64d88 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -1,7 +1,7 @@
[package]
-name = "ditchcord"
+name = "zep"
version = "0.1.0"
-description = "ditchcord - self-hosted communication"
+description = "zep - self-hosted communication"
authors = ["Adam Lamers"]
license = ""
repository = ""
@@ -11,7 +11,7 @@ rust-version = "1.77.2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
-name = "ditchcord_lib"
+name = "zep_lib"
crate-type = ["staticlib", "cdylib", "rlib"]
[build-dependencies]
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
index d07f8ca..444686e 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -2,5 +2,5 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
fn main() {
- ditchcord_lib::run();
+ zep_lib::run();
}
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index 7eb2fe7..1b28d97 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -1,8 +1,8 @@
{
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
- "productName": "ditchcord",
+ "productName": "zep",
"version": "0.1.0",
- "identifier": "com.ditchcord.chat",
+ "identifier": "com.zep.chat",
"build": {
"frontendDist": "../dist",
"devUrl": "http://localhost:5173",
@@ -12,7 +12,7 @@
"app": {
"windows": [
{
- "title": "Ditchcord",
+ "title": "Zep",
"width": 800,
"height": 600,
"resizable": true,
diff --git a/src/App.css b/src/App.css
index 5a37615..d71fbf7 100644
--- a/src/App.css
+++ b/src/App.css
@@ -1,4 +1,114 @@
:root {
+ --server-sidebar-width: 72px;
+ --channel-sidebar-width: 240px;
+ --font-primary:
+ "gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ --font-display:
+ "gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+ --font-code:
+ source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
+ --status-positive: var(--status-positive);
+ --status-danger: var(--status-danger);
+ --status-warning: var(--status-warning);
+}
+
+/* Premium Midnight Purple (Amethyst) */
+.theme-amethyst, :root {
+ --background-primary: #161126;
+ --background-secondary: #0f0a1a;
+ --background-tertiary: #0a0714;
+ --background-accent: #241b3d;
+ --header-primary: #f3eeff;
+ --text-normal: #e0d1f0;
+ --text-muted: #9a8cbb;
+ --interactive-normal: #b3a5d1;
+ --interactive-hover: #ffffff;
+ --brand: #9d4edd;
+ --brand-hover: #7b2cbf;
+ --background-modifier-hover: rgba(157, 78, 221, 0.15);
+ --background-modifier-active: rgba(157, 78, 221, 0.3);
+ --background-modifier-selected: rgba(157, 78, 221, 0.3);
+ --background-floating: #0a0714;
+}
+
+/* Slate Cyan (Cyber) */
+.theme-cyan {
+ --background-primary: #121826;
+ --background-secondary: #0c111a;
+ --background-tertiary: #080c13;
+ --background-accent: #1f2937;
+ --header-primary: #f8fafc;
+ --text-normal: #cbd5e1;
+ --text-muted: #64748b;
+ --interactive-normal: #94a3b8;
+ --interactive-hover: #f1f5f9;
+ --brand: #00d2ff;
+ --brand-hover: #00a8cc;
+ --background-modifier-hover: rgba(0, 210, 255, 0.15);
+ --background-modifier-active: rgba(0, 210, 255, 0.3);
+ --background-modifier-selected: rgba(0, 210, 255, 0.3);
+ --background-floating: #080c13;
+}
+
+/* Deep Sea Green (Emerald) */
+.theme-emerald {
+ --background-primary: #061a14;
+ --background-secondary: #04120e;
+ --background-tertiary: #020a08;
+ --background-accent: #0d2e24;
+ --header-primary: #e6fffa;
+ --text-normal: #b2f5ea;
+ --text-muted: #4e9a84;
+ --interactive-normal: #81e6d9;
+ --interactive-hover: #ffffff;
+ --brand: #10b981;
+ --brand-hover: #059669;
+ --background-modifier-hover: rgba(16, 185, 129, 0.15);
+ --background-modifier-active: rgba(16, 185, 129, 0.3);
+ --background-modifier-selected: rgba(16, 185, 129, 0.3);
+ --background-floating: #020a08;
+}
+
+/* Crimson Rose (Rose) */
+.theme-rose {
+ --background-primary: #1a0a0f;
+ --background-secondary: #12060a;
+ --background-tertiary: #0a0305;
+ --background-accent: #2e0d16;
+ --header-primary: #fff5f7;
+ --text-normal: #fed7e2;
+ --text-muted: #9b4e64;
+ --interactive-normal: #fbb6ce;
+ --interactive-hover: #ffffff;
+ --brand: #f43f5e;
+ --brand-hover: #e11d48;
+ --background-modifier-hover: rgba(244, 63, 94, 0.15);
+ --background-modifier-active: rgba(244, 63, 94, 0.3);
+ --background-modifier-selected: rgba(244, 63, 94, 0.3);
+ --background-floating: #0a0305;
+}
+
+/* Amber Gold (Amber) */
+.theme-amber {
+ --background-primary: #1a1405;
+ --background-secondary: #120e03;
+ --background-tertiary: #0a0802;
+ --background-accent: #2e240d;
+ --header-primary: #fffaf0;
+ --text-normal: #fef3c7;
+ --text-muted: #9a7b4e;
+ --interactive-normal: #fcd34d;
+ --interactive-hover: #ffffff;
+ --brand: #f59e0b;
+ --brand-hover: #d97706;
+ --background-modifier-hover: rgba(245, 158, 11, 0.15);
+ --background-modifier-active: rgba(245, 158, 11, 0.3);
+ --background-modifier-selected: rgba(245, 158, 11, 0.3);
+ --background-floating: #0a0802;
+}
+
+/* Generic Dark (Standard) */
+.theme-dark {
--background-primary: #313338;
--background-secondary: #2b2d31;
--background-tertiary: #1e1f22;
@@ -10,23 +120,32 @@
--interactive-hover: #dbdee1;
--brand: #5865f2;
--brand-hover: #4752c4;
- --server-sidebar-width: 72px;
- --channel-sidebar-width: 240px;
--background-modifier-hover: rgba(78, 80, 88, 0.3);
--background-modifier-active: rgba(78, 80, 88, 0.6);
--background-modifier-selected: rgba(78, 80, 88, 0.6);
--background-floating: #1e1f22;
- --status-positive: #23a559;
- --status-danger: #f23f43;
- --status-warning: #f0b232;
- --font-primary:
- "gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
- --font-display:
- "gg sans", "Noto Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
- --font-code:
- source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
}
+/* Generic Light (Standard) */
+.theme-light {
+ --background-primary: #ffffff;
+ --background-secondary: #f2f3f5;
+ --background-tertiary: #e3e5e8;
+ --background-accent: #ebedef;
+ --header-primary: #060607;
+ --text-normal: #313338;
+ --text-muted: #5c6067;
+ --interactive-normal: #4e5058;
+ --interactive-hover: #060607;
+ --brand: #5865f2;
+ --brand-hover: #4752c4;
+ --background-modifier-hover: rgba(78, 80, 88, 0.1);
+ --background-modifier-active: rgba(78, 80, 88, 0.2);
+ --background-modifier-selected: rgba(78, 80, 88, 0.2);
+ --background-floating: #ffffff;
+}
+
+
body {
margin: 0;
font-family: var(--font-primary);
@@ -173,7 +292,7 @@ body {
}
.server-dropdown-item.danger:hover {
- background-color: #f23f43;
+ background-color: var(--status-danger);
}
.server-dropdown-item.muted {
@@ -249,13 +368,14 @@ body {
/* User Info Bar */
.user-info-bar {
- background-color: #232428;
+ background-color: var(--background-tertiary);
padding: 0 8px;
display: flex;
align-items: center;
justify-content: space-between;
height: 52px;
flex-shrink: 0;
+ border-top: 1px solid rgba(255, 255, 255, 0.05);
}
.user-info-main {
@@ -290,7 +410,7 @@ body {
}
.avatar.talking {
- border: 2px solid #23a559;
+ border: 2px solid var(--status-positive);
box-shadow: 0 0 0 2px rgba(35, 165, 89, 0.15);
}
@@ -365,7 +485,7 @@ body {
}
.avatar.talking {
- border: 2px solid #23a559;
+ border: 2px solid var(--status-positive);
box-shadow: 0 0 0 2px rgba(35, 165, 89, 0.15);
}
@@ -448,7 +568,7 @@ body {
}
.icon-btn.danger.active {
- color: #f23f43;
+ color: var(--status-danger);
}
/* Main Content Area */
@@ -903,7 +1023,7 @@ body {
}
.video-tile.talking {
- border-color: #23a559;
+ border-color: var(--status-positive);
}
.video-tile video {
@@ -1107,7 +1227,7 @@ body {
}
.sharing-badge {
- background-color: #f23f43;
+ background-color: var(--status-danger);
color: white;
font-size: 0.6rem;
padding: 1px 4px;
@@ -1152,18 +1272,18 @@ body {
}
.status-dot.green {
- background-color: #23a559;
+ background-color: var(--status-positive);
}
.status-dot.yellow {
- background-color: #f0b232;
+ background-color: var(--status-warning);
}
.status-dot.red {
- background-color: #f23f43;
+ background-color: var(--status-danger);
}
.status-dot.grey {
- background-color: #80848e;
+ background-color: var(--text-muted);
}
/* Connection Popover */
@@ -1212,13 +1332,13 @@ body {
}
.popover-status.green {
- color: #23a559;
+ color: var(--status-positive);
}
.popover-status.yellow {
- color: #f0b232;
+ color: var(--status-warning);
}
.popover-status.red {
- color: #f23f43;
+ color: var(--status-danger);
}
.popover-info {
@@ -1379,7 +1499,7 @@ body {
}
.btn-danger {
- background-color: #f23f43;
+ background-color: var(--status-danger);
color: white;
border: none;
padding: 8px 16px;
@@ -1393,7 +1513,7 @@ body {
}
.btn-danger:hover {
- background-color: #d83c3e;
+ background-color: var(--status-danger);
}
.btn-danger.small {
@@ -1463,7 +1583,7 @@ body {
}
.login-error {
- color: #fa777a;
+ color: var(--status-danger);
background-color: rgba(250, 119, 122, 0.1);
border: 1px solid rgba(250, 119, 122, 0.2);
padding: 8px;
@@ -1475,12 +1595,12 @@ body {
/* Voice Connected Bar */
.voice-status-bar {
- background-color: #232428;
+ background-color: var(--background-tertiary);
padding: 8px;
display: flex;
justify-content: space-between;
align-items: center;
- border-top: 1px solid rgba(255, 255, 255, 0.1);
+ border-top: 1px solid rgba(255, 255, 255, 0.05);
}
.voice-info {
@@ -1489,7 +1609,7 @@ body {
}
.voice-connected-text {
- color: #23a559;
+ color: var(--status-positive);
font-size: 0.8rem;
font-weight: bold;
}
@@ -1500,7 +1620,7 @@ body {
}
.screen-share-btn {
- background-color: #3f4147;
+ background-color: var(--background-accent);
color: white;
border: none;
padding: 4px 12px;
@@ -1512,15 +1632,15 @@ body {
}
.screen-share-btn:hover {
- background-color: #4f5157;
+ background-color: var(--background-modifier-hover);
}
.screen-share-btn.active {
- background-color: #f23f43;
+ background-color: var(--status-danger);
}
.screen-share-btn.active:hover {
- background-color: #d83c3e;
+ opacity: 0.9;
}
.screen-share-controls {
@@ -1564,11 +1684,12 @@ body {
.context-menu {
position: fixed;
z-index: 3000;
- background: #18191c;
+ background: var(--background-floating);
border-radius: 4px;
padding: 8px;
min-width: 180px;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.24);
+ border: 1px solid rgba(255, 255, 255, 0.05);
}
.context-menu-header {
@@ -1599,7 +1720,7 @@ body {
}
.context-menu-item:hover {
- background: var(--primary);
+ background: var(--brand);
color: white;
}
diff --git a/src/App.svelte b/src/App.svelte
index 18bba29..8c0a120 100644
--- a/src/App.svelte
+++ b/src/App.svelte
@@ -2,15 +2,22 @@
import { AuthGate } from "./auth";
import { ChatContainer } from "./chat";
import { connectionState } from "./connection.svelte";
+ import { themeService } from "./chat/services/theme.svelte";
import "./App.css";
let showServerSettings = $state(false);
+ // Apply theme class to body globally
+ $effect(() => {
+ const theme = themeService.theme;
+ document.body.className = `theme-${theme}`;
+ });
+
function handleReconnect() {
console.log("App: Reconnection requested. Reloading page...");
// If we are showing settings, we should keep showing them after reload
if (showServerSettings) {
- localStorage.setItem("ditchcord_changing_server", "true");
+ localStorage.setItem("zep_changing_server", "true");
}
window.location.reload();
}
@@ -19,9 +26,9 @@
console.log("App: Setting showServerSettings to", val);
showServerSettings = val;
if (val) {
- localStorage.setItem("ditchcord_changing_server", "true");
+ localStorage.setItem("zep_changing_server", "true");
} else {
- localStorage.removeItem("ditchcord_changing_server");
+ localStorage.removeItem("zep_changing_server");
}
}
diff --git a/src/auth/AuthGate.svelte b/src/auth/AuthGate.svelte
index 5935c57..b812a4f 100644
--- a/src/auth/AuthGate.svelte
+++ b/src/auth/AuthGate.svelte
@@ -29,7 +29,7 @@
isMaincloud = stdbHost === MAINCLOUD_URI || stdbHost === "";
if (isMaincloud) stdbHost = MAINCLOUD_URI;
- const isChanging = localStorage.getItem("ditchcord_changing_server") === "true";
+ const isChanging = localStorage.getItem("zep_changing_server") === "true";
if (isChanging) {
isSettingsExpanded = true;
userWantsToConnect = false;
@@ -95,7 +95,7 @@
- Ditchcord
+ Zep
Decentralized. Private. Fast.
@@ -183,7 +183,7 @@
id="stdb-db"
type="text"
bind:value={stdbDbName}
- placeholder="ditchcord"
+ placeholder="zep"
style="background-color: var(--background-tertiary); color: var(--text-normal); border: 1px solid var(--background-modifier-accent); border-radius: 4px; padding: 10px;"
/>
diff --git a/src/chat/components/ProfileModal.svelte b/src/chat/components/ProfileModal.svelte
index b0b817e..5785be7 100644
--- a/src/chat/components/ProfileModal.svelte
+++ b/src/chat/components/ProfileModal.svelte
@@ -147,7 +147,7 @@
}
.status-dot.green { background-color: var(--status-positive); }
- .status-dot.grey { background-color: #80848e; }
+ .status-dot.grey { background-color: var(--text-muted); }
.profile-info {
display: flex;
diff --git a/src/chat/components/ServerList.svelte b/src/chat/components/ServerList.svelte
index 8a931b2..9135d92 100644
--- a/src/chat/components/ServerList.svelte
+++ b/src/chat/components/ServerList.svelte
@@ -29,7 +29,7 @@
(chat.showDiscoveryModal = true)}
- style="color: #23a559;"
+ style="color: var(--status-positive);"
title="Discover Servers"
>
diff --git a/src/chat/components/settings/CustomizationSettings.svelte b/src/chat/components/settings/CustomizationSettings.svelte
index c8b76bf..4bd4b74 100644
--- a/src/chat/components/settings/CustomizationSettings.svelte
+++ b/src/chat/components/settings/CustomizationSettings.svelte
@@ -5,6 +5,16 @@
const chat = getContext("chat");
+ const themes = [
+ { id: "dark", name: "Dark", color: "#5865f2", bg: "#313338" },
+ { id: "light", name: "Light", color: "#5865f2", bg: "#ffffff" },
+ { id: "amethyst", name: "Amethyst", color: "#9d4edd", bg: "#0f0a1a" },
+ { id: "cyan", name: "Cyber Cyan", color: "#00d2ff", bg: "#0c111a" },
+ { id: "emerald", name: "Emerald Sea", color: "#10b981", bg: "#04120e" },
+ { id: "rose", name: "Crimson Rose", color: "#f43f5e", bg: "#12060a" },
+ { id: "amber", name: "Amber Gold", color: "#f59e0b", bg: "#120e03" },
+ ];
+
let newEmojiName = $state("");
let newEmojiFile = $state(null);
let isEmojiUploading = $state(false);
@@ -42,6 +52,39 @@
+
+
+
+ {#each themes as theme}
+
chat.ui.setTheme(theme.id)}
+ style="--theme-accent: {theme.color}; --theme-bg: {theme.bg};"
+ >
+
+ {theme.name}
+ {#if chat.ui.theme === theme.id}
+
+
+
+ {/if}
+
+ {/each}
+
+
+
+