mirror of
https://git.shipoclu.com/moon/plspace.git
synced 2026-08-13 02:42:30 +00:00
initial commit
This commit is contained in:
@@ -0,0 +1,216 @@
|
||||
<script lang="ts">
|
||||
/**
|
||||
* Settings — mostly the layout editor, which is the feature this whole app
|
||||
* exists to have.
|
||||
*
|
||||
* Two things are editable: the CSS applied to *your* view of plspace (stored
|
||||
* locally), and whether the CSS other people publish on their profiles is
|
||||
* honoured when you visit them.
|
||||
*/
|
||||
import { session } from '$lib/stores/session.svelte'
|
||||
import { theme, CSS_FIELD_NAMES, PROFILE_SCOPE } from '$lib/stores/theme.svelte'
|
||||
import { PRESETS, EXAMPLE_CSS } from '$lib/themes'
|
||||
import { instanceDomain } from '$lib/api/endpoints'
|
||||
import { displayNameOf, profilePath } from '$lib/util/profile'
|
||||
import Module from '$components/common/Module.svelte'
|
||||
|
||||
let draft = $state(theme.viewerCss)
|
||||
let saved = $state(false)
|
||||
|
||||
const domain = $derived(session.host ? instanceDomain(session.instance, session.host) : '')
|
||||
|
||||
/** Class hooks worth documenting, grouped the way pages are built. */
|
||||
const HOOKS: Array<{ group: string; entries: Array<[string, string]> }> = [
|
||||
{
|
||||
group: 'Page skeletons',
|
||||
entries: [
|
||||
['.page', 'Every page’s outer container'],
|
||||
['.profile-page', 'The profile page — also the scope for published profile CSS'],
|
||||
['.layout--split', 'Two-column pages (left rail + main)'],
|
||||
['.layout--dashboard', 'The three-column home page'],
|
||||
['.layout-column--left / --main / --right', 'The individual columns'],
|
||||
],
|
||||
},
|
||||
{
|
||||
group: 'Boxes',
|
||||
entries: [
|
||||
['.module', 'A bordered box'],
|
||||
['.module-header', 'Its caption bar'],
|
||||
['.module-body', 'Its contents'],
|
||||
['.module--band', 'The peach-bar variant used in the main column'],
|
||||
['.section-heading', '“About me:” style orange headings'],
|
||||
],
|
||||
},
|
||||
{
|
||||
group: 'Profile',
|
||||
entries: [
|
||||
['.profile-photo', 'The big photo'],
|
||||
['.profile-headline', 'The quoted line beside it'],
|
||||
['.profile-vitals', 'Gender / age / location / last active'],
|
||||
['.interests-table, .interests-label, .interests-value', 'The Interests table'],
|
||||
['.friend-grid, .friend-card, .friend-card-photo', 'Friend Space'],
|
||||
],
|
||||
},
|
||||
{
|
||||
group: 'Blog entries',
|
||||
entries: [
|
||||
['.blog-entry', 'One entry'],
|
||||
['.blog-entry[data-mine="true"]', 'Entries you wrote'],
|
||||
['.blog-entry[data-visibility="private"]', 'Friends-only entries'],
|
||||
['.blog-entry[data-boosted="true"]', 'Reposts'],
|
||||
['.blog-action[aria-pressed="true"]', 'Kudos/Repost buttons you’ve activated'],
|
||||
['.comment[data-depth="2"]', 'Comments by nesting depth'],
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
function save(): void {
|
||||
theme.setViewerCss(draft)
|
||||
saved = true
|
||||
setTimeout(() => (saved = false), 2000)
|
||||
}
|
||||
|
||||
function applyPreset(css: string): void {
|
||||
draft = css
|
||||
theme.setViewerCss(css)
|
||||
}
|
||||
|
||||
function reset(): void {
|
||||
draft = ''
|
||||
theme.setViewerCss('')
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="page settings-page">
|
||||
<h1 class="page-title">Settings</h1>
|
||||
<p class="page-subtitle">Everything here is stored in this browser only.</p>
|
||||
|
||||
<div class="layout--single">
|
||||
<Module title="Your account">
|
||||
{#if session.signedIn && session.me}
|
||||
<p>
|
||||
Signed in as
|
||||
<a href={profilePath(session.me)}>{displayNameOf(session.me)}</a>
|
||||
on <strong>{domain}</strong>.
|
||||
</p>
|
||||
<p class="field-row">
|
||||
<button type="button" class="button" onclick={() => void session.logout()}>Sign out</button>
|
||||
<button type="button" class="button" onclick={() => session.disconnect()}>
|
||||
Forget this server
|
||||
</button>
|
||||
</p>
|
||||
{:else if session.host}
|
||||
<p>Browsing <strong>{domain}</strong> as a guest.</p>
|
||||
<p class="field-row">
|
||||
<a class="button button--primary" href="#/login">Sign in</a>
|
||||
<button type="button" class="button" onclick={() => session.disconnect()}>
|
||||
Choose a different server
|
||||
</button>
|
||||
</p>
|
||||
{:else}
|
||||
<p>Not connected to a server. <a href="#/login">Choose one</a>.</p>
|
||||
{/if}
|
||||
</Module>
|
||||
|
||||
<Module title="Pick a layout" variant="band">
|
||||
<ul class="preset-list">
|
||||
{#each PRESETS as preset (preset.id)}
|
||||
<li class="preset">
|
||||
<button type="button" class="button preset-button" onclick={() => applyPreset(preset.css)}>
|
||||
{preset.name}
|
||||
</button>
|
||||
<span class="preset-description muted">{preset.description}</span>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</Module>
|
||||
|
||||
<Module title="Your CSS" variant="band">
|
||||
<p>
|
||||
This is applied to every page you view in plspace. Overriding the custom properties in
|
||||
<code>styles/tokens.css</code> retints the entire app; the class hooks below let you go
|
||||
further.
|
||||
</p>
|
||||
|
||||
<label class="visually-hidden" for="viewer-css">Your CSS</label>
|
||||
<textarea
|
||||
id="viewer-css"
|
||||
class="css-editor"
|
||||
bind:value={draft}
|
||||
rows="14"
|
||||
spellcheck="false"
|
||||
placeholder={EXAMPLE_CSS}
|
||||
></textarea>
|
||||
|
||||
<div class="field-row">
|
||||
<button type="button" class="button button--primary" onclick={save}>Save CSS</button>
|
||||
<button type="button" class="button" onclick={reset}>Reset to default</button>
|
||||
<button type="button" class="button" onclick={() => (draft = EXAMPLE_CSS)}>
|
||||
Load the example
|
||||
</button>
|
||||
{#if saved}<span class="muted">Saved.</span>{/if}
|
||||
</div>
|
||||
|
||||
<p class="field-hint">
|
||||
<code>@import</code> and non-HTTPS <code>url()</code> values are stripped before your CSS is
|
||||
applied.
|
||||
</p>
|
||||
</Module>
|
||||
|
||||
<Module title="Other people's layouts" variant="band">
|
||||
<label class="checkbox-field">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={theme.allowProfileCss}
|
||||
onchange={(event) => theme.setAllowProfileCss(event.currentTarget.checked)}
|
||||
/>
|
||||
<span>
|
||||
Show profile layouts published by the people I visit
|
||||
</span>
|
||||
</label>
|
||||
<p class="field-hint">
|
||||
A profile can publish a stylesheet by putting CSS in a profile field named
|
||||
{#each CSS_FIELD_NAMES as name, index (name)}<code>{name}</code>{#if index < CSS_FIELD_NAMES.length - 1}, {/if}{/each}.
|
||||
Their rules are rewritten to apply only inside <code>{PROFILE_SCOPE}</code>, so a profile
|
||||
can restyle its own page but not the rest of plspace.
|
||||
</p>
|
||||
</Module>
|
||||
|
||||
<Module title="Publish your own layout" variant="band">
|
||||
<p>
|
||||
Add a profile field on <strong>{domain || 'your server'}</strong> named <code>css</code> and
|
||||
paste a stylesheet into its value. Anyone viewing your profile in plspace sees it. Since
|
||||
it's an ordinary profile field, it survives elsewhere too — other clients just show it
|
||||
as text.
|
||||
</p>
|
||||
{#if session.signedIn}
|
||||
<p>
|
||||
<a
|
||||
class="button"
|
||||
href={`https://${session.host}/settings/profile`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Edit your profile on {domain}
|
||||
</a>
|
||||
</p>
|
||||
{/if}
|
||||
</Module>
|
||||
|
||||
<Module title="Class reference" variant="band">
|
||||
{#each HOOKS as section (section.group)}
|
||||
<h3 class="section-heading">{section.group}</h3>
|
||||
<table class="data-table hooks-table">
|
||||
<tbody>
|
||||
{#each section.entries as [selector, description] (selector)}
|
||||
<tr>
|
||||
<th class="data-table-label" scope="row"><code>{selector}</code></th>
|
||||
<td class="data-table-value">{description}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
{/each}
|
||||
</Module>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user