diff options
| author | ojacobson <ojacobson@noreply.codeberg.org> | 2025-07-10 03:35:32 +0200 |
|---|---|---|
| committer | ojacobson <ojacobson@noreply.codeberg.org> | 2025-07-10 03:35:32 +0200 |
| commit | f74b82450aa15b3e3e47617839c297cd1d60780e (patch) | |
| tree | b9091f1fa4f73de40fb7b530798ce2d3b94aee09 /ui/lib | |
| parent | 223b39a57ef6ca6b8288f5a8645183c41301f411 (diff) | |
| parent | 01ed82ac4f89810161fbc3aa1cb8e4691fb8938b (diff) | |
Create swatches for Svelte components.
A swatch is a live, and ideally editable, example of an element of the service. They serve as:
* Documentation: what is this element, how do you use it, what does it do?
* Demonstration: what does this element look like?
* Manual test scaffolding: when I change this element like _so_, what happens?
Swatches are collectively available under `/.swatch/` on a running instance. They do not require setup or login for simplicity's sake and because they don't _do_ anything that requires either of those things.
Swatches are manually curated. First, we lack the technical infrastructure needed to do this based on static analysis, and second, manual curation lets us include affordances like "interesting values," that would be tricky to express as part of the type or schema for the component. The tradeoff, however, is that they will fall out of step with the components if not reviewed regularly.
Swatches are _possible_ because we've gone to efforts to avoid global data access or direct side effects (including API requests) in our components, delegating that upwards to `+page`s and `+layout`s. However, the isolation is imperfect. For example, the swatch for `Conversation`, which renders the conversation sidebar entries, causes actual attempts to boot the app as browsers pre-fetch the links on mouseover, and clicking them will take the user to the "real" application because they really are links.
Merges swatch into main.
Diffstat (limited to 'ui/lib')
| -rw-r--r-- | ui/lib/components/swatch/EventLog.svelte | 37 | ||||
| -rw-r--r-- | ui/lib/swatch/derive.js | 20 | ||||
| -rw-r--r-- | ui/lib/swatch/event-capture.svelte.js | 22 |
3 files changed, 79 insertions, 0 deletions
diff --git a/ui/lib/components/swatch/EventLog.svelte b/ui/lib/components/swatch/EventLog.svelte new file mode 100644 index 0000000..2d8c5c0 --- /dev/null +++ b/ui/lib/components/swatch/EventLog.svelte @@ -0,0 +1,37 @@ +<script> + /* + * The interface exposed by this component is designed to be compatible with the interface + * exposed by the `EventCapture` class, so that you can do this: + * + * let capture = $state(new EventCapture()); + * const someEvent = capture.on('someEvent'); + * + * // … + * + * <EventLog events={capture.events} clear={capture.clear.bind(capture)} /> + */ + + let { events, clear = () => {} } = $props(); + + function onclick() { + clear(); + } +</script> + +<button {onclick}>clear</button> +<table> + <thead> + <tr> + <th>event</th> + <th>arguments</th> + </tr> + </thead> + <tbody> + {#each events as { event, args }} + <tr> + <td>{event}</td> + <td><code>{JSON.stringify(args)}</code></td> + </tr> + {/each} + </tbody> +</table> diff --git a/ui/lib/swatch/derive.js b/ui/lib/swatch/derive.js new file mode 100644 index 0000000..85547e8 --- /dev/null +++ b/ui/lib/swatch/derive.js @@ -0,0 +1,20 @@ +// A fun fact about $derive: if the deriving expression throws an error, then Svelte (at least as of +// 5.20.2) _permanently_ stops evaluating it, even if the inputs change. To prevent this, you need +// to ensure that derive expressions never raise an exception. +function tryDerive(args, func, fallback) { + try { + return func(...args); + } catch (e) { + console.debug('deriver threw exception', e, func, args); + return fallback; + } +} + +// A "deriver" is a function that never raises; if the underlying function would raise, the +// corresponding deriver instead returns a fallback value (or `undefined`). +export function makeDeriver(func, fallback) { + return (...args) => tryDerive(args, func, fallback); +} + +// Some widely-used derivers, for convenience. +export const json = makeDeriver(JSON.parse); diff --git a/ui/lib/swatch/event-capture.svelte.js b/ui/lib/swatch/event-capture.svelte.js new file mode 100644 index 0000000..32c0f39 --- /dev/null +++ b/ui/lib/swatch/event-capture.svelte.js @@ -0,0 +1,22 @@ +/* + * The interface exposed by this class is designed to closely match the interface expected by + * the `EventLog` component, so that you can do this: + * + * let capture = $state(new EventCapture()); + * const someEvent = capture.on('someEvent'); + * + * // … + * + * <EventLog events={capture.events} clear={capture.clear.bind(capture)} /> + */ +export default class EventCapture { + events = $state([]); + + on(event) { + return (...args) => this.events.push({ event, args }); + } + + clear() { + this.events = []; + } +} |
