diff --git a/.agents/skills/flotilla-views/SKILL.md b/.agents/skills/flotilla-views/SKILL.md index 2f9a8b9c..1be87777 100644 --- a/.agents/skills/flotilla-views/SKILL.md +++ b/.agents/skills/flotilla-views/SKILL.md @@ -287,7 +287,7 @@ param change and lists key by id. A prop that does change while mounted needs `$ - `$bindable` is for input-like components: `value` on `TopicMultiSelect` and `ProfileMultiSelect`, `element` on `PageContent`, `notifications` on `SpaceJoinNotifications`. - Display flags are fine (`showRoom`, `showActivity`, `hideZap`, `class`); derived data is not. -- 24 app components and 20 lib components declare `interface Props`, against 232 using `type`. +- Props are declared as `type Props`, never an `interface` or an inline object type. ## Reading state in a component @@ -470,8 +470,10 @@ Rich text uses `makeEditor` from `src/app/editor` with `EditorContent`. - Colors are the semantic tokens registered in `base.css`: `bg-surface`, `bg-surface-more`, `text-content`, `text-content-muted`, `border-line`, `text-primary`, `text-error`. The clay, flat, navy and nature themes supply the values through `data-fl-theme`. -- A few `class:` directives remain in `src/app` as leftovers; everything else builds classes with `cx`. +- Classes are built with `cx`; no `class:` directives remain. - Container queries go on the `PageContent`, as in `@2xl:grid-cols-2` for the classifieds grid. + Beside the navs a page is about 326px narrower than the screen, so a screen `lg:` layout switch + becomes `@2xl:` inside a page, as on the home, about, goals, article and settings pages. - Icons are `import X from "@assets/icons/.svg?dataurl"` with ``, where `size` counts 4px steps. List entries animate with `in:fly` from `@lib/transition`. diff --git a/src/app/calendar.ts b/src/app/calendar.ts index 76566224..882a4525 100644 --- a/src/app/calendar.ts +++ b/src/app/calendar.ts @@ -1,4 +1,5 @@ import { + DAY, HOUR, LOCALE, dateToSeconds, @@ -174,8 +175,7 @@ export type CalendarBar = { continuesAfter: boolean } -const daysApart = (a: Date, b: Date) => - Math.round((a.getTime() - b.getTime()) / (24 * 60 * 60 * 1000)) +const daysApart = (a: Date, b: Date) => Math.round((dateToSeconds(a) - dateToSeconds(b)) / DAY) // Multi-day events spanning `days` are laid out as bars, packed into as few lanes as possible. export const layoutMultiDayBars = ( diff --git a/src/app/components/ArticleAuthorSidebar.svelte b/src/app/components/ArticleAuthorSidebar.svelte index 1f935f9c..433a5127 100644 --- a/src/app/components/ArticleAuthorSidebar.svelte +++ b/src/app/components/ArticleAuthorSidebar.svelte @@ -45,10 +45,9 @@
+ class="bg-surface flex flex-wrap items-center justify-between gap-2 border-b px-3 py-2 text-xs @lg:px-4 @lg:text-sm border-line">
{formatTimestamp(event.created_at)} {#if replyCount !== undefined} @@ -87,8 +86,7 @@
+ class="bg-surface flex shrink-0 flex-col gap-2 border-t px-3 py-3 @lg:flex-row @lg:items-center @lg:justify-between @lg:px-4 border-line"> diff --git a/src/app/components/ThunkStatus.svelte b/src/app/components/ThunkStatus.svelte index bcf7cf43..b87eab33 100644 --- a/src/app/components/ThunkStatus.svelte +++ b/src/app/components/ThunkStatus.svelte @@ -3,7 +3,7 @@ import ThunkFailure from "@app/components/ThunkFailure.svelte" import ThunkPending from "@app/components/ThunkPending.svelte" - interface Props { + type Props = { thunk: BaseThunk showToastOnRetry?: boolean inline?: boolean diff --git a/src/app/components/ThunkStatusDetail.svelte b/src/app/components/ThunkStatusDetail.svelte index c2353b2b..f426affe 100644 --- a/src/app/components/ThunkStatusDetail.svelte +++ b/src/app/components/ThunkStatusDetail.svelte @@ -8,7 +8,7 @@ import Icon from "@lib/components/Icon.svelte" import Button from "@lib/components/Button.svelte" - interface Props { + type Props = { thunk: BaseThunk retry: (url: string) => void } @@ -24,6 +24,8 @@ isPartial ? `Partial delivery ${successUrls.length}/${total} relays` : "Failed to send!", ) + const retryUrl = (url: string) => () => retry(url) + const relayMessage = (status: PublishStatus | undefined, detail: string | undefined) => { if (status === PublishStatus.Timeout) { return "request timed out." @@ -58,9 +60,7 @@

{displayRelayUrl(url)}

{relayMessage(status, detail)}

-
diff --git a/src/app/components/TopicMultiSelect.svelte b/src/app/components/TopicMultiSelect.svelte index 4d032fa7..6ba050b5 100644 --- a/src/app/components/TopicMultiSelect.svelte +++ b/src/app/components/TopicMultiSelect.svelte @@ -46,7 +46,7 @@ tippy?.hide() } - const removeTopic = (topic: string) => { + const removeTopic = (topic: string) => () => { value = remove(topic, value) } @@ -83,7 +83,7 @@
{#each value as topic (topic)}
- #{topic} diff --git a/src/app/components/VoiceRoomItem.svelte b/src/app/components/VoiceRoomItem.svelte index c48a44d3..954f9ec0 100644 --- a/src/app/components/VoiceRoomItem.svelte +++ b/src/app/components/VoiceRoomItem.svelte @@ -24,7 +24,7 @@ import {cancelJoinVoiceRoom} from "@app/callEngine" import {profiles} from "@app/core" - interface Props { + type Props = { url: string h: string notification?: boolean diff --git a/src/app/components/WotScore.svelte b/src/app/components/WotScore.svelte index e3b9dca5..93802757 100644 --- a/src/app/components/WotScore.svelte +++ b/src/app/components/WotScore.svelte @@ -18,7 +18,7 @@ import {WotScope} from "@welshman/app" import {followLists, user, wot} from "@app/core" - interface Props { + type Props = { pubkey: string } diff --git a/src/app/components/ZapForm.svelte b/src/app/components/ZapForm.svelte index 8d745951..208cbc79 100644 --- a/src/app/components/ZapForm.svelte +++ b/src/app/components/ZapForm.svelte @@ -25,7 +25,7 @@ content = emoji } - const selectAmount = (preset: number) => { + const selectAmount = (preset: number) => () => { amount = preset } @@ -55,7 +55,7 @@ "button-primary": preset === amount, "button-neutral": preset !== amount, })} - onclick={() => selectAmount(preset)}> + onclick={selectAmount(preset)}> {preset} {/each} diff --git a/src/app/components/hosting/DataTransferModal.svelte b/src/app/components/hosting/DataTransferModal.svelte index 8e45f6e7..a8c6c740 100644 --- a/src/app/components/hosting/DataTransferModal.svelte +++ b/src/app/components/hosting/DataTransferModal.svelte @@ -36,8 +36,8 @@ const back = () => history.back() - const onFileChange = (event: Event) => { - file = (event.target as HTMLInputElement).files?.[0] + const onFileChange = (event: Event & {currentTarget: HTMLInputElement}) => { + file = event.currentTarget.files?.[0] result = undefined } diff --git a/src/app/components/hosting/PricingTable.svelte b/src/app/components/hosting/PricingTable.svelte index e52faf21..908ee0e5 100644 --- a/src/app/components/hosting/PricingTable.svelte +++ b/src/app/components/hosting/PricingTable.svelte @@ -22,6 +22,10 @@ const memberLabel = (members: Plan["members"]) => members ? `Up to ${members} members` : "Unlimited members" + const select = (planId: string) => () => onSelect?.(planId) + + const start = (planId: string) => () => onCta?.(planId) + const visiblePlans = $derived(plans.filter(plan => !plan.hidden)) const gridCols: Record = { @@ -70,7 +74,7 @@ "button-primary": isPopular, "button-neutral": !isPopular, })} - onclick={() => onCta?.(plan.id)}> + onclick={start(plan.id)}> Get started {/if} @@ -85,7 +89,7 @@ class={cx("card card-interactive relative flex flex-col text-left", { "border-primary": value === plan.id, })} - onclick={() => onSelect?.(plan.id)}> + onclick={select(plan.id)}> {@render cardBody(plan, isPopular)} {:else} diff --git a/src/app/editor/index.ts b/src/app/editor/index.ts index 4049365a..923e2beb 100644 --- a/src/app/editor/index.ts +++ b/src/app/editor/index.ts @@ -229,7 +229,7 @@ export const makeEditor = async ({ addProseMirrorPlugins() { return [ MentionSuggestion({ - editor: (this as any).editor, + editor: this.editor, search: (term: string) => get(searchProfiles)(term), getRelays: (pubkey: string) => relayLists.get().writeUrls(pubkey).get(), updateSignal: searchProfiles, @@ -244,7 +244,7 @@ export const makeEditor = async ({ TippySuggestion({ char: "~", name: "roomref", - editor: (this as any).editor, + editor: this.editor, search: (term: string) => get(roomReferenceSearch).searchValues(term), updateSignal: roomReferenceSearch, select: (id: string, props) => { @@ -265,7 +265,7 @@ export const makeEditor = async ({ ...(commandTarget ? [ CommandSuggestion({ - editor: (this as any).editor, + editor: this.editor, search: (term: string) => get(commandSearch)?.searchValues(term) ?? [], updateSignal: commandSearch, getAttributes: getCommandAttributes, diff --git a/src/app/routes.ts b/src/app/routes.ts index 6495c14e..a1d1eaf3 100644 --- a/src/app/routes.ts +++ b/src/app/routes.ts @@ -28,6 +28,7 @@ import {entityLink, PLATFORM_URL, PLATFORM_RELAYS} from "@app/env" import {decodeRelay, encodeRelay} from "@app/relays" import {DM_KINDS} from "@app/content" import {navigate, pushModal} from "@app/modal" +import type {NavigateOptions} from "@app/modal" import type {NotePointer} from "@app/social" import ChatEnable from "@app/components/ChatEnable.svelte" import NoteDetail from "@app/components/NoteDetail.svelte" @@ -288,7 +289,7 @@ export const goToNote = (pointer: NotePointer) => { return goToHome() } -export const goToEvent = (event: TrustedEvent, options: Record = {}) => { +export const goToEvent = (event: TrustedEvent, options: NavigateOptions = {}) => { const urls = Array.from(app.get().tracker.getRelays(event.id)) if (event.kind === NOTE) { diff --git a/src/app/uploads.ts b/src/app/uploads.ts index 307b6cae..82dea44b 100644 --- a/src/app/uploads.ts +++ b/src/app/uploads.ts @@ -210,10 +210,10 @@ export const uploadFile = async (file: File, options: UploadFileOptions = {}) => const result = {...task, tags, url} return {result} - } catch (e: any) { + } catch (e) { console.error("Error caught when uploading file:", e) - return {error: e.toString()} + return {error: String(e)} } } diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 9cd3cefd..fab98e15 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -42,8 +42,9 @@ import {syncKeyboard} from "@app/keyboard" import {getPageTitle} from "@app/title" import NewNotificationSound from "@src/app/components/NewNotificationSound.svelte" + import type {LayoutProps} from "./$types" - const {children} = $props() + const {children}: LayoutProps = $props() applySavedTheme() diff --git a/src/routes/[bech32]/+page.svelte b/src/routes/[bech32]/+page.svelte index 1a442e89..73393135 100644 --- a/src/routes/[bech32]/+page.svelte +++ b/src/routes/[bech32]/+page.svelte @@ -15,29 +15,17 @@ const {bech32} = params - const attemptToNavigate = async () => { - const profilePubkey = decodePubkey(bech32) - - if (profilePubkey) { - return goto(makeProfilePath(profilePubkey), {replaceState: true}) - } - - const {type, data} = nip19.decode(bech32) as any - - if (!["nevent", "naddr"].includes(type) && data.relays.length > 0) { - return goto("/", {replaceState: true}) - } - + const findEvent = (idOrAddress: string, relays: string[] = []) => { let found = false $network.loadComplete({ - relays: [LOCAL_RELAY_URL, ...data.relays], - filters: getIdFilters([type === "nevent" ? data.id : Address.fromNaddr(bech32).toString()]), + relays: [LOCAL_RELAY_URL, ...relays], + filters: getIdFilters([idOrAddress]), onEvent: (event: TrustedEvent) => { found = true if (event.kind === NOTE) { - goToNote({id: event.id, pubkey: event.pubkey, relays: data.relays}) + goToNote({id: event.id, pubkey: event.pubkey, relays}) } else { goToEvent(event, {replaceState: true}) } @@ -50,6 +38,24 @@ }) } + const attemptToNavigate = async () => { + const profilePubkey = decodePubkey(bech32) + + if (profilePubkey) { + await goto(makeProfilePath(profilePubkey), {replaceState: true}) + } else { + const decoded = nip19.decode(bech32) + + if (decoded.type === "nevent") { + findEvent(decoded.data.id, decoded.data.relays) + } else if (decoded.type === "naddr") { + findEvent(Address.fromNaddr(bech32).toString(), decoded.data.relays) + } else { + await goto("/", {replaceState: true}) + } + } + } + onMount(async () => { try { await attemptToNavigate() diff --git a/src/routes/chat/[chat]/+page.svelte b/src/routes/chat/[chat]/+page.svelte index 8823354d..7cced9ce 100644 --- a/src/routes/chat/[chat]/+page.svelte +++ b/src/routes/chat/[chat]/+page.svelte @@ -11,18 +11,15 @@ - +

@@ -39,18 +39,18 @@ Browse spaces

-
- -
+
- +
-
+
{#if HOSTING_ENABLED} - + {/if}
diff --git a/src/routes/settings/alerts/+page.svelte b/src/routes/settings/alerts/+page.svelte index 20683a4d..4526b422 100644 --- a/src/routes/settings/alerts/+page.svelte +++ b/src/routes/settings/alerts/+page.svelte @@ -24,15 +24,14 @@ settings.reminder_minutes = [...settings.reminder_minutes, 60] } - const removeReminder = (index: number) => { + const removeReminder = (index: number) => () => { settings.reminder_minutes = removeAt(index, settings.reminder_minutes) } - const onReminderInput = (e: Event) => { - const target = e.currentTarget as HTMLInputElement - const index = Number(target.dataset.index) + const onReminderInput = (index: number) => (e: Event & {currentTarget: HTMLInputElement}) => { + const minutes = Number(e.currentTarget.value) - settings.reminder_minutes = replaceAt(index, Number(target.value), settings.reminder_minutes) + settings.reminder_minutes = replaceAt(index, minutes, settings.reminder_minutes) } const onsubmit = preventDefault(async () => { @@ -157,16 +156,15 @@
diff --git a/src/routes/settings/content/+page.svelte b/src/routes/settings/content/+page.svelte index 7f6447d3..b98f685c 100644 --- a/src/routes/settings/content/+page.svelte +++ b/src/routes/settings/content/+page.svelte @@ -36,6 +36,8 @@ blossomServers = $userBlossomServerList?.urls() ?? [] } + const setTheme = (value: string) => () => theme.set(value) + const addServer = () => { blossomServers = [...blossomServers, ""] } @@ -98,7 +100,7 @@ diff --git a/src/routes/settings/profile/+page.svelte b/src/routes/settings/profile/+page.svelte index cd23fc7f..bd0def04 100644 --- a/src/routes/settings/profile/+page.svelte +++ b/src/routes/settings/profile/+page.svelte @@ -85,11 +85,15 @@ const startRecovery = () => pushModal(InfoKeys) + const toggleAdvanced = () => { + showAdvanced = !showAdvanced + } + let loading = $state(false) let showAdvanced = $state(false) - +
@@ -179,7 +183,7 @@ {/if} {#if pomadeEmail} -
+
@@ -194,7 +198,7 @@ Advanced - -
+
{/each} diff --git a/src/routes/spaces/[relay]/about/+page.svelte b/src/routes/spaces/[relay]/about/+page.svelte index 0eb2bb4e..7742f75f 100644 --- a/src/routes/spaces/[relay]/about/+page.svelte +++ b/src/routes/spaces/[relay]/about/+page.svelte @@ -32,7 +32,7 @@ {/snippet} - +
@@ -103,8 +103,8 @@
{/if}
-
-
+
+
diff --git a/src/routes/spaces/[relay]/admin/+page.svelte b/src/routes/spaces/[relay]/admin/+page.svelte index 9840459b..b8f218dd 100644 --- a/src/routes/spaces/[relay]/admin/+page.svelte +++ b/src/routes/spaces/[relay]/admin/+page.svelte @@ -8,6 +8,7 @@ import SpaceBar from "@app/components/SpaceBar.svelte" import RelayDetailCard from "@app/components/hosting/RelayDetailCard.svelte" import ActivityFeed from "@app/components/hosting/ActivityFeed.svelte" + import {PLATFORM_NAME} from "@app/env" import {deriveHostedRelay, deriveRelayActivity} from "@app/hosting" import {decodeRelay} from "@app/relays" import type {PageProps} from "./$types" @@ -40,9 +41,9 @@
-

Not a Coracle-hosted space

+

Not a {PLATFORM_NAME}-hosted space

- This space isn't hosted by Coracle Hosting, or you don't administer it. Only the owner of + This space isn't hosted by {PLATFORM_NAME}, or you don't administer it. Only the owner of a hosted relay can manage its plan, policies, and billing here.

diff --git a/src/routes/spaces/[relay]/articles/[address]/+page.svelte b/src/routes/spaces/[relay]/articles/[address]/+page.svelte index 05529692..61a509c2 100644 --- a/src/routes/spaces/[relay]/articles/[address]/+page.svelte +++ b/src/routes/spaces/[relay]/articles/[address]/+page.svelte @@ -63,13 +63,13 @@ - + {#if $event && $article} -
-
+
+
@@ -104,7 +104,7 @@
{#if topics.length > 0} -
+
{#each topics as topic (topic)} @@ -115,19 +115,19 @@
{/if} -
+
- -
+ +

Written by

-
+
diff --git a/src/routes/spaces/[relay]/calendar/+page.svelte b/src/routes/spaces/[relay]/calendar/+page.svelte index 550592e2..f274882b 100644 --- a/src/routes/spaces/[relay]/calendar/+page.svelte +++ b/src/routes/spaces/[relay]/calendar/+page.svelte @@ -2,6 +2,7 @@ import {onDestroy, onMount} from "svelte" import type {Readable} from "svelte/store" import {readable} from "svelte/store" + import {dateToSeconds} from "@welshman/lib" import type {Maybe} from "@welshman/lib" import type {TrustedEvent} from "@welshman/util" import {EVENT_TIME} from "@welshman/util" @@ -134,7 +135,7 @@ rangeLoading = true feed - .load(days[0].getTime() / 1000, addDays(days[days.length - 1], 1).getTime() / 1000) + .load(dateToSeconds(days[0]), dateToSeconds(addDays(days[days.length - 1], 1))) .finally(() => { // Paging faster than the relay answers would otherwise clear a newer range's spinner if (range === latestRange) { diff --git a/src/routes/spaces/[relay]/goals/+page.svelte b/src/routes/spaces/[relay]/goals/+page.svelte index cb0d3589..8a747cb4 100644 --- a/src/routes/spaces/[relay]/goals/+page.svelte +++ b/src/routes/spaces/[relay]/goals/+page.svelte @@ -107,7 +107,7 @@ {/snippet} - + {#if goals.length > 0}
@@ -138,7 +138,7 @@
{/if} -
+
{#each items as event (event.id)}
diff --git a/src/routes/spaces/[relay]/library/+page.svelte b/src/routes/spaces/[relay]/library/+page.svelte index 85904fdf..874d5743 100644 --- a/src/routes/spaces/[relay]/library/+page.svelte +++ b/src/routes/spaces/[relay]/library/+page.svelte @@ -63,7 +63,7 @@ const addLink = () => pushModal(PinSave, {url, address}) - const selectBoard = (board: PinboardReader) => + const selectBoard = (board: PinboardReader) => () => goto(makeLibraryPath(url, board.address()), {replaceState: true, noScroll: true}) onMount(() => { @@ -111,7 +111,7 @@ {url} {board} selected={board.address() === address} - onclick={() => selectBoard(board)} /> + onclick={selectBoard(board)} /> {/each}
{:else} diff --git a/src/routes/spaces/[relay]/threads/[id]/+page.svelte b/src/routes/spaces/[relay]/threads/[id]/+page.svelte index 6df82d71..be616147 100644 --- a/src/routes/spaces/[relay]/threads/[id]/+page.svelte +++ b/src/routes/spaces/[relay]/threads/[id]/+page.svelte @@ -164,7 +164,7 @@
{#if $event} -
+
- + {#snippet title()}
Choose your Hosting Plan
@@ -28,8 +28,8 @@

{/snippet}
-
-
+
+