M README.md => README.md +5 -2
@@ 1,7 1,10 @@
**basingstoke** is an exciting activitypub server for your computer.
-[read the manual](https://goober.city/manual/) for build instructions and so
-on.
+### links
+
+* [build it](./bonus/build.md) (tl;dr `npm i && npm run dev`)
+* [faq](./bonus/faq.md)
+* [contribute](./bonus/contributing.md)
### attribution
A bonus/code_of_conduct.md => bonus/code_of_conduct.md +0 -0
A bonus/contributing.md => bonus/contributing.md +1 -0
@@ 0,0 1,1 @@
+# <
\ No newline at end of file
A bonus/faq.md => bonus/faq.md +5 -0
@@ 0,0 1,5 @@
+# frequently anticipated questions
+
+i know many of basingstoke's design decisions are questionable. some of that is
+due to incompetence, but some of it was done that way on purpose.
+
M => +8 -6
@@ 6,7 6,7 @@
stagger,
type TimelineDefinition,
} from "motion";
import { afterUpdate, onMount } from "svelte";
import { afterUpdate, onMount, setContext } from "svelte";
const expo_out: Easing = [0.16, 1, 0.3, 1];
const padding = 24;
@@ 16,6 16,8 @@
export let trigger: Element | undefined = undefined;
export let title: string;
export let anchor: "left" | "right" = "left";
export let mode: "list" | "freestyle" = "list";
setContext("thecloser", () => close_menu());
let menu: HTMLDialogElement | null = null;
let bg: HTMLDivElement | null = null;
@@ 24,7 26,7 @@
// TODO: this makes the items invisible!
let reduced_motion = true;
function outside_click(event: MouseEvent) {
function outside_click(event: MouseEvent) {
if (
open &&
event.target &&
@@ 57,7 59,7 @@
],
];
if (!$$slots.notamenu) {
if (mode == "list") {
timeline_def.push([
//@ts-ignore this is fine. it works
content.children,
@@ 85,7 87,7 @@
},
]);
}
if (anchor == "right") {
xy[0] -= menu!.offsetWidth;
}
@@ 146,9 148,9 @@
{open}
>
<div bind:this={bg} class="bg" aria-hidden></div>
{#if $$slots.notamenu}
{#if mode == "freestyle"}
<div bind:this={content}>
<slot name="notamenu" />
<slot />
</div>
{:else}
<ul role="menu" bind:this={content} class="content">
M => +10 -2
@@ 1,12 1,20 @@
<script lang="ts">
import Icon from "$lib/Icons/Icon.svelte";
import { getContext } from "svelte";
export let icon: string | undefined = undefined,
shortcut: string[] = [];
shortcut: string[] = [],
autoclose = true;
const the_closer = getContext<() => void | undefined>("thecloser");
</script>
<li>
<button on:click role="menuitem">
<button
on:click
on:click={() => (autoclose && the_closer ? the_closer() : null)}
role="menuitem"
>
{#if icon}
<Icon source={icon}></Icon>
{/if}
A => +94 -0
@@ 0,0 1,94 @@
<script lang="ts">
import { animate } from "motion";
import { onDestroy, onMount } from "svelte";
let scrolled = false,
footer: HTMLElement,
bg: HTMLDivElement,
observer: IntersectionObserver;
onMount(() => {
observer = new IntersectionObserver(
([e]) => {
if (e.intersectionRatio == 1) {
enbiggen();
} else {
ensmallen();
}
},
{
threshold: [1],
rootMargin: "0px 0px -1px 0px",
},
);
observer.observe(footer);
});
function enbiggen() {
if (bg) {
animate(
bg,
{
opacity: 0,
},
{
duration: 0.1,
easing: "ease-out",
},
).finished.then(() => (bg.style.display = "none"));
}
scrolled = false;
}
function ensmallen() {
if (bg) {
bg.style.display = "block";
animate(
bg,
{
opacity: 1,
},
{
duration: 0.1,
easing: "ease-out",
},
);
}
scrolled = true;
}
onDestroy(() => observer.disconnect());
</script>
<div bind:this={bg} class="bg" class:scrolled></div>
<footer bind:this={footer} class:scrolled {...$$restProps}>
<slot {scrolled} />
</footer>
<style>
footer {
position: sticky;
bottom: 0px;
width: 100%;
height: 3.5rem;
display: flex;
align-items: end;
padding: 1rem 0;
}
.bg {
position: fixed;
bottom: 0;
left: 0;
width: 100vw;
height: 3.5rem;
pointer-events: none;
background: color-mix(in srgb, var(--background), var(--foreground) 7.5%);
&.scrolled {
opacity: 1;
}
}
</style>
M => +4 -5
@@ 3,7 3,8 @@
export let open = false,
title: string,
anchor: "left" | "right" = "left";
anchor: "left" | "right" = "left",
mode: "list" | "freestyle" = "list";
let xy: [number, number] = [0, 0],
trigger: HTMLButtonElement;
@@ 17,8 18,6 @@
<slot name="button" open={open_menu} />
<Menu bind:open bind:trigger bind:xy {title} {anchor}>
<svelte:fragment slot="notamenu">
<slot />
</svelte:fragment>
<Menu bind:open bind:trigger bind:xy bind:mode {title} {anchor}>
<slot />
</Menu>
M src/lib/Colours/colours.json => src/lib/Colours/colours.json +14 -114
@@ 1,116 1,16 @@
[
- {
- "background": "#fafafa",
- "foreground": "#0a0a0a",
- "backgroundTone": "#f6f6f6",
- "foregroundTone": "#161616"
- },
- {
- "background": "#faf972",
- "foreground": "#3a2e0c",
- "backgroundMix": "black",
- "foregroundMix": "black",
- "backgroundTone": "#E4E35E",
- "foregroundTone": "#574921"
- },
- {
- "background": "#e7dbf4",
- "foreground": "#71213b",
- "backgroundMix": "white",
- "foregroundMix": "black",
- "backgroundTone": "#e7dbf4",
- "foregroundTone": "#71213b"
- },
- {
- "background": "#E7F6DA",
- "foreground": "#7C5DB4",
- "backgroundMix": "white",
- "foregroundMix": "black",
- "backgroundTone": "#dbe2d5",
- "foregroundTone": "#7658ad"
- },
- {
- "background": "#2f024c",
- "foreground": "#00ff7f",
- "backgroundMix": "white",
- "foregroundMix": "white",
- "backgroundTone": "#4F1276",
- "foregroundTone": "#1BDA7A"
- },
- {
- "background": "#287b61",
- "foreground": "#effaec",
- "backgroundMix": "black",
- "foregroundMix": "white",
- "backgroundTone": "#349979",
- "foregroundTone": "#DAEBD5"
- },
- {
- "background": "#731919",
- "foreground": "#f6bc06",
- "backgroundMix": "black",
- "foregroundMix": "white",
- "backgroundTone": "#5C1010",
- "foregroundTone": "#DAAA16"
- },
- {
- "background": "#f9f3fe",
- "foreground": "#8929ea",
- "backgroundTone": "#f0e5fb",
- "foregroundTone": "#a65df0"
- },
- {
- "background": "#8A1C22",
- "foreground": "#F5C79E",
- "backgroundTone": "#7f181d",
- "foregroundTone": "#eab98f"
- },
- {
- "background": "#0000FF",
- "foreground": "#FFFFFF",
- "backgroundTone": "#0000D0",
- "foregroundTone": "#DADADA"
- },
- {
- "background": "#02635F",
- "foreground": "#26EBCE",
- "backgroundTone": "#025451",
- "foregroundTone": "#1edbbf"
- },
- {
- "background": "#E173CC",
- "foreground": "#570B3E",
- "backgroundTone": "#d365bf",
- "foregroundTone": "#490834"
- },
- {
- "background": "#030A0D",
- "foreground": "#B8604C",
- "backgroundTone": "#0a161c",
- "foregroundTone": "#9e4c3a"
- },
- {
- "background": "#0D2714",
- "foreground": "#94AB5B",
- "backgroundTone": "#14381e",
- "foregroundTone": "#859b4f"
- },
- {
- "background": "#addcd3",
- "foreground": "#6712ba",
- "backgroundTone": "#98cec4",
- "foregroundTone": "#5d0faa"
- },
- {
- "background": "#f4edf3",
- "foreground": "#a53554",
- "backgroundTone": "#eadae8",
- "foregroundTone": "#9e314f"
- },
- {
- "background": "#DAD5FD",
- "foreground": "#7111F9",
- "backgroundTone": "#cec8f4",
- "foregroundTone": "#7b24f4"
- }
+ ["b&w", "#fafafa", "#0a0a0a"],
+ ["high contrast", "#FFFFFF", "#0000FF"],
+ ["sandy", "#FEF3C4", "#6B4336"],
+ ["wes anderson", "#872720", "#F9C600"],
+ ["red", "#F4EDF3", "#A53554"],
+ ["joe", "#F8D1AE", "#9E2C2C"],
+ ["halloween", "#341606", "#F25C21"],
+ ["rust", "#040C10", "#C6755E"],
+ ["forestry", "#81CE8B", "#144D31"],
+ ["swampy", "#0F3219", "#A4B76E"],
+ ["foliage-y", "#F2FAF0", "#2F8C74"],
+ ["quiet", "#E7F6DA", "#7C5DB4"],
+ ["parma violet", "#E7E4FB", "#490DF4"],
+ ["charcoal", "#021F2C", "#808FA6"]
]
M src/lib/Colours/index.ts => src/lib/Colours/index.ts +8 -24
@@ 1,36 1,26 @@
-import { set, z } from 'zod';
import coloursJson from './colours.json';
import { writable, type Writable } from 'svelte/store';
import { notify_colours_changed } from '$lib/switching';
-const colour_set_schema = z.object({
- background: z.string(),
- foreground: z.string(),
- backgroundTone: z.string(),
- foregroundTone: z.string(),
-});
+export type ColourSet = [name: string, bg: string, fg: string]
+export const colours: ColourSet[] = coloursJson as ColourSet[];
-export type ColourSet = z.infer<typeof colour_set_schema>;
-
-export const colours: ColourSet[] = coloursJson;
-
-export const colour_store: Writable<ColourSet> = writable();
+export const colour_store: Writable<ColourSet> = writable(colours[0]);
const local_storage_key = 'colours';
export function get_current_colours() {
- const json = localStorage.getItem(local_storage_key);
- if (!json) return colours[0];
+ const value = localStorage.getItem(local_storage_key);
+ if (!value) return colours[0];
- const parsed = colour_set_schema.parse(JSON.parse(json));
- return parsed;
+ return JSON.parse(value);
}
export function update_colours(
- colours: ColourSet,
+ [name , bg, fg]: ColourSet,
opt?: { reverse?: boolean },
) {
- const value = opt?.reverse ? reverse_colours(colours) : colours;
+ const value: ColourSet = opt?.reverse ? [name, fg, bg] : [name, bg, fg]
notify_colours_changed(value);
set_colours(value);
}
@@ 40,9 30,3 @@ export function set_colours(set: ColourSet) {
localStorage.setItem(local_storage_key, JSON.stringify(set));
}
-export const reverse_colours = (colours: ColourSet): ColourSet => ({
- background: colours.foreground,
- foreground: colours.background,
- backgroundTone: colours.foregroundTone,
- foregroundTone: colours.backgroundTone,
-});
M => +1 -1
@@ 4,7 4,7 @@
import Action from "./Action.svelte";
</script>
<SensibleMenu title="Content Warnings">
<SensibleMenu title="Content Warnings" mode="freestyle">
<Action
let:open
slot="button"
M src/lib/Composer/EmojiPicker.svelte => src/lib/Composer/EmojiPicker.svelte +1 -1
@@ 14,7 14,7 @@
const dispatch = createEventDispatcher();
</script>
-<SensibleMenu title="Emoji Picket">
+<SensibleMenu title="Emoji Picket" mode="freestyle">
<Action let:open slot="button" title="Emoji" label="Emoji" on:click={open}>
:)
</Action>
M => +1 -1
@@ 4,7 4,7 @@
import Action from "./Action.svelte";
</script>
<SensibleMenu title="More">
<SensibleMenu title="More" mode="freestyle">
<Action
let:open
slot="button"
M src/lib/Navigation/Navigation.svelte => src/lib/Navigation/Navigation.svelte +12 -3
@@ 4,8 4,11 @@
import ComposerDialog from "$lib/Composer/ComposerDialog.svelte";
import CollapsedNav from "./CollapsedNav.svelte";
import { getContext } from "svelte";
+ import PrefsMenu from "./PrefsMenu.svelte";
- export let username: string | undefined, admin: boolean | undefined;
+ export let username: string | undefined,
+ admin: boolean | undefined,
+ email: string | undefined;
const available_pages = getContext(
"available_pages",
@@ 15,7 18,7 @@
<nav bind:this={nav}>
<h1>Basingstoke <span class="alpha">the alpha!!</span></h1>
- {#if username}
+ {#if username && email}
<a aria-label="Home" href="/">Timeline</a>
<a href="#content">Skip Navigation</a>
<ComposerDialog let:open>
@@ 29,7 32,9 @@
<a href="/its/{username}">@{username}</a>
</UserMenu>
<a href="/its/{username}?filter=drafts">Drafts</a>
- <a href="/prefs" aria-label="Preferences">Prefs</a>
+ <PrefsMenu {username} {email}>
+ <a href="/prefs" aria-label="Preferences">Prefs</a>
+ </PrefsMenu>
{#if admin}
<a href="/admin">Admin</a>
{/if}
@@ 56,8 61,12 @@
z-index: 100;
+ max-width: 20rem;
width: 20rem;
+ min-width: 20rem;
+
height: max-content;
+ margin: 0;
padding: 5rem 0;
}
A => +100 -0
@@ 0,0 1,100 @@
<script lang="ts">
import { goto } from "$app/navigation";
import ContextMenu from "$lib/Bonus/ContextMenu.svelte";
import Item from "$lib/Bonus/MenuItem.svelte";
import { colours } from "$lib/Colours";
import {
Accessibility,
Bell,
CircleUser,
Cog,
Cube,
Hand,
HeadSide,
List,
PencilSquare,
Rss,
Swatches,
User,
} from "$lib/Icons";
export let username: string, email: string;
</script>
<ContextMenu anchor="right">
<slot />
<svelte:fragment slot="menu">
<ul class="colours">
{#each colours.slice(0, 6) as colour}
<li class="colour">
<div style="--colour: {colour[1]}" />
<div style="--colour: {colour[2]}" />
</li>
{/each}
<li class="colour more">-></li>
</ul>
<h3>@{username}</h3>
<Item on:click={() => goto("/prefs/profile")} icon={CircleUser}>
Profile
</Item>
<Item on:click={() => goto("/prefs/stationery")} icon={PencilSquare}>
Stationery
</Item>
<Item on:click={() => goto("/prefs/syndication")} icon={Rss}>
Syndication
</Item>
<h3>{email}</h3>
<Item on:click={() => goto("/prefs/account")} icon={List}>Details</Item>
<Item on:click={() => goto("/prefs/appearance")} icon={Swatches}>
Appearance
</Item>
<Item
on:click={() => goto("/prefs/accessibility")}
icon={Accessibility}
>
Accessibility
</Item>
<Item on:click={() => goto("/prefs/wellbeing")} icon={HeadSide}>
Wellbeing
</Item>
<Item on:click={() => goto("/prefs/pings")} icon={Bell}>Pings</Item>
<Item on:click={() => goto("/prefs/apps")} icon={Cube}>Applications</Item>
<Item on:click={() => goto("/prefs/safety")} icon={Hand}>Safety</Item>
</svelte:fragment>
</ContextMenu>
<style>
ul {
list-style: none;
padding: 0;
display: flex;
justify-content: space-between;
max-width: 16rem;
padding: 0.25rem 0.5rem;
}
li {
min-width: 1.5rem;
height: 1.5rem;
aspect-ratio: 1 / 1;
display: grid;
border-radius: 2px;
overflow: hidden;
grid-template-columns: 1fr 1fr;
}
div {
background-color: var(--colour);
}
.more {
display: flex;
align-items: center;
justify-content: center;
background-color: color-mix(
in srgb,
var(--foreground) 15%,
transparent
);
}
</style>
M src/routes/(guarded)/prefs/appearance/ColourPicker.svelte => src/routes/(guarded)/prefs/appearance/ColourPicker.svelte +33 -26
@@ 2,34 2,41 @@
import {
colour_store,
colours,
- reverse_colours,
update_colours,
- } from '$lib/Colours';
+ type ColourSet,
+ } from "$lib/Colours";
+
+ const is_selected = (colour_set: ColourSet) =>
+ $colour_store.includes(colour_set[1]) &&
+ $colour_store.includes(colour_set[2]);
+
+ const is_selected_strict = (colour_set: ColourSet) =>
+ $colour_store[1] == colour_set[1] && $colour_store[2] == colour_set[2];
</script>
<ul>
- {#each colours as colour_set}
- <li
- style="
- --set-background: {colour_set.background};
- --set-foreground: {colour_set.foreground};
- --set-background-tone: {colour_set.backgroundTone};
- --set-foreground-tone: {colour_set.foregroundTone};
- "
- >
- <button
- class:selected={$colour_store == colour_set ||
- $colour_store == reverse_colours(colour_set)}
- on:click={() =>
- update_colours(colour_set, {
- reverse: $colour_store == colour_set,
- })}
+ {#key $colour_store}
+ {#each colours as colour_set}
+ <li
+ title={colour_set[0]}
+ style="--set-background: {colour_set[1]};
+ --set-foreground: {colour_set[2]};
+ --set-background-tone: color-mix(in srgb, {colour_set[1]}, {colour_set[2]} 10%);
+ --set-foreground-tone: color-mix(in srgb, {colour_set[2]}, {colour_set[1]} 10%);"
>
- <div class="foreground" />
- <div class="background" />
- </button>
- </li>
- {/each}
+ <button
+ class:selected={is_selected(colour_set)}
+ on:click={() =>
+ update_colours(colour_set, {
+ reverse: is_selected_strict(colour_set),
+ })}
+ >
+ <div class="foreground" />
+ <div class="background" />
+ </button>
+ </li>
+ {/each}
+ {/key}
</ul>
<style>
@@ 71,11 78,11 @@
}
button:hover > .background {
- background-color: var(--set-background-tone)
+ background-color: var(--set-background-tone);
}
button:hover > .foreground {
- background-color: var(--set-foreground-tone)
+ background-color: var(--set-foreground-tone);
}
.selected {
@@ 99,6 106,6 @@
place-items: center;
justify-content: center;
- content: '✓';
+ content: "✓";
}
</style>
M src/routes/(guarded)/prefs/profile/+page.svelte => src/routes/(guarded)/prefs/profile/+page.svelte +2 -1
@@ 15,7 15,8 @@
<Header parents={[["preferences", "/prefs"]]}>profile</Header>
<p>
- put some flavour text here lol <br />
+ put some words about yourself here and they'll show up in various places
+ <br />
</p>
{#if browser}
M src/routes/(guarded)/prefs/profile/FancyForm.svelte => src/routes/(guarded)/prefs/profile/FancyForm.svelte +63 -48
@@ 1,7 1,12 @@
<script lang="ts">
+ import PrefsFooter from "$lib/Bonus/PrefsFooter.svelte";
import Button from "$lib/Buttons/Button.svelte";
import FormInput from "$lib/Forms/FormInput.svelte";
- import { MAX_DISPLAY_NAME_LEN, MAX_HOMEPAGE_LEN, MAX_PRONOUNS_LEN } from "$lib/constants";
+ import {
+ MAX_DISPLAY_NAME_LEN,
+ MAX_HOMEPAGE_LEN,
+ MAX_PRONOUNS_LEN,
+ } from "$lib/constants";
import type { UserPageData } from "$lib/server/Model/userpage";
import ImageControls from "./ImageControls.svelte";
@@ 12,57 17,67 @@
<form method="post" class="grid">
<div class="text">
- <FormInput
- label="Display Name"
- flavour_text="a bit of text that gets put next to your username in various places"
- >
- <input
- bind:value={display_name}
- maxlength={MAX_DISPLAY_NAME_LEN}
- type="text"
- name="display_name"
- />
- </FormInput>
- <FormInput
- label="Pronouns"
- flavour_text="how people should address you (e.g., they/them)"
- >
- <input
- bind:value={pronouns}
- maxlength={MAX_PRONOUNS_LEN}
- name="pronouns"
- type="text"
- />
- </FormInput>
- <FormInput
- label="Homepage"
- flavour_text="a link to your webbed site, if you've got one"
- >
- <input
- bind:value={homepage}
- maxlength={MAX_HOMEPAGE_LEN}
- type="url"
- name="homepage"
- />
- </FormInput>
- <FormInput
- label="Bio"
- flavour_text="some words to let the world know who you are
+ <FormInput
+ label="Display Name"
+ flavour_text="a bit of text that gets put next to your username sometimes"
+ >
+ <input
+ bind:value={display_name}
+ maxlength={MAX_DISPLAY_NAME_LEN}
+ type="text"
+ name="display_name"
+ />
+ </FormInput>
+ <FormInput
+ label="Pronouns"
+ flavour_text="how people should address you (e.g., they/them)"
+ >
+ <input
+ bind:value={pronouns}
+ maxlength={MAX_PRONOUNS_LEN}
+ name="pronouns"
+ type="text"
+ />
+ </FormInput>
+ <FormInput
+ label="Homepage"
+ flavour_text="a link to your webbed site, if you've got one"
+ >
+ <input
+ bind:value={homepage}
+ maxlength={MAX_HOMEPAGE_LEN}
+ type="url"
+ name="homepage"
+ />
+ </FormInput>
+ <FormInput
+ label="Bio"
+ flavour_text="some words to let the world know who you are
and what your deal is. markdown works here."
- >
- <textarea
- bind:value={bio}
- rows="12"
- name="bio"
- style="resize: none"
- />
- </FormInput>
+ >
+ <textarea
+ bind:value={bio}
+ rows="12"
+ name="bio"
+ style="resize: none"
+ />
+ </FormInput>
</div>
<ImageControls {page} />
+ <PrefsFooter
+ style="grid-column: 1 / span 2; justify-self: end;"
+ let:scrolled
+ >
+ <Button size="medium">
+ ↶ Revert
+ </Button>
- <Button style="grid-column: 2; justify-self: end;" size="big" type="submit">
- Save ✓
- </Button>
+ <Button
+ size={scrolled ? "medium" : "big"}
+ style="margin-left: auto;"
+ type="submit">Save ✓</Button
+ >
+ </PrefsFooter>
</form>
<style>
M src/routes/(guarded)/prefs/profile/ProfileForm.svelte => src/routes/(guarded)/prefs/profile/ProfileForm.svelte +3 -3
@@ 1,8 1,8 @@
<script lang="ts">
import {
- MAX_DISPLAY_NAME_LEN,
- MAX_HOMEPAGE_LEN,
- MAX_PRONOUNS_LEN,
+ MAX_DISPLAY_NAME_LEN,
+ MAX_HOMEPAGE_LEN,
+ MAX_PRONOUNS_LEN,
} from "$lib/constants";
export let data,
M src/routes/+layout.svelte => src/routes/+layout.svelte +8 -11
@@ 16,7 16,7 @@
import { themed_favicon } from "$lib/Bonus/favicon";
import { setContext } from "svelte";
import HeadsUpses from "$lib/Pings/HeadsUpses.svelte";
- import Footer from "./Footer.svelte";
+ import Footer from "./Footer.svelte";
export let data;
@@ 25,11 25,11 @@
}
$: style = `
- --foreground-color: ${$colour_store?.foreground ?? "black"};
- --background-color: ${$colour_store?.background ?? "white"};
- --foreground-tone: ${$colour_store?.foregroundTone};
- --background-tone: ${$colour_store?.backgroundTone};
- `;
+ --foreground-color: ${$colour_store[2] ?? "black"};
+ --background-color: ${$colour_store[1] ?? "white"};
+ --foreground-tone: color-mix(in srgb, ${$colour_store[2]}, ${$colour_store[1]} 10%);
+ --background-tone: color-mix(in srgb, ${$colour_store[1]}, ${$colour_store[2]} 10%);
+ `;
$: meta = $page.url.pathname.startsWith("/meta");
@@ 43,10 43,7 @@
{#if browser}
<link
rel="icon"
- href={themed_favicon(
- $colour_store?.foreground,
- $colour_store?.background,
- )}
+ href={themed_favicon($colour_store[2], $colour_store[1])}
/>
{/if}
</svelte:head>
@@ 62,6 59,7 @@
<Navigation
admin={data.user?.administrator}
username={data.page?.username}
+ email={data.user?.email}
/>
{/if}
<main class:meta id="content"><slot /></main>
@@ 69,7 67,6 @@
<Footer wisdom={data.wisdom} />
</div>
-
<style>
div {
--background: var(--background-color);
M => +9 -6
@@ 1,7 1,5 @@
<script lang="ts">
import Button from "$lib/Buttons/Button.svelte";
import { ArrowUp } from "$lib/Icons";
import Icon from "$lib/Icons/Icon.svelte";
export let wisdom: string;
</script>
@@ 9,10 7,15 @@
<footer>
<div class="inner">
<section>
<h1>Basingstoke A1</h1>
<h1>Basingstoke</h1>
<p>
Basingstoke is licensed under the GNU Affero General Public
License
Basingstoke is licensed under the
<a
target="_blank"
href="https://git.sr.ht/~leah/basingstoke/tree/main/item/LICENSE"
>
GNU Affero General Public License
</a>
</p>
<ul>
<li>
@@ 64,7 67,7 @@
background: linear-gradient(
to bottom,
var(--background),
color-mix(in srgb, var(--foreground) 20%, transparent)
color-mix(in srgb, var(--foreground) 15%, transparent)
);
margin-left: -1rem;
margin-right: -1rem;
M src/routes/its/[username]/Filters.svelte => src/routes/its/[username]/Filters.svelte +19 -10
@@ 17,14 17,16 @@
let trigger: HTMLButtonElement, open: boolean, xy: [number, number];
- const filters = <{ id: FilterType; label: string }[]>[
- { id: "everything", label: own_page ? "Public" : "All Posts" },
- own_page ? { id: "drafts", label: "Drafts" } : undefined,
- own_page ? { id: "bookmarks", label: "Bookmarks" } : undefined,
- { id: "pinned", label: "Pinned" },
- own_page ? { id: "yeahd", label: "Yeah'd" } : undefined,
- { id: "media", label: "Media" },
- ].filter((o) => o);
+ const filters = <{ id: FilterType; label: string }[]>(
+ [
+ { id: "everything", label: own_page ? "Public" : "All Posts" },
+ own_page ? { id: "drafts", label: "Drafts" } : undefined,
+ own_page ? { id: "bookmarks", label: "Bookmarks" } : undefined,
+ { id: "pinned", label: "Pinned" },
+ own_page ? { id: "yeahd", label: "Yeah'd" } : undefined,
+ { id: "media", label: "Media" },
+ ].filter((o) => o)
+ );
function onclick(e: unknown) {
let event = e as MouseEvent & {
@@ 55,8 57,15 @@
<button bind:this={trigger} on:click={onclick}>
<Icon source={Sliders} label="Settings" />
</button>
- <Menu anchor="right" title="Filter Settings" bind:trigger bind:xy bind:open>
- <MenuProse class="" slot="notamenu" style="height:max-content">
+ <Menu
+ anchor="right"
+ mode="freestyle"
+ title="Filter Settings"
+ bind:trigger
+ bind:xy
+ bind:open
+ >
+ <MenuProse style="height:max-content">
<h1>Filters</h1>
<div class="boxes">
<Checkbox checked bigness="maybe">Show Reposts</Checkbox>
A src/routes/its/[username]/MoreButton.svelte => src/routes/its/[username]/MoreButton.svelte +25 -0
@@ 0,0 1,25 @@
+<script lang="ts">
+ import MenuItem from "$lib/Bonus/MenuItem.svelte";
+ import SensibleMenu from "$lib/Bonus/SensibleMenu.svelte";
+ import Button from "$lib/Buttons/Button.svelte";
+ import type { UserPageData } from "$lib/server/Model/userpage";
+ import Icon from "$lib/Icons/Icon.svelte";
+ import { ThreeDotsHorizontal, At, DoorOpen } from "$lib/Icons";
+
+ export let page: UserPageData;
+</script>
+
+<SensibleMenu title="Page Actions">
+ <svelte:fragment slot="button" let:open>
+ <Button on:click={(e) => open(e)}>
+ More
+ <Icon source={ThreeDotsHorizontal} />
+ </Button>
+ </svelte:fragment>
+ <MenuItem icon={At}>Mention</MenuItem>
+ <hr />
+ <MenuItem>Mute</MenuItem>
+ <MenuItem>Block</MenuItem>
+ <MenuItem>Block Instance</MenuItem>
+ <MenuItem>Report</MenuItem>
+</SensibleMenu>
M => +4 -0
@@ 6,6 6,7 @@
import { Pencil } from "$lib/Icons";
import Icon from "$lib/Icons/Icon.svelte";
import type { UserPageData } from "$lib/server/Model/userpage";
import MoreButton from "./MoreButton.svelte";
export let page: UserPageData;
export let own_page = false;
@@ 40,6 41,7 @@
{:else}
<UrlHandlerRedirectDialog {page} />
{/if}
<MoreButton {page} />
</div>
</div>
@@ 92,6 94,8 @@
}
div.buttons {
display: flex;
gap: 1rem;
margin-top: auto;
margin-bottom: 0.5rem;
}
M tests/test.ts => tests/test.ts +1 -4
@@ 1,6 1,3 @@
import { expect, test } from '@playwright/test';
-test('index page has expected h1', async ({ page }) => {
- await page.goto('/');
- await expect(page.getByRole('heading', { name: 'Welcome to SvelteKit' })).toBeVisible();
-});
+// todo :3<
\ No newline at end of file