2024-10-03 21:32:55 +00:00
|
|
|
<script lang="ts">
|
2026-07-30 01:24:57 +00:00
|
|
|
import {BlossomServerLists, MuteLists, publish} from "@welshman/app"
|
2026-03-11 22:48:36 +00:00
|
|
|
import NotesMinimalistic from "@assets/icons/notes-minimalistic.svg?dataurl"
|
2026-04-17 21:58:52 +00:00
|
|
|
import AddCircle from "@assets/icons/add-circle.svg?dataurl"
|
2026-09-01 04:56:37 +00:00
|
|
|
import Microphone from "@assets/icons/microphone.svg?dataurl"
|
2025-02-04 03:28:29 +00:00
|
|
|
import {preventDefault} from "@lib/html"
|
2024-10-08 18:39:16 +00:00
|
|
|
import Field from "@lib/components/Field.svelte"
|
2024-10-17 22:21:22 +00:00
|
|
|
import FieldInline from "@lib/components/FieldInline.svelte"
|
2026-07-15 16:30:17 +00:00
|
|
|
import ToggleInput from "@lib/components/ToggleInput.svelte"
|
2025-04-29 18:04:39 +00:00
|
|
|
import InputList from "@lib/components/InputList.svelte"
|
2026-09-01 04:56:37 +00:00
|
|
|
import Link from "@lib/components/Link.svelte"
|
2026-03-11 22:48:36 +00:00
|
|
|
import Icon from "@lib/components/Icon.svelte"
|
2026-06-24 20:56:53 +00:00
|
|
|
import Card from "@lib/components/Card.svelte"
|
2024-10-08 18:39:16 +00:00
|
|
|
import Button from "@lib/components/Button.svelte"
|
2026-08-31 23:03:20 +00:00
|
|
|
import Spinner from "@lib/components/Spinner.svelte"
|
2026-06-24 20:56:53 +00:00
|
|
|
import PageContent from "@lib/components/PageContent.svelte"
|
2024-10-08 18:39:16 +00:00
|
|
|
import ProfileMultiSelect from "@app/components/ProfileMultiSelect.svelte"
|
2026-07-28 16:16:26 +00:00
|
|
|
import {blossomServerLists, deriveUserItem, muteLists} from "@app/core"
|
2026-06-08 21:14:40 +00:00
|
|
|
import {pushToast} from "@app/toast"
|
2026-06-08 22:33:38 +00:00
|
|
|
import {PLATFORM_NAME} from "@app/env"
|
2026-08-22 20:12:11 +00:00
|
|
|
import {userSettingsValues, publishSettings, createSettingsForm} from "@app/settings"
|
2024-10-17 23:03:50 +00:00
|
|
|
|
2026-07-30 01:24:57 +00:00
|
|
|
const userMuteList = deriveUserItem(MuteLists)
|
|
|
|
|
const userBlossomServerList = deriveUserItem(BlossomServerLists)
|
2026-07-28 16:16:26 +00:00
|
|
|
|
2024-10-03 21:32:55 +00:00
|
|
|
const reset = () => {
|
2026-08-22 20:12:11 +00:00
|
|
|
settings.set({...$userSettingsValues})
|
2026-07-28 16:16:26 +00:00
|
|
|
mutedPubkeys = $userMuteList?.pubkeys() ?? []
|
|
|
|
|
blossomServers = $userBlossomServerList?.urls() ?? []
|
2024-10-03 21:32:55 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-17 21:58:52 +00:00
|
|
|
const addServer = () => {
|
|
|
|
|
blossomServers = [...blossomServers, ""]
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-04 03:28:29 +00:00
|
|
|
const onsubmit = preventDefault(async () => {
|
2026-08-31 23:03:20 +00:00
|
|
|
loading = true
|
2024-10-17 22:46:26 +00:00
|
|
|
|
2026-08-31 23:03:20 +00:00
|
|
|
try {
|
|
|
|
|
await publishSettings($settings)
|
2024-10-03 21:32:55 +00:00
|
|
|
|
2026-08-31 23:03:20 +00:00
|
|
|
await $muteLists
|
|
|
|
|
.setMutes({publicTags: mutedPubkeys.map(pubkey => ["p", pubkey])})
|
|
|
|
|
.then(publish)
|
2025-04-29 18:04:39 +00:00
|
|
|
|
2026-08-31 23:03:20 +00:00
|
|
|
await $blossomServerLists.setUrls($state.snapshot(blossomServers)).then(publish)
|
|
|
|
|
|
|
|
|
|
pushToast({message: "Your settings have been saved!"})
|
|
|
|
|
} finally {
|
|
|
|
|
loading = false
|
|
|
|
|
}
|
2025-02-04 03:28:29 +00:00
|
|
|
})
|
2024-10-17 22:46:26 +00:00
|
|
|
|
2026-08-22 20:12:11 +00:00
|
|
|
const settings = createSettingsForm()
|
2026-08-31 23:03:20 +00:00
|
|
|
let loading = $state(false)
|
2026-07-28 16:16:26 +00:00
|
|
|
let mutedPubkeys = $state($userMuteList?.pubkeys() ?? [])
|
|
|
|
|
let blossomServers = $state($userBlossomServerList?.urls() ?? [])
|
2026-09-09 01:07:48 +00:00
|
|
|
let loadedMutes = Boolean($userMuteList)
|
|
|
|
|
let loadedServers = Boolean($userBlossomServerList)
|
|
|
|
|
|
|
|
|
|
// Both lists come off the wire, so on a fresh load they land after this page has mounted. Each
|
|
|
|
|
// field takes its stored value up when it arrives, since a form that never saw it would publish
|
|
|
|
|
// its own emptiness back over it.
|
|
|
|
|
$effect(() => {
|
|
|
|
|
if (!loadedMutes && $userMuteList) {
|
|
|
|
|
loadedMutes = true
|
|
|
|
|
mutedPubkeys = $userMuteList.pubkeys()
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
$effect(() => {
|
|
|
|
|
if (!loadedServers && $userBlossomServerList) {
|
|
|
|
|
loadedServers = true
|
|
|
|
|
blossomServers = $userBlossomServerList.urls()
|
|
|
|
|
}
|
|
|
|
|
})
|
2024-10-03 21:32:55 +00:00
|
|
|
</script>
|
|
|
|
|
|
2026-06-24 20:56:53 +00:00
|
|
|
<form {onsubmit}>
|
|
|
|
|
<PageContent>
|
|
|
|
|
<Card class="flex flex-col gap-4">
|
|
|
|
|
<strong class="flex items-center gap-3 text-lg">
|
|
|
|
|
<Icon icon={NotesMinimalistic} />
|
|
|
|
|
Content Settings
|
|
|
|
|
</strong>
|
|
|
|
|
<FieldInline>
|
|
|
|
|
{#snippet label()}
|
|
|
|
|
<p>Hide sensitive content?</p>
|
|
|
|
|
{/snippet}
|
|
|
|
|
{#snippet input()}
|
2026-08-22 20:12:11 +00:00
|
|
|
<ToggleInput bind:checked={$settings.hide_sensitive} />
|
2026-06-24 20:56:53 +00:00
|
|
|
{/snippet}
|
|
|
|
|
{#snippet info()}
|
|
|
|
|
<p>
|
|
|
|
|
If content is marked by the author as sensitive, {PLATFORM_NAME} will hide it by default.
|
|
|
|
|
</p>
|
|
|
|
|
{/snippet}
|
|
|
|
|
</FieldInline>
|
|
|
|
|
<FieldInline>
|
|
|
|
|
{#snippet label()}
|
|
|
|
|
<p>Show media?</p>
|
|
|
|
|
{/snippet}
|
|
|
|
|
{#snippet input()}
|
2026-08-22 20:12:11 +00:00
|
|
|
<ToggleInput bind:checked={$settings.show_media} />
|
2026-06-24 20:56:53 +00:00
|
|
|
{/snippet}
|
|
|
|
|
{#snippet info()}
|
|
|
|
|
<p>Use this to disable link previews and image rendering.</p>
|
|
|
|
|
{/snippet}
|
|
|
|
|
</FieldInline>
|
|
|
|
|
<Field>
|
|
|
|
|
{#snippet label()}
|
|
|
|
|
<p>Muted Accounts</p>
|
|
|
|
|
{/snippet}
|
|
|
|
|
{#snippet input()}
|
|
|
|
|
<div>
|
|
|
|
|
<ProfileMultiSelect bind:value={mutedPubkeys} />
|
|
|
|
|
</div>
|
|
|
|
|
{/snippet}
|
|
|
|
|
</Field>
|
|
|
|
|
</Card>
|
|
|
|
|
<Card class="flex flex-col gap-4 shadow-md">
|
|
|
|
|
<strong class="text-lg">Editor Settings</strong>
|
|
|
|
|
<Field>
|
|
|
|
|
{#snippet label()}
|
|
|
|
|
<p>Send Delay</p>
|
|
|
|
|
{/snippet}
|
|
|
|
|
{#snippet input()}
|
|
|
|
|
<input
|
|
|
|
|
class="range w-full"
|
|
|
|
|
type="range"
|
|
|
|
|
min="0"
|
|
|
|
|
max="10000"
|
|
|
|
|
step="1000"
|
2026-08-22 20:12:11 +00:00
|
|
|
bind:value={$settings.send_delay} />
|
2026-06-24 20:56:53 +00:00
|
|
|
{/snippet}
|
|
|
|
|
{#snippet info()}
|
|
|
|
|
<p>
|
2026-08-22 20:12:11 +00:00
|
|
|
Delay sending chat messages for {$settings.send_delay / 1000}
|
|
|
|
|
{$settings.send_delay === 1000 ? "second" : "seconds"}.
|
2026-06-24 20:56:53 +00:00
|
|
|
</p>
|
|
|
|
|
{/snippet}
|
2026-09-01 04:56:37 +00:00
|
|
|
</Field>
|
|
|
|
|
<Field>
|
|
|
|
|
{#snippet label()}
|
2026-09-09 14:43:55 +00:00
|
|
|
<p>OpenRouter</p>
|
2026-09-01 04:56:37 +00:00
|
|
|
{/snippet}
|
|
|
|
|
{#snippet input()}
|
|
|
|
|
<label class="input flex w-full items-center gap-2">
|
|
|
|
|
<Icon icon={Microphone} />
|
|
|
|
|
<input
|
|
|
|
|
bind:value={$settings.openrouter_key}
|
|
|
|
|
autocomplete="off"
|
|
|
|
|
name="flotilla-openrouter-key"
|
|
|
|
|
placeholder="OpenRouter API key"
|
|
|
|
|
class="grow"
|
|
|
|
|
type="password" />
|
|
|
|
|
</label>
|
|
|
|
|
{/snippet}
|
|
|
|
|
{#snippet info()}
|
|
|
|
|
<p>
|
|
|
|
|
Add an <Link external href="https://openrouter.ai/settings/keys" class="text-primary"
|
2026-09-09 14:43:55 +00:00
|
|
|
>OpenRouter API key</Link> to dictate messages using the microphone button in your composer,
|
|
|
|
|
and to have messages read out loud to you.
|
2026-09-01 04:56:37 +00:00
|
|
|
</p>
|
|
|
|
|
{/snippet}
|
2026-06-24 20:56:53 +00:00
|
|
|
</Field>
|
|
|
|
|
<Field>
|
|
|
|
|
{#snippet label()}
|
|
|
|
|
<p>Media Server</p>
|
|
|
|
|
{/snippet}
|
|
|
|
|
{#snippet secondary()}
|
|
|
|
|
<Button class="link text-sm underline flex items-center gap-1" onclick={addServer}>
|
|
|
|
|
<Icon icon={AddCircle} size={4} />
|
|
|
|
|
Add Server
|
|
|
|
|
</Button>
|
|
|
|
|
{/snippet}
|
|
|
|
|
{#snippet input()}
|
|
|
|
|
<InputList allowAdd={false} bind:value={blossomServers} />
|
|
|
|
|
{/snippet}
|
|
|
|
|
{#snippet info()}
|
|
|
|
|
<p>Choose a media server type and url for files you upload to {PLATFORM_NAME}.</p>
|
|
|
|
|
{/snippet}
|
|
|
|
|
</Field>
|
|
|
|
|
</Card>
|
|
|
|
|
<Card class="sticky -bottom-3 shadow-md flex flex-row items-center justify-between gap-4">
|
2026-08-31 23:03:20 +00:00
|
|
|
<Button class="button button-neutral" onclick={reset} disabled={loading}
|
|
|
|
|
>Discard Changes</Button>
|
|
|
|
|
<Button class="button button-primary" type="submit" disabled={loading}>
|
|
|
|
|
<Spinner {loading}>Save Changes</Spinner>
|
|
|
|
|
</Button>
|
2026-06-24 20:56:53 +00:00
|
|
|
</Card>
|
|
|
|
|
</PageContent>
|
2024-10-03 21:32:55 +00:00
|
|
|
</form>
|