Compare commits

..

19 Commits

Author SHA1 Message Date
mplorentz b3d49f2327 Factor video code and stores out of voice.ts 2026-04-08 10:30:22 -04:00
mplorentz 3f64db0f0d Clean up VideoCallContent 2026-04-08 09:46:33 -04:00
mplorentz 8d10d1700c Simplify how video call layout is stored 2026-04-07 14:57:24 -04:00
mplorentz ff83fbd61f incorporate page layout changes from dev and other cleanup 2026-04-07 13:51:51 -04:00
mplorentz 54829d96a6 Add permissions for cameras on mobile 2026-04-07 13:51:09 -04:00
mplorentz fc3100193d Hide screen share button on ios and android 2026-04-07 13:51:09 -04:00
mplorentz 9c69f2272b Show unread indicator on chat icon in VoiceWidget 2026-04-07 13:51:09 -04:00
mplorentz 0241cd2d1d Allow clicking voice widget to go back to call 2026-04-07 13:51:09 -04:00
mplorentz 8e2fde8ae7 rework video + text chat display controls 2026-04-07 13:51:09 -04:00
mplorentz 453e4e9b22 Style pin icon more better 2026-04-07 13:50:39 -04:00
mplorentz 97aa9408b6 Style voice widget icons to be less red 2026-04-07 13:50:39 -04:00
mplorentz 2e01d4783c Add video settings to VoiceCallAudioSettingsDialog 2026-04-07 13:50:39 -04:00
mplorentz 5766826905 Fix merge artifacts 2026-04-07 13:50:39 -04:00
mplorentz bdaa8b3450 Add settings button to configure audio devices in call 2026-04-07 13:50:39 -04:00
mplorentz 2e31cf0bd5 Change screen sharing icon 2026-04-07 13:50:39 -04:00
mplorentz a9cfc9f19f Improve pinned video layout 2026-04-07 13:50:39 -04:00
mplorentz 28d867bece Add a button to spotlight a video feed 2026-04-07 13:50:24 -04:00
mplorentz ee4fa066ee Add basic screen sharing 2026-04-07 13:50:24 -04:00
mplorentz d596d8ba49 add video to livekit calls 2026-04-07 13:50:24 -04:00
72 changed files with 819 additions and 834 deletions
+3 -4
View File
@@ -22,7 +22,6 @@
"@eslint/js": "^9.39.2",
"@sveltejs/kit": "^2.50.1",
"@sveltejs/vite-plugin-svelte": "^4.0.4",
"@tailwindcss/postcss": "^4.2.2",
"@tauri-apps/cli": "^2.9.6",
"@types/eslint": "^9.6.1",
"autoprefixer": "^10.4.23",
@@ -36,7 +35,7 @@
"prettier-plugin-svelte": "^3.4.1",
"svelte": "^5.48.0",
"svelte-check": "^4.3.5",
"tailwindcss": "^4.2.2",
"tailwindcss": "^3.4.19",
"typescript": "^5.9.3",
"typescript-eslint": "^8.53.1",
"vite": "^5.4.21"
@@ -78,7 +77,7 @@
"@welshman/store": "^0.8.12",
"@welshman/util": "^0.8.12",
"compressorjs-next": "^1.1.2",
"daisyui": "^5.5.19",
"daisyui": "^4.12.24",
"date-picker-svelte": "^2.17.0",
"dotenv": "^16.6.1",
"emoji-picker-element": "^1.28.1",
@@ -88,7 +87,7 @@
"livekit-client": "^2.17.2",
"nostr-signer-capacitor-plugin": "github:coracle-social/nostr-signer-capacitor-plugin#main",
"nostr-tools": "^2.19.4",
"prettier-plugin-tailwindcss": "^0.7.2",
"prettier-plugin-tailwindcss": "^0.6.14",
"qr-scanner": "^1.4.2",
"qrcode": "^1.5.4",
"throttle-debounce": "^5.0.2",
+369 -388
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -1,5 +1,6 @@
export default {
plugins: {
"@tailwindcss/postcss": {},
tailwindcss: {},
autoprefixer: {},
},
}
+239 -247
View File
@@ -1,25 +1,45 @@
@import 'tailwindcss';
@import "@welshman/editor/index.css";
@config "../tailwind.config.js";
@tailwind base;
@tailwind components;
@tailwind utilities;
@utility pt-sai {
padding-top: var(--sait);
/* Fonts */
@font-face {
font-family: "Satoshis";
font-style: normal;
font-weight: 400;
src:
local(""),
url("/fonts/Satoshi Symbol.ttf") format("truetype");
}
@utility pr-sai {
padding-right: var(--sair);
@font-face {
font-family: "Lato";
font-style: normal;
font-weight: 400;
src:
local(""),
url("/fonts/Lato-Regular.ttf") format("truetype");
}
@utility pb-sai {
padding-bottom: var(--saib);
@font-face {
font-family: "Lato";
font-style: bold;
font-weight: 600;
src:
local(""),
url("/fonts/Lato-Bold.ttf") format("truetype");
}
@utility pl-sai {
padding-left: var(--sail);
}
@utility px-sai {
@apply pl-sai pr-sai;
@font-face {
font-family: "Lato";
font-style: italic;
font-weight: 400;
src:
local(""),
url("/fonts/Italic.ttf") format("truetype");
}
/* root */
@@ -32,224 +52,98 @@
--sair: var(--safe-area-inset-right, env(safe-area-inset-right));
}
@utility py-sai {
@apply pt-sai pb-sai;
[data-theme] {
@apply bg-base-300;
--base-100: oklch(var(--b1));
--base-200: oklch(var(--b2));
--base-300: oklch(var(--b3));
--base-content: oklch(var(--bc));
--primary: oklch(var(--p));
--primary-content: oklch(var(--pc));
--secondary: oklch(var(--s));
--secondary-content: oklch(var(--sc));
--neutral: oklch(var(--n));
--neutral-content: oklch(var(--nc));
}
@utility p-sai {
@apply py-sai px-sai;
.mobile [data-tip]::before {
display: none !important;
}
@utility mt-sai {
margin-top: var(--sait);
}
/* safe area insets */
@utility mr-sai {
margin-right: var(--sair);
}
@utility mb-sai {
margin-bottom: var(--saib);
}
@utility ml-sai {
margin-left: var(--sail);
}
@utility mx-sai {
@apply ml-sai mr-sai;
}
@utility my-sai {
@apply mt-sai mb-sai;
}
@utility m-sai {
@apply my-sai mx-sai;
}
@utility top-sai {
top: var(--sait);
}
@utility right-sai {
right: var(--sair);
}
@utility bottom-sai {
bottom: var(--saib);
}
@utility left-sai {
left: var(--sail);
}
@utility card2 {
@apply rounded-box text-base-content p-4 sm:p-6;
}
@utility column {
@apply flex flex-col;
}
@utility center {
@apply flex items-center justify-center;
}
@utility row-2 {
@apply flex items-center gap-2;
}
@utility row-3 {
@apply flex items-center gap-3;
}
@utility row-4 {
@apply flex items-center gap-4;
}
@utility col-2 {
@apply flex flex-col gap-2;
}
@utility col-3 {
@apply flex flex-col gap-3;
}
@utility col-4 {
@apply flex flex-col gap-4;
}
@utility col-8 {
@apply flex flex-col gap-8;
}
@utility ellipsize {
@apply overflow-hidden text-ellipsis;
}
@utility content-padding-x {
@apply px-4 sm:px-8 md:px-12;
}
@utility content-padding-t {
@apply pt-4 sm:pt-8 md:pt-12;
}
@utility content-padding-b {
@apply pb-4 sm:pb-8 md:pb-12;
}
@utility content-padding-y {
@apply pt-4 sm:pt-8 md:pt-12 pb-4 sm:pb-8 md:pb-12;
}
@utility content-sizing {
@apply m-auto w-full max-w-3xl;
}
@utility content {
@apply m-auto w-full max-w-3xl px-4 sm:px-8 md:px-12 pt-4 sm:pt-8 md:pt-12 pb-4 sm:pb-8 md:pb-12;
}
@utility heading {
@apply text-center text-2xl;
}
@utility subheading {
@apply text-center text-xl;
}
@utility superheading {
@apply text-center text-4xl;
}
@utility link {
@apply text-primary cursor-pointer underline;
}
/* content visibility */
@utility cv {
content-visibility: auto;
}
/*
The default border color has changed to `currentcolor` in Tailwind CSS v4,
so we've added these compatibility styles to make sure everything still
looks the same as it did with Tailwind CSS v3.
If we ever want to remove these styles, we need to add an explicit border
color utility to any element that depends on these defaults.
*/
@layer base {
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: var(--color-gray-200, currentcolor);
}
}
@layer utilities {
/* Fonts */
@font-face {
font-family: "Satoshis";
font-style: normal;
font-weight: 400;
src:
local(""),
url("/fonts/Satoshi Symbol.ttf") format("truetype");
@layer components {
.pt-sai {
padding-top: var(--sait);
}
@font-face {
font-family: "Lato";
font-style: normal;
font-weight: 400;
src:
local(""),
url("/fonts/Lato-Regular.ttf") format("truetype");
.pr-sai {
padding-right: var(--sair);
}
@font-face {
font-family: "Lato";
font-style: bold;
font-weight: 600;
src:
local(""),
url("/fonts/Lato-Bold.ttf") format("truetype");
.pb-sai {
padding-bottom: var(--saib);
}
@font-face {
font-family: "Lato";
font-style: italic;
font-weight: 400;
src:
local(""),
url("/fonts/Italic.ttf") format("truetype");
.pl-sai {
padding-left: var(--sail);
}
/* root */
:root {
font-family: Lato;
--sait: var(--safe-area-inset-top, env(safe-area-inset-top));
--saib: var(--safe-area-inset-bottom, env(safe-area-inset-bottom));
--sail: var(--safe-area-inset-left, env(safe-area-inset-left));
--sair: var(--safe-area-inset-right, env(safe-area-inset-right));
.px-sai {
@apply pl-sai pr-sai;
}
[data-theme] {
@apply bg-base-300;
.py-sai {
@apply pt-sai pb-sai;
}
.mobile [data-tip]::before {
display: none !important;
.p-sai {
@apply py-sai px-sai;
}
/* safe area insets */
.mt-sai {
margin-top: var(--sait);
}
.mr-sai {
margin-right: var(--sair);
}
.mb-sai {
margin-bottom: var(--saib);
}
.ml-sai {
margin-left: var(--sail);
}
.mx-sai {
@apply ml-sai mr-sai;
}
.my-sai {
@apply mt-sai mb-sai;
}
.m-sai {
@apply my-sai mx-sai;
}
.top-sai {
top: var(--sait);
}
.right-sai {
right: var(--sair);
}
.bottom-sai {
bottom: var(--saib);
}
.left-sai {
left: var(--sail);
}
}
/* utilities */
@@ -271,18 +165,110 @@
@apply bg-base-300 text-base-content transition-colors;
}
.card2 {
@apply rounded-box p-4 text-base-content sm:p-6;
}
.card2.card2-sm {
@apply text-base-content p-2 sm:p-4;
@apply p-2 text-base-content sm:p-4;
}
.column {
@apply flex flex-col;
}
.center {
@apply flex items-center justify-center;
}
.row-2 {
@apply flex items-center gap-2;
}
.row-3 {
@apply flex items-center gap-3;
}
.row-4 {
@apply flex items-center gap-4;
}
.col-2 {
@apply flex flex-col gap-2;
}
.col-3 {
@apply flex flex-col gap-3;
}
.col-4 {
@apply flex flex-col gap-4;
}
.col-8 {
@apply flex flex-col gap-8;
}
.badge {
@apply justify-start overflow-hidden text-ellipsis whitespace-nowrap;
}
.ellipsize {
@apply overflow-hidden text-ellipsis;
}
[data-tip]::before {
@apply overflow-hidden text-ellipsis;
@apply ellipsize;
}
.content-padding-x {
@apply px-4 sm:px-8 md:px-12;
}
.content-padding-t {
@apply pt-4 sm:pt-8 md:pt-12;
}
.content-padding-b {
@apply pb-4 sm:pb-8 md:pb-12;
}
.content-padding-y {
@apply content-padding-t content-padding-b;
}
.content-sizing {
@apply m-auto w-full max-w-3xl;
}
.content {
@apply content-sizing content-padding-x content-padding-y;
}
.heading {
@apply text-center text-2xl;
}
.subheading {
@apply text-center text-xl;
}
.superheading {
@apply text-center text-4xl;
}
.link {
@apply cursor-pointer text-primary underline;
}
.input input::placeholder {
opacity: 0.5;
}
.shadow-top-xl {
@apply shadow-[0_20px_25px_-5px_rgb(0,0,0,0.1)_0_8px_10px_-6px_rgb(0,0,0,0.1)];
}
/* tiptap */
.input-editor,
@@ -292,21 +278,21 @@
}
.tiptap {
--tiptap-object-bg: var(--color-neutral);
--tiptap-object-fg: var(--color-neutral-content);
--tiptap-active-bg: var(--color-primary);
--tiptap-active-fg: var(--color-primary-content);
--tiptap-object-bg: var(--neutral);
--tiptap-object-fg: var(--neutral-content);
--tiptap-active-bg: var(--primary);
--tiptap-active-fg: var(--primary-content);
}
.tiptap-suggestions {
--tiptap-object-bg: var(--color-base-100);
--tiptap-object-fg: var(--color-base-content);
--tiptap-active-bg: var(--color-base-300);
--tiptap-active-fg: var(--color-base-content);
--tiptap-object-bg: var(--base-100);
--tiptap-object-fg: var(--base-content);
--tiptap-active-bg: var(--base-300);
--tiptap-active-fg: var(--base-content);
}
.tiptap-suggestions__item {
@apply border-base-100 border-l-2 border-solid;
@apply border-l-2 border-solid border-base-100;
}
.tiptap-suggestions__selected {
@@ -326,13 +312,13 @@
}
.note-editor .tiptap {
--tiptap-object-bg: var(--color-base-200);
@apply input rounded-box h-auto min-h-32 p-[.65rem] pb-6;
--tiptap-object-bg: var(--base-200);
@apply input input-bordered h-auto min-h-32 rounded-box p-[.65rem] pb-6;
}
.input-editor .tiptap {
--tiptap-object-bg: var(--color-base-200);
@apply input h-auto p-[.65rem];
--tiptap-object-bg: var(--base-200);
@apply input input-bordered h-auto p-[.65rem];
}
/* link-content, based on tiptap */
@@ -344,8 +330,8 @@
white-space: nowrap;
border-radius: 3px;
padding: 0 0.25rem;
background-color: var(--color-base-100);
color: var(--color-base-content);
background-color: var(--base-100);
color: var(--base-content);
}
/* content rendered by welshman/content */
@@ -361,25 +347,25 @@
/* date input */
.picker {
--date-picker-foreground: var(--color-base-content);
--date-picker-background: var(--color-base-300);
--date-picker-highlight-border: var(--color-primary);
--date-picker-selected-color: var(--color-primary-content);
--date-picker-selected-background: var(--color-primary);
--date-picker-foreground: var(--base-content);
--date-picker-background: var(--base-300);
--date-picker-highlight-border: var(--primary);
--date-picker-selected-color: var(--primary-content);
--date-picker-selected-background: var(--primary);
}
.date-time-field {
@apply input rounded-lg px-0;
@apply input input-bordered rounded-lg px-0;
}
.date-time-field input {
@apply h-full! w-full! rounded-lg! border-none! bg-inherit! px-4! text-inherit!;
@apply !h-full !w-full !rounded-lg !border-none !bg-inherit !px-4 !text-inherit;
}
/* tippy popover */
.tippy-target {
@apply z-tooltip pointer-events-none fixed inset-0;
@apply pointer-events-none fixed inset-0 z-tooltip;
}
.tippy-target > * {
@@ -393,15 +379,15 @@
/* emoji picker */
emoji-picker {
--background: var(--color-base-100);
--border-color: var(--color-base-100);
--background: var(--base-100);
--border-color: var(--base-100);
--border-radius: var(--rounded-box);
--button-active-background: var(--color-base-content);
--button-hover-background: var(--color-base-content);
--indicator-color: var(--color-base-content);
--input-border-color: var(--color-base-100);
--input-font-color: var(--color-base-content);
--outline-color: var(--color-base-100);
--button-active-background: var(--base-content);
--button-hover-background: var(--base-content);
--indicator-color: var(--base-content);
--input-border-color: var(--base-100);
--input-font-color: var(--base-content);
--outline-color: var(--base-100);
}
/* progress */
@@ -433,5 +419,11 @@ body.keyboard-open .hide-on-keyboard {
}
.chat__scroll-down {
@apply pb-sai z-feature fixed right-4 bottom-28 md:bottom-16;
@apply pb-sai fixed bottom-28 right-4 z-feature md:bottom-16;
}
/* content visibility */
.cv {
content-visibility: auto;
}
+1
View File
@@ -47,6 +47,7 @@ export const isParticipantSpeaking = derived(
$participants.some(sp => participantKey(sp) === participantKey(p)),
)
/** True when the local user is in LiveKits active-speakers list (currently talking). */
export const isLocalSpeaking = derived(
[currentVoiceSession, speakingParticipants],
([$session, $speaking]) => {
+20 -14
View File
@@ -71,14 +71,17 @@ export const toggleCamera = async () => {
if (!session) return
const cameraOn = !session.cameraOn
try {
await session.room.localParticipant.setCameraEnabled(cameraOn)
if (!cameraOn) {
session.room.localParticipant.setCameraEnabled(false)
currentVoiceSession.set({...session, cameraOn})
} catch {
pushToast({
theme: "error",
message: cameraOn ? "Could not access camera" : "Could not turn off camera",
})
return
}
try {
await session.room.localParticipant.setCameraEnabled(true)
currentVoiceSession.set({...session, cameraOn})
} catch (e) {
pushToast({theme: "error", message: "Could not access camera"})
}
}
@@ -87,13 +90,16 @@ export const toggleScreenShare = async () => {
if (!session) return
const screenShareOn = !session.screenShareOn
try {
await session.room.localParticipant.setScreenShareEnabled(screenShareOn)
if (!screenShareOn) {
session.room.localParticipant.setScreenShareEnabled(false)
currentVoiceSession.set({...session, screenShareOn})
} catch {
pushToast({
theme: "error",
message: screenShareOn ? "Could not start screen sharing" : "Could not stop screen sharing",
})
return
}
try {
await session.room.localParticipant.setScreenShareEnabled(true)
currentVoiceSession.set({...session, screenShareOn})
} catch (e) {
pushToast({theme: "error", message: "Could not start screen sharing"})
}
}
+2 -2
View File
@@ -325,7 +325,7 @@ export const leaveVoiceRoom = async () => {
try {
await session.room.localParticipant.setCameraEnabled(false)
} catch {
pushToast({theme: "error", message: "Error turning off camera."})
/* pass */
}
}
@@ -333,7 +333,7 @@ export const leaveVoiceRoom = async () => {
try {
await session.room.localParticipant.setScreenShareEnabled(false)
} catch {
pushToast({theme: "error", message: "Error turning off screen sharing."})
/* pass */
}
}
@@ -38,7 +38,7 @@
publishReaction({...template, event, relays: [url], protect: await shouldProtect})
</script>
<div class="flex grow flex-wrap justify-end gap-2">
<div class="flex flex-grow flex-wrap justify-end gap-2">
{#if h && showRoom}
<Link href={makeSpacePath(url, h)} class="btn btn-neutral btn-xs rounded-full">
Posted in #<RoomName {h} {url} />
+1 -1
View File
@@ -155,7 +155,7 @@
{#snippet input()}
<div
class="relative z-feature flex gap-2 border-t border-solid border-base-100 bg-base-100">
<div class="input-editor grow overflow-hidden">
<div class="input-editor flex-grow overflow-hidden">
<EditorContent {editor} />
</div>
<Button data-tip="Add an image" class="center btn tooltip" onclick={selectFiles}>
@@ -19,7 +19,7 @@
const end = $derived(parseInt(meta.end))
</script>
<div class="flex grow flex-wrap justify-between gap-2">
<div class="flex flex-grow flex-wrap justify-between gap-2">
<p class="text-xl">{meta.title || meta.name}</p>
{#if !isNaN(start) && !isNaN(end)}
{@const startDateDisplay = formatTimestampAsDate(start)}
+1 -1
View File
@@ -23,7 +23,7 @@
{#if meta.location}
<span class="flex items-start gap-1">
<Icon icon={MapPoint} class="mt-[2px]" size={4} />
<span class="wrap-break-word">{meta.location}</span>
<span class="break-words">{meta.location}</span>
</span>
{/if}
</div>
+1 -1
View File
@@ -43,7 +43,7 @@
const uploading = writable(false)
const editorClass = $derived(
cx("chat-editor grow overflow-hidden", {
cx("chat-editor flex-grow overflow-hidden", {
"pointer-events-none opacity-50": disabled,
}),
)
+1 -1
View File
@@ -35,7 +35,7 @@
<Button class="flex flex-col justify-start gap-1 w-full" onclick={openChat}>
<div
class="cursor-pointer border-t border-solid border-base-100 px-3 py-2 transition-colors hover:bg-base-100 {props.class}"
class="cursor-pointer border-t border-solid border-base-100 px-6 py-2 transition-colors hover:bg-base-100 {props.class}"
class:bg-base-100={active}>
<div class="flex flex-col justify-start gap-1">
<div class="flex items-center justify-between gap-2">
+1 -1
View File
@@ -42,7 +42,7 @@
publishReaction({...template, event, relays: [url], protect: await shouldProtect})
</script>
<div class="flex grow flex-wrap justify-end gap-2">
<div class="flex flex-grow flex-wrap justify-end gap-2">
{#if h && showRoom}
<Link href={makeSpacePath(url, h)} class="btn btn-neutral btn-xs rounded-full">
Posted in #<RoomName {h} {url} />
+1 -1
View File
@@ -172,7 +172,7 @@
<p>Description*</p>
{/snippet}
{#snippet input()}
<div class="note-editor grow overflow-hidden">
<div class="note-editor flex-grow overflow-hidden">
<EditorContent {editor} />
</div>
{/snippet}
+1 -1
View File
@@ -28,7 +28,7 @@
</script>
<div class="flex flex-wrap items-center justify-between gap-2">
<div class="flex grow flex-wrap justify-end gap-2">
<div class="flex flex-grow flex-wrap justify-end gap-2">
<ReactionSummary {url} {event} {deleteReaction} {createReaction} reactionClass="tooltip-left" />
<ThunkStatusOrDeleted {event} />
{#if showActivity}
+1 -1
View File
@@ -150,7 +150,7 @@
</div>
{:else}
<div
class="overflow-hidden text-ellipsis wrap-break-word"
class="overflow-hidden text-ellipsis break-words"
style={expandBlock ? "mask-image: linear-gradient(0deg, transparent 0px, black 100px)" : ""}>
{#each shortContent as parsed, i}
{#if isNewline(parsed) && !isBlock(i - 1)}
+1 -1
View File
@@ -101,7 +101,7 @@
</p>
</div>
{:else}
<div class="overflow-hidden text-ellipsis wrap-break-word">
<div class="overflow-hidden text-ellipsis break-words">
{#each shortContent as parsed, i}
{#if isNewline(parsed)}
<ContentNewline value={parsed.value} />
+1 -1
View File
@@ -45,7 +45,7 @@
{#if $quote.kind === MESSAGE}
<div
class="border-l-2 border-solid border-l-primary py-1 pl-2 opacity-90"
style="background-color: color-mix(in srgb, var(--color-primary) 10%, var(--color-base-300) 90%);">
style="background-color: color-mix(in srgb, var(--primary) 10%, var(--base-300) 90%);">
<NoteContentMinimal trimParent {url} event={$quote} />
</div>
{:else}
+2 -2
View File
@@ -101,7 +101,7 @@
{/if}
<div class="relative">
<pre class="card2 card2-sm bg-alt overflow-auto text-xs"><code>{json}</code></pre>
<p class="absolute right-2 top-2 flex grow items-center justify-between">
<p class="absolute right-2 top-2 flex flex-grow items-center justify-between">
<Button onclick={copyJson} class="btn btn-neutral btn-sm flex items-center">
<Icon icon={Copy} /> Copy
</Button>
@@ -109,6 +109,6 @@
</div>
</ModalBody>
<ModalFooter>
<Button class="btn btn-primary grow" onclick={() => history.back()}>Got it</Button>
<Button class="btn btn-primary flex-grow" onclick={() => history.back()}>Got it</Button>
</ModalFooter>
</Modal>
+1 -1
View File
@@ -87,7 +87,7 @@
class="left-content bottom-sai right-sai ml-2 pl-2 fixed z-feature">
<div class="card2 mx-2 my-2 bg-alt shadow-md">
<div class="relative">
<div class="note-editor grow overflow-hidden">
<div class="note-editor flex-grow overflow-hidden">
<EditorContent {autofocus} {editor} />
</div>
<Button
+1 -1
View File
@@ -30,7 +30,7 @@
publishReaction({...template, event, relays: [url], protect: await shouldProtect})
</script>
<div class="flex grow flex-wrap justify-end gap-2">
<div class="flex flex-grow flex-wrap justify-end gap-2">
{#if h && showRoom}
<Link href={makeSpacePath(url, h)} class="btn btn-neutral btn-xs rounded-full">
Posted in #<RoomName {h} {url} />
+2 -2
View File
@@ -146,7 +146,7 @@
<p>Details*</p>
{/snippet}
{#snippet input()}
<div class="note-editor grow overflow-hidden">
<div class="note-editor flex-grow overflow-hidden">
<EditorContent {editor} />
</div>
{/snippet}
@@ -168,7 +168,7 @@
Goal Amount (sats)*
{/snippet}
{#snippet input()}
<div class="flex grow justify-end">
<div class="flex flex-grow justify-end">
<label class="input input-bordered flex items-center gap-2">
<Icon icon={Bolt} />
<input bind:value={amount} type="number" class="w-28" />
+1 -1
View File
@@ -23,7 +23,7 @@
<ModalTitle>Unable to Zap</ModalTitle>
</ModalHeader>
<p>
Zapping <ProfileLink {pubkey} class="text-primary!" /> isn't possible right now because
Zapping <ProfileLink {pubkey} class="!text-primary" /> isn't possible right now because
{#if $zapper}
their zap receiver isn't correctly set up.
{:else}
@@ -97,10 +97,10 @@
tabindex="-1"
onmousedown={stopPropagation(onClear)}
ontouchstart={stopPropagation(onClear)}>
<Icon icon={CloseCircle} class="scale-150 bg-base-300!" />
<Icon icon={CloseCircle} class="scale-150 !bg-base-300" />
</span>
{:else}
<Icon icon={AddCircle} class="scale-150 bg-base-300!" />
<Icon icon={AddCircle} class="scale-150 !bg-base-300" />
{/if}
</div>
{#if !url}
@@ -9,10 +9,10 @@
<div class="flex items-start gap-4">
<CalendarEventDate event={props.event} />
<div class="flex grow flex-col">
<div class="flex flex-grow flex-col">
<CalendarEventHeader event={props.event} />
<div class="flex py-2 opacity-50">
<div class="h-px grow bg-base-content opacity-25"></div>
<div class="h-px flex-grow bg-base-content opacity-25"></div>
</div>
<Content {...props} />
</div>
@@ -17,7 +17,7 @@
</script>
<div class="flex flex-col">
<div class="flex grow flex-wrap justify-between gap-2">
<div class="flex flex-grow flex-wrap justify-between gap-2">
<p class="text-sm">{meta.title || meta.name}</p>
{#if !isNaN(start) && !isNaN(end)}
{@const startDateDisplay = formatTimestampAsDate(start)}
+1 -1
View File
@@ -43,7 +43,7 @@
<div class="flex flex-col gap-2 card2 card2-sm bg-alt">
<div class="flex items-center justify-between gap-2">
<label class="flex min-w-0 grow items-center gap-2">
<label class="flex min-w-0 flex-grow items-center gap-2">
{#if !closed}
{#if pollType === "singlechoice"}
<input
+2 -2
View File
@@ -32,7 +32,7 @@
</script>
<div
class="ml-sai mt-sai mb-sai relative z-popover isolate hidden w-14 shrink-0 bg-base-200 pt-2 md:block">
class="ml-sai mt-sai mb-sai relative z-popover isolate hidden w-14 flex-shrink-0 bg-base-200 pt-2 md:block">
<div class="flex h-full flex-col" class:justify-between={PLATFORM_RELAYS.length === 0}>
<PrimaryNavSpaces />
{#if PLATFORM_RELAYS.length > 0}
@@ -63,7 +63,7 @@
<!-- a little extra something for ios -->
<div
class="hide-on-keyboard fixed bottom-0 left-0 right-0 z-nav h-(--saib) bg-base-100 md:hidden">
class="hide-on-keyboard fixed bottom-0 left-0 right-0 z-nav h-[var(--saib)] bg-base-100 md:hidden">
</div>
<div
class="hide-on-keyboard border-top bottom-sai fixed left-0 right-0 z-nav h-14 border border-base-200 bg-base-100 md:hidden">
+2 -2
View File
@@ -25,10 +25,10 @@
<div class="flex flex-col gap-2">
{#each spaceUrls as url (url)}
<div class="card2 bg-alt flex flex-row items-center gap-2">
<div class="shrink-0">
<div class="flex-shrink-0">
<RelayIcon {url} size={12} />
</div>
<div class="flex grow flex-col">
<div class="flex flex-grow flex-col">
<RelayName {url} />
<div class="text-sm opacity-75">
{url}
+1 -1
View File
@@ -121,6 +121,6 @@
</div>
</ModalBody>
<ModalFooter>
<Button class="btn btn-primary grow" onclick={back}>Done</Button>
<Button class="btn btn-primary flex-grow" onclick={back}>Done</Button>
</ModalFooter>
</Modal>
+2 -2
View File
@@ -26,8 +26,8 @@
type="button"
class="btn font-normal flex h-[unset] w-full flex-nowrap py-4 text-left items-start justify-between"
{onclick}>
<div class="flex grow flex-row items-start gap-4">
<div class="flex h-7 w-7 shrink-0 items-center justify-center">
<div class="flex flex-grow flex-row items-start gap-4">
<div class="flex h-7 w-7 flex-shrink-0 items-center justify-center">
<Icon {icon} />
</div>
<div class="flex flex-col gap-1">
+1 -1
View File
@@ -23,7 +23,7 @@
<div class="relative">
<div class="avatar relative">
<div
class="center flex! h-12 w-12 min-w-12 rounded-full border-2 border-solid border-base-300 bg-base-300">
class="center !flex h-12 w-12 min-w-12 rounded-full border-2 border-solid border-base-300 bg-base-300">
<RelayIcon {url} />
</div>
</div>
+1 -1
View File
@@ -132,7 +132,7 @@
</Button>
</Tippy>
</div>
<div class="chat-editor grow overflow-hidden">
<div class="chat-editor flex-grow overflow-hidden">
<EditorContent {autofocus} {editor} />
</div>
<Button
+1 -1
View File
@@ -131,7 +131,7 @@
<p>Icon</p>
{/snippet}
{#snippet input()}
<div class="flex grow items-center justify-between gap-4">
<div class="flex flex-grow items-center justify-between gap-4">
{#if imagePreview}
<div class="flex items-center gap-2">
<span class="text-sm opacity-75">Selected:</span>
+1 -1
View File
@@ -94,7 +94,7 @@
{:else}
<div class="w-8 shrink-0"></div>
{/if}
<div class="min-w-0 grow pr-1">
<div class="min-w-0 flex-grow pr-1">
{#if showPubkey}
<div class="flex items-center gap-2">
<Button onclick={openProfile} class="text-sm font-bold" style="color: {colorValue}">
+1 -1
View File
@@ -11,7 +11,7 @@
const {url, h, ...props}: Props = $props()
</script>
<div class="flex grow items-center justify-between gap-4 {props.class}">
<div class="flex flex-grow items-center justify-between gap-4 {props.class}">
<div class="flex items-center gap-3">
<RoomImage {url} {h} />
<div class="min-w-0 overflow-hidden text-ellipsis">
+1 -1
View File
@@ -27,7 +27,7 @@
<Button onclick={back} class="place-self-start pr-3 md:hidden">
<Icon icon={ArrowLeft} size={7} />
</Button>
<div class="ellipsize whitespace-nowrap flex grow items-center justify-between gap-4">
<div class="ellipsize whitespace-nowrap flex flex-grow items-center justify-between gap-4">
<div class="flex flex-col">
<div class="flex gap-2 items-center">
{@render title?.()}
+1 -1
View File
@@ -42,7 +42,7 @@
<div class="relative">
<div class="avatar relative">
<div
class="center flex! h-16 w-16 min-w-16 rounded-full border-2 border-solid border-base-300 bg-base-300">
class="center !flex h-16 w-16 min-w-16 rounded-full border-2 border-solid border-base-300 bg-base-300">
<RelayIcon {url} size={10} />
</div>
</div>
+1 -1
View File
@@ -134,7 +134,7 @@
<p>Icon</p>
{/snippet}
{#snippet input()}
<div class="flex items-center gap-4 justify-between grow">
<div class="flex items-center gap-4 justify-between flex-grow">
{#if imagePreview}
<div class="flex items-center gap-2">
<span class="text-sm opacity-75">Selected:</span>
+1 -1
View File
@@ -100,6 +100,6 @@
</div>
</ModalBody>
<ModalFooter>
<Button class="btn btn-primary grow" onclick={back}>Done</Button>
<Button class="btn btn-primary flex-grow" onclick={back}>Done</Button>
</ModalFooter>
</Modal>
+6 -6
View File
@@ -140,7 +140,7 @@
<div bind:this={element} class="flex min-h-0 flex-1 flex-col">
<SecondaryNavSection class="min-h-0 flex-1 flex flex-col overflow-hidden pb-0">
<div class="shrink-0">
<div class="flex-shrink-0">
<Button
class="relative flex w-full flex-col rounded-xl p-3 transition-all hover:bg-base-100"
onclick={openMenu}>
@@ -270,14 +270,14 @@
{/if}
{#if hasNip29($relay)}
{#if $userRooms.length > 0}
<div class="h-2 shrink-0"></div>
<div class="h-2 flex-shrink-0"></div>
<SecondaryNavHeader>Your Rooms</SecondaryNavHeader>
{/if}
{#each $userRooms as h (h)}
<SpaceMenuRoomItem {url} {h} />
{/each}
{#if $otherRooms.length > 0}
<div class="h-2 shrink-0"></div>
<div class="h-2 flex-shrink-0"></div>
<SecondaryNavHeader>
{#if $userRooms.length > 0}
Other Rooms
@@ -296,7 +296,7 @@
<SpaceMenuRoomItem {url} {h} />
{/each}
{#if $otherVoiceRooms.length > 0}
<div class="h-2 shrink-0"></div>
<div class="h-2 flex-shrink-0"></div>
<SecondaryNavHeader>Voice Rooms</SecondaryNavHeader>
{#each $otherVoiceRooms as h (h)}
<SpaceMenuRoomItem {url} {h} />
@@ -309,11 +309,11 @@
</SecondaryNavItem>
{/if}
{/if}
<div class="h-5 shrink-0"></div>
<div class="h-5 flex-shrink-0"></div>
</div>
</SecondaryNavSection>
<div
class="flex shrink-0 flex-col gap-2 p-2 pt-0 -mt-4 pb-[calc(var(--saib)+0.25rem)] md:pb-2 z-nav">
class="flex flex-shrink-0 flex-col gap-2 p-2 pt-0 -mt-4 pb-[calc(var(--saib)+0.25rem)] md:pb-2 z-nav">
<VoiceWidget />
<Button class="btn btn-neutral btn-sm h-10" onclick={showDetail}>
<SocketStatusIndicator {url} />
+1 -1
View File
@@ -30,7 +30,7 @@
publishReaction({...template, event, relays: [url], protect: await shouldProtect})
</script>
<div class="flex grow flex-wrap justify-end gap-2">
<div class="flex flex-grow flex-wrap justify-end gap-2">
{#if h && showRoom}
<Link href={makeSpacePath(url, h)} class="btn btn-neutral btn-xs rounded-full">
Posted in #<RoomName {h} {url} />
+1 -1
View File
@@ -130,7 +130,7 @@
<p>Message*</p>
{/snippet}
{#snippet input()}
<div class="note-editor grow overflow-hidden">
<div class="note-editor flex-grow overflow-hidden">
<EditorContent {editor} />
</div>
{/snippet}
+14 -14
View File
@@ -6,7 +6,7 @@
import Button from "@lib/components/Button.svelte"
import Icon from "@lib/components/Icon.svelte"
import ProfileCircle from "@app/components/ProfileCircle.svelte"
import VideoCallTile from "@app/components/VideoCallTile.svelte"
import VideoCallVideo from "@app/components/VideoCallVideo.svelte"
import VoiceWidget from "@app/components/VoiceWidget.svelte"
import {get} from "svelte/store"
import {
@@ -28,11 +28,11 @@
class?: string
}
type VideoTileData = {
type VideoTile = {
identity: string
isLocal: boolean
trackSid: string
track: Track | undefined
attachable: Track | undefined
source: Track.Source.Camera | Track.Source.ScreenShare
}
@@ -75,7 +75,7 @@
}
const room = session.room
const videoTiles: VideoTileData[] = []
const videoTiles: VideoTile[] = []
const user = room.localParticipant
if (session.cameraOn) {
@@ -84,7 +84,7 @@
identity: user.identity,
isLocal: true,
trackSid: localPub?.trackSid ?? "local-camera",
track: localPub?.track,
attachable: localPub?.track,
source: Track.Source.Camera,
})
}
@@ -95,7 +95,7 @@
identity: user.identity,
isLocal: true,
trackSid: localPub?.trackSid ?? "local-screen",
track: localPub?.track,
attachable: localPub?.track,
source: Track.Source.ScreenShare,
})
}
@@ -107,7 +107,7 @@
identity: rp.identity,
isLocal: false,
trackSid: camPub.trackSid,
track: camPub.track,
attachable: camPub.track,
source: Track.Source.Camera,
})
}
@@ -117,7 +117,7 @@
identity: rp.identity,
isLocal: false,
trackSid: screenPub.trackSid,
track: screenPub.track,
attachable: screenPub.track,
source: Track.Source.ScreenShare,
})
}
@@ -127,7 +127,7 @@
})
/** Identity + source only — LiveKit can change trackSid after publish, which broke spotlight + stale-key effect. */
const tileKey = (t: VideoTileData) => `${t.identity}\x1f${t.source}`
const tileKey = (t: VideoTile) => `${t.identity}\x1f${t.source}`
const primaryTile = $derived.by(() => {
const k = $videoPrimaryTileKey
@@ -160,7 +160,7 @@
}
})
const labelFor = (identity: string, source: VideoTileData["source"]) => {
const labelFor = (identity: string, source: VideoTile["source"]) => {
const pk = pubkeyFromLiveKitIdentity(identity)
const name = pk ? displayProfileByPubkey(pk) : "Unknown"
return source === Track.Source.ScreenShare ? `${name} · screen` : name
@@ -183,7 +183,7 @@
)
</script>
{#snippet videoTile(tile: VideoTileData, layout: TileLayout)}
{#snippet videoTile(tile: VideoTile, layout: TileLayout)}
<div
class={cx(
"relative isolate overflow-hidden rounded-box shadow-sm",
@@ -192,9 +192,9 @@
layout === "strip" && "aspect-video w-44 shrink-0",
tile.source === Track.Source.ScreenShare ? "bg-black" : "bg-base-100",
)}>
{#if tile.track}
<VideoCallTile
track={tile.track}
{#if tile.attachable}
<VideoCallVideo
track={tile.attachable}
muted={tile.isLocal}
fit={tile.source === Track.Source.ScreenShare ? "contain" : "cover"}
class="pointer-events-none absolute inset-0" />
@@ -11,21 +11,21 @@
const {track, muted = true, fit = "cover", class: className = ""}: Props = $props()
let videoElement = $state<HTMLVideoElement | undefined>()
let el = $state<HTMLVideoElement | undefined>()
$effect(() => {
const element = videoElement
const activeTrack = track
if (!element) return
activeTrack.attach(element)
const v = el
const t = track
if (!v) return
t.attach(v)
return () => {
activeTrack.detach(element)
t.detach(v)
}
})
</script>
<video
bind:this={videoElement}
bind:this={el}
class={cx("h-full w-full", fit === "contain" ? "object-contain" : "object-cover", className)}
playsinline
{muted}></video>
@@ -41,9 +41,16 @@
}
$effect(() => {
loadDevices()
navigator.mediaDevices?.addEventListener?.("devicechange", loadDevices)
return () => navigator.mediaDevices?.removeEventListener?.("devicechange", loadDevices)
void loadDevices()
const md = navigator.mediaDevices
if (!md?.addEventListener) return
const onDeviceChange = () => {
void loadDevices()
}
md.addEventListener("devicechange", onDeviceChange)
return () => {
md.removeEventListener("devicechange", onDeviceChange)
}
})
$effect(() => {
+1 -1
View File
@@ -63,7 +63,7 @@
{replaceState}
{notification}
onclick={handleClick}
class={cx("items-start!", isActive && "bg-base-100! text-base-content!")}>
class={cx("!items-start", isActive && "!bg-base-100 !text-base-content")}>
<div class="flex w-full min-w-0 flex-col gap-2">
<div class="flex gap-2 items-center">
{#if isJoining}
+28 -24
View File
@@ -53,14 +53,6 @@
)
const routeDisplayedRoom = $derived($displayedRoomStore)
const isViewingCurrentVoiceRoom = $derived(
$currentVoiceRoom !== undefined &&
url !== undefined &&
typeof h === "string" &&
$currentVoiceRoom.url === url &&
$currentVoiceRoom.h === h,
)
const targetRoom = $derived.by((): Room | undefined => {
if ($voiceState === VoiceState.Joining || $voiceState === VoiceState.Connected) {
return $currentVoiceRoom
@@ -98,17 +90,26 @@
pushModal(VoiceCallAudioSettingsDialog)
}
const showChatButton = $derived($voiceState === VoiceState.Connected && isViewingCurrentVoiceRoom)
const showVoiceLayoutToggle = $derived(
$voiceState === VoiceState.Connected &&
targetRoom !== undefined &&
getRoomType(targetRoom) === RoomType.Voice &&
typeof h === "string" &&
relay !== undefined &&
decodeRelay(relay) === targetRoom.url &&
h === targetRoom.h,
)
const isChatPanelActive = $derived(
showChatButton &&
(isDesktopLayout.current
? $videoCallLayout === VideoCallLayout.Split
: $videoCallLayout === VideoCallLayout.Chat),
showVoiceLayoutToggle &&
((!isDesktopLayout.current &&
($videoCallLayout === VideoCallLayout.Chat ||
$videoCallLayout === VideoCallLayout.Split)) ||
(isDesktopLayout.current && $videoCallLayout === VideoCallLayout.Split)),
)
const onChatToggle = () => {
if (!showChatButton) return
if (!showVoiceLayoutToggle) return
if (isDesktopLayout.current) {
videoCallLayout.update(p =>
p === VideoCallLayout.Split ? VideoCallLayout.Video : VideoCallLayout.Split,
@@ -127,6 +128,17 @@
const mediaToggleClass = "center tooltip tooltip-top btn btn-sm btn-square btn-ghost"
</script>
{#snippet mutedSlash(show: boolean)}
{#if show}
<span
class="pointer-events-none absolute inset-0 flex items-center justify-center overflow-visible"
aria-hidden="true">
<span class="h-[1.3px] w-[150%] max-w-none shrink-0 -rotate-45 rounded-full bg-current"
></span>
</span>
{/if}
{/snippet}
{#if targetRoom}
<div
in:fly={{y: 60, duration: 350}}
@@ -151,7 +163,7 @@
</span>
</div>
</button>
{#if showChatButton}
{#if showVoiceLayoutToggle}
<Button
data-tip="Toggle Chat"
class={cx(
@@ -194,15 +206,7 @@
onclick={toggleMute}>
<span class="relative inline-flex items-center justify-center overflow-visible">
<Icon icon={Microphone} size={4} />
{#if $currentVoiceSession.muted}
<span
class="pointer-events-none absolute inset-0 flex items-center justify-center overflow-visible"
aria-hidden="true">
<span
class="h-[1.3px] w-[150%] max-w-none shrink-0 -rotate-45 rounded-full bg-current"
></span>
</span>
{/if}
{@render mutedSlash($currentVoiceSession.muted)}
</span>
</Button>
<Button
+1 -1
View File
@@ -70,7 +70,7 @@
Amount (satoshis)
{/snippet}
{#snippet input()}
<div class="flex grow justify-end">
<div class="flex flex-grow justify-end">
<label class="input input-bordered flex items-center gap-2">
<Icon icon={Bolt} />
<input
+1 -1
View File
@@ -80,7 +80,7 @@
Amount (satoshis)
{/snippet}
{#snippet input()}
<div class="flex grow justify-end">
<div class="flex flex-grow justify-end">
<label class="input input-bordered flex items-center gap-2">
<Icon icon={Bolt} />
<input bind:value={sats} type="number" class="w-14" placeholder="0" />
+2 -2
View File
@@ -1,7 +1,7 @@
<style>
.wot-background {
fill: transparent;
stroke: var(--color-base-content);
stroke: var(--base-content);
opacity: 30%;
}
@@ -32,7 +32,7 @@
const normalizedScore = $derived(clamp([0, max], $score) / max)
const dashOffset = $derived(100 - 44 * normalizedScore)
const style = $derived(`transform: rotate(${135 - normalizedScore * 180}deg)`)
const stroke = $derived(active ? "var(--color-primary)" : "var(--color-base-content)")
const stroke = $derived(active ? "var(--primary)" : "var(--base-content)")
</script>
<div class="relative h-[14px] w-[14px]">
+5 -5
View File
@@ -118,26 +118,26 @@
<ModalBody>
<ModalHeader>
<ModalTitle>Send a Zap</ModalTitle>
<ModalSubtitle>To <ProfileLink {pubkey} class="text-primary!" /></ModalSubtitle>
<ModalSubtitle>To <ProfileLink {pubkey} class="!text-primary" /></ModalSubtitle>
</ModalHeader>
<FieldInline class="grid-cols-3!">
<FieldInline class="!grid-cols-3">
{#snippet label()}
Emoji Reaction
{/snippet}
{#snippet input()}
<div class="flex grow items-center justify-end gap-4">
<div class="flex flex-grow items-center justify-end gap-4">
<EmojiButton {onEmoji} class="btn btn-neutral">
{content}
</EmojiButton>
</div>
{/snippet}
</FieldInline>
<FieldInline class="grid-cols-3!">
<FieldInline class="!grid-cols-3">
{#snippet label()}
Amount
{/snippet}
{#snippet input()}
<div class="flex grow justify-end">
<div class="flex flex-grow justify-end">
<label class="input input-bordered flex items-center gap-2">
<Icon icon={Bolt} />
<input bind:value={amount} type="number" class="w-24" />
+6 -6
View File
@@ -147,7 +147,7 @@
<ModalBody>
<ModalHeader>
<ModalTitle>Send a Zap</ModalTitle>
<ModalSubtitle>To <ProfileLink {pubkey} class="text-primary!" /></ModalSubtitle>
<ModalSubtitle>To <ProfileLink {pubkey} class="!text-primary" /></ModalSubtitle>
</ModalHeader>
{#if invoice}
@@ -158,30 +158,30 @@
</p>
</div>
<label class="input input-bordered flex w-full items-center justify-between gap-2">
<input readonly class="ellipsize grow" value={invoice} />
<input readonly class="ellipsize flex-grow" value={invoice} />
<Button class="flex items-center" onclick={copyInvoice}>
<Icon icon={Copy} />
</Button>
</label>
{:else}
<FieldInline class="grid-cols-3!">
<FieldInline class="!grid-cols-3">
{#snippet label()}
Emoji Reaction
{/snippet}
{#snippet input()}
<div class="flex grow items-center justify-end gap-4">
<div class="flex flex-grow items-center justify-end gap-4">
<EmojiButton {onEmoji} class="btn btn-neutral">
{content}
</EmojiButton>
</div>
{/snippet}
</FieldInline>
<FieldInline class="grid-cols-3!">
<FieldInline class="!grid-cols-3">
{#snippet label()}
Amount
{/snippet}
{#snippet input()}
<div class="flex grow justify-end">
<div class="flex flex-grow justify-end">
<label class="input input-bordered flex items-center gap-2">
<Icon icon={Bolt} />
<input bind:value={amount} type="number" class="w-24" />
+2 -2
View File
@@ -12,8 +12,8 @@
</script>
<div class="btn flex h-[unset] w-full flex-nowrap py-4 text-left {props.class}">
<div class="flex grow flex-row items-start gap-4">
<div class="flex h-14 w-12 shrink-0 items-center justify-center">
<div class="flex flex-grow flex-row items-start gap-4">
<div class="flex h-14 w-12 flex-shrink-0 items-center justify-center">
{@render props.icon?.()}
</div>
<div class="flex flex-col gap-1">
+1 -1
View File
@@ -29,7 +29,7 @@
const innerClass = $derived(
cx(
"relative text-base-content text-base-content grow pointer-events-auto",
"relative text-base-content text-base-content flex-grow pointer-events-auto",
"rounded-t-box sm:rounded-box",
{
"bg-alt shadow-m max-h-[90vh] flex flex-col max-w-full pb-sai sm:pb-0": !fullscreen,
+2 -2
View File
@@ -9,9 +9,9 @@
</script>
<div class="flex items-center gap-2 p-2 text-xs uppercase opacity-50">
<div class="h-px grow bg-base-content opacity-25"></div>
<div class="h-px flex-grow bg-base-content opacity-25"></div>
{#if children}
<p>{@render children?.()}</p>
<div class="h-px grow bg-base-content opacity-25"></div>
<div class="h-px flex-grow bg-base-content opacity-25"></div>
{/if}
</div>
+2 -2
View File
@@ -8,9 +8,9 @@
const {children}: Props = $props()
</script>
<div class="h-20 shrink-0"></div>
<div class="h-20 flex-shrink-0"></div>
<div class="flex absolute bottom-sai left-0 right-0 p-6 py-4 rounded-b-box bg-base-200">
<div class="flex grow gap-4 items-center justify-between">
<div class="flex flex-grow gap-4 items-center justify-between">
{@render children?.()}
</div>
</div>
+1 -1
View File
@@ -9,6 +9,6 @@
<div
data-component="Page"
class="relative grow flex flex-col min-w-0 ml-sai mb-sai mt-sai mr-sai bg-base-200 md:ml-0 md:mb-0 {props.class}">
class="relative flex-grow flex flex-col min-w-0 ml-sai mb-sai mt-sai mr-sai bg-base-200 md:ml-0 md:mb-0 {props.class}">
{@render props.children?.()}
</div>
+1 -1
View File
@@ -12,7 +12,7 @@
<div
class={cx(
"mt-sai mb-sai max-h-screen w-60 min-h-0 shrink-0 flex-col gap-1 bg-base-300 z-nav hidden md:flex",
"mt-sai mb-sai max-h-screen w-60 min-h-0 flex-shrink-0 flex-col gap-1 bg-base-300 z-nav hidden md:flex",
props.class,
)}>
{@render children?.()}
+1 -1
View File
@@ -36,7 +36,7 @@
const active = $derived($page.url.pathname === href)
const wrapperClass = $derived(
cx(restProps.class, "relative flex shrink-0 items-center gap-3 text-left transition-all", {
cx(restProps.class, "relative flex flex-shrink-0 items-center gap-3 text-left transition-all", {
"hover:bg-base-100 hover:text-base-content": true,
"text-base-content bg-base-100": active,
"tooltip tooltip-right": title,
+6 -6
View File
@@ -1,7 +1,7 @@
<style>
:global(.tippy-box[data-theme~="tooltip"]) {
background-color: var(--color-neutral);
color: var(--color-neutral-content);
background-color: var(--neutral);
color: var(--neutral-content);
border-radius: 0.5rem;
padding: 0.25rem 0.5rem;
font-size: 0.875rem;
@@ -11,19 +11,19 @@
}
:global(.tippy-box[data-theme~="tooltip"][data-placement^="top"] > .tippy-arrow::before) {
border-top-color: var(--color-neutral);
border-top-color: var(--neutral);
}
:global(.tippy-box[data-theme~="tooltip"][data-placement^="bottom"] > .tippy-arrow::before) {
border-bottom-color: var(--color-neutral);
border-bottom-color: var(--neutral);
}
:global(.tippy-box[data-theme~="tooltip"][data-placement^="left"] > .tippy-arrow::before) {
border-left-color: var(--color-neutral);
border-left-color: var(--neutral);
}
:global(.tippy-box[data-theme~="tooltip"][data-placement^="right"] > .tippy-arrow::before) {
border-right-color: var(--color-neutral);
border-right-color: var(--neutral);
}
</style>
-1
View File
@@ -1,6 +1,5 @@
<script lang="ts">
import "@src/app.css"
import "@welshman/editor/index.css"
import "@capacitor-community/safe-area"
import * as nip19 from "nostr-tools/nip19"
import type {Unsubscriber} from "svelte/store"
+3 -2
View File
@@ -6,6 +6,7 @@
import {shouldUnwrap} from "@welshman/app"
import MenuDots from "@assets/icons/menu-dots.svg?dataurl"
import ChatSquarePlus from "@assets/icons/chat-square-plus.svg?dataurl"
import AddCircle from "@assets/icons/add-circle.svg?dataurl"
import Magnifier from "@assets/icons/magnifier.svg?dataurl"
import Icon from "@lib/components/Icon.svelte"
import Page from "@lib/components/Page.svelte"
@@ -50,8 +51,8 @@
<Icon icon={MenuDots} />
</Button>
</SecondaryNavHeader>
<Button class="btn btn-primary w-full btn-sm" onclick={startChat}>
<Icon icon={ChatSquarePlus} />
<Button class="btn btn-primary w-full row-2 min-h-0 h-[30px]" onclick={startChat}>
<Icon icon={AddCircle} />
Start New Chat
</Button>
<label class="input input-sm input-bordered flex items-center gap-2">
+3 -3
View File
@@ -37,8 +37,8 @@
<ContentSearch class="md:hidden">
{#snippet input()}
<div class="row-2 min-w-0 grow items-center">
<label class="input input-bordered flex grow items-center gap-2">
<div class="row-2 min-w-0 flex-grow items-center">
<label class="input input-bordered flex flex-grow items-center gap-2">
<Icon icon={Magnifier} />
<input
bind:value={term}
@@ -46,7 +46,7 @@
type="text"
placeholder="Search for conversations..." />
</label>
<Button class="btn btn-neutral" onclick={openMenu}>
<Button class="btn btn-primary" onclick={openMenu}>
<Icon icon={MenuDots} />
</Button>
</div>
+2 -5
View File
@@ -12,7 +12,6 @@
import Bell from "@assets/icons/bell.svg?dataurl"
import Icon from "@lib/components/Icon.svelte"
import Page from "@lib/components/Page.svelte"
import PageContent from "@lib/components/PageContent.svelte"
import SecondaryNav from "@lib/components/SecondaryNav.svelte"
import SecondaryNavItem from "@lib/components/SecondaryNavItem.svelte"
import SecondaryNavSection from "@lib/components/SecondaryNavSection.svelte"
@@ -33,7 +32,7 @@
<SecondaryNav>
<SecondaryNavSection>
<SecondaryNavItem class="w-full justify-between!">
<SecondaryNavItem class="w-full !justify-between">
<strong class="ellipsize flex items-center gap-3"> Your Settings </strong>
</SecondaryNavItem>
<SecondaryNavItem href="/settings/profile">
@@ -69,7 +68,5 @@
</SecondaryNav>
<Page>
<PageContent>
{@render children?.()}
</PageContent>
{@render children?.()}
</Page>
+3 -3
View File
@@ -67,7 +67,7 @@
</script>
<div class="content column gap-4">
<div class="card2 bg-alt shadow-md col-2">
<div class="card2 bg-alt shadow-md">
<div class="flex justify-between gap-2">
<div class="flex max-w-full gap-3">
<div class="py-1">
@@ -116,9 +116,9 @@
{/snippet}
{#snippet input()}
<label class="input input-bordered flex w-full items-center justify-between gap-2">
<div class="row-2 grow items-center">
<div class="row-2 flex-grow items-center">
<Icon icon={LinkRound} />
<input readonly class="ellipsize grow" value={npub} />
<input readonly class="ellipsize flex-grow" value={npub} />
</div>
<Button class="flex items-center" onclick={copyNpub}>
<Icon icon={Copy} />
+2 -2
View File
@@ -22,10 +22,10 @@
<svelte:window bind:innerWidth={width} />
{#if width <= md}
<div class="ml-sai mt-sai mb-sai relative z-nav w-14 shrink-0 bg-base-200 pt-2">
<div class="ml-sai mt-sai mb-sai relative z-nav w-14 flex-shrink-0 bg-base-200 pt-2">
<PrimaryNavSpaces />
</div>
<SecondaryNav class="flex! w-auto! grow pb-16">
<SecondaryNav class="!flex !w-auto flex-grow pb-16">
<SpaceMenu {url} />
</SecondaryNav>
{/if}
@@ -128,9 +128,9 @@
<div class={"calendar-event-" + event.id}>
{#if isFirstFutureEvent}
<div class="flex items-center gap-2 p-2">
<div class="h-px grow bg-primary"></div>
<div class="h-px flex-grow bg-primary"></div>
<p class="text-xs uppercase text-primary">Today</p>
<div class="h-px grow bg-primary"></div>
<div class="h-px flex-grow bg-primary"></div>
</div>
{/if}
{#if dateDisplay}
@@ -69,11 +69,11 @@
<div class="card2 bg-alt col-3 z-feature">
<div class="flex items-start gap-4">
<CalendarEventDate event={$event} />
<div class="flex min-w-0 grow flex-col gap-1">
<div class="flex min-w-0 flex-grow flex-col gap-1">
<CalendarEventHeader event={$event} />
<CalendarEventMeta event={$event} {url} />
<div class="flex py-2 opacity-50">
<div class="h-px grow bg-base-content opacity-25"></div>
<div class="h-px flex-grow bg-base-content opacity-25"></div>
</div>
<Content showEntire event={$event} {url} />
</div>
+2 -2
View File
@@ -311,9 +311,9 @@
{id}
class="flex items-center py-2 text-xs transition-colors"
class:opacity-0={showFixedNewMessages}>
<div class="h-px grow bg-primary"></div>
<div class="h-px flex-grow bg-primary"></div>
<p class="rounded-full bg-primary px-2 py-1 text-primary-content">New Messages</p>
<div class="h-px grow bg-primary"></div>
<div class="h-px flex-grow bg-primary"></div>
</div>
{:else if type === "date"}
<Divider>{value}</Divider>
+24 -27
View File
@@ -1,7 +1,6 @@
import {config} from "dotenv"
import daisyui from "daisyui"
import daisyTheme from "daisyui/theme"
import themes from "daisyui/theme/object"
import themes from "daisyui/src/theming/themes"
config({path: ".env.local"})
config({path: ".env"})
@@ -26,29 +25,27 @@ export default {
toast: 9,
},
},
plugins: [
daisyui({
themes: ["light --default", "dark --prefersdark"],
}),
daisyTheme({
name: "dark",
...themes["night"],
"--color-base-content": "oklch(75% 0.029 256.847)",
"--color-primary": process.env.VITE_PLATFORM_ACCENT,
"--color-primary-content": process.env.VITE_PLATFORM_ACCENT_CONTENT || "#EAE7FF",
"--color-secondary": process.env.VITE_PLATFORM_SECONDARY,
"--color-secondary-content": process.env.VITE_PLATFORM_SECONDARY_CONTENT || "#EAE7FF",
}),
daisyTheme({
name: "light",
...themes["winter"],
"--color-neutral": "#F2F7FF",
"--color-neutral-content": "var(--color-base-content)",
"--color-warning": "#FD8D0B",
"--color-primary": process.env.VITE_PLATFORM_ACCENT,
"--color-primary-content": process.env.VITE_PLATFORM_ACCENT_CONTENT || "#EAE7FF",
"--color-secondary": process.env.VITE_PLATFORM_SECONDARY,
"--color-secondary-content": process.env.VITE_PLATFORM_SECONDARY_CONTENT || "#EAE7FF",
}),
],
plugins: [daisyui],
daisyui: {
themes: [
{
dark: {
...themes["dark"],
primary: process.env.VITE_PLATFORM_ACCENT,
"primary-content": process.env.VITE_PLATFORM_ACCENT_CONTENT || "#EAE7FF",
secondary: process.env.VITE_PLATFORM_SECONDARY,
"secondary-content": process.env.VITE_PLATFORM_SECONDARY_CONTENT || "#EAE7FF",
},
light: {
...themes["winter"],
neutral: "#F2F7FF",
warning: "#FD8D0B",
primary: process.env.VITE_PLATFORM_ACCENT,
"primary-content": process.env.VITE_PLATFORM_ACCENT_CONTENT || "#EAE7FF",
secondary: process.env.VITE_PLATFORM_SECONDARY,
"secondary-content": process.env.VITE_PLATFORM_SECONDARY_CONTENT || "#EAE7FF",
},
},
],
},
}