Add themes page
This commit is contained in:
+4
-34
@@ -42,36 +42,6 @@
|
|||||||
font-family: Lato;
|
font-family: Lato;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dark"] {
|
|
||||||
--stark: #111;
|
|
||||||
--stark-content: #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="light"] {
|
|
||||||
--stark: #eee;
|
|
||||||
--stark-content: #111;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-stark,
|
|
||||||
.hover\:text-stark:hover {
|
|
||||||
color: var(--stark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-stark-content,
|
|
||||||
.hover\:text-stark-content:hover {
|
|
||||||
color: var(--stark-content);
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-stark,
|
|
||||||
.hover\:bg-stark:hover {
|
|
||||||
background-color: var(--stark);
|
|
||||||
}
|
|
||||||
|
|
||||||
.bg-stark-content,
|
|
||||||
.hover\:bg-stark-content:hover {
|
|
||||||
background-color: var(--stark-content);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card2 {
|
.card2 {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
background-color: oklch(var(--b2) / 1);
|
background-color: oklch(var(--b2) / 1);
|
||||||
@@ -87,19 +57,19 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
@apply max-w-2xl w-full p-12 m-auto;
|
@apply max-w-3xl w-full p-12 m-auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.heading {
|
.heading {
|
||||||
@apply text-2xl text-stark-content text-center;
|
@apply text-2xl text-center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.subheading {
|
.subheading {
|
||||||
@apply text-xl text-stark-content text-center;
|
@apply text-xl text-center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.superheading {
|
.superheading {
|
||||||
@apply text-4xl text-stark-content text-center;
|
@apply text-4xl text-center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.link {
|
.link {
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import {goto} from '$app/navigation'
|
||||||
|
import {append, uniqBy, now} from '@welshman/lib'
|
||||||
|
import {GROUPS, asDecryptedEvent, readList, editList, makeList, createList} from "@welshman/util"
|
||||||
|
import {pushToast} from '@app/toast'
|
||||||
|
import {pk, signer, repository, INDEXER_RELAYS} from '@app/base'
|
||||||
|
import {splitGroupId, loadRelay, loadGroup, getWriteRelayUrls, loadRelaySelections, publish, ensurePlaintext} from '@app/state'
|
||||||
|
|
||||||
|
export type ModifyTags = (tags: string[][]) => string[][]
|
||||||
|
|
||||||
|
export const updateList = async (kind: number, modifyTags: ModifyTags) => {
|
||||||
|
const $pk = pk.get()!
|
||||||
|
const $signer = signer.get()!
|
||||||
|
const [prev] = repository.query([{kinds: [kind], authors: [$pk]}])
|
||||||
|
|
||||||
|
// Preserve content instead of use encrypted tags because kind 3 content is used for
|
||||||
|
// relay selections in many places. Content isn't supported for mutes or relays so this is ok
|
||||||
|
const relays = [...INDEXER_RELAYS, ...getWriteRelayUrls(await loadRelaySelections($pk))]
|
||||||
|
const encrypt = (content: string) => $signer.nip44.encrypt($pk, content)
|
||||||
|
|
||||||
|
let encryptable
|
||||||
|
if (prev) {
|
||||||
|
const content = await ensurePlaintext(prev)
|
||||||
|
const list = readList(asDecryptedEvent(prev, {content}))
|
||||||
|
const publicTags = modifyTags(list.publicTags)
|
||||||
|
|
||||||
|
encryptable = editList({...list, publicTags})
|
||||||
|
} else {
|
||||||
|
const list = makeList({kind})
|
||||||
|
const publicTags = modifyTags(list.publicTags)
|
||||||
|
|
||||||
|
encryptable = createList({...list, publicTags})
|
||||||
|
}
|
||||||
|
|
||||||
|
const template = await encryptable.reconcile(encrypt)
|
||||||
|
const event = await $signer.sign({...template, created_at: now()})
|
||||||
|
|
||||||
|
await publish({event, relays})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const joinGroup = async (id: string) => {
|
||||||
|
const [url, nom] = splitGroupId(id)
|
||||||
|
const relay = await loadRelay(url)
|
||||||
|
|
||||||
|
if (!relay) {
|
||||||
|
return pushToast({
|
||||||
|
theme: "error",
|
||||||
|
message: "Sorry, we weren't able to find that relay."
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!relay.supported_nips?.includes(29)) {
|
||||||
|
return pushToast({
|
||||||
|
theme: "error",
|
||||||
|
message: "Sorry, it looks like that relay doesn't support nostr spaces."
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const group = await loadGroup(nom, [url])
|
||||||
|
|
||||||
|
if (!group) {
|
||||||
|
return pushToast({
|
||||||
|
theme: "error",
|
||||||
|
message: "Sorry, we weren't able to find that space."
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await updateList(GROUPS, (tags: string[][]) => uniqBy(t => t.join(''), append(["group", nom, url], tags)))
|
||||||
|
|
||||||
|
goto(`/spaces/${nom}`)
|
||||||
|
pushToast({
|
||||||
|
message: "Welcome to the space!"
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
<div class="absolute z-nav-active ml-2 h-[144px] w-12 bg-base-300" style={`top: ${$activeOffset}px`} />
|
<div class="absolute z-nav-active ml-2 h-[144px] w-12 bg-base-300" style={`top: ${$activeOffset}px`} />
|
||||||
<div class="flex h-full flex-col justify-between">
|
<div class="flex h-full flex-col justify-between">
|
||||||
<div>
|
<div>
|
||||||
<PrimaryNavItem title={$userProfile?.name} on:click={gotoHome}>
|
<PrimaryNavItem on:click={gotoHome}>
|
||||||
<div class="!flex w-10 items-center justify-center rounded-full border border-solid border-base-300">
|
<div class="!flex w-10 items-center justify-center rounded-full border border-solid border-base-300">
|
||||||
{#if $userProfile?.picture}
|
{#if $userProfile?.picture}
|
||||||
<img alt="" src={$userProfile.picture} />
|
<img alt="" src={$userProfile.picture} />
|
||||||
|
|||||||
@@ -40,10 +40,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
in:fly={{delay: 150}}
|
in:fly={{delay: 150}}
|
||||||
class="text-stark-content flex items-center justify-between px-4 py-2 text-sm font-bold uppercase">
|
class="flex items-center justify-between px-4 py-2 text-sm font-bold uppercase">
|
||||||
Conversations
|
Conversations
|
||||||
<div class="cursor-pointer">
|
<div class="cursor-pointer">
|
||||||
<Icon icon="add-circle" class="bg-stark-content" />
|
<Icon icon="add-circle" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -10,59 +10,24 @@
|
|||||||
import SpaceCreateFinish from '@app/components/SpaceCreateFinish.svelte'
|
import SpaceCreateFinish from '@app/components/SpaceCreateFinish.svelte'
|
||||||
import {pushModal} from '@app/modal'
|
import {pushModal} from '@app/modal'
|
||||||
import {pushToast} from '@app/toast'
|
import {pushToast} from '@app/toast'
|
||||||
import {GROUP_DELIMITER, splitGroupId, loadRelay, loadGroup, updateList} from '@app/state'
|
import {GROUP_DELIMITER, splitGroupId, loadRelay, loadGroup} from '@app/state'
|
||||||
|
import {joinGroup} from '@app/commands'
|
||||||
|
|
||||||
const back = () => history.back()
|
const back = () => history.back()
|
||||||
|
|
||||||
const browse = () => goto("/browse", {state: {}})
|
const browse = () => goto("/browse", {state: {}})
|
||||||
|
|
||||||
const tryJoin = async () => {
|
|
||||||
const [url, nom] = splitGroupId(id)
|
|
||||||
|
|
||||||
const info = await loadRelay(url)
|
|
||||||
|
|
||||||
if (!info) {
|
|
||||||
return pushToast({
|
|
||||||
theme: "error",
|
|
||||||
message: "Sorry, we weren't able to find that relay."
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!info.supported_nips?.includes(29)) {
|
|
||||||
return pushToast({
|
|
||||||
theme: "error",
|
|
||||||
message: "Sorry, it looks like that relay doesn't support nostr spaces."
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const group = await loadGroup(nom, [url])
|
|
||||||
|
|
||||||
if (!group) {
|
|
||||||
return pushToast({
|
|
||||||
theme: "error",
|
|
||||||
message: "Sorry, we weren't able to find that space."
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
await updateList(GROUPS, (tags: string[][]) => uniqBy(t => t.join(''), append(["group", nom, url], tags)))
|
|
||||||
|
|
||||||
goto(`/spaces/${nom}`)
|
|
||||||
pushToast({
|
|
||||||
message: "Welcome to the space!"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const join = async () => {
|
const join = async () => {
|
||||||
loading = true
|
loading = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await tryJoin()
|
await joinGroup(id)
|
||||||
} finally {
|
} finally {
|
||||||
loading = false
|
loading = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let id = "wss://devrelay.highlighter.com'group628195"
|
let id = ""
|
||||||
let loading = false
|
let loading = false
|
||||||
|
|
||||||
$: linkIsValid = Boolean(id.match(/.+\..+'.+/))
|
$: linkIsValid = Boolean(id.match(/.+\..+'.+/))
|
||||||
|
|||||||
+8
-34
@@ -5,8 +5,8 @@ import type {Maybe} from "@welshman/lib"
|
|||||||
import {uniq, uniqBy, groupBy, pushToMapKey, nthEq, batcher, postJson, stripProtocol, assoc, indexBy, now} from "@welshman/lib"
|
import {uniq, uniqBy, groupBy, pushToMapKey, nthEq, batcher, postJson, stripProtocol, assoc, indexBy, now} from "@welshman/lib"
|
||||||
import {getIdentifier, getRelayTags, getRelayTagValues, normalizeRelayUrl, getPubkeyTagValues, GROUP_META, PROFILE, RELAYS, FOLLOWS, MUTES, GROUPS, getGroupTags, readProfile, readList, asDecryptedEvent, editList, makeList, createList} from "@welshman/util"
|
import {getIdentifier, getRelayTags, getRelayTagValues, normalizeRelayUrl, getPubkeyTagValues, GROUP_META, PROFILE, RELAYS, FOLLOWS, MUTES, GROUPS, getGroupTags, readProfile, readList, asDecryptedEvent, editList, makeList, createList} from "@welshman/util"
|
||||||
import type {Filter, SignedEvent, CustomEvent, PublishedProfile, PublishedList} from '@welshman/util'
|
import type {Filter, SignedEvent, CustomEvent, PublishedProfile, PublishedList} from '@welshman/util'
|
||||||
import type {SubscribeRequest} from '@welshman/net'
|
import type {SubscribeRequest, PublishRequest} from '@welshman/net'
|
||||||
import {publish, subscribe} from '@welshman/net'
|
import {publish as basePublish, subscribe} from '@welshman/net'
|
||||||
import {decrypt} from '@welshman/signer'
|
import {decrypt} from '@welshman/signer'
|
||||||
import {deriveEvents, deriveEventsMapped, getter, withGetter} from "@welshman/store"
|
import {deriveEvents, deriveEventsMapped, getter, withGetter} from "@welshman/store"
|
||||||
import {parseJson, createSearch} from '@lib/util'
|
import {parseJson, createSearch} from '@lib/util'
|
||||||
@@ -68,6 +68,12 @@ export const createCollection = <T>({
|
|||||||
return {indexStore, getIndex, deriveItem, loadItem, getItem}
|
return {indexStore, getIndex, deriveItem, loadItem, getItem}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const publish = (request: PublishRequest) => {
|
||||||
|
repository.publish(request.event)
|
||||||
|
|
||||||
|
return basePublish(request)
|
||||||
|
}
|
||||||
|
|
||||||
export const load = (request: SubscribeRequest) =>
|
export const load = (request: SubscribeRequest) =>
|
||||||
new Promise<Maybe<CustomEvent>>(resolve => {
|
new Promise<Maybe<CustomEvent>>(resolve => {
|
||||||
const sub = subscribe({closeOnEose: true, timeout: 3000, delay: 50, ...request})
|
const sub = subscribe({closeOnEose: true, timeout: 3000, delay: 50, ...request})
|
||||||
@@ -81,38 +87,6 @@ export const load = (request: SubscribeRequest) =>
|
|||||||
sub.emitter.on('complete', () => resolve(undefined))
|
sub.emitter.on('complete', () => resolve(undefined))
|
||||||
})
|
})
|
||||||
|
|
||||||
export type ModifyTags = (tags: string[][]) => string[][]
|
|
||||||
|
|
||||||
export const updateList = async (kind: number, modifyTags: ModifyTags) => {
|
|
||||||
const $pk = pk.get()!
|
|
||||||
const $signer = signer.get()!
|
|
||||||
const [prev] = repository.query([{kinds: [kind], authors: [$pk]}])
|
|
||||||
|
|
||||||
// Preserve content instead of use encrypted tags because kind 3 content is used for
|
|
||||||
// relay selections in many places. Content isn't supported for mutes or relays so this is ok
|
|
||||||
const relays = [...INDEXER_RELAYS, ...getWriteRelayUrls(await loadRelaySelections($pk))]
|
|
||||||
const encrypt = (content: string) => $signer.nip44.encrypt($pk, content)
|
|
||||||
|
|
||||||
let encryptable
|
|
||||||
if (prev) {
|
|
||||||
const content = await ensurePlaintext(prev)
|
|
||||||
const list = readList(asDecryptedEvent(prev, {content}))
|
|
||||||
const publicTags = modifyTags(list.publicTags)
|
|
||||||
|
|
||||||
encryptable = editList({...list, publicTags})
|
|
||||||
} else {
|
|
||||||
const list = makeList({kind})
|
|
||||||
const publicTags = modifyTags(list.publicTags)
|
|
||||||
|
|
||||||
encryptable = createList({...list, publicTags})
|
|
||||||
}
|
|
||||||
|
|
||||||
const template = await encryptable.reconcile(encrypt)
|
|
||||||
const event = await $signer.sign({...template, created_at: now()})
|
|
||||||
|
|
||||||
await publish({event, relays})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Freshness
|
// Freshness
|
||||||
|
|
||||||
export const freshness = withGetter(writable<Record<string, number>>({}))
|
export const freshness = withGetter(writable<Record<string, number>>({}))
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
import {synced} from '@lib/util'
|
||||||
|
|
||||||
|
export const theme = synced<string>("theme", "dark")
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
<div class="flex gap-6 py-4 flex-grow items-center">
|
<div class="flex gap-6 py-4 flex-grow items-center">
|
||||||
<Icon class="bg-accent" size={7} {icon} />
|
<Icon class="bg-accent" size={7} {icon} />
|
||||||
<div class="flex flex-col gap-1 flex-grow">
|
<div class="flex flex-col gap-1 flex-grow">
|
||||||
<p class="text-stark-content">{title}</p>
|
<p class="text-bold">{title}</p>
|
||||||
<p class="text-xs"><slot /></p>
|
<p class="text-xs"><slot /></p>
|
||||||
</div>
|
</div>
|
||||||
<Icon size={7} icon="alt-arrow-right" />
|
<Icon size={7} icon="alt-arrow-right" />
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<slot name="label" />
|
<slot name="label" />
|
||||||
</label>
|
</label>
|
||||||
<slot name="input" />
|
<slot name="input" />
|
||||||
<p class="text-sm text-neutral-200">
|
<p class="text-sm">
|
||||||
<slot name="info" />
|
<slot name="info" />
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
|
|
||||||
export let title
|
export let title = ""
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Button on:click class="relative z-nav-item flex h-14 w-14 items-center justify-center">
|
<Button on:click class="relative z-nav-item flex h-14 w-14 items-center justify-center">
|
||||||
<div
|
<div
|
||||||
class="avatar tooltip tooltip-right cursor-pointer rounded-full bg-base-300 p-1"
|
class="avatar tooltip-right cursor-pointer rounded-full bg-base-300 p-1"
|
||||||
|
class:tooltip={title}
|
||||||
data-tip={title}>
|
data-tip={title}>
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
|
|
||||||
<a
|
<a
|
||||||
{href}
|
{href}
|
||||||
class="group justify-start border-none transition-all hover:bg-base-100"
|
class="group justify-start border-none transition-all hover:bg-base-100 hover:text-base-content"
|
||||||
class:text-stark-content={active}
|
class:text-base-content={active}
|
||||||
class:bg-base-100={active}>
|
class:bg-base-100={active}>
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<slot />
|
<slot />
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
import PrimaryNav from "@app/components/PrimaryNav.svelte"
|
import PrimaryNav from "@app/components/PrimaryNav.svelte"
|
||||||
import SecondaryNav from "@app/components/SecondaryNav.svelte"
|
import SecondaryNav from "@app/components/SecondaryNav.svelte"
|
||||||
import {modals, clearModal} from "@app/modal"
|
import {modals, clearModal} from "@app/modal"
|
||||||
|
import {theme} from "@app/theme"
|
||||||
import {session, sessions, pk, repository} from "@app/base"
|
import {session, sessions, pk, repository} from "@app/base"
|
||||||
import {plaintext, relays, handles} from "@app/state"
|
import {plaintext, relays, handles} from "@app/state"
|
||||||
import {initStorage} from "@app/storage"
|
import {initStorage} from "@app/storage"
|
||||||
@@ -59,9 +60,9 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#await ready}
|
{#await ready}
|
||||||
<div data-theme="dark" />
|
<div data-theme={$theme} />
|
||||||
{:then}
|
{:then}
|
||||||
<div data-theme="dark">
|
<div data-theme={$theme}>
|
||||||
<div class="flex h-screen">
|
<div class="flex h-screen">
|
||||||
<PrimaryNav />
|
<PrimaryNav />
|
||||||
<SecondaryNav />
|
<SecondaryNav />
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
<div class="hero min-h-screen bg-base-200">
|
<div class="hero min-h-screen bg-base-200">
|
||||||
<div class="hero-content">
|
<div class="hero-content">
|
||||||
<div class="column content gap-4">
|
<div class="column content gap-4">
|
||||||
<h1 class="text-stark-content text-center text-5xl">Welcome to</h1>
|
<h1 class="text-center text-5xl">Welcome to</h1>
|
||||||
<h1 class="text-stark-content mb-4 text-center text-5xl font-bold uppercase">Flotilla</h1>
|
<h1 class="mb-4 text-center text-5xl font-bold uppercase">Flotilla</h1>
|
||||||
<div class="grid lg:grid-cols-2 gap-3">
|
<div class="grid lg:grid-cols-2 gap-3">
|
||||||
<CardButton icon="add-circle" title="Create a space" class="h-24" on:click={createSpace}>
|
<CardButton icon="add-circle" title="Create a space" class="h-24" on:click={createSpace}>
|
||||||
Invite all your friends, do life together.
|
Invite all your friends, do life together.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import Masonry from 'svelte-bricks'
|
import Masonry from 'svelte-bricks'
|
||||||
import {GROUP_META} from '@welshman/util'
|
import {GROUP_META} from '@welshman/util'
|
||||||
import Icon from '@lib/components/Icon.svelte'
|
import Icon from '@lib/components/Icon.svelte'
|
||||||
import {load, relays, searchGroups} from '@app/state'
|
import {load, relays, groups, searchGroups} from '@app/state'
|
||||||
|
|
||||||
let term = ""
|
let term = ""
|
||||||
|
|
||||||
@@ -15,18 +15,16 @@
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="content column gap-8">
|
<div class="content column gap-4">
|
||||||
<div class="column gap-4 pt-20 pb-12">
|
<h1 class="superheading mt-20">Discover Spaces</h1>
|
||||||
<h1 class="superheading">Discover Spaces</h1>
|
<p class="text-center">Find communities all across the nostr network</p>
|
||||||
<p class="text-center">Find communities all across the nostr network</p>
|
<label class="input input-bordered w-full flex items-center gap-2">
|
||||||
<label class="input input-bordered w-full flex items-center gap-2">
|
<Icon icon="magnifer" />
|
||||||
<Icon icon="magnifer" />
|
<input bind:value={term} class="grow" type="text" placeholder="Search for spaces..." />
|
||||||
<input bind:value={term} class="grow" type="text" placeholder="Search for spaces..." />
|
</label>
|
||||||
</label>
|
<Masonry animate={false} items={$searchGroups.searchOptions(term)} minColWidth={250} maxColWidth={800} gap={16} idKey="nom" let:item>
|
||||||
</div>
|
|
||||||
<Masonry items={$searchGroups.searchOptions(term)} minColWidth={200} maxColWidth={400} gap={16} idKey="nom" let:item>
|
|
||||||
<div class="card bg-base-100 shadow-xl">
|
<div class="card bg-base-100 shadow-xl">
|
||||||
<div class="avatar center mt-4">
|
<div class="avatar center mt-8">
|
||||||
<div class="w-20 rounded-full bg-base-300 border-2 border-solid border-base-300 !flex center">
|
<div class="w-20 rounded-full bg-base-300 border-2 border-solid border-base-300 !flex center">
|
||||||
{#if item?.picture}
|
{#if item?.picture}
|
||||||
<img alt="" src={item.picture} />
|
<img alt="" src={item.picture} />
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import themes from "daisyui/src/theming/themes"
|
||||||
|
import {identity} from '@welshman/lib'
|
||||||
|
import {createSearch} from '@lib/util'
|
||||||
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
|
import {theme} from '@app/theme'
|
||||||
|
|
||||||
|
let term = ""
|
||||||
|
|
||||||
|
const searchThemes = createSearch(Object.keys(themes), {getValue: identity})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="content column gap-4">
|
||||||
|
<h1 class="superheading mt-20">Discover Themes</h1>
|
||||||
|
<p class="text-center">Make Flotilla look just how you like it</p>
|
||||||
|
<label class="input input-bordered w-full flex items-center gap-2">
|
||||||
|
<Icon icon="magnifer" />
|
||||||
|
<input bind:value={term} class="grow" type="text" placeholder="Search for themes..." />
|
||||||
|
</label>
|
||||||
|
<div class="grid grid-cols-2 md:grid-cols-2 gap-4">
|
||||||
|
{#each searchThemes.searchValues(term) as name}
|
||||||
|
<div class="card bg-base-100 shadow-xl" data-theme={name}>
|
||||||
|
<div class="card-body">
|
||||||
|
<h2 class="card-title justify-center capitalize card2">{name}</h2>
|
||||||
|
<div class="card-actions">
|
||||||
|
<button class="btn btn-primary w-full" on:click={() => theme.set(name)}>Use Theme</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
Reference in New Issue
Block a user