@axium/client 0.12.3 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/assets/animations.css +30 -0
- package/assets/styles.css +33 -20
- package/dist/access.d.ts +4 -2
- package/dist/access.js +8 -2
- package/dist/requests.js +6 -1
- package/lib/AccessControlDialog.svelte +77 -42
- package/lib/Upload.svelte +21 -3
- package/lib/UserDiscovery.svelte +144 -0
- package/lib/ZodInput.svelte +0 -15
- package/package.json +2 -2
- package/styles/list.css +0 -1
package/README.md
CHANGED
|
@@ -1 +1,7 @@
|
|
|
1
1
|
# Axium Client
|
|
2
|
+
|
|
3
|
+
Axium Client provides client-side functionality for Axium. This includes:
|
|
4
|
+
|
|
5
|
+
- UI components, which are currently used by the web UI
|
|
6
|
+
- The `axium-client`/`axc` CLI
|
|
7
|
+
- Some utility functions like `fetchAPI` that provide strongly-typed API interaction
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
@keyframes zoom {
|
|
2
|
+
from {
|
|
3
|
+
transform: scale(0.95);
|
|
4
|
+
}
|
|
5
|
+
to {
|
|
6
|
+
transform: scale(1);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
@keyframes fade {
|
|
11
|
+
from {
|
|
12
|
+
opacity: 0;
|
|
13
|
+
}
|
|
14
|
+
to {
|
|
15
|
+
opacity: 1;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
:root {
|
|
20
|
+
--A-zoom: zoom 0.25s cubic-bezier(0.35, 1.55, 0.65, 1);
|
|
21
|
+
--A-fade: fade 0.25s ease-out;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
dialog[open] {
|
|
25
|
+
animation: var(--A-zoom);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
dialog[open]::backdrop {
|
|
29
|
+
animation: var(--A-fade);
|
|
30
|
+
}
|
package/assets/styles.css
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
@import './animations.css';
|
|
2
|
+
|
|
1
3
|
* {
|
|
2
4
|
box-sizing: border-box;
|
|
3
5
|
color: hsl(0 0 var(--fg-light));
|
|
@@ -65,6 +67,21 @@ textarea {
|
|
|
65
67
|
outline: none;
|
|
66
68
|
}
|
|
67
69
|
|
|
70
|
+
label.checkbox {
|
|
71
|
+
cursor: pointer;
|
|
72
|
+
width: 1.5em;
|
|
73
|
+
height: 1.5em;
|
|
74
|
+
border: 1px solid var(--border-accent);
|
|
75
|
+
border-radius: 0.5em;
|
|
76
|
+
display: inline-flex;
|
|
77
|
+
justify-content: center;
|
|
78
|
+
align-items: center;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
input[type='checkbox'] {
|
|
82
|
+
display: none;
|
|
83
|
+
}
|
|
84
|
+
|
|
68
85
|
select,
|
|
69
86
|
::picker(select) {
|
|
70
87
|
appearance: base-select;
|
|
@@ -118,34 +135,30 @@ dialog::backdrop {
|
|
|
118
135
|
background: #0003;
|
|
119
136
|
}
|
|
120
137
|
|
|
121
|
-
dialog
|
|
122
|
-
|
|
138
|
+
dialog form {
|
|
139
|
+
display: contents;
|
|
123
140
|
}
|
|
124
141
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
142
|
+
progress {
|
|
143
|
+
appearance: none;
|
|
144
|
+
border: none;
|
|
145
|
+
height: 0.5em;
|
|
146
|
+
border-radius: 1em;
|
|
147
|
+
overflow: hidden;
|
|
148
|
+
background-color: var(--bg-normal);
|
|
149
|
+
border: 1px solid var(--border-accent);
|
|
132
150
|
}
|
|
133
151
|
|
|
134
|
-
|
|
135
|
-
|
|
152
|
+
progress::-webkit-progress-bar {
|
|
153
|
+
background-color: transparent;
|
|
136
154
|
}
|
|
137
155
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
opacity: 0;
|
|
141
|
-
}
|
|
142
|
-
to {
|
|
143
|
-
opacity: 1;
|
|
144
|
-
}
|
|
156
|
+
progress::-webkit-progress-value {
|
|
157
|
+
background-color: hsl(0 0 var(--fg-light));
|
|
145
158
|
}
|
|
146
159
|
|
|
147
|
-
|
|
148
|
-
|
|
160
|
+
progress::-moz-progress-bar {
|
|
161
|
+
background-color: hsl(0 0 var(--fg-light));
|
|
149
162
|
}
|
|
150
163
|
|
|
151
164
|
:not(input).error {
|
package/dist/access.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
import type { AccessControl,
|
|
2
|
-
export declare function
|
|
1
|
+
import type { AccessControl, AccessTarget } from '@axium/core';
|
|
2
|
+
export declare function updateACL(itemType: string, itemId: string, target: AccessTarget, permissions: Partial<Record<string, any>>): Promise<AccessControl>;
|
|
3
3
|
export declare function getACL(itemType: string, itemId: string): Promise<AccessControl[]>;
|
|
4
|
+
export declare function addToACL(itemType: string, itemId: string, target: AccessTarget): Promise<AccessControl>;
|
|
5
|
+
export declare function removeFromACL(itemType: string, itemId: string, target: AccessTarget): Promise<AccessControl>;
|
package/dist/access.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { fetchAPI } from './requests.js';
|
|
2
|
-
export async function
|
|
3
|
-
return await fetchAPI('
|
|
2
|
+
export async function updateACL(itemType, itemId, target, permissions) {
|
|
3
|
+
return await fetchAPI('PATCH', 'acl/:itemType/:itemId', { target, permissions }, itemType, itemId);
|
|
4
4
|
}
|
|
5
5
|
export async function getACL(itemType, itemId) {
|
|
6
6
|
return await fetchAPI('GET', 'acl/:itemType/:itemId', {}, itemType, itemId);
|
|
7
7
|
}
|
|
8
|
+
export async function addToACL(itemType, itemId, target) {
|
|
9
|
+
return await fetchAPI('PUT', 'acl/:itemType/:itemId', target, itemType, itemId);
|
|
10
|
+
}
|
|
11
|
+
export async function removeFromACL(itemType, itemId, target) {
|
|
12
|
+
return await fetchAPI('DELETE', 'acl/:itemType/:itemId', target, itemType, itemId);
|
|
13
|
+
}
|
package/dist/requests.js
CHANGED
|
@@ -49,6 +49,11 @@ export async function fetchAPI(method, endpoint, data, ...params) {
|
|
|
49
49
|
const json = await response.json().catch(() => ({ message: 'Unknown server error (invalid JSON response)' }));
|
|
50
50
|
if (!response.ok)
|
|
51
51
|
throw new Error(json.message);
|
|
52
|
+
if (typeof json == 'object' && json != null && '_warnings' in json) {
|
|
53
|
+
for (const warning of json._warnings)
|
|
54
|
+
console.warn('[API]', warning);
|
|
55
|
+
delete json._warnings;
|
|
56
|
+
}
|
|
52
57
|
if (!schema)
|
|
53
58
|
return json;
|
|
54
59
|
const Output = Array.isArray(schema) ? schema[1] : schema;
|
|
@@ -56,6 +61,6 @@ export async function fetchAPI(method, endpoint, data, ...params) {
|
|
|
56
61
|
return Output.parse(json);
|
|
57
62
|
}
|
|
58
63
|
catch (e) {
|
|
59
|
-
throw prettifyError(e)
|
|
64
|
+
throw `${method} ${endpoint}:\n${prettifyError(e)}`;
|
|
60
65
|
}
|
|
61
66
|
}
|
|
@@ -1,43 +1,46 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { getACL,
|
|
2
|
+
import { addToACL, getACL, updateACL } from '@axium/client/access';
|
|
3
3
|
import { userInfo } from '@axium/client/user';
|
|
4
|
-
import type {
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import UserCard from './UserCard.svelte';
|
|
8
|
-
import Icon from './Icon.svelte';
|
|
4
|
+
import type { AccessControllable, AccessTarget, User } from '@axium/core';
|
|
5
|
+
import { getTarget, pickPermissions } from '@axium/core/access';
|
|
6
|
+
import { errorText } from '@axium/core/io';
|
|
9
7
|
import type { HTMLDialogAttributes } from 'svelte/elements';
|
|
8
|
+
import Icon from './Icon.svelte';
|
|
9
|
+
import UserCard from './UserCard.svelte';
|
|
10
|
+
import UserDiscovery from './UserDiscovery.svelte';
|
|
10
11
|
|
|
11
12
|
interface Props extends HTMLDialogAttributes {
|
|
12
13
|
editable: boolean;
|
|
13
14
|
dialog?: HTMLDialogElement;
|
|
14
15
|
itemType: string;
|
|
15
|
-
item
|
|
16
|
-
acl?: AccessControl[];
|
|
16
|
+
item: { name?: string; user?: User; id: string } & AccessControllable;
|
|
17
17
|
}
|
|
18
|
-
let { item
|
|
18
|
+
let { item, itemType, editable, dialog = $bindable(), ...rest }: Props = $props();
|
|
19
|
+
|
|
20
|
+
let error = $state<string>();
|
|
21
|
+
|
|
22
|
+
const acl = $state(item.acl ?? (await getACL(itemType, item.id)));
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
async function onSelect(target: AccessTarget) {
|
|
25
|
+
const control = await addToACL(itemType, item.id, target);
|
|
26
|
+
if (control.userId) control.user = await userInfo(control.userId);
|
|
27
|
+
acl.push(control);
|
|
28
|
+
}
|
|
21
29
|
</script>
|
|
22
30
|
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
<h3>Permissions for <strong>{item.name}</strong></h3>
|
|
34
|
-
{:else}
|
|
35
|
-
<h3>Permissions</h3>
|
|
36
|
-
{/if}
|
|
37
|
-
{/snippet}
|
|
31
|
+
<dialog bind:this={dialog} {...rest}>
|
|
32
|
+
{#if item.name}
|
|
33
|
+
<h3>Permissions for <strong>{item.name}</strong></h3>
|
|
34
|
+
{:else}
|
|
35
|
+
<h3>Permissions</h3>
|
|
36
|
+
{/if}
|
|
37
|
+
|
|
38
|
+
{#if error}
|
|
39
|
+
<div class="error">{error}</div>
|
|
40
|
+
{/if}
|
|
38
41
|
|
|
39
42
|
<div class="AccessControl">
|
|
40
|
-
{#if item
|
|
43
|
+
{#if item.user}
|
|
41
44
|
<UserCard user={item.user} />
|
|
42
45
|
{:else if item}
|
|
43
46
|
{#await userInfo(item.userId) then user}<UserCard {user} />{/await}
|
|
@@ -45,7 +48,15 @@
|
|
|
45
48
|
<span>Owner</span>
|
|
46
49
|
</div>
|
|
47
50
|
|
|
48
|
-
{#each acl
|
|
51
|
+
{#each acl as control}
|
|
52
|
+
{@const update = (key: string) => async (e: Event & { currentTarget: HTMLInputElement }) => {
|
|
53
|
+
try {
|
|
54
|
+
const updated = await updateACL(itemType, item.id, getTarget(control), { [key]: e.currentTarget.checked });
|
|
55
|
+
Object.assign(control, updated);
|
|
56
|
+
} catch (e) {
|
|
57
|
+
error = errorText(e);
|
|
58
|
+
}
|
|
59
|
+
}}
|
|
49
60
|
<div class="AccessControl">
|
|
50
61
|
{#if control.user}
|
|
51
62
|
<UserCard user={control.user} />
|
|
@@ -58,25 +69,49 @@
|
|
|
58
69
|
{:else}
|
|
59
70
|
<i>Unknown</i>
|
|
60
71
|
{/if}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
{
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
<div class="permissions">
|
|
73
|
+
{#each Object.entries(pickPermissions(control) as Record<string, boolean>) as [key, value]}
|
|
74
|
+
{@const id = `${getTarget(control)}.${key}`}
|
|
75
|
+
<span class="icon-text">
|
|
76
|
+
{#if editable}
|
|
77
|
+
<input {id} type="checkbox" onchange={update(key)} />
|
|
78
|
+
<label for={id} class="checkbox">
|
|
79
|
+
{#if value}<Icon i="check" --size="1.3em" />{/if}
|
|
80
|
+
</label>
|
|
81
|
+
{:else}
|
|
82
|
+
<Icon i={value ? 'check' : 'xmark'} />
|
|
83
|
+
{/if}
|
|
84
|
+
<span>{key}</span>
|
|
85
|
+
</span>
|
|
86
|
+
{/each}
|
|
87
|
+
</div>
|
|
75
88
|
</div>
|
|
76
89
|
{/each}
|
|
77
|
-
|
|
90
|
+
|
|
91
|
+
<UserDiscovery {onSelect} excludeTargets={acl.map(getTarget)} />
|
|
92
|
+
|
|
93
|
+
<div>
|
|
94
|
+
<button class="done" onclick={() => dialog!.close()}>Done</button>
|
|
95
|
+
</div>
|
|
96
|
+
</dialog>
|
|
78
97
|
|
|
79
98
|
<style>
|
|
99
|
+
dialog:open {
|
|
100
|
+
display: flex;
|
|
101
|
+
flex-direction: column;
|
|
102
|
+
gap: 1em;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.done {
|
|
106
|
+
float: right;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.permissions {
|
|
110
|
+
display: flex;
|
|
111
|
+
flex-direction: column;
|
|
112
|
+
gap: 0.1em;
|
|
113
|
+
}
|
|
114
|
+
|
|
80
115
|
.AccessControl {
|
|
81
116
|
display: grid;
|
|
82
117
|
gap: 1em;
|
package/lib/Upload.svelte
CHANGED
|
@@ -7,17 +7,23 @@
|
|
|
7
7
|
name = 'files',
|
|
8
8
|
input = $bindable(),
|
|
9
9
|
files = $bindable(),
|
|
10
|
+
progress = $bindable(),
|
|
10
11
|
...rest
|
|
11
|
-
}: HTMLInputAttributes & { input?: HTMLInputElement } = $props();
|
|
12
|
+
}: HTMLInputAttributes & { input?: HTMLInputElement; progress?: [current: number, max: number][] } = $props();
|
|
12
13
|
|
|
13
14
|
const id = $props.id();
|
|
14
15
|
</script>
|
|
15
16
|
|
|
16
17
|
<div>
|
|
17
18
|
<label for={id} class={[files?.length && 'file']}>
|
|
18
|
-
{#each files! as file}
|
|
19
|
+
{#each files! as file, i}
|
|
19
20
|
<Icon i={forMime(file.type)} />
|
|
20
|
-
<
|
|
21
|
+
<div class="name">
|
|
22
|
+
<span>{file.name}</span>
|
|
23
|
+
{#if progress?.[i]}
|
|
24
|
+
<progress value={progress[i][0]} max={progress[i][1]}></progress>
|
|
25
|
+
{/if}
|
|
26
|
+
</div>
|
|
21
27
|
<button
|
|
22
28
|
onclick={e => {
|
|
23
29
|
e.preventDefault();
|
|
@@ -53,6 +59,18 @@
|
|
|
53
59
|
width: 20em;
|
|
54
60
|
}
|
|
55
61
|
|
|
62
|
+
.name {
|
|
63
|
+
display: flex;
|
|
64
|
+
flex-direction: column;
|
|
65
|
+
justify-content: center;
|
|
66
|
+
min-width: 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
progress {
|
|
70
|
+
width: 100%;
|
|
71
|
+
height: 4px;
|
|
72
|
+
}
|
|
73
|
+
|
|
56
74
|
label.file {
|
|
57
75
|
display: grid;
|
|
58
76
|
grid-template-columns: 2em 1fr 2em;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { fetchAPI } from '@axium/client/requests';
|
|
3
|
+
import { getUserImage, type AccessTarget, type UserPublic } from '@axium/core';
|
|
4
|
+
import { colorHash } from '@axium/core/color';
|
|
5
|
+
import Icon from './Icon.svelte';
|
|
6
|
+
import { errorText } from '@axium/core/io';
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
onSelect,
|
|
10
|
+
enableTags = false,
|
|
11
|
+
excludeTargets = [],
|
|
12
|
+
}: {
|
|
13
|
+
onSelect(target: AccessTarget): unknown;
|
|
14
|
+
enableTags?: boolean;
|
|
15
|
+
excludeTargets?: string[];
|
|
16
|
+
} = $props();
|
|
17
|
+
|
|
18
|
+
type Result = { type: 'user'; value: UserPublic; target: string } | { type: 'role' | 'tag'; value: string; target: string };
|
|
19
|
+
|
|
20
|
+
let results = $state<Result[]>([]);
|
|
21
|
+
let value = $state<string>();
|
|
22
|
+
let gotError = $state<boolean>(false);
|
|
23
|
+
|
|
24
|
+
async function onchange() {
|
|
25
|
+
if (!value || !value.length) {
|
|
26
|
+
results = [];
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const users = await fetchAPI('POST', 'users/discover', value);
|
|
32
|
+
results = [
|
|
33
|
+
...users.map(value => ({ type: 'user', value, target: value.id }) as const),
|
|
34
|
+
{ type: 'role', value, target: '@' + value } as const,
|
|
35
|
+
enableTags && ({ type: 'tag', value, target: '#' + value } as const),
|
|
36
|
+
].filter<Result>(r => !!r);
|
|
37
|
+
} catch (e) {
|
|
38
|
+
gotError = true;
|
|
39
|
+
console.warn('Can not use user discovery:', errorText(e));
|
|
40
|
+
results = [];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function select(target: string) {
|
|
45
|
+
return (e: Event) => {
|
|
46
|
+
e.stopPropagation();
|
|
47
|
+
onSelect(target);
|
|
48
|
+
results = [];
|
|
49
|
+
value = '';
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<input bind:value type="text" placeholder="Add users and roles" {onchange} onkeyup={onchange} />
|
|
55
|
+
{#if !gotError}
|
|
56
|
+
<!-- Don't show results when we can't use the discovery API -->
|
|
57
|
+
<div class="results">
|
|
58
|
+
{#each results as result}
|
|
59
|
+
{#if !excludeTargets.includes(result.target)}
|
|
60
|
+
<div class="result" onclick={select(result.target)}>
|
|
61
|
+
{#if result.type == 'user'}
|
|
62
|
+
<span><img src={getUserImage(result.value)} alt={result.value.name} />{result.value.name}</span>
|
|
63
|
+
{:else if result.type == 'role'}
|
|
64
|
+
<span>
|
|
65
|
+
<span class="icon-text tag-or-role" style:background-color={colorHash(result.value)}
|
|
66
|
+
><Icon i="at" />{result.value}</span
|
|
67
|
+
>
|
|
68
|
+
</span>
|
|
69
|
+
{:else if result.type == 'tag'}
|
|
70
|
+
<span>
|
|
71
|
+
<span class="icon-text tag-or-role" style:background-color={colorHash(result.value)}
|
|
72
|
+
><Icon i="hashtag" />{result.value}</span
|
|
73
|
+
>
|
|
74
|
+
</span>
|
|
75
|
+
{/if}
|
|
76
|
+
</div>
|
|
77
|
+
{/if}
|
|
78
|
+
{:else}
|
|
79
|
+
<i>No results</i>
|
|
80
|
+
{/each}
|
|
81
|
+
</div>
|
|
82
|
+
{/if}
|
|
83
|
+
|
|
84
|
+
<style>
|
|
85
|
+
:host {
|
|
86
|
+
anchor-scope: --discovery-input;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
input {
|
|
90
|
+
anchor-name: --discovery-input;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
input:focus + .results,
|
|
94
|
+
.results:active {
|
|
95
|
+
display: flex;
|
|
96
|
+
animation: var(--A-zoom);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.results {
|
|
100
|
+
position: fixed;
|
|
101
|
+
position-anchor: --discovery-input;
|
|
102
|
+
inset: anchor(bottom) anchor(right) auto anchor(left);
|
|
103
|
+
display: none;
|
|
104
|
+
flex-direction: column;
|
|
105
|
+
gap: 0.25em;
|
|
106
|
+
height: fit-content;
|
|
107
|
+
max-height: 25em;
|
|
108
|
+
background-color: var(--bg-accent);
|
|
109
|
+
border-radius: 0.25em 0.25em 0.75em 0.75em;
|
|
110
|
+
padding: 1em;
|
|
111
|
+
border: 1px solid var(--border-accent);
|
|
112
|
+
align-items: stretch;
|
|
113
|
+
|
|
114
|
+
i {
|
|
115
|
+
text-align: center;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.result {
|
|
120
|
+
padding: 0.5em;
|
|
121
|
+
border-radius: 0.5em;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.result:hover {
|
|
125
|
+
cursor: pointer;
|
|
126
|
+
background-color: var(--bg-strong);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.tag-or-role {
|
|
130
|
+
border-radius: 1em;
|
|
131
|
+
padding: 0.25em 0.75em;
|
|
132
|
+
display: inline-flex;
|
|
133
|
+
align-items: center;
|
|
134
|
+
gap: 0.25em;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
img {
|
|
138
|
+
width: 2em;
|
|
139
|
+
height: 2em;
|
|
140
|
+
border-radius: 50%;
|
|
141
|
+
vertical-align: middle;
|
|
142
|
+
margin-right: 0.5em;
|
|
143
|
+
}
|
|
144
|
+
</style>
|
package/lib/ZodInput.svelte
CHANGED
|
@@ -204,21 +204,6 @@
|
|
|
204
204
|
{/if}
|
|
205
205
|
|
|
206
206
|
<style>
|
|
207
|
-
input[type='checkbox'] {
|
|
208
|
-
display: none;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
label.checkbox {
|
|
212
|
-
cursor: pointer;
|
|
213
|
-
width: 1.5em;
|
|
214
|
-
height: 1.5em;
|
|
215
|
-
border: 1px solid var(--border-accent);
|
|
216
|
-
border-radius: 0.5em;
|
|
217
|
-
display: inline-flex;
|
|
218
|
-
justify-content: center;
|
|
219
|
-
align-items: center;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
207
|
.ZodInput-error {
|
|
223
208
|
position: fixed;
|
|
224
209
|
position-anchor: --zod-input;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axium/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.1",
|
|
4
4
|
"author": "James Prevett <jp@jamespre.dev>",
|
|
5
5
|
"funding": {
|
|
6
6
|
"type": "individual",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"build": "tsc"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@axium/core": ">=0.
|
|
43
|
+
"@axium/core": ">=0.19.0",
|
|
44
44
|
"utilium": "^2.3.8",
|
|
45
45
|
"zod": "^4.0.5",
|
|
46
46
|
"svelte": "^5.36.0"
|