diff --git a/src/SpacetimeProvider.svelte b/src/SpacetimeProvider.svelte index 19d77ef..aacf804 100644 --- a/src/SpacetimeProvider.svelte +++ b/src/SpacetimeProvider.svelte @@ -1,7 +1,6 @@ - -
- + + +

{message}

- - - -
+ + {/snippet} +
diff --git a/src/chat/components/Modal.svelte b/src/chat/components/Modal.svelte index faca6ac..4e749c2 100644 --- a/src/chat/components/Modal.svelte +++ b/src/chat/components/Modal.svelte @@ -1,7 +1,4 @@ (chat.showDiscoveryModal = false)} maxWidth="500px"> -
- - - {/each} {/if}
- +
diff --git a/src/chat/components/ServerSettingsPanel.svelte b/src/chat/components/ServerSettingsPanel.svelte index f578d80..afaae81 100644 --- a/src/chat/components/ServerSettingsPanel.svelte +++ b/src/chat/components/ServerSettingsPanel.svelte @@ -2,7 +2,9 @@ import { getContext } from "svelte"; import type { ChatService } from "../services/chat.svelte"; import { optimizeEmoji } from "../utils"; - import { portal } from "../../portal"; + import Button from "./ui/Button.svelte"; + import Input from "./ui/Input.svelte"; + import Switch from "./ui/Switch.svelte"; let { onClose } = $props<{ onClose: () => void }>(); @@ -111,45 +113,53 @@ {/each} - - - + - - + +
@@ -179,7 +189,7 @@ {/if}
{#if isOwner} -
-
- - -
+ -
- -
+
@@ -237,7 +234,7 @@ {/if} {/if} @@ -352,6 +354,33 @@ margin: 8px 10px; } + :global(.sidebar-item-btn) { + margin: 2px 0; + justify-content: flex-start !important; + text-align: left; + width: 100% !important; + padding: 6px 10px !important; + background: none !important; + border: none !important; + font-size: 1rem !important; + color: var(--interactive-normal) !important; + transition: all 0.1s !important; + } + + :global(.sidebar-item-btn.danger) { + color: var(--status-danger) !important; + } + + :global(.sidebar-item-btn:hover) { + background-color: var(--background-modifier-hover) !important; + color: var(--interactive-hover) !important; + } + + :global(.sidebar-item-btn.danger:hover) { + background-color: var(--status-danger) !important; + color: white !important; + } + .settings-main { flex: 1; display: flex; @@ -427,89 +456,6 @@ flex: 1; } - .form-group { - display: flex; - flex-direction: column; - gap: 8px; - } - - .form-group label { - font-size: 0.75rem; - font-weight: 700; - color: var(--text-muted); - text-transform: uppercase; - } - - .styled-input { - background-color: var(--background-tertiary); - border: 1px solid var(--background-modifier-accent); - color: var(--text-normal); - padding: 10px; - border-radius: 4px; - font-size: 1rem; - outline: none; - } - - .styled-input:focus { - border-color: var(--brand); - } - - .styled-input:disabled { - opacity: 0.6; - cursor: not-allowed; - } - - /* Toggle Switch */ - .toggle-switch { - position: relative; - display: inline-block; - width: 40px; - height: 22px; - flex-shrink: 0; - } - - .toggle-switch input { - opacity: 0; - width: 0; - height: 0; - } - - .slider { - position: absolute; - cursor: pointer; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: #72767d; - transition: .2s; - border-radius: 22px; - } - - .slider:before { - position: absolute; - content: ""; - height: 16px; - width: 16px; - left: 3px; - bottom: 3px; - background-color: white; - transition: .2s; - border-radius: 50%; - } - - input:checked + .slider { - background-color: var(--status-positive); - } - - input:focus + .slider { - box-shadow: 0 0 1px var(--status-positive); - } - - input:checked + .slider:before { - transform: translateX(18px); - } - /* Footer */ .settings-footer { position: absolute; @@ -541,35 +487,6 @@ gap: 12px; } - .btn-success { - background-color: var(--status-positive); - color: white; - border: none; - padding: 8px 20px; - border-radius: 4px; - font-weight: 600; - cursor: pointer; - transition: opacity 0.1s; - } - - .btn-success:hover:not(:disabled) { - opacity: 0.9; - } - - .btn-success:disabled { - opacity: 0.5; - cursor: not-allowed; - } - - .btn-ghost { - background: none; - border: none; - color: white; - padding: 8px 16px; - cursor: pointer; - font-weight: 500; - } - /* Custom Confirmation Modal */ .danger-overlay { background-color: rgba(0, 0, 0, 0.85); @@ -606,21 +523,6 @@ gap: 12px; } - .btn-danger { - background-color: var(--status-danger); - color: white; - border: none; - padding: 10px 24px; - border-radius: 4px; - font-weight: 600; - cursor: pointer; - transition: opacity 0.1s; - } - - .btn-danger:hover { - opacity: 0.9; - } - @keyframes slideInUp { from { transform: translateY(100%); opacity: 0; } to { transform: translateY(0); opacity: 1; } diff --git a/src/chat/components/SettingsPanel.svelte b/src/chat/components/SettingsPanel.svelte index bf147df..5581334 100644 --- a/src/chat/components/SettingsPanel.svelte +++ b/src/chat/components/SettingsPanel.svelte @@ -11,6 +11,7 @@ import AudioSettings from "./settings/AudioSettings.svelte"; import ScreenSharingSettings from "./settings/ScreenSharingSettings.svelte"; import SecuritySettings from "./settings/SecuritySettings.svelte"; + import Button from "./ui/Button.svelte"; let { onClose, currentUser }: { onClose: () => void, currentUser: Types.User | undefined } = $props(); @@ -166,19 +167,24 @@ {/each} - +
@@ -225,7 +231,7 @@ {/if}
{/if} @@ -321,21 +327,39 @@ color: var(--interactive-active); } - .sidebar-item.danger { - color: var(--status-danger); - } - - .sidebar-item.danger:hover { - background-color: var(--status-danger); - color: white; - } - .sidebar-separator { height: 1px; background-color: var(--background-modifier-accent); margin: 8px 10px; } + :global(.sidebar-item-btn) { + margin: 2px 0; + justify-content: flex-start !important; + text-align: left; + width: 100% !important; + padding: 6px 10px !important; + background: none !important; + border: none !important; + font-size: 1rem !important; + color: var(--interactive-normal) !important; + transition: all 0.1s !important; + } + + :global(.sidebar-item-btn.danger) { + color: var(--status-danger) !important; + } + + :global(.sidebar-item-btn:hover) { + background-color: var(--background-modifier-hover) !important; + color: var(--interactive-hover) !important; + } + + :global(.sidebar-item-btn.danger:hover) { + background-color: var(--status-danger) !important; + color: white !important; + } + .settings-main { flex: 1; display: flex; @@ -404,30 +428,6 @@ gap: 12px; } - .btn-success { - background-color: var(--status-positive); - color: white; - border: none; - padding: 8px 20px; - border-radius: 4px; - font-weight: 600; - cursor: pointer; - transition: opacity 0.1s; - } - - .btn-success:hover { - opacity: 0.9; - } - - .btn-ghost { - background: none; - border: none; - color: white; - padding: 8px 16px; - cursor: pointer; - font-weight: 500; - } - @keyframes slideInUp { from { transform: translateY(100%); opacity: 0; } to { transform: translateY(0); opacity: 1; } diff --git a/src/chat/components/settings/AccountSettings.svelte b/src/chat/components/settings/AccountSettings.svelte index 32e3268..19bb944 100644 --- a/src/chat/components/settings/AccountSettings.svelte +++ b/src/chat/components/settings/AccountSettings.svelte @@ -2,6 +2,8 @@ import { getContext } from "svelte"; import type { ChatService } from "../../services/chat.svelte"; import type * as Types from "../../../module_bindings/types"; + import Input from "../ui/Input.svelte"; + import Button from "../ui/Button.svelte"; let { localName = $bindable(), @@ -86,58 +88,54 @@
-
- -
- - -
+ + {/if}
-
- -
- - -
+ + {/if}
-
- - - -
+
- - + + }}>Remove Banner
@@ -269,58 +267,34 @@ margin: 0 16px 16px 16px; padding: 16px; border-radius: 8px; - } - - .form-group { display: flex; flex-direction: column; - gap: 8px; - margin-bottom: 16px; + gap: 16px; } - .form-group label { - font-size: 0.75rem; - font-weight: 700; - color: var(--text-muted); - text-transform: uppercase; - } - - .input-wrapper { + .edit-group { + position: relative; display: flex; align-items: center; - background-color: var(--background-primary); - border-radius: 4px; - padding-right: 8px; width: 100%; - box-sizing: border-box; } - .form-group input[type="text"], .form-group textarea { - padding: 10px; - background-color: var(--background-primary); - color: var(--text-normal); - border: none; - font-size: 1rem; - outline: none; - border-radius: 4px; - font-family: inherit; + :global(.clear-btn) { + position: absolute; + right: 8px; + top: 32px; /* Position it correctly relative to the label and input */ + padding: 4px !important; + font-size: 1.1rem !important; + } + + :global(.edit-group .form-group) { width: 100%; - box-sizing: border-box; + margin-bottom: 0 !important; } - .input-wrapper input[type="text"] { - flex: 1; - background-color: transparent; - } - - .form-group textarea { - resize: none; - } - - .textarea-footer { - text-align: right; - font-size: 0.7rem; - color: var(--text-muted); + :global(.bio-input textarea) { + height: 100px; + resize: none !important; } .avatar-actions { diff --git a/src/chat/components/settings/AudioSettings.svelte b/src/chat/components/settings/AudioSettings.svelte index b8c0f1c..9496d6c 100644 --- a/src/chat/components/settings/AudioSettings.svelte +++ b/src/chat/components/settings/AudioSettings.svelte @@ -2,6 +2,7 @@ import { getContext } from "svelte"; import type { WebRTCService } from "../../services/webrtc/webrtc.svelte"; import Dropdown from "../Dropdown.svelte"; + import Button from "../ui/Button.svelte"; const webrtc = getContext("webrtc"); @@ -35,13 +36,14 @@
- +
@@ -144,22 +146,6 @@ z-index: 2; } - .test-mic-btn { - background-color: var(--brand); - color: white; - border: none; - padding: 6px 12px; - border-radius: 4px; - font-size: 0.75rem; - font-weight: 600; - cursor: pointer; - transition: background-color 0.1s; - } - - .test-mic-btn.active { - background-color: var(--status-danger); - } - .threshold-slider { width: 100%; cursor: pointer; diff --git a/src/chat/components/settings/CustomizationSettings.svelte b/src/chat/components/settings/CustomizationSettings.svelte index e5cf1c4..eb0bb3b 100644 --- a/src/chat/components/settings/CustomizationSettings.svelte +++ b/src/chat/components/settings/CustomizationSettings.svelte @@ -2,6 +2,8 @@ import { getContext } from "svelte"; import type { ChatService } from "../../services/chat.svelte"; import { optimizeEmoji, getCustomEmojiUrl } from "../../utils"; + import Button from "../ui/Button.svelte"; + import Input from "../ui/Input.svelte"; const chat = getContext("chat"); @@ -138,20 +140,22 @@
-
- -
- : - - : -
+
+ + {#snippet prefix()} + : + {/snippet} + {#snippet suffix()} + : + {/snippet} +
-
+
- + Upload +
{#if emojiError}
diff --git a/src/chat/components/settings/SecuritySettings.svelte b/src/chat/components/settings/SecuritySettings.svelte index 1cb25d5..026bf5c 100644 --- a/src/chat/components/settings/SecuritySettings.svelte +++ b/src/chat/components/settings/SecuritySettings.svelte @@ -1,6 +1,8 @@ + + + + diff --git a/src/chat/components/ui/Input.svelte b/src/chat/components/ui/Input.svelte new file mode 100644 index 0000000..78b7287 --- /dev/null +++ b/src/chat/components/ui/Input.svelte @@ -0,0 +1,173 @@ + + +
+ {#if label} + + {/if} + +
+ {#if prefix} +
+ {@render prefix()} +
+ {/if} + + {#if type === "textarea"} + + {:else} + + {/if} + + {#if suffix} +
+ {@render suffix()} +
+ {/if} +
+ + {#if description && !error} +

{description}

+ {/if} + + {#if error} +

{error}

+ {/if} +
+ + diff --git a/src/chat/components/ui/ModalLayout.svelte b/src/chat/components/ui/ModalLayout.svelte new file mode 100644 index 0000000..33404fb --- /dev/null +++ b/src/chat/components/ui/ModalLayout.svelte @@ -0,0 +1,96 @@ + + + + + diff --git a/src/chat/components/ui/Switch.svelte b/src/chat/components/ui/Switch.svelte new file mode 100644 index 0000000..482afd8 --- /dev/null +++ b/src/chat/components/ui/Switch.svelte @@ -0,0 +1,123 @@ + + +
+ +
+ + diff --git a/src/chat/services/chat.svelte.ts b/src/chat/services/chat.svelte.ts index 970a7fc..1914638 100644 --- a/src/chat/services/chat.svelte.ts +++ b/src/chat/services/chat.svelte.ts @@ -1,8 +1,7 @@ import { Identity } from "spacetimedb"; -import { SvelteMap, SvelteSet } from "svelte/reactivity"; +import { SvelteSet } from "svelte/reactivity"; import * as Types from "../../module_bindings/types"; import { getUsername, formatTime } from "../utils"; -import { getConnection } from "../../config"; import { DatabaseService } from "./database.svelte"; import { NavigationService } from "./navigation.svelte"; import { ThemeService, themeService } from "./theme.svelte"; diff --git a/src/chat/services/database.svelte.ts b/src/chat/services/database.svelte.ts index 1ff94e5..de3c149 100644 --- a/src/chat/services/database.svelte.ts +++ b/src/chat/services/database.svelte.ts @@ -1,8 +1,6 @@ import { tables } from "../../module_bindings"; import { useTable } from "spacetimedb/svelte"; import * as Types from "../../module_bindings/types"; -import { getConnection } from "../../config"; -import { untrack } from "svelte"; import type { Identity } from "spacetimedb"; export class DatabaseService { @@ -75,7 +73,7 @@ export class DatabaseService { return map; }); - constructor(identity: () => Identity | null) { + constructor(_identity: () => Identity | null) { const [serversStore, serversReadyStore] = useTable(tables.visible_servers); const [channelsStore, channelsReadyStore] = useTable(tables.visible_channels); const [directMessagesStore] = useTable(tables.visible_direct_messages);