feat: Phase 1 — extract the client (web SPA + desktop) to dtoro/oikos-web
Problem: the hexagonal refactor churns the backend tree for nine more phases; the UI delivery stack (web/ SPA, cmd/desktop Wails wrapper, compose/web image) must move to its own repo first so doc/layout rewrites land once on a backend-only tree. Change: - New repo git.hubris.network/dtoro/oikos-web (v0.33.0): web/, desktop/ (updateURL repointed to oikos-web releases), compose/, own CI (web + desktop jobs), own deploy script (CI-green gate, TOCTOU guard, version-tagged images, prune-to-3), own webhook receiver on :9798 + launchd unit, own compose project publishing the same 8091:80. - Cutover executed on mac-mini in order: oikos stack's web service stopped+removed, oikos-web project brought up on 8091; outer Caddy untouched (targets the published port) — serving + Authentik flow + /wails 404 quirk verified post-cutover. - Stripped from oikos: web/, cmd/desktop/, compose/web/, desktop CI workflow, ci.yml web job, Makefile ui/desktop/desktop-package/install targets, the compose web service, oikos-web from deploy.sh's fallback prune list; wails + go-keyring dropped from go.mod, vendor synced. - README / CONTRIBUTING / AGENTS.md / .agents dev+operations docs now point at the new repo; mbse + mascot design docs carry a path note. Risk: production SPA serving depends on the new pipeline now; rollback is versioned-image re-up of the old web service from a pre-split checkout (port 8091). Desktop builds installed before the split still check dtoro/oikos releases — one manual reinstall, noted in the oikos-web release notes. Verification: go vet, make test (race), make generate-check, golangci (no new findings; baseline down 400→365); post-cutover curls — localhost:8091 200, /wails/runtime.js 404, outer Caddy 302 Authentik.
This commit is contained in:
383
web/node_modules/svelte-sonner/README.md
generated
vendored
Normal file
383
web/node_modules/svelte-sonner/README.md
generated
vendored
Normal file
@@ -0,0 +1,383 @@
|
||||
https://github.com/wobsoriano/svelte-sonner/assets/13049130/4b9c250f-1431-4130-9c5b-5a4f8b0210c5
|
||||
|
||||
# svelte-sonner
|
||||
|
||||
[](https://pnpm.io/)
|
||||
|
||||
An opinionated toast component for Svelte. A port of Emil Kowalski's [Sonner](https://sonner.emilkowal.ski/).
|
||||
|
||||
## Quick start
|
||||
|
||||
Install it:
|
||||
|
||||
```bash
|
||||
npm i svelte-sonner
|
||||
# or
|
||||
yarn add svelte-sonner
|
||||
# or
|
||||
pnpm add svelte-sonner
|
||||
```
|
||||
|
||||
Add `<Toaster />` to your app, it will be the place where all your toasts will be rendered. After that, you can use `toast()` from anywhere in your app.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { Toaster, toast } from 'svelte-sonner';
|
||||
</script>
|
||||
|
||||
<Toaster />
|
||||
<button onclick={() => toast('My first toast')}>Give me a toast</button>
|
||||
```
|
||||
|
||||
## Types
|
||||
|
||||
### Default
|
||||
|
||||
Most basic toast. You can customize it (and any other type) by passing an options object as the second argument.
|
||||
|
||||
```js
|
||||
toast('Event has been created');
|
||||
```
|
||||
|
||||
With custom icon and description:
|
||||
|
||||
```js
|
||||
import Icon from './Icon.svelte';
|
||||
|
||||
toast('Event has been created', {
|
||||
description: 'Monday, January 3rd at 6:00pm',
|
||||
icon: Icon
|
||||
});
|
||||
```
|
||||
|
||||
### Success
|
||||
|
||||
Renders a checkmark icon in front of the message.
|
||||
|
||||
```js
|
||||
toast.success('Event has been created');
|
||||
```
|
||||
|
||||
### Info
|
||||
|
||||
Renders a question mark icon in front of the message.
|
||||
|
||||
```js
|
||||
toast.info('Event has new information');
|
||||
```
|
||||
|
||||
### Warning
|
||||
|
||||
Renders a warning icon in front of the message.
|
||||
|
||||
```js
|
||||
toast.warning('Event has warning');
|
||||
```
|
||||
|
||||
### Error
|
||||
|
||||
Renders an error icon in front of the message.
|
||||
|
||||
```js
|
||||
toast.error('Event has not been created');
|
||||
```
|
||||
|
||||
### Action
|
||||
|
||||
Renders a button.
|
||||
|
||||
```js
|
||||
toast('Event has been created', {
|
||||
action: {
|
||||
label: 'Undo',
|
||||
onClick: () => console.log('Undo')
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Promise
|
||||
|
||||
Starts in a loading state and will update automatically after the promise resolves or fails.
|
||||
|
||||
```js
|
||||
toast.promise(() => new Promise((resolve) => setTimeout(resolve, 2000)), {
|
||||
loading: 'Loading',
|
||||
success: 'Success',
|
||||
error: 'Error'
|
||||
});
|
||||
```
|
||||
|
||||
You can pass a function to the success/error messages to incorporate the result/error of the promise.
|
||||
|
||||
```js
|
||||
toast.promise(promise, {
|
||||
loading: 'Loading...',
|
||||
success: (data) => {
|
||||
return `${data.name} has been added!`;
|
||||
},
|
||||
error: 'Error'
|
||||
});
|
||||
```
|
||||
|
||||
### Custom Component
|
||||
|
||||
You can pass a component as the first argument instead of a string to render custom component while maintaining default styling. You can use the headless version below for a custom, unstyled toast.
|
||||
|
||||
```js
|
||||
toast(CustomComponent);
|
||||
```
|
||||
|
||||
### Updating a toast
|
||||
|
||||
You can update a toast by using the `toast` function and passing it the id of the toast you want to update, the rest stays the same.
|
||||
|
||||
```js
|
||||
const toastId = toast('Sonner');
|
||||
|
||||
toast.success('Toast has been updated', {
|
||||
id: toastId
|
||||
});
|
||||
```
|
||||
|
||||
## Customization
|
||||
|
||||
### Headless
|
||||
|
||||
You can use `toast.custom` to render an unstyled toast with custom component while maintaining the functionality.
|
||||
|
||||
```svelte
|
||||
<script>
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
</script>
|
||||
|
||||
<div>
|
||||
This is a custom component <button onclick={() => dispatch('closeToast')}>close</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
```js
|
||||
import HeadlessToast from './HeadlessToast.svelte';
|
||||
|
||||
toast.custom(HeadlessToast);
|
||||
```
|
||||
|
||||
### Theme
|
||||
|
||||
You can change the theme using the `theme` prop. Default theme is light.
|
||||
|
||||
```svelte
|
||||
<Toaster theme="dark" />
|
||||
```
|
||||
|
||||
### Position
|
||||
|
||||
You can change the position through the `position` prop on the `<Toaster />` component. Default is `bottom-right`.
|
||||
|
||||
```svelte
|
||||
<!-- Available positions -->
|
||||
<!-- top-left, top-center, top-right, bottom-left, bottom-center, bottom-right -->
|
||||
|
||||
<Toaster position="top-center" />
|
||||
```
|
||||
|
||||
### Expanded
|
||||
|
||||
Toasts can also be expanded by default through the `expand` prop. You can also change the amount of visible toasts which is 3 by default.
|
||||
|
||||
```svelte
|
||||
<Toaster expand visibleToasts={9} />
|
||||
```
|
||||
|
||||
### Styling
|
||||
|
||||
Styling can be done globally via `toastOptions`, this way every toast will have the same styling.
|
||||
|
||||
```svelte
|
||||
<Toaster
|
||||
toastOptions={{
|
||||
style: 'background: red;',
|
||||
class: 'my-toast',
|
||||
descriptionClass: 'my-toast-description'
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
You can also use the same props when calling `toast` to style a specific toast.
|
||||
|
||||
```js
|
||||
toast('Event has been created', {
|
||||
style: 'background: red;',
|
||||
class: 'my-toast',
|
||||
descriptionClass: 'my-toast-description'
|
||||
});
|
||||
```
|
||||
|
||||
### Tailwind CSS
|
||||
|
||||
The preferred way to style the toasts with tailwind is by using the `unstyled` prop. That will give you an unstyled toast which you can then style with tailwind.
|
||||
|
||||
```svelte
|
||||
<Toaster
|
||||
toastOptions={{
|
||||
unstyled: true,
|
||||
classes: {
|
||||
toast: 'bg-blue-400',
|
||||
title: 'text-red-400',
|
||||
description: 'text-red-400',
|
||||
actionButton: 'bg-zinc-400',
|
||||
cancelButton: 'bg-orange-400',
|
||||
closeButton: 'bg-lime-400'
|
||||
}
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
You can do the same when calling `toast()`.
|
||||
|
||||
```js
|
||||
toast('Hello World', {
|
||||
unstyled: true,
|
||||
classes: {
|
||||
toast: 'bg-blue-400',
|
||||
title: 'text-red-400 text-2xl',
|
||||
description: 'text-red-400',
|
||||
actionButton: 'bg-zinc-400',
|
||||
cancelButton: 'bg-orange-400',
|
||||
closeButton: 'bg-lime-400'
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
Styling per toast type is also possible.
|
||||
|
||||
```svelte
|
||||
<Toaster
|
||||
toastOptions={{
|
||||
unstyled: true,
|
||||
classes: {
|
||||
error: 'bg-red-400',
|
||||
success: 'text-green-400',
|
||||
warning: 'text-yellow-400',
|
||||
info: 'bg-blue-400'
|
||||
}
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
### Changing Icon
|
||||
|
||||
You can change the default icons using snippets:
|
||||
|
||||
```svelte
|
||||
<Toaster>
|
||||
{#snippet loadingIcon()}
|
||||
<LoadingIcon />
|
||||
{/snippet}
|
||||
{#snippet successIcon()}
|
||||
<SuccessIcon />
|
||||
{/snippet}
|
||||
{#snippet errorIcon()}
|
||||
<ErrorIcon />
|
||||
{/snippet}
|
||||
{#snippet infoIcon()}
|
||||
<InfoIcon />
|
||||
{/snippet}
|
||||
{#snippet warningIcon()}
|
||||
<WarningIcon />
|
||||
{/snippet}
|
||||
</Toaster>
|
||||
```
|
||||
|
||||
### Close button
|
||||
|
||||
Add a close button to all toasts that shows on hover by adding the `closeButton` prop.
|
||||
|
||||
```svelte
|
||||
<Toaster closeButton />
|
||||
```
|
||||
|
||||
### Rich colors
|
||||
|
||||
You can make error and success state more colorful by adding the `richColors` prop.
|
||||
|
||||
```svelte
|
||||
<Toaster richColors />
|
||||
```
|
||||
|
||||
### Custom offset
|
||||
|
||||
Offset from the edges of the screen.
|
||||
|
||||
```svelte
|
||||
<Toaster offset="80px" />
|
||||
```
|
||||
|
||||
### Programmatically remove toast
|
||||
|
||||
To remove a toast programmatically use `toast.dismiss(id)`. The `toast()` function returns the id of the toast.
|
||||
|
||||
```js
|
||||
const toastId = toast('Event has been created');
|
||||
|
||||
toast.dismiss(toastId);
|
||||
```
|
||||
|
||||
You can also dismiss all toasts at once by calling `toast.dismiss()` without an id.
|
||||
|
||||
```js
|
||||
toast.dismiss();
|
||||
```
|
||||
|
||||
### `useSonner`
|
||||
|
||||
You can use the `useSonner` hook to retrieve all visible toasts like below.
|
||||
|
||||
```ts
|
||||
const sonner = useSonner();
|
||||
|
||||
$effect(() => console.log(sonner.toasts));
|
||||
```
|
||||
|
||||
### Duration
|
||||
|
||||
You can change the duration of each toast by using the `duration` property, or change the duration of all toasts like this:
|
||||
|
||||
```svelte
|
||||
<Toaster duration={10000} />
|
||||
```
|
||||
|
||||
```js
|
||||
toast('Event has been created', {
|
||||
duration: 10000
|
||||
});
|
||||
|
||||
// Persisent toast
|
||||
toast('Event has been created', {
|
||||
duration: Number.POSITIVE_INFINITY
|
||||
});
|
||||
```
|
||||
|
||||
### On Close Callback
|
||||
|
||||
You can pass `onDismiss` and `onAutoClose` callbacks. `onDismiss` gets fired when either the close button gets clicked or the toast is swiped. `onAutoClose` fires when the toast disappears automatically after it's timeout (`duration` prop).
|
||||
|
||||
```js
|
||||
toast('Event has been created', {
|
||||
onDismiss: (t) => console.log(`Toast with id ${t.id} has been dismissed`),
|
||||
onAutoClose: (t) => console.log(`Toast with id ${t.id} has been closed automatically`)
|
||||
});
|
||||
```
|
||||
|
||||
## Keyboard focus
|
||||
|
||||
You can focus on the toast area by pressing ⌥/alt + T. You can override it by providing an array of `event.code` values for each key.
|
||||
|
||||
```svelte
|
||||
<Toaster hotkey={['KeyC']} />
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
Reference in New Issue
Block a user