@axium/client 0.16.6 → 0.17.0

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.
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { register } from '@axium/client/user';
2
+ import { register, text } from '@axium/client';
3
3
  import FormDialog from './FormDialog.svelte';
4
4
  import authRedirect from './auth_redirect.js';
5
5
 
@@ -12,19 +12,19 @@
12
12
  }
13
13
  </script>
14
14
 
15
- <FormDialog bind:dialog submitText="Register" {submit} pageMode={fullPage}>
15
+ <FormDialog bind:dialog submitText={text('generic.register')} {submit} pageMode={fullPage}>
16
16
  <div>
17
- <label for="name">Display Name</label>
17
+ <label for="name">{text('component.Register.name')}</label>
18
18
  <input name="name" type="text" required />
19
19
  </div>
20
20
  <div>
21
- <label for="email">Email</label>
21
+ <label for="email">{text('component.Register.email')}</label>
22
22
  <input name="email" type="email" required />
23
23
  </div>
24
24
  {#snippet footer()}
25
25
  {#if fullPage}
26
26
  <div class="footer">
27
- <a href="/login">Login instead</a>
27
+ <a href="/login">{text('component.Register.login')}</a>
28
28
  </div>
29
29
  {/if}
30
30
  {/snippet}
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { logout, logoutAll } from '@axium/client/user';
2
+ import { logout, logoutAll, text } from '@axium/client';
3
3
  import type { Session, User } from '@axium/core';
4
4
  import FormDialog from './FormDialog.svelte';
5
5
  import Icon from './Icon.svelte';
@@ -15,19 +15,19 @@
15
15
  {#each sessions as session}
16
16
  <div class="item session">
17
17
  <p>
18
- {session.id.slice(0, 4)}...{session.id.slice(-4)}
18
+ {session.name ?? `${session.id.slice(0, 4)}...${session.id.slice(-4)}`}
19
19
  {#if session.id == currentSession?.id}
20
- <span class="current">Current</span>
20
+ <span class="current">{text('component.SessionList.current')}</span>
21
21
  {/if}
22
22
  {#if session.elevated}
23
- <span class="elevated">Elevated</span>
23
+ <span class="elevated">{text('component.SessionList.elevated')}</span>
24
24
  {/if}
25
25
  </p>
26
- <p class="timestamp">Created {session.created.toLocaleString()}</p>
27
- <p class="timestamp">Expires {session.expires.toLocaleString()}</p>
26
+ <p class="timestamp">{text('component.SessionList.created', { date: session.created.toLocaleString() })}</p>
27
+ <p class="timestamp">{text('component.SessionList.expires', { date: session.expires.toLocaleString() })}</p>
28
28
  <button command="show-modal" commandfor={'logout-session:' + session.id} class="logout icon-text">
29
29
  <Icon i="right-from-bracket" --size="16px" />
30
- <span class="mobile-only">Logout</span>
30
+ <span class="mobile-only">{text('generic.logout')}</span>
31
31
  </button>
32
32
  </div>
33
33
  <FormDialog
@@ -37,19 +37,20 @@
37
37
  sessions.splice(sessions.indexOf(session), 1);
38
38
  if (session.id == currentSession?.id) window.location.href = '/';
39
39
  }}
40
- submitText="Logout"
40
+ submitText={text('generic.logout')}
41
41
  >
42
- <p>Are you sure you want to log out this session?</p>
42
+ <p>{text('component.SessionList.logout_single')}</p>
43
43
  </FormDialog>
44
44
  {/each}
45
- <button command="show-modal" commandfor="logout-all" class="danger inline-button">Logout All</button>
45
+ <button command="show-modal" commandfor="logout-all" class="danger inline-button">{text('component.SessionList.logout_all_trigger')}</button
46
+ >
46
47
  <FormDialog
47
48
  id="logout-all"
48
49
  submit={() => logoutAll(user.id).then(() => (redirectAfterLogoutAll ? (window.location.href = '/') : null))}
49
- submitText="Logout All Sessions"
50
+ submitText={text('component.SessionList.logout_all_submit')}
50
51
  submitDanger
51
52
  >
52
- <p>Are you sure you want to log out all sessions?</p>
53
+ <p>{text('component.SessionList.logout_all_question')}</p>
53
54
  </FormDialog>
54
55
 
55
56
  <style>
@@ -15,8 +15,7 @@
15
15
  <div class="sidebar-container">
16
16
  <div class="sidebar">
17
17
  {#each tabs as { href, name, icon: i, active }}
18
- <a {href} class={['item', 'icon-text', active && 'active']}><Icon {i} /> <span class="sidebar-text">{capitalize(name)}</span></a
19
- >
18
+ <a {href} class={['item', 'icon-text', active && 'active']}><Icon {i} /> <span class="sidebar-text">{name}</span></a>
20
19
  {/each}
21
20
 
22
21
  {#if bottom}
package/lib/Upload.svelte CHANGED
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { text } from '@axium/client';
2
3
  import { forMime } from '@axium/core/icons';
3
4
  import type { HTMLInputAttributes } from 'svelte/elements';
4
5
  import Icon from './Icon.svelte';
@@ -45,7 +46,7 @@
45
46
  <Icon i="cloud-arrow-up" />
46
47
  {/if}
47
48
  {:else}
48
- <Icon i="upload" /> Upload
49
+ <Icon i="upload" /> {text('component.Upload.upload')}
49
50
  {/each}
50
51
  </label>
51
52
 
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
- import type { User } from '@axium/core/user';
2
+ import { text } from '@axium/client';
3
3
  import { getUserImage } from '@axium/core';
4
+ import type { User } from '@axium/core/user';
4
5
 
5
6
  const {
6
7
  user,
@@ -27,7 +28,7 @@
27
28
  {/if}
28
29
  {user.name}
29
30
  {#if self && you}
30
- <span class="subtle">(You)</span>
31
+ <span class="subtle">{text('component.UserCard.you')}</span>
31
32
  {/if}
32
33
  </a>
33
34
 
@@ -1,4 +1,5 @@
1
1
  <script lang="ts">
2
+ import { text } from '@axium/client';
2
3
  import { fetchAPI } from '@axium/client/requests';
3
4
  import { getUserImage, type UserPublic } from '@axium/core';
4
5
  import { colorHashRGB } from '@axium/core/color';
@@ -55,7 +56,7 @@
55
56
  }
56
57
  </script>
57
58
 
58
- <input bind:value type="text" placeholder="Add users and roles" {oninput} />
59
+ <input bind:value type="text" placeholder={text('component.UserDiscovery.placeholder')} {oninput} />
59
60
  {#if !gotError && value}
60
61
  <!-- Don't show results when we can't use the discovery API -->
61
62
  <div class="results">
@@ -82,7 +83,7 @@
82
83
  </div>
83
84
  {/if}
84
85
  {:else}
85
- <i>No results</i>
86
+ <i>{text('component.UserDiscovery.no_results')}</i>
86
87
  {/each}
87
88
  </div>
88
89
  {/if}
@@ -114,7 +115,7 @@
114
115
  background-color: var(--bg-accent);
115
116
  border-radius: 0.25em 0.25em 0.75em 0.75em;
116
117
  padding: 1em;
117
- border: 1px solid var(--border-accent);
118
+ border: var(--border-accent);
118
119
  align-items: stretch;
119
120
 
120
121
  i {
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { fetchAPI } from '@axium/client/requests';
2
+ import { fetchAPI, text } from '@axium/client';
3
3
  import { getUserImage } from '@axium/core';
4
4
  import type { UserPublic } from '@axium/core/user';
5
5
  import Icon from './Icon.svelte';
@@ -20,18 +20,18 @@
20
20
 
21
21
  <a class="menu-item" href="/account">
22
22
  <Icon i="user" --size="1.5em" />
23
- <span>Your Account</span>
23
+ <span>{text('component.UserMenu.account')}</span>
24
24
  </a>
25
25
 
26
26
  {#if user.isAdmin}
27
27
  <a class="menu-item" href="/admin">
28
28
  <Icon i="gear-complex" --size="1.5em" />
29
- <span>Administration</span>
29
+ <span>{text('component.UserMenu.admin')}</span>
30
30
  </a>
31
31
  {/if}
32
32
 
33
33
  {#await fetchAPI('GET', 'apps')}
34
- <i>Loading...</i>
34
+ <i>{text('generic.loading')}</i>
35
35
  {:then apps}
36
36
  {#each apps as app}
37
37
  <a class="menu-item" href="/{app.id}">
@@ -42,18 +42,18 @@
42
42
  {:else}
43
43
  <Icon i="image-circle-xmark" --size="1.5em" />
44
44
  {/if}
45
- <span>{app.name}</span>
45
+ <span>{text('app_name.' + app.id, { $default: app.name })}</span>
46
46
  </a>
47
47
  {:else}
48
- <i>No apps available.</i>
48
+ <i>{text('component.AppMenu.none')}</i>
49
49
  {/each}
50
50
  {:catch}
51
- <i>Couldn't load apps.</i>
51
+ <i>{text('component.AppMenu.failed')}</i>
52
52
  {/await}
53
53
 
54
54
  <button class="menu-item logout reset" command="show-modal" commandfor="logout">
55
55
  <Icon i="right-from-bracket" --size="1.5em" --fill="hsl(0 33 var(--fg-light))" />
56
- <span>Logout</span>
56
+ <span>{text('generic.logout')}</span>
57
57
  </button>
58
58
  </Popover>
59
59
 
@@ -68,7 +68,7 @@
68
68
  .UserMenu {
69
69
  border-radius: 0.5em;
70
70
  padding: 0.5em;
71
- border: 1px solid var(--border-accent);
71
+ border: var(--border-accent);
72
72
  cursor: pointer;
73
73
  background-color: var(--bg-alt);
74
74
  }
@@ -1,18 +1,27 @@
1
1
  <script lang="ts">
2
- const { v, latest }: { v: string; latest?: string | null } = $props();
2
+ import { text } from '@axium/client';
3
+ import { error } from '@axium/core/io';
3
4
  import { lt as ltVersion } from 'semver';
5
+
6
+ const { v, latest: _latest }: { v: string; latest?: string | null | Promise<string> } = $props();
7
+
8
+ if (_latest && typeof _latest != 'string') _latest.catch(error);
4
9
  </script>
5
10
 
6
11
  <span class="version">{v}</span>
7
12
 
8
- {#if latest}
9
- <span class="latest">
10
- {#if ltVersion(v, latest)}
11
- <span class="version">{latest}</span> available
12
- {:else}
13
- Latest
14
- {/if}
15
- </span>
13
+ {#if _latest}
14
+ {#await _latest then latest}
15
+ <span class="latest">
16
+ {#if ltVersion(v, latest)}
17
+ {@html text('component.Version.upgrade', { $html: true, latest })}
18
+ {:else}
19
+ {text('component.Version.latest')}
20
+ {/if}
21
+ </span>
22
+ {:catch}
23
+ <span class="latest error">{text('component.Version.error')}</span>
24
+ {/await}
16
25
  {/if}
17
26
 
18
27
  <style>
@@ -23,6 +32,10 @@
23
32
  border-radius: 1em;
24
33
  }
25
34
 
35
+ .error {
36
+ border: var(--border-error);
37
+ }
38
+
26
39
  :global(h1 h2, h3, h4, h5, h6) {
27
40
  .latest {
28
41
  margin-left: 1em;
@@ -1,10 +1,12 @@
1
1
  <script lang="ts">
2
+ import { text } from '@axium/client';
2
3
  import type { ZodPref } from '@axium/core';
4
+ import { zKeys } from '@axium/core/locales';
3
5
  import type { HTMLInputAttributes } from 'svelte/elements';
4
6
  import { getByString, pick, setByString } from 'utilium';
7
+ import { prettifyError } from 'zod';
5
8
  import Icon from './Icon.svelte';
6
9
  import ZodInput from './ZodInput.svelte';
7
- import { prettifyError } from 'zod';
8
10
 
9
11
  interface Props {
10
12
  idPrefix?: string;
@@ -18,17 +20,10 @@
18
20
  updateValue(value: any): void;
19
21
  }
20
22
 
21
- let {
22
- rootValue = $bindable(),
23
- label,
24
- path,
25
- schema,
26
- optional = false,
27
- defaultValue,
28
- idPrefix,
29
- updateValue,
30
- noLabel = false,
31
- }: Props = $props();
23
+ let { rootValue = $bindable(), ...props }: Props = $props();
24
+
25
+ let { label, path, schema, optional, defaultValue, idPrefix, updateValue, noLabel } = props;
26
+
32
27
  const id = (idPrefix ? idPrefix + ':' : '') + path.replaceAll(' ', '_');
33
28
 
34
29
  let value = $state<any>(getByString(rootValue, path));
@@ -81,18 +76,20 @@
81
76
  if (defaultValue == val) {
82
77
  const parts = path.split('.');
83
78
  const prop = parts.pop()!;
84
- delete getByString<Record<string, any>>(rootValue, parts.join('.'))[prop];
79
+ delete getByString<Record<string, any>>(rootValue, parts.join('.'))![prop];
85
80
  } else setByString(rootValue, path, val);
86
81
 
87
82
  updateValue(rootValue);
88
83
  }
89
84
 
90
85
  const oninput = onchange;
86
+
87
+ const labelText = $derived(zKeys.has(props.schema) ? text(zKeys.get(props.schema)!.key) : label || path);
91
88
  </script>
92
89
 
93
90
  {#snippet _in(rest: HTMLInputAttributes)}
94
91
  <div class="ZodInput">
95
- {#if !noLabel}<label for={id}>{label || path}</label>{/if}
92
+ {#if !noLabel}<label for={id}>{labelText}</label>{/if}
96
93
  {#if error}<span class="ZodInput-error error-text">{error}</span>{/if}
97
94
  <input {id} {...rest} bind:value {onchange} {oninput} required={!optional} {defaultValue} class={[error && 'error']} />
98
95
  </div>
@@ -106,7 +103,7 @@
106
103
  {@render _in({ type: 'number', min: Number(schema.minValue), max: Number(schema.maxValue), step: 1 })}
107
104
  {:else if schema.type == 'boolean'}
108
105
  <div class="ZodInput">
109
- {#if !noLabel}<label for="{id}:checkbox">{label || path}</label>{/if}
106
+ {#if !noLabel}<label for="{id}:checkbox">{labelText}</label>{/if}
110
107
  <input bind:checked={value} id="{id}:checkbox" type="checkbox" {onchange} required={!optional} />
111
108
  <label for="{id}:checkbox" {id} class="checkbox">
112
109
  {#if value}<Icon i="check" --size="1.3em" />{/if}
@@ -122,7 +119,7 @@
122
119
  <!-- todo -->
123
120
  {:else if schema.type == 'literal'}
124
121
  <div class="ZodInput">
125
- <label for={id}>{label || path}</label>
122
+ <label for={id}>{labelText}</label>
126
123
  <select bind:value {id} {oninput} required={!optional}>
127
124
  {#each schema.values as value}
128
125
  <option {value} selected={value === value}>{value}</option>
@@ -132,13 +129,13 @@
132
129
  {:else if schema.type == 'template_literal'}
133
130
  <!-- todo -->
134
131
  {:else if schema.type == 'default'}
135
- <ZodInput bind:rootValue {updateValue} {idPrefix} {path} schema={schema.def.innerType} defaultValue={schema.def.defaultValue} />
132
+ <ZodInput bind:rootValue {...props} label={labelText} schema={schema.def.innerType} defaultValue={schema.def.defaultValue} />
136
133
  {:else if schema.type == 'nullable' || schema.type == 'optional'}
137
134
  <!-- defaults are handled differently -->
138
- <ZodInput bind:rootValue {updateValue} {idPrefix} {path} {defaultValue} schema={schema.def.innerType} optional={true} />
135
+ <ZodInput bind:rootValue {...props} label={labelText} schema={schema.def.innerType} optional={true} />
139
136
  {:else if schema.type == 'array'}
140
137
  <div class="ZodInput">
141
- {#if !noLabel}<label for={id}>{label || path}</label>{/if}
138
+ {#if !noLabel}<label for={id}>{labelText}</label>{/if}
142
139
  {#if error}<span class="ZodInput-error error-text">{error}</span>{/if}
143
140
  <div class="ZodInput-array">
144
141
  {#each value, i}
@@ -164,25 +161,25 @@
164
161
  {#each Object.keys(value) as key}
165
162
  <div class="ZodInput-record-entry">
166
163
  {#if !noLabel}<label for={id}>{key}</label>{/if}
167
- <ZodInput bind:rootValue {updateValue} {idPrefix} {defaultValue} path="{path}.{key}" schema={schema.valueType} />
164
+ <ZodInput bind:rootValue {updateValue} {idPrefix} path="{path}.{key}" schema={schema.valueType} />
168
165
  </div>
169
166
  {/each}
170
167
  </div>
171
168
  {:else if schema.type == 'object'}
172
169
  <!-- <div class="ZodInput-object"> -->
173
170
  {#each Object.entries(schema.shape) as [key, value]}
174
- <ZodInput bind:rootValue {updateValue} {idPrefix} {defaultValue} path="{path}.{key}" schema={value} />
171
+ <ZodInput bind:rootValue {updateValue} {idPrefix} path="{path}.{key}" schema={value} />
175
172
  {/each}
176
173
  <!-- </div> -->
177
174
  {:else if schema.type == 'tuple'}
178
175
  <div class="ZodInput-tuple" data-rest={schema.def.rest}>
179
176
  {#each schema.def.items as item, i}
180
- <ZodInput bind:rootValue {updateValue} {idPrefix} {defaultValue} path="{path}.{i}" schema={item} />
177
+ <ZodInput bind:rootValue {updateValue} {idPrefix} defaultValue={defaultValue?.[i]} path="{path}.{i}" schema={item} />
181
178
  {/each}
182
179
  </div>
183
180
  {:else if schema.type == 'enum'}
184
181
  <div class="ZodInput">
185
- {#if !noLabel}<label for={id}>{label || path}</label>{/if}
182
+ {#if !noLabel}<label for={id}>{labelText}</label>{/if}
186
183
  {#if error}<span class="ZodInput-error error-text">{error}</span>{/if}
187
184
  <select {id} {onchange} bind:value required={!optional}>
188
185
  {#each Object.entries(schema.enum) as [key, value]}
@@ -192,7 +189,7 @@
192
189
  </div>
193
190
  {:else}
194
191
  <!-- No idea how to render this -->
195
- <i class="error-text">Invalid preference type: {JSON.stringify((schema as ZodPref)?.def?.type)}</i>
192
+ <i class="error-text">{text('component.ZodInput.invalid_type', { type: JSON.stringify((schema as ZodPref)?.def?.type) })}</i>
196
193
  {/if}
197
194
 
198
195
  <style>
package/lib/tsconfig.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "compilerOptions": {
4
4
  "target": "ES2023",
5
5
  "strict": true,
6
- "rootDir": ".",
6
+ "rootDir": "..",
7
7
  "lib": ["ESNext", "DOM", "DOM.Iterable"],
8
8
  "types": ["@sveltejs/kit"]
9
9
  },
@@ -0,0 +1,213 @@
1
+ {
2
+ "component": {
3
+ "AccessControlDialog": {
4
+ "named_title": "Permissions for <strong>{name}</strong>",
5
+ "owner": "Owner",
6
+ "title": "Permissions"
7
+ },
8
+ "AppMenu": {
9
+ "failed": "Couldn't load apps.",
10
+ "none": "No apps available."
11
+ },
12
+ "Login": {
13
+ "email": "Email",
14
+ "register": "Register instead"
15
+ },
16
+ "Logout": {
17
+ "back": "Take me back",
18
+ "question": "Are you sure you want to log out?"
19
+ },
20
+ "Register": {
21
+ "email": "Email",
22
+ "login": "Login instead",
23
+ "name": "Display Name"
24
+ },
25
+ "SessionList": {
26
+ "created": "Created {date}",
27
+ "current": "Current",
28
+ "elevated": "Elevated",
29
+ "expires": "Expires {date}",
30
+ "logout_all_question": "Are you sure you want to log out all sessions?",
31
+ "logout_all_submit": "Logout All Sessions",
32
+ "logout_all_trigger": "Logout All",
33
+ "logout_single": "Are you sure you want to log out this session?"
34
+ },
35
+ "Upload": {
36
+ "upload": "Upload"
37
+ },
38
+ "UserCard": {
39
+ "you": "(You)"
40
+ },
41
+ "UserDiscovery": {
42
+ "no_results": "No results",
43
+ "placeholder": "Add users and roles"
44
+ },
45
+ "UserMenu": {
46
+ "account": "Your Account",
47
+ "admin": "Administration"
48
+ },
49
+ "Version": {
50
+ "error": "Latest unknown",
51
+ "latest": "Latest",
52
+ "upgrade": "<span class=\"version\">{latest}</span> available"
53
+ },
54
+ "ZodInput": {
55
+ "invalid_type": "Invalid input type: {type}"
56
+ }
57
+ },
58
+ "generic": {
59
+ "action_irreversible": "This action can't be undone.",
60
+ "cancel": "Cancel",
61
+ "change": "Change",
62
+ "create": "Create",
63
+ "delete": "Delete",
64
+ "done": "Done",
65
+ "email": "Email",
66
+ "loading": "Loading...",
67
+ "login": "Login",
68
+ "logout": "Logout",
69
+ "no": "No",
70
+ "none": "None",
71
+ "ok": "Okay",
72
+ "preferences": "Preferences",
73
+ "register": "Register",
74
+ "sessions": "Sessions",
75
+ "unknown": "Unknown",
76
+ "unnamed": "Unnamed",
77
+ "username": "Name",
78
+ "yes": "Yes"
79
+ },
80
+ "page": {
81
+ "account": {
82
+ "delete_account": "Delete Account",
83
+ "delete_account_confirm": "Are you sure you want to delete your account?",
84
+ "edit_email": "Email Address",
85
+ "edit_name": "What do you want to be called?",
86
+ "email_verified_on": "Email verified on {date}",
87
+ "greeting": "Welcome, {name}",
88
+ "passkeys": {
89
+ "backed_up": "This passkey is backed up",
90
+ "create": "Create",
91
+ "created": "Created {date}",
92
+ "delete": "Delete",
93
+ "delete_confirm": "Are you sure you want to delete this passkey?",
94
+ "edit_name": "Passkey Name",
95
+ "min_one": "You must have at least one passkey",
96
+ "multi_device": "Multiple devices",
97
+ "name_type_error": "Passkey name must be a string",
98
+ "not_backed_up": "This passkey is not backed up",
99
+ "rename": "Rename",
100
+ "single_device": "Single device",
101
+ "title": "Passkeys"
102
+ },
103
+ "personal_info": "Personal Information",
104
+ "preferences": "Preferences",
105
+ "profile_alt": "User profile",
106
+ "sessions": "Sessions",
107
+ "title": "Your Account",
108
+ "user_id": "User ID",
109
+ "user_id_hint": "This is your UUID. It can't be changed.",
110
+ "verification_sent": "Verification email sent",
111
+ "verify": "Verify"
112
+ },
113
+ "admin": {
114
+ "heading": "Administration",
115
+ "audit": {
116
+ "any": "Any",
117
+ "apply": "Apply",
118
+ "error_stack": "Error Stack",
119
+ "event_heading": "Audit Event",
120
+ "event_title": "Admin — Audit Log Event #{id}",
121
+ "extra_data": "Extra Data",
122
+ "filter": {
123
+ "event": "Event Name:",
124
+ "severity": "Minimum Severity:",
125
+ "since": "Since:",
126
+ "source": "Source:",
127
+ "tags": "Tags:",
128
+ "until": "Until:",
129
+ "user": "User UUID:"
130
+ },
131
+ "filters": "Filters",
132
+ "heading": "Audit Log",
133
+ "invalid_filter": "Invalid Filter:",
134
+ "name": "Name",
135
+ "no_events": "No audit log events found",
136
+ "reset": "Reset",
137
+ "severity": "Severity",
138
+ "source": "Source",
139
+ "tags": "Tags",
140
+ "timestamp": "Timestamp",
141
+ "title": "Admin — Audit Log",
142
+ "user": "User",
143
+ "uuid": "UUID"
144
+ },
145
+ "config": {
146
+ "active": "Active Configuration",
147
+ "loaded_files": "Loaded Files",
148
+ "title": "Admin — Configuration"
149
+ },
150
+ "dashboard": {
151
+ "audit_link": "Audit Log",
152
+ "config_files": "{count} files loaded.",
153
+ "config_link": "Configuration",
154
+ "plugins_link": "Plugins",
155
+ "plugins_loaded": "{count} plugins loaded.",
156
+ "stats": "{users} users, {sessions} sessions, {passkeys} passkeys.",
157
+ "title": "Admin — Dashboard",
158
+ "users_link": "Users"
159
+ },
160
+ "plugins": {
161
+ "author": "Author:",
162
+ "configuration": "Configuration",
163
+ "heading": "Plugins",
164
+ "loaded_from": "Loaded from",
165
+ "none": "No plugins loaded.",
166
+ "provided_apps": "Provided apps:",
167
+ "title": "Admin — Plugins"
168
+ },
169
+ "users": {
170
+ "admin_tag": "Admin",
171
+ "administrator": "Administrator",
172
+ "attributes": "Attributes",
173
+ "audit": "Audit",
174
+ "back": "Back to all users",
175
+ "create": "Create User",
176
+ "created_title": "New User Created",
177
+ "created_url": "They can log in using this URL:",
178
+ "default_image": "Default",
179
+ "delete_confirm": "Are you sure you want to delete this user?",
180
+ "delete_user": "Delete User",
181
+ "display_name": "Display Name",
182
+ "email_not_verified": "not verified",
183
+ "email_verified": "verified {date}",
184
+ "heading": "Users",
185
+ "manage": "Manage",
186
+ "manage_heading": "User Management",
187
+ "manage_title": "Admin — User Management",
188
+ "none": "No users!",
189
+ "profile_image": "Profile Image",
190
+ "registered": "Registered",
191
+ "roles": "Roles",
192
+ "suspend": "Suspend",
193
+ "suspended": "Suspended",
194
+ "tags": "Tags",
195
+ "title": "Admin — Users",
196
+ "unsuspend": "Unsuspend",
197
+ "uuid": "UUID"
198
+ }
199
+ },
200
+ "login": {
201
+ "client": {
202
+ "authorize": "Authorize",
203
+ "confirm": "Are you sure you want to log in to this local client?",
204
+ "success": "Login successful! You can close this tab.",
205
+ "title": "Local Client Login"
206
+ },
207
+ "failed": "Login Failed"
208
+ }
209
+ },
210
+ "preference": {
211
+ "debug": "Debug mode"
212
+ }
213
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axium/client",
3
- "version": "0.16.6",
3
+ "version": "0.17.0",
4
4
  "author": "James Prevett <jp@jamespre.dev>",
5
5
  "funding": {
6
6
  "type": "individual",
@@ -26,10 +26,12 @@
26
26
  "assets",
27
27
  "dist",
28
28
  "lib",
29
+ "locales",
29
30
  "styles",
30
31
  "axium-client.service"
31
32
  ],
32
33
  "exports": {
34
+ "./package.json": "./package.json",
33
35
  ".": "./dist/index.js",
34
36
  "./*": "./dist/*.js",
35
37
  "./components": "./lib/index.js",
@@ -41,10 +43,10 @@
41
43
  "build": "tsc"
42
44
  },
43
45
  "peerDependencies": {
44
- "@axium/core": ">=0.20.0",
45
- "utilium": "^2.6.3",
46
- "zod": "^4.0.5",
47
- "svelte": "^5.36.0"
46
+ "@axium/core": ">=0.20.3",
47
+ "svelte": "^5.36.0",
48
+ "utilium": "^2.8.0",
49
+ "zod": "^4.0.5"
48
50
  },
49
51
  "dependencies": {
50
52
  "@simplewebauthn/browser": "^13.1.0",