@webx-ui/module-auth 0.7.4 → 0.8.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.
- package/README.md +21 -0
- package/dist/AdminList.d.ts +1 -1
- package/dist/AdminPicker.d.ts +2 -2
- package/dist/AdminsPage.d.ts +22 -6
- package/dist/CallList.d.ts +6 -0
- package/dist/ConnectPage.d.ts +2 -0
- package/dist/ConnectionList.d.ts +28 -0
- package/dist/admins.d.ts +3 -1
- package/dist/connections.d.ts +17 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.js +1695 -630
- package/dist/index.js.map +1 -1
- package/dist/module.d.ts +18 -0
- package/dist/session.d.ts +9 -1
- package/dist/style.css +1 -1
- package/dist/types.d.ts +81 -0
- package/package.json +3 -3
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/session.ts","../src/LoginCard.vue","../src/messages.ts","../src/admins.ts","../src/i18n.ts","../src/AdminDialog.vue","../src/AdminList.vue","../src/AdminsPage.vue","../src/module.ts","../src/AdminPicker.vue","../src/selectAdmin.ts","../src/ProfileDialog.vue","../src/UserMenu.vue","../src/index.ts"],"sourcesContent":["import { inject, type App, type Component, type InjectionKey } from 'vue'\nimport type { AdminContext, AdminUser } from '@webx-ui/module-admin'\n\nexport interface Credentials {\n email: string\n password: string\n remember?: boolean\n}\n\nexport interface AuthSession {\n /** Who is signed in, or null. A 401 is an answer, not a failure. */\n me(): Promise<AdminUser | null>\n /** Signs in and tells the panel to load itself. Throws `HttpError` on a refusal. */\n login(credentials: Credentials): Promise<AdminUser>\n logout(): Promise<void>\n /**\n * Change the language this administrator reads the panel in.\n *\n * Stored on the person rather than in this browser, so it follows them to the next machine\n * and so the server writes its own messages — a 422 under a field — in the same language as\n * the label above it.\n */\n setLocale(code: string): Promise<void>\n /**\n * Change what is yours to change about yourself: your name, your photograph, your password.\n *\n * Not the administrators endpoint. That one is behind `admins.manage`, which is a permission\n * about other people — an editor who may not manage anybody still has a name to spell and\n * a password to rotate.\n */\n updateProfile(profile: ProfileInput): Promise<AdminUser>\n}\n\n/** What somebody may send about themselves. A blank password is \"leave it alone\". */\nexport interface ProfileInput {\n name: string\n avatar: string | null\n password?: string\n /** Required by the server whenever `password` is filled in. */\n current_password?: string\n}\n\nexport const authKey: InjectionKey<AuthSession> = Symbol('webx-auth')\n\nexport function useAuth(): AuthSession {\n const session = inject(authKey, null)\n\n if (session === null) {\n throw new Error('useAuth() was called outside a panel that installed the auth plugin.')\n }\n\n return session\n}\n\n/**\n * The three calls `webx-ui/module-auth` answers, and nothing else.\n *\n * The session lives in a cookie, so there is nothing to keep here: the source of truth is the\n * server, and the panel asks it rather than remembering.\n */\nexport function createAuthSession(admin: AdminContext): AuthSession {\n const base = `${admin.apiPath}/auth`\n\n return {\n async me() {\n try {\n const body = await admin.http.get<{ data: AdminUser | null }>(`${base}/me`)\n\n return body.data\n } catch (error) {\n if (isUnauthenticated(error)) {\n return null\n }\n\n throw error\n }\n },\n\n async login(credentials) {\n const body = await admin.http.post<{ data: AdminUser }>(`${base}/login`, {\n email: credentials.email,\n password: credentials.password,\n remember: credentials.remember ?? false,\n })\n\n admin.setUser(body.data)\n\n // Signing in is the moment the panel becomes knowable: the manifest needs a session,\n // so it could not have been loaded before now.\n await admin.reload()\n\n return body.data\n },\n\n async setLocale(code) {\n // The panel redraws only after the server has accepted the choice: a language that\n // half took — menus switched, error messages not — is worse than one that did not.\n const body = await admin.http.put<{ data: AdminUser }>(`${base}/locale`, { locale: code })\n\n admin.setUser(body.data)\n\n await admin.setLocale(code)\n },\n\n async updateProfile(profile) {\n const body = await admin.http.put<{ data: AdminUser }>(\n `${base}/me`,\n profile as unknown as Record<string, unknown>,\n )\n\n // The corner of the panel is drawn from this, so the new name and the new photograph\n // are on screen before the dialog has finished closing.\n admin.setUser(body.data)\n\n return body.data\n },\n\n async logout() {\n try {\n await admin.http.post(`${base}/logout`)\n } finally {\n // Whatever the server said, this browser is done: leaving the panel drawn behind a\n // failed sign-out would be worse than signing out optimistically.\n admin.setUser(null)\n admin.state.status = 'unauthenticated'\n admin.state.manifest = null\n }\n },\n }\n}\n\n/** Turns the key an avatar is stored under into an address the browser can load. */\nexport type AvatarResolver = (key: string) => Promise<string | null>\n\n/**\n * The resolver the panel handed to `auth()`, for whatever draws the signed-in person — the\n * menu in the corner, first of all. Null in a panel that has no library to resolve against,\n * which is what initials are for.\n */\nexport const avatarResolverKey: InjectionKey<AvatarResolver | null> = Symbol('webx-auth-avatar')\n\n/**\n * The field the panel picks photographs with, for the profile dialog behind the corner menu.\n *\n * Provided rather than passed: the shell renders the user menu itself, and a component it\n * builds is not a place to hand a component down through. Null in a panel with no library —\n * the profile still opens, with everything but the photograph.\n */\nexport const avatarFieldKey: InjectionKey<Component | null> = Symbol('webx-auth-avatar-field')\n\nexport function provideAuth(\n app: App,\n session: AuthSession,\n resolveAvatar: AvatarResolver | null = null,\n avatarField: Component | null = null,\n): void {\n app.provide(authKey, session)\n app.provide(avatarResolverKey, resolveAvatar)\n app.provide(avatarFieldKey, avatarField)\n}\n\nfunction isUnauthenticated(error: unknown): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'status' in error &&\n (error as { status: unknown }).status === 401\n )\n}\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, ref, useTemplateRef } from 'vue'\nimport { HttpError, useTranslate } from '@webx-ui/module-admin'\nimport { useAuth } from './session'\n\n/**\n * The card a panel opens on.\n *\n * No logo, no product name, no \"welcome back\" — an admin panel's front door should say as\n * little as possible to somebody who has no business behind it, and the people who do have\n * business behind it already know where they are.\n *\n * Every string is still a prop and a prop given still wins. What changed is what happens when\n * one is not: the label comes from the panel's dictionary, which the server assembles from the\n * same `lang` files it answers its own messages from. A panel gets its language without being\n * told every label twice, and a card placed outside a panel still speaks English.\n */\nconst props = withDefaults(\n defineProps<{\n emailLabel?: string\n passwordLabel?: string\n rememberLabel?: string\n submitLabel?: string\n capsLockWarning?: string\n /** `:seconds` is replaced with however long the server said to wait. */\n throttleMessage?: string\n revealLabel?: string\n hideLabel?: string\n /** Offer to stay signed in. The server honours it with a long-lived cookie. */\n remember?: boolean\n /** Put the cursor in the email field. Off for a card that is not the only thing on screen. */\n autofocus?: boolean\n }>(),\n {\n // Undefined rather than a string, so \"not given\" is tellable from \"given as English\" and\n // the dictionary gets its turn.\n emailLabel: undefined,\n passwordLabel: undefined,\n rememberLabel: undefined,\n submitLabel: undefined,\n capsLockWarning: undefined,\n throttleMessage: undefined,\n revealLabel: undefined,\n hideLabel: undefined,\n remember: true,\n autofocus: true,\n },\n)\n\nconst emit = defineEmits<{ success: [] }>()\n\nconst auth = useAuth()\nconst t = useTranslate('webx-auth')\n\nconst labels = computed(() => ({\n email: props.emailLabel ?? t('card.email'),\n password: props.passwordLabel ?? t('card.password'),\n remember: props.rememberLabel ?? t('card.remember'),\n submit: props.submitLabel ?? t('card.submit'),\n capsLock: props.capsLockWarning ?? t('card.caps-lock'),\n reveal: props.revealLabel ?? t('card.reveal'),\n hide: props.hideLabel ?? t('card.hide'),\n}))\n\nconst email = ref('')\nconst password = ref('')\nconst rememberMe = ref(false)\n\nconst busy = ref(false)\nconst revealed = ref(false)\nconst capsLock = ref(false)\nconst message = ref('')\nconst errors = ref<Record<string, string[]>>({})\nconst secondsLeft = ref(0)\n\nconst emailField = useTemplateRef<HTMLInputElement>('emailField')\n\nlet countdown: ReturnType<typeof setInterval> | null = null\n\nonBeforeUnmount(stopCountdown)\n\nconst throttled = computed(() => secondsLeft.value > 0)\n\nconst throttleNotice = computed(() =>\n props.throttleMessage === undefined\n ? t('card.throttled', { seconds: secondsLeft.value })\n : props.throttleMessage.replace(':seconds', String(secondsLeft.value)),\n)\n\nasync function submit(): Promise<void> {\n if (busy.value || throttled.value) {\n return\n }\n\n busy.value = true\n message.value = ''\n errors.value = {}\n\n try {\n await auth.login({\n email: email.value,\n password: password.value,\n remember: rememberMe.value,\n })\n\n password.value = ''\n emit('success')\n } catch (error) {\n handle(error)\n } finally {\n busy.value = false\n }\n}\n\nfunction handle(error: unknown): void {\n if (!(error instanceof HttpError)) {\n message.value = error instanceof Error ? error.message : String(error)\n\n return\n }\n\n // A bad password comes back as a 422 against the email field, the same as an address\n // nobody owns — the server refuses to say which, and so does this.\n if (error.isValidation) {\n errors.value = error.errors\n\n return\n }\n\n if (error.isThrottled) {\n startCountdown(error.retryAfter ?? 60)\n\n return\n }\n\n message.value = error.message\n}\n\nfunction startCountdown(seconds: number): void {\n stopCountdown()\n secondsLeft.value = seconds\n\n countdown = setInterval(() => {\n secondsLeft.value -= 1\n\n if (secondsLeft.value <= 0) {\n stopCountdown()\n }\n }, 1000)\n}\n\nfunction stopCountdown(): void {\n if (countdown !== null) {\n clearInterval(countdown)\n countdown = null\n }\n\n secondsLeft.value = 0\n}\n\n/**\n * Worth saying out loud: a password typed in capitals fails with the same unhelpful message\n * as a wrong one, and people retype it three times before noticing.\n */\nfunction trackCapsLock(event: KeyboardEvent): void {\n capsLock.value = event.getModifierState('CapsLock')\n}\n\ndefineExpose({ focus: () => emailField.value?.focus() })\n</script>\n\n<template>\n <wx-card class=\"wx-login\" :padding=\"24\">\n <wx-form :errors=\"errors\" :disabled=\"busy\" gap=\"md\" @submit.prevent=\"submit\">\n <wx-alert\n v-if=\"message !== ''\"\n type=\"danger\"\n variant=\"soft\"\n live\n :description=\"message\"\n class=\"wx-login__message\"\n />\n\n <wx-alert\n v-else-if=\"throttled\"\n type=\"warning\"\n variant=\"soft\"\n live\n :description=\"throttleNotice\"\n class=\"wx-login__message\"\n />\n\n <wx-form-item name=\"email\">\n <wx-input\n ref=\"emailField\"\n v-model=\"email\"\n type=\"email\"\n :placeholder=\"labels.email\"\n :aria-label=\"labels.email\"\n autocomplete=\"username\"\n :autofocus=\"autofocus\"\n inputmode=\"email\"\n size=\"lg\"\n >\n <template #prefix><wx-icon name=\"mail\" /></template>\n </wx-input>\n </wx-form-item>\n\n <wx-form-item name=\"password\" :help=\"capsLock ? labels.capsLock : undefined\">\n <wx-input\n v-model=\"password\"\n :type=\"revealed ? 'text' : 'password'\"\n :placeholder=\"labels.password\"\n :aria-label=\"labels.password\"\n autocomplete=\"current-password\"\n size=\"lg\"\n @keydown=\"trackCapsLock\"\n @keyup=\"trackCapsLock\"\n >\n <template #prefix><wx-icon name=\"lock\" /></template>\n <template #suffix>\n <!-- A bare icon rather than a button with a surface: it sits inside the field, and\n a second box in there reads as a second control. Out of the tab order because\n it is a convenience, not a step. -->\n <button\n type=\"button\"\n class=\"wx-login__reveal\"\n :title=\"revealed ? labels.hide : labels.reveal\"\n :aria-label=\"revealed ? labels.hide : labels.reveal\"\n :aria-pressed=\"revealed\"\n tabindex=\"-1\"\n @click=\"revealed = !revealed\"\n >\n <wx-icon :name=\"revealed ? 'eye-off' : 'eye'\" />\n </button>\n </template>\n </wx-input>\n </wx-form-item>\n\n <wx-checkbox\n v-if=\"remember\"\n v-model=\"rememberMe\"\n :label=\"labels.remember\"\n class=\"wx-login__remember\"\n />\n\n <wx-button\n type=\"primary\"\n native-type=\"submit\"\n size=\"lg\"\n block\n :loading=\"busy\"\n :disabled=\"throttled\"\n >\n {{ labels.submit }}\n </wx-button>\n </wx-form>\n </wx-card>\n</template>\n\n<style scoped>\n.wx-login {\n width: 100%;\n /* Wide enough for an email address, narrow enough to stay a card rather than a page. */\n max-width: 26rem;\n}\n\n.wx-login__message {\n margin-block-end: var(--wx-space-4);\n}\n\n/* The checkbox belongs to the password above it, not to the form's row of fields: pulled up\n out of the form's gap so it reads as an option under the field rather than as a field of its\n own. */\n.wx-login__remember {\n margin-block-start: calc(-1 * var(--wx-space-12));\n}\n\n.wx-login__reveal {\n display: inline-flex;\n align-items: center;\n padding: 0;\n /* A button has a font size of its own — 13.3px in every browser — and the icon inside is\n sized in em, so without this it came out smaller than the lock at the other end. */\n font: inherit;\n color: var(--wx-text-muted);\n background: none;\n border: 0;\n cursor: pointer;\n}\n\n.wx-login__reveal:hover,\n.wx-login__reveal[aria-pressed='true'] {\n color: var(--wx-text-default);\n}\n</style>\n","import type { Messages } from '@webx-ui/module-admin'\n\n/**\n * What this package says, in English.\n *\n * The same keys `webx-ui/module-auth` ships as `lang/en/*.php`. Kept here so a card placed by\n * hand, with no WebX UI server behind it, still has words — and so a key never reaches the\n * screen when a translation is missing.\n */\nexport const authMessages: Record<string, Messages> = {\n card: {\n email: 'Email',\n password: 'Password',\n remember: 'Stay signed in',\n submit: 'Sign in',\n 'caps-lock': 'Caps Lock is on.',\n reveal: 'Show the password',\n hide: 'Hide the password',\n throttled: 'Too many attempts. Try again in :seconds s.',\n failed: 'Those details do not match an account.',\n },\n admins: {\n title: 'Administrators',\n 'filter-role': 'Role',\n 'filter-state': 'State',\n search: 'Search administrators',\n new: 'New administrator',\n edit: 'Edit administrator',\n // The same two, short enough for a button on a phone and for a dialog's heading.\n 'new-short': 'Add',\n 'edit-short': 'Edit',\n name: 'Name',\n email: 'Email',\n password: 'Password',\n 'password-hint': 'At least 12 characters',\n 'password-keep': 'Leave blank to keep the current one',\n avatar: 'Photo',\n roles: 'Roles',\n 'no-roles': 'No roles',\n active: 'Active',\n super: 'Super administrator',\n 'super-hint': 'Answers yes to everything, whatever the roles say',\n locale: 'Panel language',\n 'locale-auto': 'Follow the browser',\n 'last-login': 'Last signed in',\n save: 'Save',\n cancel: 'Cancel',\n delete: 'Delete',\n 'delete-title': 'Delete :name?',\n 'delete-text': 'They lose access at once. What they have written stays.',\n 'all-roles': 'All roles',\n 'any-state': 'Active and not',\n 'only-active': 'Active only',\n 'only-inactive': 'Switched off only',\n empty: 'Nobody matches that',\n 'select-one': 'Choose an administrator',\n 'select-many': 'Choose administrators',\n chosen: 'Chosen: :count',\n created: 'Administrator created',\n updated: 'Administrator saved',\n deleted: 'Administrator deleted',\n },\n errors: {\n unauthenticated: 'Unauthenticated.',\n inactive: 'This account is no longer active.',\n forbidden: 'This account may not do that.',\n 'not-yourself': 'You cannot do that to your own account.',\n 'last-super': 'There has to be one super administrator left.',\n },\n // Somebody editing themselves, which is a shorter list than an administrator editing them.\n profile: {\n title: 'Your profile',\n menu: 'Edit your profile',\n name: 'Name',\n avatar: 'Photo',\n password: 'New password',\n 'password-hint': 'At least 12 characters. Leave blank to keep the current one.',\n 'current-password': 'Current password',\n 'current-password-hint': 'Asked for before the password is changed.',\n 'wrong-password': 'That is not the current password.',\n saved: 'Your profile is saved.',\n save: 'Save',\n cancel: 'Cancel',\n },\n menu: {\n 'sign-out': 'Sign out',\n language: 'Interface language',\n },\n}\n","import type { AdminContext } from '@webx-ui/module-admin'\nimport type { Admin, AdminInput, AdminPage, AdminQuery, Role } from './types'\n\nexport interface AdminsApi {\n list(query?: AdminQuery): Promise<AdminPage>\n get(id: number): Promise<Admin>\n create(input: AdminInput): Promise<Admin>\n update(id: number, input: AdminInput): Promise<Admin>\n remove(id: number): Promise<void>\n roles(): Promise<Role[]>\n}\n\n/**\n * The calls behind the administrators screen, and behind the picker that opens from code.\n *\n * Reading is separate from managing on the server, so a module that only wants to say who\n * wrote something — or offer a list of people to assign work to — can use `list` and `roles`\n * without being allowed anywhere near the rest.\n */\nexport function createAdminsApi(admin: AdminContext): AdminsApi {\n const base = `${admin.apiPath}/auth`\n const data = <T>(body: { data: T }): T => body.data\n\n return {\n list: (query = {}) =>\n admin.http\n .get<{ data: Admin[]; meta: Omit<AdminPage, 'data'> }>(`${base}/admins`, {\n query: {\n q: query.q || undefined,\n role: query.role ?? undefined,\n active: query.active ?? undefined,\n sort: query.sort,\n page: query.page,\n per_page: query.per_page,\n },\n })\n .then((body) => ({ ...body.meta, data: body.data })),\n\n get: (id) => admin.http.get<{ data: Admin }>(`${base}/admins/${id}`).then(data),\n\n create: (input) => admin.http.post<{ data: Admin }>(`${base}/admins`, input).then(data),\n\n update: (id, input) =>\n admin.http.patch<{ data: Admin }>(`${base}/admins/${id}`, input).then(data),\n\n remove: (id) => admin.http.delete<void>(`${base}/admins/${id}`),\n\n roles: () => admin.http.get<{ data: Role[] }>(`${base}/roles`).then(data),\n }\n}\n","import { useAdmin } from '@webx-ui/module-admin'\nimport { authMessages } from './messages'\n\nconst seeded = new WeakSet<object>()\n\n/**\n * Put this package's English under the panel's dictionary, once per panel.\n *\n * The plugin does this too, but a panel can use the administrators screen — or just the picker\n * — without installing the sign-in plugin at all, and a screen showing `admins.title` instead\n * of a word is not a thing to leave to chance.\n */\nexport function useAuthMessages(): void {\n try {\n const { i18n } = useAdmin()\n\n if (!seeded.has(i18n)) {\n i18n.defaults('webx-auth', authMessages)\n seeded.add(i18n)\n }\n } catch {\n // Outside a panel there is no dictionary to seed, and `useTranslate` falls back on its own.\n }\n}\n","<script setup lang=\"ts\">\nimport { computed, ref, watch, type Component } from 'vue'\nimport { useAdmin, useErrorText, useI18n, useTranslate } from '@webx-ui/module-admin'\nimport {\n toast,\n useModal,\n WxButton,\n WxCheckbox,\n WxDialog,\n WxFormItem,\n WxInput,\n WxSelect,\n WxSpace,\n WxText,\n} from '@webx-ui/core'\n\nimport { createAdminsApi } from './admins'\nimport { useAuthMessages } from './i18n'\nimport type { Admin, AdminInput, Role } from './types'\n\n/**\n * Creating an administrator and editing one — the same form, because they differ in one field.\n *\n * The password is required the first time and optional afterwards, and a blank one on an edit\n * means \"leave it alone\" rather than \"set it to nothing\". That is the only conditional here.\n */\nconst props = withDefaults(\n defineProps<{\n /** Absent creates somebody; present edits them. */\n admin?: Admin | null\n /**\n * The field to pick the photograph with — `WxMediaField` in a panel that has the library.\n *\n * Handed in rather than imported: this package does not depend on the media one, and a\n * panel without a library still edits everything else about a person.\n */\n avatarField?: Component\n }>(),\n { admin: null, avatarField: undefined },\n)\n\nconst { resolve, dismiss, open } = useModal<Admin>()\n\nconst context = useAdmin()\nconst api = createAdminsApi(context)\nconst i18n = useI18n()\nuseAuthMessages()\n\nconst t = useTranslate('webx-auth')\n/* Not the server's `message`: the panel says how a request failed in its own words (§13.3). */\nconst message = useErrorText()\n\nconst roles = ref<Role[]>([])\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\n\nconst form = ref<AdminInput & { avatar: string | null }>({\n name: '',\n email: '',\n password: '',\n avatar: null,\n is_active: true,\n is_super: false,\n locale: null,\n roles: [],\n})\n\n/** The field hands back `{ path, … }`; what is stored is the key. */\nconst photo = computed({\n get: () => (form.value.avatar ? { path: form.value.avatar } : null),\n set: (value: { path: string } | null) => {\n form.value.avatar = value?.path ?? null\n },\n})\n\nconst editing = computed(() => props.admin !== null)\n\nconst roleOptions = computed(() => roles.value.map((one) => ({ value: one.id, label: one.name })))\n\n/* A word, not an empty string: a select reads \"\" as nothing chosen and shows a blank control. */\nconst localeOptions = computed(() => [\n { value: 'auto', label: t('admins.locale-auto') },\n ...i18n.state.panelLocales.map((one) => ({ value: one.code, label: one.nativeName })),\n])\n\nvoid api.roles().then((all) => (roles.value = all))\n\nwatch(\n () => props.admin,\n (admin) => {\n form.value = {\n name: admin?.name ?? '',\n email: admin?.email ?? '',\n password: '',\n avatar: admin?.avatar ?? null,\n is_active: admin?.is_active ?? true,\n is_super: admin?.is_super ?? false,\n locale: admin?.locale ?? 'auto',\n roles: admin?.roles.map((one) => one.id) ?? [],\n }\n errors.value = {}\n },\n { immediate: true },\n)\n\nfunction errorOf(field: string): string | undefined {\n return errors.value[field]?.[0]\n}\n\nasync function save(): Promise<void> {\n saving.value = true\n errors.value = {}\n\n const payload: AdminInput = {\n name: form.value.name,\n email: form.value.email,\n avatar: form.value.avatar,\n is_active: form.value.is_active,\n is_super: form.value.is_super,\n locale: form.value.locale === 'auto' ? null : form.value.locale,\n roles: form.value.roles ?? [],\n }\n\n if (form.value.password) {\n payload.password = form.value.password\n }\n\n try {\n const saved = props.admin\n ? await api.update(props.admin.id, payload)\n : await api.create(payload)\n\n toast.success(t(editing.value ? 'admins.updated' : 'admins.created'))\n resolve(saved)\n } catch (error) {\n const body = (error as { body?: { errors?: Record<string, string[]>; message?: string } }).body\n\n if (body?.errors) {\n errors.value = body.errors\n } else {\n // The two refusals that keep a panel reachable — the last super administrator, and doing\n // it to yourself — arrive as a 422 with a message and no field, which is the one shape\n // the panel repeats back word for word.\n toast.danger(message(error))\n }\n } finally {\n saving.value = false\n }\n}\n</script>\n\n<template>\n <wx-dialog\n v-model:open=\"open\"\n :title=\"editing ? t('admins.edit-short') : t('admins.new-short')\"\n :width=\"720\"\n >\n <!--\n The photograph beside the fields rather than above them: it is square, the fields are a\n column, and stacking the two makes somebody scroll past a picture to reach a name. Under\n `--wx-admin-form-wrap` the sidebar goes back on top, which is the only shape that fits a\n phone.\n -->\n <div class=\"wx-admin-form\">\n <div class=\"wx-admin-form__grid\">\n <div v-if=\"avatarField\" class=\"wx-admin-form__aside\">\n <component\n :is=\"avatarField\"\n v-model=\"photo\"\n :label=\"t('admins.avatar')\"\n aspect=\"1/1\"\n :captions=\"false\"\n />\n </div>\n\n <div class=\"wx-admin-form__fields\">\n <wx-form-item :label=\"t('admins.name')\" :error=\"errorOf('name')\">\n <wx-input v-model=\"form.name\" name=\"name\" />\n </wx-form-item>\n\n <wx-form-item :label=\"t('admins.email')\" :error=\"errorOf('email')\">\n <wx-input v-model=\"form.email\" type=\"email\" name=\"email\" autocomplete=\"off\" />\n </wx-form-item>\n\n <wx-form-item\n :label=\"t('admins.password')\"\n :error=\"errorOf('password')\"\n :help=\"editing ? t('admins.password-keep') : t('admins.password-hint')\"\n >\n <wx-input\n v-model=\"form.password\"\n type=\"password\"\n name=\"password\"\n autocomplete=\"new-password\"\n />\n </wx-form-item>\n\n <wx-form-item :label=\"t('admins.roles')\" :error=\"errorOf('roles')\">\n <wx-select v-model=\"form.roles\" :options=\"roleOptions\" multiple />\n </wx-form-item>\n\n <wx-form-item :label=\"t('admins.locale')\">\n <wx-select v-model=\"form.locale\" :options=\"localeOptions\" />\n </wx-form-item>\n\n <wx-space size=\"md\">\n <wx-checkbox v-model=\"form.is_active\" :label=\"t('admins.active')\" />\n <wx-checkbox v-model=\"form.is_super\" :label=\"t('admins.super')\" />\n </wx-space>\n\n <wx-text size=\"sm\" tone=\"muted\">{{ t('admins.super-hint') }}</wx-text>\n </div>\n </div>\n </div>\n\n <template #footer>\n <wx-space size=\"sm\">\n <wx-button variant=\"outline\" @click=\"dismiss()\">{{ t('admins.cancel') }}</wx-button>\n <wx-button type=\"primary\" :loading=\"saving\" @click=\"save\">\n {{ t('admins.save') }}\n </wx-button>\n </wx-space>\n </template>\n </wx-dialog>\n</template>\n\n<style>\n/* The container is the wrapper, not the grid: an element cannot query its own width. */\n.wx-admin-form {\n container-type: inline-size;\n}\n\n.wx-admin-form__grid {\n display: grid;\n grid-template-columns: 200px minmax(0, 1fr);\n gap: var(--wx-space-24);\n}\n\n.wx-admin-form__fields {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-12);\n min-width: 0;\n}\n\n/* The dialog's own width decides this, not the window's. */\n@container (max-width: 520px) {\n .wx-admin-form__grid {\n grid-template-columns: minmax(0, 1fr);\n }\n\n .wx-admin-form__aside {\n max-width: 200px;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, useTemplateRef, watch } from 'vue'\nimport {\n useAdmin,\n useTranslate,\n WxDate,\n WxFilterChips,\n rowMenuWidth,\n WxRowMenu,\n type AppliedFilter,\n type RowAction,\n} from '@webx-ui/module-admin'\nimport {\n WxAvatar,\n WxBadge,\n WxButton,\n WxEntityCard,\n WxFormItem,\n WxIcon,\n WxSelect,\n WxSpace,\n WxTable,\n WxText,\n useElementWidth,\n type TableColumn,\n type TableState,\n} from '@webx-ui/core'\nimport { createAdminsApi } from './admins'\nimport { useAuthMessages } from './i18n'\nimport type { Admin, AdminPage, Role } from './types'\n\n/**\n * The list of administrators — the screen, and the inside of the picker.\n *\n * One component for both so that searching, filtering and paging behave the same wherever\n * somebody is looking at these people. What changes is only what a row is for: opening it, or\n * choosing it.\n */\nconst props = withDefaults(\n defineProps<{\n /** Rows are chosen rather than opened. */\n picking?: boolean\n /** More than one may be chosen at a time. */\n multiple?: boolean\n /** Adds the column that removes somebody. */\n removable?: boolean\n /**\n * Offers the role and state filters.\n *\n * Off by default: a panel has half a dozen administrators, and a filter over six rows is a\n * control to read and decide about where a glance would have done.\n */\n filters?: boolean\n /** Turns an avatar key into an address. The panel wires this; this package cannot. */\n resolveAvatar?: (key: string) => Promise<string | null>\n }>(),\n {\n picking: false,\n multiple: false,\n removable: false,\n filters: false,\n resolveAvatar: undefined,\n },\n)\n\nconst emit = defineEmits<{\n open: [admin: Admin]\n chosen: [admins: Admin[]]\n /** Which rows are ticked, for a footer that counts them. */\n selection: [admins: Admin[]]\n remove: [admin: Admin]\n}>()\n\nconst admin = useAdmin()\nconst api = createAdminsApi(admin)\nuseAuthMessages()\n\n/** Where a row stops being a row. The table and the columns read the same number. */\nconst CARDS = 480\n\nconst root = useTemplateRef<HTMLElement>('root')\nconst width = useElementWidth(root)\nconst asCards = computed(() => width.value > 0 && width.value < CARDS)\n\nconst t = useTranslate('webx-auth')\n/* The funnel and 'reset all' are the panel's words, not this module's. `admin` is taken:\n in this file that is the panel context. */\nconst panel = useTranslate('webx-admin')\n\nconst page = ref<AdminPage | null>(null)\nconst roles = ref<Role[]>([])\nconst loading = ref(false)\n/* A word rather than an empty string: a select reads \"\" as nothing chosen and shows a\n blank control where the option said \"all roles\". */\nconst role = ref('all')\nconst active = ref<'any' | 'yes' | 'no'>('any')\nconst selected = ref<Admin[]>([])\nconst avatars = ref<Record<string, string>>({})\n\nlet last: TableState = { page: 1, perPage: 20, sort: null, search: '' }\n\nconst roleOptions = computed(() => [\n { value: 'all', label: t('admins.all-roles') },\n ...roles.value.map((one) => ({ value: one.slug, label: one.name })),\n])\n\nconst stateOptions = computed(() => [\n { value: 'any', label: t('admins.any-state') },\n { value: 'yes', label: t('admins.only-active') },\n { value: 'no', label: t('admins.only-inactive') },\n])\n\n/**\n * Which of the two are on, in the reader's words.\n *\n * They stand behind the funnel now, and a shut panel says nothing about itself — a list of six\n * people showing two of them looks broken until something says why.\n */\nconst applied = computed<AppliedFilter[]>(() => {\n const chips: AppliedFilter[] = []\n\n const picked = roles.value.find((one) => one.slug === role.value)\n if (picked) {\n chips.push({\n key: 'role',\n label: `${t('admins.filter-role')}: ${picked.name}`,\n clear: () => (role.value = 'all'),\n })\n }\n\n if (active.value !== 'any') {\n chips.push({\n key: 'state',\n label: `${t('admins.filter-state')}: ${\n active.value === 'yes' ? t('admins.only-active') : t('admins.only-inactive')\n }`,\n clear: () => (active.value = 'any'),\n })\n }\n\n return chips\n})\n\nfunction clearFilters(): void {\n role.value = 'all'\n active.value = 'any'\n}\n\n/*\n * No widths but the one the buttons need.\n *\n * A declared width is a promise the table has to keep, and five of them add up to more than the\n * screen — so it scrolls sideways while half the row is whitespace. Left alone, the columns take\n * what their contents need. What does not fit is dropped instead, in the order it can be spared.\n */\nconst columns = computed<TableColumn<Admin>[]>(() => {\n if (asCards.value) return [{ key: 'card', label: '' }]\n\n return [\n { key: 'name', label: t('admins.name'), sortable: true },\n { key: 'email', label: t('admins.email'), sortable: true, hideBelow: 560 },\n { key: 'roles', label: t('admins.roles'), hideBelow: 760 },\n { key: 'is_active', label: t('admins.active'), align: 'center', hideBelow: 660 },\n {\n key: 'last_login_at',\n label: t('admins.last-login'),\n sortable: true,\n hideBelow: 900,\n // The first thing to go when a row becomes a card: it is a date somebody scans down a\n // column, and a card has no column to scan.\n hideOnCards: true,\n },\n {\n key: 'actions',\n label: '',\n width: rowMenuWidth,\n align: 'right',\n hidden: !props.removable,\n // A card puts them along its top instead, through the `card-actions` slot.\n hideOnCards: true,\n },\n ]\n})\n\nvoid api.roles().then((all) => (roles.value = all))\n\nwatch([role, active], () => load(last))\n\nasync function load(state: TableState): Promise<void> {\n last = state\n loading.value = true\n\n try {\n page.value = await api.list({\n q: state.search,\n role: role.value === 'all' ? null : role.value,\n active: active.value === 'any' ? null : active.value,\n sort: state.sort ? `${state.sort.order === 'desc' ? '-' : ''}${state.sort.key}` : 'name',\n page: state.page,\n per_page: state.perPage,\n })\n\n void resolveAvatars()\n } finally {\n loading.value = false\n }\n}\n\n/**\n * A row stores the library's key, not an address, and this package has no business knowing how\n * to turn one into the other. The panel hands that in; without it a row shows initials, which\n * is what it would have shown anyway for everybody who never uploaded a photograph.\n */\nasync function resolveAvatars(): Promise<void> {\n const resolve = props.resolveAvatar\n\n if (!resolve) return\n\n for (const row of page.value?.data ?? []) {\n if (!row.avatar || avatars.value[row.avatar]) continue\n\n const url = await resolve(row.avatar)\n\n if (url) avatars.value = { ...avatars.value, [row.avatar]: url }\n }\n}\n\n/**\n * One line, and the same `···` every other list of the panel puts a record's actions behind.\n * A bare red bin in every row says deleting somebody is the thing this list is for (§20).\n */\nfunction actionsFor(row: Admin): RowAction[] {\n return [\n {\n key: 'delete',\n icon: 'trash',\n label: t('admins.delete'),\n danger: true,\n run: () => emit('remove', row),\n },\n ]\n}\n\nfunction onRow(row: Admin): void {\n if (props.picking) {\n emit('chosen', [row])\n\n return\n }\n\n emit('open', row)\n}\n\n/**\n * The listeners the table gets, rather than a handler that decides to do nothing (§13).\n *\n * Picking several is the one case where a row leads nowhere: the tick box is what chooses, and\n * a click on the row itself would have to either choose one and drop the rest or do nothing at\n * all. So it does nothing and says so — no cursor, no highlight, which is what `WxTable` reads\n * out of the missing listener. `v-bind`, not `v-on`: only that reaches it (CLAUDE.md §4).\n */\nconst clickable = computed(() => !(props.picking && props.multiple))\n\nfunction onSelection(_keys: unknown, rows: Admin[]): void {\n selected.value = rows\n emit('selection', rows)\n}\n\n/** Reload without losing where somebody was, for a caller that changed something. */\ndefineExpose({ reload: () => load(last), chosen: () => selected.value })\n</script>\n\n<template>\n <div ref=\"root\" class=\"wx-admin-list\">\n <wx-table\n :data=\"page\"\n :columns=\"columns\"\n row-key=\"id\"\n searchable\n :clickable=\"clickable\"\n :hover=\"clickable\"\n flush\n :loading=\"loading\"\n :search-placeholder=\"t('admins.search')\"\n :empty-text=\"t('admins.empty')\"\n :selectable=\"picking && multiple\"\n :cards-below=\"CARDS\"\n :filters-count=\"applied.length\"\n :filters-label=\"panel('filters.title')\"\n @row-click=\"onRow\"\n @state-change=\"load\"\n @selection-change=\"onSelection\"\n >\n <!-- Behind the funnel, and what they are set to comes back as chips beside it: two\n dropdowns standing open over six people are two controls to read before the rows. -->\n <template v-if=\"filters\" #filters>\n <wx-form-item :label=\"t('admins.filter-role')\">\n <wx-select v-model=\"role\" :options=\"roleOptions\" size=\"sm\" />\n </wx-form-item>\n\n <wx-form-item :label=\"t('admins.filter-state')\">\n <wx-select v-model=\"active\" :options=\"stateOptions\" size=\"sm\" />\n </wx-form-item>\n\n <wx-button v-if=\"applied.length > 0\" variant=\"text\" size=\"sm\" block @click=\"clearFilters\">\n <template #icon><wx-icon name=\"close\" /></template>\n {{ panel('filters.reset') }}\n </wx-button>\n </template>\n\n <template v-if=\"filters\" #applied>\n <wx-filter-chips :filters=\"applied\" />\n </template>\n\n <template #cell-name=\"{ row }\">\n <wx-space size=\"sm\" align=\"center\">\n <wx-avatar\n :name=\"row.name\"\n :src=\"row.avatar ? avatars[row.avatar] : undefined\"\n size=\"sm\"\n />\n <span>{{ row.name }}</span>\n <wx-badge v-if=\"row.is_super\" type=\"warning\">{{ t('admins.super') }}</wx-badge>\n </wx-space>\n </template>\n\n <template #cell-roles=\"{ row }\">\n <wx-space v-if=\"row.roles.length > 0\" size=\"xs\" wrap>\n <wx-badge v-for=\"one in row.roles\" :key=\"one.id\">{{ one.name }}</wx-badge>\n </wx-space>\n <wx-text v-else size=\"sm\" tone=\"muted\">{{ t('admins.no-roles') }}</wx-text>\n </template>\n\n <template #cell-is_active=\"{ row }\">\n <wx-badge :type=\"row.is_active ? 'success' : 'default'\" dot>\n {{ row.is_active ? t('admins.active') : t('admins.only-inactive') }}\n </wx-badge>\n </template>\n\n <template #card-actions=\"{ row }\">\n <wx-row-menu v-if=\"removable\" :actions=\"actionsFor(row)\" :label=\"row.name\" />\n </template>\n\n <template #cell-actions=\"{ row }\">\n <wx-row-menu :actions=\"actionsFor(row)\" :label=\"row.name\" />\n </template>\n\n <template #cell-last_login_at=\"{ row }\">\n <wx-date\n :value=\"row.last_login_at\"\n :tone=\"row.last_login_at ? 'default' : 'muted'\"\n compact\n />\n </template>\n\n <!--\n Narrow, a row is one entity rather than four labelled lines: a face, a name, the address\n that identifies them and what they are allowed. `circle`, because these are people.\n -->\n <template #cell-card=\"{ row }\">\n <wx-entity-card\n variant=\"plain\"\n shape=\"circle\"\n :title=\"row.name\"\n :image=\"row.avatar ? avatars[row.avatar] : undefined\"\n :subtitle=\"row.email\"\n >\n <template #meta>\n <wx-badge v-if=\"row.is_super\" type=\"warning\" size=\"sm\">\n {{ t('admins.super') }}\n </wx-badge>\n <wx-badge v-for=\"one in row.roles\" :key=\"one.id\" size=\"sm\">{{ one.name }}</wx-badge>\n <wx-badge :type=\"row.is_active ? 'success' : 'default'\" dot size=\"sm\">\n {{ row.is_active ? t('admins.active') : t('admins.only-inactive') }}\n </wx-badge>\n </template>\n </wx-entity-card>\n </template>\n </wx-table>\n </div>\n</template>\n\n<style>\n.wx-admin-list {\n min-width: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, useTemplateRef, type Component } from 'vue'\nimport {\n useAdmin,\n useErrorText,\n useTranslate,\n WxListScreen,\n type ScreenAction,\n} from '@webx-ui/module-admin'\nimport { confirm, createModal, toast } from '@webx-ui/core'\nimport AdminDialog from './AdminDialog.vue'\nimport AdminList from './AdminList.vue'\nimport { createAdminsApi } from './admins'\nimport { useAuthMessages } from './i18n'\nimport type { Admin } from './types'\n\n/**\n * The administrators section: the list, and the form over it.\n *\n * A dialog rather than a second route. Editing somebody is half a dozen fields, and a screen\n * that takes over the page for that loses the list you were reading — which is where you\n * decide who to open next.\n *\n * The heading and `Add` used to live inside the card, which made this the one section of the\n * panel with no line of its own at the top. They stand outside it now, where every other list\n * keeps them (§19).\n */\nconst props = withDefaults(\n defineProps<{\n avatarField?: Component\n resolveAvatar?: (key: string) => Promise<string | null>\n }>(),\n { avatarField: undefined, resolveAvatar: undefined },\n)\n\nconst context = useAdmin()\nconst api = createAdminsApi(context)\nuseAuthMessages()\n\nconst t = useTranslate('webx-auth')\n/* Not the server's `message`: the panel says how a request failed in its own words (§13.3). */\nconst message = useErrorText()\n\nconst list = useTemplateRef<{ reload: () => void }>('list')\nconst editing = ref<Admin | null>(null)\n\nconst edit = createModal<Admin, { admin: Admin | null; avatarField?: Component }>(AdminDialog)\n\nconst canManage = context.can('admins.manage')\n\n/** The section's name as the server translated it; the built-in English until it arrives. */\nconst title = computed(\n () =>\n context.state.manifest?.modules.find((module) => module.id === 'admins')?.title ??\n t('admins.title'),\n)\n\n/* Short enough for a phone, where the name and the button share one line. */\nconst actions = computed<ScreenAction[]>(() =>\n canManage\n ? [\n {\n key: 'new',\n label: t('admins.new-short'),\n icon: 'plus',\n primary: true,\n run: () => void open(null),\n },\n ]\n : [],\n)\n\nasync function open(admin: Admin | null): Promise<void> {\n editing.value = admin\n\n const saved = await edit({ admin, avatarField: props.avatarField })\n\n if (saved) {\n list.value?.reload()\n }\n}\n\nasync function remove(admin: Admin): Promise<void> {\n const agreed = await confirm({\n title: t('admins.delete-title', { name: admin.name }),\n message: t('admins.delete-text'),\n confirmText: t('admins.delete'),\n cancelText: t('admins.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.remove(admin.id)\n toast.success(t('admins.deleted'))\n list.value?.reload()\n } catch (error) {\n toast.danger(message(error))\n }\n}\n</script>\n\n<template>\n <wx-list-screen :title=\"title\" :actions=\"actions\">\n <admin-list\n ref=\"list\"\n :removable=\"canManage\"\n :resolve-avatar=\"props.resolveAvatar\"\n @open=\"open\"\n @remove=\"remove\"\n />\n </wx-list-screen>\n</template>\n","import { h, type Component } from 'vue'\nimport type { AdminModule } from '@webx-ui/module-admin'\nimport AdminsPage from './AdminsPage.vue'\n\nexport interface AdminsOptions {\n /** Where the section lives inside the panel. */\n path?: string\n /**\n * The field the photograph is picked with — `WxMediaField` in a panel that has the library.\n *\n * Handed in rather than imported: this package does not depend on the media one, and a panel\n * without a library still edits everything else about a person.\n */\n avatarField?: Component\n /** Turns an avatar key into an address. Omitted, rows and forms show initials. */\n resolveAvatar?: (key: string) => Promise<string | null>\n}\n\n/**\n * The administrators, as a section of the panel.\n *\n * The id matches the module the server reports, which is what makes the entry appear in the\n * navigation: a section shows up when both halves are installed.\n */\nexport function admins(options: AdminsOptions = {}): AdminModule {\n const path = options.path ?? '/admins'\n\n return {\n id: 'admins',\n path,\n routes: [\n {\n path,\n name: 'webx.admins',\n component: {\n render: () =>\n h(AdminsPage, {\n avatarField: options.avatarField,\n resolveAvatar: options.resolveAvatar,\n }),\n },\n },\n ],\n }\n}\n","<script setup lang=\"ts\">\nimport { ref, useTemplateRef } from 'vue'\nimport { useTranslate } from '@webx-ui/module-admin'\nimport { useModal, WxButton, WxDialog, WxSpace, WxText } from '@webx-ui/core'\nimport AdminList from './AdminList.vue'\nimport { useAuthMessages } from './i18n'\nimport type { Admin } from './types'\n\n/**\n * The list of administrators as a dialog, resolving with whoever was chosen.\n *\n * The same list as the screen — one component, so that searching and filtering behave the same\n * wherever these people are being looked at.\n */\nconst props = withDefaults(\n defineProps<{\n multiple?: boolean\n title?: string\n resolveAvatar?: (key: string) => Promise<string | null>\n }>(),\n { multiple: false, title: undefined, resolveAvatar: undefined },\n)\n\nconst { open, resolve, dismiss } = useModal<Admin[]>()\n\nuseAuthMessages()\n\nconst t = useTranslate('webx-auth')\n\nconst list = useTemplateRef<{ chosen: () => Admin[] }>('list')\nconst count = ref(0)\n\n/** One is chosen by clicking the row; several are ticked and then confirmed. */\nfunction chosen(admins: Admin[]): void {\n resolve(admins)\n}\n\nfunction track(admins: Admin[]): void {\n count.value = admins.length\n}\n</script>\n\n<template>\n <wx-dialog\n v-model:open=\"open\"\n :title=\"title ?? t(multiple ? 'admins.select-many' : 'admins.select-one')\"\n :width=\"880\"\n >\n <admin-list\n ref=\"list\"\n picking\n :multiple=\"multiple\"\n :resolve-avatar=\"props.resolveAvatar\"\n @chosen=\"chosen\"\n @selection=\"track\"\n />\n\n <template v-if=\"multiple\" #footer>\n <wx-space size=\"sm\" align=\"center\">\n <wx-text size=\"sm\" tone=\"muted\">{{ t('admins.chosen', { count }) }}</wx-text>\n <wx-button variant=\"outline\" @click=\"dismiss()\">{{ t('admins.cancel') }}</wx-button>\n <wx-button type=\"primary\" :disabled=\"count === 0\" @click=\"resolve(list?.chosen() ?? [])\">\n {{ t('admins.save') }}\n </wx-button>\n </wx-space>\n </template>\n </wx-dialog>\n</template>\n","import { createModal } from '@webx-ui/core'\nimport AdminPicker from './AdminPicker.vue'\nimport type { Admin } from './types'\n\nexport interface AdminPickerOptions {\n /** Heading of the dialog, when the default is not specific enough. */\n title?: string\n /** Turns an avatar key into an address. Omitted, rows show initials. */\n resolveAvatar?: (key: string) => Promise<string | null>\n}\n\nconst pick = createModal<Admin[], AdminPickerOptions & { multiple?: boolean }>(AdminPicker)\n\n/**\n * One administrator, chosen from a dialog.\n *\n * ```ts\n * const owner = await selectAdmin()\n * if (owner) task.assignedTo = owner.id\n * ```\n *\n * Resolves with `undefined` when the dialog was closed without choosing. Needs `admins.view`,\n * which is deliberately separate from managing them: assigning work to somebody is not the\n * same as being allowed to edit their account.\n */\nexport async function selectAdmin(options: AdminPickerOptions = {}): Promise<Admin | undefined> {\n const chosen = await pick({ ...options, multiple: false })\n\n return chosen?.[0]\n}\n\n/**\n * Several administrators, ticked and confirmed.\n *\n * Resolves with `undefined` when the dialog was closed, and never with an empty list — the\n * confirming button is disabled until somebody is ticked, so \"none\" can only mean \"cancelled\".\n */\nexport function selectAdmins(options: AdminPickerOptions = {}): Promise<Admin[] | undefined> {\n return pick({ ...options, multiple: true })\n}\n","<script setup lang=\"ts\">\nimport { ref, type Component } from 'vue'\nimport { useAdmin, useErrorText, useTranslate } from '@webx-ui/module-admin'\nimport { toast, useModal, WxButton, WxDialog, WxFormItem, WxInput } from '@webx-ui/core'\nimport { useAuth } from './session'\nimport { useAuthMessages } from './i18n'\n\n/**\n * Somebody editing themselves.\n *\n * Not `AdminDialog` with the fields taken away. What a person may change about themselves and\n * what an administrator may change about them are different lists, and the difference is the\n * whole point: roles, super, active and the sign-in address are not here, which is what makes\n * this reachable by everybody rather than by whoever holds `admins.manage`.\n *\n * The address is shown and not editable: changing where somebody signs in has to be proved at\n * the new address first, and nothing here can do that yet.\n */\nconst props = withDefaults(\n defineProps<{\n /** The field to pick the photograph with — `WxMediaField` in a panel with the library. */\n avatarField?: Component\n }>(),\n { avatarField: undefined },\n)\n\nconst { resolve, dismiss, open } = useModal<true>()\n\nconst context = useAdmin()\nconst auth = useAuth()\nuseAuthMessages()\n\nconst t = useTranslate('webx-auth')\n/* Not the server's `message`: the panel says how a request failed in its own words (§13.3). */\nconst message = useErrorText()\n\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\n\nconst form = ref({\n name: context.state.user?.name ?? '',\n avatar: (context.state.user?.avatar as string | null | undefined) ?? null,\n password: '',\n current_password: '',\n})\n\n/** The field hands back `{ path, … }`; what is stored is the key. */\nconst photo = ref<{ path: string } | null>(form.value.avatar ? { path: form.value.avatar } : null)\n\nfunction errorOf(field: string): string | undefined {\n return errors.value[field]?.[0]\n}\n\nasync function save(): Promise<void> {\n saving.value = true\n errors.value = {}\n\n try {\n await auth.updateProfile({\n name: form.value.name,\n avatar: photo.value?.path ?? null,\n // Blank is left out entirely rather than sent as an empty string: the server reads a\n // filled password as \"change it\", and an empty one would be a change to nothing.\n ...(form.value.password\n ? { password: form.value.password, current_password: form.value.current_password }\n : {}),\n })\n\n toast.success(t('profile.saved'))\n resolve(true)\n } catch (error) {\n const body = (error as { body?: { errors?: Record<string, string[]> } }).body\n\n if (body?.errors) {\n errors.value = body.errors\n } else {\n toast.danger(message(error))\n }\n } finally {\n saving.value = false\n }\n}\n</script>\n\n<template>\n <wx-dialog v-model:open=\"open\" :title=\"t('profile.title')\" :width=\"560\">\n <!-- The host is what the query below measures: a container query on an element's own\n class resolves against its nearest ancestor container, never against itself. -->\n <div class=\"wx-profile-host\">\n <div class=\"wx-profile\">\n <div v-if=\"props.avatarField\" class=\"wx-profile__aside\">\n <component\n :is=\"props.avatarField\"\n v-model=\"photo\"\n :label=\"t('profile.avatar')\"\n aspect=\"1/1\"\n :captions=\"false\"\n />\n </div>\n\n <div class=\"wx-profile__fields\">\n <wx-form-item :label=\"t('profile.name')\" :error=\"errorOf('name')\">\n <wx-input v-model=\"form.name\" name=\"name\" />\n </wx-form-item>\n\n <!-- Shown, not offered: see the note on the component. -->\n <wx-form-item :label=\"t('admins.email')\">\n <wx-input :model-value=\"context.state.user?.email ?? ''\" readonly />\n </wx-form-item>\n\n <wx-form-item\n :label=\"t('profile.password')\"\n :help=\"t('profile.password-hint')\"\n :error=\"errorOf('password')\"\n >\n <wx-input v-model=\"form.password\" type=\"password\" autocomplete=\"new-password\" />\n </wx-form-item>\n\n <wx-form-item\n v-if=\"form.password\"\n :label=\"t('profile.current-password')\"\n :help=\"t('profile.current-password-hint')\"\n :error=\"errorOf('current_password')\"\n >\n <wx-input\n v-model=\"form.current_password\"\n type=\"password\"\n autocomplete=\"current-password\"\n />\n </wx-form-item>\n </div>\n </div>\n </div>\n\n <template #footer>\n <wx-button @click=\"dismiss()\">{{ t('profile.cancel') }}</wx-button>\n <wx-button type=\"primary\" :loading=\"saving\" @click=\"save\">{{ t('profile.save') }}</wx-button>\n </template>\n </wx-dialog>\n</template>\n\n<style scoped>\n/*\n * The photograph beside the fields, the same shape as the administrators form — and on a\n * narrow dialog it goes back on top, which is the only arrangement that fits a phone.\n */\n.wx-profile-host {\n container-type: inline-size;\n}\n\n.wx-profile {\n display: flex;\n gap: var(--wx-space-16);\n}\n\n.wx-profile__aside {\n flex: 0 0 180px;\n}\n\n.wx-profile__fields {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-12);\n flex: 1 1 auto;\n min-width: 0;\n}\n\n@container (max-width: 480px) {\n .wx-profile {\n flex-direction: column;\n }\n\n .wx-profile__aside {\n flex: none;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, inject, ref, watch, type Component } from 'vue'\nimport { useAdmin, useI18n, useTranslate } from '@webx-ui/module-admin'\nimport { createModal } from '@webx-ui/core'\nimport ProfileDialog from './ProfileDialog.vue'\nimport { avatarFieldKey, avatarResolverKey, useAuth, type AvatarResolver } from './session'\n\n/**\n * The corner of the header: who this is, which language they read the panel in, and the way\n * out.\n *\n * The language picker lives here rather than in a settings screen because it is a property of\n * the person, not of the site — and because somebody who has landed in a language they cannot\n * read needs it within reach, not three clicks into a section they cannot navigate.\n */\nconst props = withDefaults(\n defineProps<{\n signOutLabel?: string\n /** Overrides the resolver `auth()` was given. Without either, initials. */\n resolveAvatar?: AvatarResolver\n /**\n * The field to pick a photograph with, handed down from the plugin. Without one the\n * profile still opens — a panel with no library edits everything else about a person.\n */\n avatarField?: Component\n /**\n * There is room beside the face for a name. The shell says so: the foot of an open\n * sidebar and the foot of the drawer have it, the icon rail and a phone's bar do not.\n */\n expanded?: boolean\n }>(),\n { signOutLabel: undefined, resolveAvatar: undefined, avatarField: undefined, expanded: false },\n)\n\nconst admin = useAdmin()\nconst auth = useAuth()\nconst i18n = useI18n()\nconst t = useTranslate('webx-auth')\n\nconst user = computed(() => admin.state.user)\n\n// One language is not a choice, and a menu that offers it is noise.\nconst languages = computed(() =>\n i18n.state.panelLocales.length > 1 ? i18n.state.panelLocales : [],\n)\n\nconst provided = inject(avatarResolverKey, null)\nconst providedField = inject(avatarFieldKey, null)\nconst avatarUrl = ref<string | undefined>(undefined)\n\n/*\n * The server sends the key the photograph is stored under, not a picture: turning one into\n * the other is the library's job, and the panel says which library. Resolved again whenever\n * the key changes — somebody editing their own photograph is told about it by the row, and\n * the corner should agree with the row.\n */\nwatch(\n () => (typeof user.value?.avatar === 'string' ? user.value.avatar : null),\n async (key) => {\n const resolve = props.resolveAvatar ?? provided\n\n if (key === null || key === '' || resolve === null) {\n avatarUrl.value = undefined\n\n return\n }\n\n const url = await resolve(key)\n\n // Only the answer to the key still on screen: a slow answer to a previous one is stale.\n if (user.value?.avatar === key) {\n avatarUrl.value = url ?? undefined\n }\n },\n { immediate: true },\n)\n\n/*\n * Opened from a menu, which is itself a dismissable layer: Reka builds the dialog's own layer\n * in the middle of handling this very click, then sees the click end outside it and treats\n * that as a click away. So it opens on the next turn of the loop instead.\n */\nconst editProfile = createModal<true, { avatarField?: Component }>(ProfileDialog)\n\nfunction openProfile(): void {\n setTimeout(\n () => void editProfile({ avatarField: props.avatarField ?? providedField ?? undefined }),\n 0,\n )\n}\n\nasync function choose(code: string): Promise<void> {\n if (code === i18n.state.locale) {\n return\n }\n\n await auth.setLocale(code)\n}\n</script>\n\n<template>\n <wx-dropdown v-if=\"user !== null\">\n <!--\n The avatar is the button. Wrapped in an action it was a small picture inside a box the\n same size as every icon beside it, which reads as one more tool rather than as who is\n signed in.\n -->\n <template #trigger>\n <button\n type=\"button\"\n class=\"wx-user-menu__trigger\"\n :class=\"{ 'wx-user-menu__trigger--named': expanded }\"\n :title=\"user.name\"\n >\n <wx-avatar :name=\"user.name\" :src=\"avatarUrl\" size=\"lg\" />\n\n <!--\n Initials are a way of telling two people apart, not of saying who somebody is. Where\n the corner is wide enough, it says it.\n -->\n <span v-if=\"expanded\" class=\"wx-user-menu__name\">{{ user.name }}</span>\n </button>\n </template>\n\n <wx-dropdown-item disabled>\n <wx-text size=\"sm\">{{ user.email }}</wx-text>\n </wx-dropdown-item>\n\n <wx-divider spacing=\"sm\" />\n\n <wx-dropdown-item icon=\"user\" @click=\"openProfile\">\n {{ t('profile.menu') }}\n </wx-dropdown-item>\n\n <template v-if=\"languages.length > 0\">\n <wx-divider spacing=\"sm\" />\n\n <wx-dropdown-item disabled>\n <wx-text size=\"sm\" tone=\"muted\">{{ t('menu.language') }}</wx-text>\n </wx-dropdown-item>\n\n <wx-dropdown-item\n v-for=\"language in languages\"\n :key=\"language.code\"\n :icon=\"language.code === i18n.state.locale ? 'check' : undefined\"\n @click=\"choose(language.code)\"\n >\n {{ language.nativeName }}\n </wx-dropdown-item>\n\n <wx-divider spacing=\"sm\" />\n </template>\n\n <wx-dropdown-item icon=\"logout\" @click=\"auth.logout()\">\n {{ signOutLabel ?? t('menu.sign-out') }}\n </wx-dropdown-item>\n </wx-dropdown>\n</template>\n\n<style scoped>\n/*\n * The avatar is the whole button: no border, no background, nothing of the browser's own —\n * a bordered box around a round picture reads as one more tool in the row of icons, which is\n * what wrapping it in an action looked like in the first place.\n */\n.wx-user-menu__trigger {\n display: inline-flex;\n padding: 0;\n border: none;\n border-radius: var(--wx-radius-full);\n background: transparent;\n cursor: pointer;\n transition: opacity var(--wx-duration-fast) var(--wx-easing-standard);\n}\n\n.wx-user-menu__trigger:hover {\n opacity: 0.85;\n}\n\n.wx-user-menu__trigger:focus-visible {\n outline: 2px solid var(--wx-border-focus);\n outline-offset: 2px;\n}\n\n/*\n * With a name beside it the button is a row rather than a circle, so it takes the width it is\n * given and rounds like the menu items above it instead of like the picture inside it.\n */\n.wx-user-menu__trigger--named {\n align-items: center;\n gap: var(--wx-space-8);\n width: 100%;\n min-width: 0;\n padding: var(--wx-space-4);\n border-radius: var(--wx-radius-control);\n text-align: start;\n}\n\n.wx-user-menu__trigger--named:hover {\n opacity: 1;\n background: var(--wx-bg-subtle);\n}\n\n.wx-user-menu__name {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n color: var(--wx-text-default);\n font-family: var(--wx-font-family-sans);\n font-size: var(--wx-font-size-sm);\n font-weight: var(--wx-font-weight-medium);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n</style>\n","import { h, watch, type Component } from 'vue'\nimport type { Admin, AdminPlugin } from '@webx-ui/module-admin'\nimport LoginCard from './LoginCard.vue'\nimport { authMessages } from './messages'\nimport { createAuthSession, provideAuth, type AvatarResolver } from './session'\n\nexport interface AuthOptions {\n /** Where the sign-in screen lives, inside the panel. */\n path?: string\n /** Props handed to the card — the labels, mostly. */\n card?: Record<string, unknown>\n /**\n * Turns the key the signed-in person's photograph is stored under into an address, so the\n * menu in the corner shows the picture rather than initials. The same function `admins()`\n * takes: this package does not depend on the media one, and the panel is the only place\n * that knows both are installed.\n */\n resolveAvatar?: AvatarResolver\n /**\n * The field that picks a photograph — `WxMediaField` in a panel with the library. Handed\n * in for the same reason as the resolver, and used by the profile dialog in the corner\n * menu. Without it a person edits everything about themselves except their picture.\n */\n avatarField?: Component\n}\n\n/**\n * Signing in, as a plugin rather than a section: it owns a route and a guard, and it has no\n * place in the menu.\n *\n * Installing it also tells the panel how to find out who is signed in, which is why the panel\n * itself knows nothing about authentication — it holds the answer, not the question.\n */\nexport function auth(options: AuthOptions = {}): AdminPlugin {\n const path = options.path ?? '/login'\n\n return {\n install(admin: Admin) {\n const session = createAuthSession(admin.context)\n\n // The floor under this module's labels: what the server has not translated, and what\n // a panel assembled without a server has to fall back on.\n admin.i18n.defaults('webx-auth', authMessages)\n\n provideAuth(admin.app, session, options.resolveAvatar ?? null, options.avatarField ?? null)\n admin.context.useSessionLoader(() => session.me())\n\n admin.router.addRoute({\n path,\n name: 'webx.login',\n meta: { public: true },\n component: { render: () => h(LoginCard, { ...options.card }) },\n })\n\n admin.router.beforeEach((to) => {\n if (to.meta.public === true) {\n return true\n }\n\n if (admin.context.state.status !== 'unauthenticated') {\n return true\n }\n\n return { path, query: to.fullPath === '/' ? {} : { next: to.fullPath } }\n })\n\n // Both directions are driven by the state rather than by an event, because the card\n // cannot be relied on to announce its own success: signing in flips the panel to\n // `ready`, the shell swaps the branch it renders, and the card is unmounted before the\n // line after `await` runs.\n watch(\n () => admin.context.state.status,\n (status) => {\n const current = admin.router.currentRoute.value\n\n // A session can also end between requests — a sign-out in another tab, an account\n // switched off. The panel learns that from a 401 on whatever it asked for next,\n // which is not a navigation, so nothing would move without this.\n if (status === 'unauthenticated' && current.path !== path) {\n void admin.router.replace({ path, query: { next: current.fullPath } })\n\n return\n }\n\n if (status === 'ready' && current.path === path) {\n const next = current.query.next\n\n void admin.router.replace(typeof next === 'string' ? next : '/')\n }\n },\n )\n },\n }\n}\n\nexport { admins, type AdminsOptions } from './module'\nexport { createAdminsApi, type AdminsApi } from './admins'\nexport { selectAdmin, selectAdmins, type AdminPickerOptions } from './selectAdmin'\nexport { default as WxAdminsPage } from './AdminsPage.vue'\nexport { default as WxAdminList } from './AdminList.vue'\nexport type { Admin, AdminInput, AdminPage, AdminQuery, AdminRole, Role } from './types'\nexport {\n createAuthSession,\n provideAuth,\n useAuth,\n authKey,\n avatarFieldKey,\n avatarResolverKey,\n type ProfileInput,\n} from './session'\nexport { default as WxProfileDialog } from './ProfileDialog.vue'\nexport type { AuthSession, AvatarResolver, Credentials } from './session'\nexport { default as WxLoginCard } from './LoginCard.vue'\nexport { default as WxUserMenu } from './UserMenu.vue'\nexport { authMessages } from './messages'\n"],"names":["authKey","useAuth","session","inject","createAuthSession","admin","base","error","isUnauthenticated","credentials","body","code","profile","avatarResolverKey","avatarFieldKey","provideAuth","app","resolveAvatar","avatarField","props","__props","emit","__emit","auth","t","useTranslate","labels","computed","email","ref","password","rememberMe","busy","revealed","capsLock","message","errors","secondsLeft","emailField","useTemplateRef","countdown","onBeforeUnmount","stopCountdown","throttled","throttleNotice","submit","handle","HttpError","startCountdown","seconds","trackCapsLock","event","__expose","_createBlock","_component_wx_card","_createVNode","_component_wx_form","_component_wx_alert","_component_wx_form_item","_component_wx_input","$event","_component_wx_icon","_createElementVNode","_cache","_component_wx_checkbox","_component_wx_button","_createTextVNode","_toDisplayString","authMessages","createAdminsApi","data","query","id","input","seeded","useAuthMessages","i18n","useAdmin","resolve","dismiss","open","useModal","context","api","useI18n","useErrorText","roles","saving","form","photo","value","editing","roleOptions","one","localeOptions","all","watch","errorOf","field","save","payload","saved","toast","_unref","WxDialog","WxSpace","WxButton","_hoisted_1","_hoisted_2","_openBlock","_createElementBlock","_hoisted_3","_resolveDynamicComponent","_hoisted_4","WxFormItem","WxInput","WxSelect","WxCheckbox","WxText","CARDS","root","width","useElementWidth","asCards","panel","page","loading","role","active","selected","avatars","last","stateOptions","applied","chips","picked","clearFilters","columns","rowMenuWidth","load","state","resolveAvatars","row","url","actionsFor","onRow","clickable","onSelection","_keys","rows","WxTable","_withCtx","WxAvatar","WxBadge","_Fragment","_renderList","WxRowMenu","WxDate","WxEntityCard","WxIcon","WxFilterChips","list","edit","createModal","AdminDialog","canManage","title","module","actions","remove","confirm","WxListScreen","AdminList","admins","options","path","h","AdminsPage","count","chosen","track","pick","AdminPicker","selectAdmin","selectAdmins","user","languages","provided","providedField","avatarUrl","key","editProfile","ProfileDialog","openProfile","choose","_component_wx_dropdown","_normalizeClass","_component_wx_avatar","_component_wx_dropdown_item","_component_wx_text","_component_wx_divider","language","LoginCard","to","status","current","next"],"mappings":";;;AA0CO,MAAMA,4BAA4C,WAAW;AAE7D,SAASC,KAAuB;AACrC,QAAMC,IAAUC,GAAOH,IAAS,IAAI;AAEpC,MAAIE,MAAY;AACd,UAAM,IAAI,MAAM,sEAAsE;AAGxF,SAAOA;AACT;AAQO,SAASE,GAAkBC,GAAkC;AAClE,QAAMC,IAAO,GAAGD,EAAM,OAAO;AAE7B,SAAO;AAAA,IACL,MAAM,KAAK;AACT,UAAI;AAGF,gBAFa,MAAMA,EAAM,KAAK,IAAgC,GAAGC,CAAI,KAAK,GAE9D;AAAA,MACd,SAASC,GAAO;AACd,YAAIC,GAAkBD,CAAK;AACzB,iBAAO;AAGT,cAAMA;AAAA,MACR;AAAA,IACF;AAAA,IAEA,MAAM,MAAME,GAAa;AACvB,YAAMC,IAAO,MAAML,EAAM,KAAK,KAA0B,GAAGC,CAAI,UAAU;AAAA,QACvE,OAAOG,EAAY;AAAA,QACnB,UAAUA,EAAY;AAAA,QACtB,UAAUA,EAAY,YAAY;AAAA,MAAA,CACnC;AAED,aAAAJ,EAAM,QAAQK,EAAK,IAAI,GAIvB,MAAML,EAAM,OAAA,GAELK,EAAK;AAAA,IACd;AAAA,IAEA,MAAM,UAAUC,GAAM;AAGpB,YAAMD,IAAO,MAAML,EAAM,KAAK,IAAyB,GAAGC,CAAI,WAAW,EAAE,QAAQK,EAAA,CAAM;AAEzF,MAAAN,EAAM,QAAQK,EAAK,IAAI,GAEvB,MAAML,EAAM,UAAUM,CAAI;AAAA,IAC5B;AAAA,IAEA,MAAM,cAAcC,GAAS;AAC3B,YAAMF,IAAO,MAAML,EAAM,KAAK;AAAA,QAC5B,GAAGC,CAAI;AAAA,QACPM;AAAA,MAAA;AAKF,aAAAP,EAAM,QAAQK,EAAK,IAAI,GAEhBA,EAAK;AAAA,IACd;AAAA,IAEA,MAAM,SAAS;AACb,UAAI;AACF,cAAML,EAAM,KAAK,KAAK,GAAGC,CAAI,SAAS;AAAA,MACxC,UAAA;AAGE,QAAAD,EAAM,QAAQ,IAAI,GAClBA,EAAM,MAAM,SAAS,mBACrBA,EAAM,MAAM,WAAW;AAAA,MACzB;AAAA,IACF;AAAA,EAAA;AAEJ;AAUO,MAAMQ,4BAAgE,kBAAkB,GASlFC,4BAAwD,wBAAwB;AAEtF,SAASC,GACdC,GACAd,GACAe,IAAuC,MACvCC,IAAgC,MAC1B;AACN,EAAAF,EAAI,QAAQhB,IAASE,CAAO,GAC5Bc,EAAI,QAAQH,IAAmBI,CAAa,GAC5CD,EAAI,QAAQF,IAAgBI,CAAW;AACzC;AAEA,SAASV,GAAkBD,GAAyB;AAClD,SACE,OAAOA,KAAU,YACjBA,MAAU,QACV,YAAYA,KACXA,EAA8B,WAAW;AAE9C;;;;;;;;;;;;;;;;;ACvJA,UAAMY,IAAQC,GAgCRC,IAAOC,GAEPC,IAAOtB,GAAA,GACPuB,IAAIC,EAAa,WAAW,GAE5BC,IAASC,EAAS,OAAO;AAAA,MAC7B,OAAOR,EAAM,cAAcK,EAAE,YAAY;AAAA,MACzC,UAAUL,EAAM,iBAAiBK,EAAE,eAAe;AAAA,MAClD,UAAUL,EAAM,iBAAiBK,EAAE,eAAe;AAAA,MAClD,QAAQL,EAAM,eAAeK,EAAE,aAAa;AAAA,MAC5C,UAAUL,EAAM,mBAAmBK,EAAE,gBAAgB;AAAA,MACrD,QAAQL,EAAM,eAAeK,EAAE,aAAa;AAAA,MAC5C,MAAML,EAAM,aAAaK,EAAE,WAAW;AAAA,IAAA,EACtC,GAEII,IAAQC,EAAI,EAAE,GACdC,IAAWD,EAAI,EAAE,GACjBE,IAAaF,EAAI,EAAK,GAEtBG,IAAOH,EAAI,EAAK,GAChBI,IAAWJ,EAAI,EAAK,GACpBK,IAAWL,EAAI,EAAK,GACpBM,IAAUN,EAAI,EAAE,GAChBO,IAASP,EAA8B,EAAE,GACzCQ,IAAcR,EAAI,CAAC,GAEnBS,IAAaC,GAAiC,YAAY;AAEhE,QAAIC,IAAmD;AAEvD,IAAAC,GAAgBC,CAAa;AAE7B,UAAMC,IAAYhB,EAAS,MAAMU,EAAY,QAAQ,CAAC,GAEhDO,IAAiBjB;AAAA,MAAS,MAC9BR,EAAM,oBAAoB,SACtBK,EAAE,kBAAkB,EAAE,SAASa,EAAY,OAAO,IAClDlB,EAAM,gBAAgB,QAAQ,YAAY,OAAOkB,EAAY,KAAK,CAAC;AAAA,IAAA;AAGzE,mBAAeQ,IAAwB;AACrC,UAAI,EAAAb,EAAK,SAASW,EAAU,QAI5B;AAAA,QAAAX,EAAK,QAAQ,IACbG,EAAQ,QAAQ,IAChBC,EAAO,QAAQ,CAAA;AAEf,YAAI;AACF,gBAAMb,EAAK,MAAM;AAAA,YACf,OAAOK,EAAM;AAAA,YACb,UAAUE,EAAS;AAAA,YACnB,UAAUC,EAAW;AAAA,UAAA,CACtB,GAEDD,EAAS,QAAQ,IACjBT,EAAK,SAAS;AAAA,QAChB,SAASd,GAAO;AACd,UAAAuC,EAAOvC,CAAK;AAAA,QACd,UAAA;AACE,UAAAyB,EAAK,QAAQ;AAAA,QACf;AAAA;AAAA,IACF;AAEA,aAASc,EAAOvC,GAAsB;AACpC,UAAI,EAAEA,aAAiBwC,KAAY;AACjC,QAAAZ,EAAQ,QAAQ5B,aAAiB,QAAQA,EAAM,UAAU,OAAOA,CAAK;AAErE;AAAA,MACF;AAIA,UAAIA,EAAM,cAAc;AACtB,QAAA6B,EAAO,QAAQ7B,EAAM;AAErB;AAAA,MACF;AAEA,UAAIA,EAAM,aAAa;AACrB,QAAAyC,GAAezC,EAAM,cAAc,EAAE;AAErC;AAAA,MACF;AAEA,MAAA4B,EAAQ,QAAQ5B,EAAM;AAAA,IACxB;AAEA,aAASyC,GAAeC,GAAuB;AAC7C,MAAAP,EAAA,GACAL,EAAY,QAAQY,GAEpBT,IAAY,YAAY,MAAM;AAC5B,QAAAH,EAAY,SAAS,GAEjBA,EAAY,SAAS,KACvBK,EAAA;AAAA,MAEJ,GAAG,GAAI;AAAA,IACT;AAEA,aAASA,IAAsB;AAC7B,MAAIF,MAAc,SAChB,cAAcA,CAAS,GACvBA,IAAY,OAGdH,EAAY,QAAQ;AAAA,IACtB;AAMA,aAASa,EAAcC,GAA4B;AACjD,MAAAjB,EAAS,QAAQiB,EAAM,iBAAiB,UAAU;AAAA,IACpD;AAEA,WAAAC,EAAa,EAAE,OAAO,MAAMd,EAAW,OAAO,MAAA,GAAS;;kBAIrDe,EAqFUC,IAAA;AAAA,QArFD,OAAM;AAAA,QAAY,SAAS;AAAA,MAAA;mBAClC,MAmFU;AAAA,UAnFVC,EAmFUC,GAAA;AAAA,YAnFA,QAAQpB,EAAA;AAAA,YAAS,UAAUJ,EAAA;AAAA,YAAM,KAAI;AAAA,YAAM,aAAgBa,GAAM,CAAA,SAAA,CAAA;AAAA,UAAA;uBACzE,MAOE;AAAA,cANMV,EAAA,UAAO,WADfkB,EAOEI,IAAA;AAAA;gBALA,MAAK;AAAA,gBACL,SAAQ;AAAA,gBACR,MAAA;AAAA,gBACC,aAAatB,EAAA;AAAA,gBACd,OAAM;AAAA,cAAA,gCAIKQ,EAAA,cADbU,EAOEI,IAAA;AAAA;gBALA,MAAK;AAAA,gBACL,SAAQ;AAAA,gBACR,MAAA;AAAA,gBACC,aAAab,EAAA;AAAA,gBACd,OAAM;AAAA,cAAA;cAGRW,EAceG,GAAA,EAdD,MAAK,WAAO;AAAA,2BACxB,MAYW;AAAA,kBAZXH,EAYWI,IAAA;AAAA,6BAXL;AAAA,oBAAJ,KAAIrB;AAAA,gCACKV,EAAA;AAAA,kEAAAA,EAAK,QAAAgC;AAAA,oBACd,MAAK;AAAA,oBACJ,aAAalC,EAAA,MAAO;AAAA,oBACpB,cAAYA,EAAA,MAAO;AAAA,oBACpB,cAAa;AAAA,oBACZ,WAAWN,EAAA;AAAA,oBACZ,WAAU;AAAA,oBACV,MAAK;AAAA,kBAAA;oBAEM,UAAO,MAAuB;AAAA,sBAAvBmC,EAAuBM,GAAA,EAAd,MAAK,QAAM;AAAA,oBAAA;;;;;;cAI1CN,EA6BeG,GAAA;AAAA,gBA7BD,MAAK;AAAA,gBAAY,MAAMxB,EAAA,QAAWR,QAAO,WAAW;AAAA,cAAA;2BAChE,MA2BW;AAAA,kBA3BX6B,EA2BWI,IAAA;AAAA,gCA1BA7B,EAAA;AAAA,kEAAAA,EAAQ,QAAA8B;AAAA,oBAChB,MAAM3B,EAAA,QAAQ,SAAA;AAAA,oBACd,aAAaP,EAAA,MAAO;AAAA,oBACpB,cAAYA,EAAA,MAAO;AAAA,oBACpB,cAAa;AAAA,oBACb,MAAK;AAAA,oBACJ,WAASwB;AAAA,oBACT,SAAOA;AAAA,kBAAA;oBAEG,UAAO,MAAuB;AAAA,sBAAvBK,EAAuBM,GAAA,EAAd,MAAK,QAAM;AAAA,oBAAA;oBAC3B,UAIT,MAUS;AAAA,sBAVTC,EAUS,UAAA;AAAA,wBATP,MAAK;AAAA,wBACL,OAAM;AAAA,wBACL,OAAO7B,UAAWP,EAAA,MAAO,OAAOA,EAAA,MAAO;AAAA,wBACvC,cAAYO,UAAWP,EAAA,MAAO,OAAOA,EAAA,MAAO;AAAA,wBAC5C,gBAAcO,EAAA;AAAA,wBACf,UAAS;AAAA,wBACR,SAAK8B,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAH,MAAE3B,EAAA,QAAQ,CAAIA,EAAA;AAAA,sBAAA;wBAEpBsB,EAAgDM,GAAA;AAAA,0BAAtC,MAAM5B,EAAA,QAAQ,YAAA;AAAA,wBAAA;;;;;;;;cAOxBb,EAAA,iBADRiC,EAKEW,GAAA;AAAA;4BAHSjC,EAAA;AAAA,8DAAAA,EAAU,QAAA6B;AAAA,gBAClB,OAAOlC,EAAA,MAAO;AAAA,gBACf,OAAM;AAAA,cAAA;cAGR6B,EASYU,GAAA;AAAA,gBARV,MAAK;AAAA,gBACL,eAAY;AAAA,gBACZ,MAAK;AAAA,gBACL,OAAA;AAAA,gBACC,SAASjC,EAAA;AAAA,gBACT,UAAUW,EAAA;AAAA,cAAA;2BAEX,MAAmB;AAAA,kBAAhBuB,EAAAC,EAAAzC,EAAA,MAAO,MAAM,GAAA,CAAA;AAAA,gBAAA;;;;;;;;;;;;;;;;oECrPX0C,KAAyC;AAAA,EACpD,MAAM;AAAA,IACJ,OAAO;AAAA,IACP,UAAU;AAAA,IACV,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,WAAW;AAAA,IACX,QAAQ;AAAA,EAAA;AAAA,EAEV,QAAQ;AAAA,IACN,OAAO;AAAA,IACP,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,QAAQ;AAAA,IACR,KAAK;AAAA,IACL,MAAM;AAAA;AAAA,IAEN,aAAa;AAAA,IACb,cAAc;AAAA,IACd,MAAM;AAAA,IACN,OAAO;AAAA,IACP,UAAU;AAAA,IACV,iBAAiB;AAAA,IACjB,iBAAiB;AAAA,IACjB,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,eAAe;AAAA,IACf,cAAc;AAAA,IACd,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,eAAe;AAAA,IACf,aAAa;AAAA,IACb,aAAa;AAAA,IACb,eAAe;AAAA,IACf,iBAAiB;AAAA,IACjB,OAAO;AAAA,IACP,cAAc;AAAA,IACd,eAAe;AAAA,IACf,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,SAAS;AAAA,IACT,SAAS;AAAA,EAAA;AAAA,EAEX,QAAQ;AAAA,IACN,iBAAiB;AAAA,IACjB,UAAU;AAAA,IACV,WAAW;AAAA,IACX,gBAAgB;AAAA,IAChB,cAAc;AAAA,EAAA;AAAA;AAAA,EAGhB,SAAS;AAAA,IACP,OAAO;AAAA,IACP,MAAM;AAAA,IACN,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,iBAAiB;AAAA,IACjB,oBAAoB;AAAA,IACpB,yBAAyB;AAAA,IACzB,kBAAkB;AAAA,IAClB,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,EAAA;AAAA,EAEV,MAAM;AAAA,IACJ,YAAY;AAAA,IACZ,UAAU;AAAA,EAAA;AAEd;ACrEO,SAASC,GAAgBhE,GAAgC;AAC9D,QAAMC,IAAO,GAAGD,EAAM,OAAO,SACvBiE,IAAO,CAAI5D,MAAyBA,EAAK;AAE/C,SAAO;AAAA,IACL,MAAM,CAAC6D,IAAQ,CAAA,MACblE,EAAM,KACH,IAAsD,GAAGC,CAAI,WAAW;AAAA,MACvE,OAAO;AAAA,QACL,GAAGiE,EAAM,KAAK;AAAA,QACd,MAAMA,EAAM,QAAQ;AAAA,QACpB,QAAQA,EAAM,UAAU;AAAA,QACxB,MAAMA,EAAM;AAAA,QACZ,MAAMA,EAAM;AAAA,QACZ,UAAUA,EAAM;AAAA,MAAA;AAAA,IAClB,CACD,EACA,KAAK,CAAC7D,OAAU,EAAE,GAAGA,EAAK,MAAM,MAAMA,EAAK,KAAA,EAAO;AAAA,IAEvD,KAAK,CAAC8D,MAAOnE,EAAM,KAAK,IAAqB,GAAGC,CAAI,WAAWkE,CAAE,EAAE,EAAE,KAAKF,CAAI;AAAA,IAE9E,QAAQ,CAACG,MAAUpE,EAAM,KAAK,KAAsB,GAAGC,CAAI,WAAWmE,CAAK,EAAE,KAAKH,CAAI;AAAA,IAEtF,QAAQ,CAACE,GAAIC,MACXpE,EAAM,KAAK,MAAuB,GAAGC,CAAI,WAAWkE,CAAE,IAAIC,CAAK,EAAE,KAAKH,CAAI;AAAA,IAE5E,QAAQ,CAACE,MAAOnE,EAAM,KAAK,OAAa,GAAGC,CAAI,WAAWkE,CAAE,EAAE;AAAA,IAE9D,OAAO,MAAMnE,EAAM,KAAK,IAAsB,GAAGC,CAAI,QAAQ,EAAE,KAAKgE,CAAI;AAAA,EAAA;AAE5E;AC9CA,MAAMI,yBAAa,QAAA;AASZ,SAASC,KAAwB;AACtC,MAAI;AACF,UAAM,EAAE,MAAAC,EAAA,IAASC,EAAA;AAEjB,IAAKH,GAAO,IAAIE,CAAI,MAClBA,EAAK,SAAS,aAAaR,EAAY,GACvCM,GAAO,IAAIE,CAAI;AAAA,EAEnB,QAAQ;AAAA,EAER;AACF;;;;;;;;;;;ACGA,UAAMzD,IAAQC,GAeR,EAAE,SAAA0D,GAAS,SAAAC,GAAS,MAAAC,EAAA,IAASC,GAAA,GAE7BC,IAAUL,EAAA,GACVM,IAAMd,GAAgBa,CAAO,GAC7BN,IAAOQ,GAAA;AACb,IAAAT,GAAA;AAEA,UAAMnD,IAAIC,EAAa,WAAW,GAE5BU,IAAUkD,GAAA,GAEVC,IAAQzD,EAAY,EAAE,GACtB0D,IAAS1D,EAAI,EAAK,GAClBO,IAASP,EAA8B,EAAE,GAEzC2D,IAAO3D,EAA4C;AAAA,MACvD,MAAM;AAAA,MACN,OAAO;AAAA,MACP,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,OAAO,CAAA;AAAA,IAAC,CACT,GAGK4D,IAAQ9D,EAAS;AAAA,MACrB,KAAK,MAAO6D,EAAK,MAAM,SAAS,EAAE,MAAMA,EAAK,MAAM,OAAA,IAAW;AAAA,MAC9D,KAAK,CAACE,MAAmC;AACvC,QAAAF,EAAK,MAAM,SAASE,GAAO,QAAQ;AAAA,MACrC;AAAA,IAAA,CACD,GAEKC,IAAUhE,EAAS,MAAMR,EAAM,UAAU,IAAI,GAE7CyE,IAAcjE,EAAS,MAAM2D,EAAM,MAAM,IAAI,CAACO,OAAS,EAAE,OAAOA,EAAI,IAAI,OAAOA,EAAI,KAAA,EAAO,CAAC,GAG3FC,IAAgBnE,EAAS,MAAM;AAAA,MACnC,EAAE,OAAO,QAAQ,OAAOH,EAAE,oBAAoB,EAAA;AAAA,MAC9C,GAAGoD,EAAK,MAAM,aAAa,IAAI,CAACiB,OAAS,EAAE,OAAOA,EAAI,MAAM,OAAOA,EAAI,aAAa;AAAA,IAAA,CACrF;AAED,IAAKV,EAAI,QAAQ,KAAK,CAACY,MAAST,EAAM,QAAQS,CAAI,GAElDC;AAAA,MACE,MAAM7E,EAAM;AAAA,MACZ,CAACd,MAAU;AACT,QAAAmF,EAAK,QAAQ;AAAA,UACX,MAAMnF,GAAO,QAAQ;AAAA,UACrB,OAAOA,GAAO,SAAS;AAAA,UACvB,UAAU;AAAA,UACV,QAAQA,GAAO,UAAU;AAAA,UACzB,WAAWA,GAAO,aAAa;AAAA,UAC/B,UAAUA,GAAO,YAAY;AAAA,UAC7B,QAAQA,GAAO,UAAU;AAAA,UACzB,OAAOA,GAAO,MAAM,IAAI,CAACwF,MAAQA,EAAI,EAAE,KAAK,CAAA;AAAA,QAAC,GAE/CzD,EAAO,QAAQ,CAAA;AAAA,MACjB;AAAA,MACA,EAAE,WAAW,GAAA;AAAA,IAAK;AAGpB,aAAS6D,EAAQC,GAAmC;AAClD,aAAO9D,EAAO,MAAM8D,CAAK,IAAI,CAAC;AAAA,IAChC;AAEA,mBAAeC,IAAsB;AACnC,MAAAZ,EAAO,QAAQ,IACfnD,EAAO,QAAQ,CAAA;AAEf,YAAMgE,IAAsB;AAAA,QAC1B,MAAMZ,EAAK,MAAM;AAAA,QACjB,OAAOA,EAAK,MAAM;AAAA,QAClB,QAAQA,EAAK,MAAM;AAAA,QACnB,WAAWA,EAAK,MAAM;AAAA,QACtB,UAAUA,EAAK,MAAM;AAAA,QACrB,QAAQA,EAAK,MAAM,WAAW,SAAS,OAAOA,EAAK,MAAM;AAAA,QACzD,OAAOA,EAAK,MAAM,SAAS,CAAA;AAAA,MAAC;AAG9B,MAAIA,EAAK,MAAM,aACbY,EAAQ,WAAWZ,EAAK,MAAM;AAGhC,UAAI;AACF,cAAMa,IAAQlF,EAAM,QAChB,MAAMgE,EAAI,OAAOhE,EAAM,MAAM,IAAIiF,CAAO,IACxC,MAAMjB,EAAI,OAAOiB,CAAO;AAE5B,QAAAE,EAAM,QAAQ9E,EAAEmE,EAAQ,QAAQ,mBAAmB,gBAAgB,CAAC,GACpEb,EAAQuB,CAAK;AAAA,MACf,SAAS9F,GAAO;AACd,cAAMG,IAAQH,EAA6E;AAE3F,QAAIG,GAAM,SACR0B,EAAO,QAAQ1B,EAAK,SAKpB4F,EAAM,OAAOnE,EAAQ5B,CAAK,CAAC;AAAA,MAE/B,UAAA;AACE,QAAAgF,EAAO,QAAQ;AAAA,MACjB;AAAA,IACF;2BAIElC,EAuEYkD,EAAAC,EAAA,GAAA;AAAA,MAtEF,MAAMD,EAAAvB,CAAA;AAAA,sDAAAA,EAAI,QAAApB,IAAA;AAAA,MACjB,OAAO+B,EAAA,QAAUY,EAAA/E,CAAA,yBAAyB+E,EAAA/E,CAAA,EAAC,kBAAA;AAAA,MAC3C,OAAO;AAAA,IAAA;MA4DG,UACT,MAKW;AAAA,QALX+B,EAKWgD,EAAAE,EAAA,GAAA,EALD,MAAK,QAAI;AAAA,qBACjB,MAAoF;AAAA,YAApFlD,EAAoFgD,EAAAG,CAAA,GAAA;AAAA,cAAzE,SAAQ;AAAA,cAAW,gCAAOH,EAAAxB,CAAA,EAAA;AAAA,YAAO;yBAAI,MAAwB;AAAA,oBAArBwB,EAAA/E,CAAA,EAAC,eAAA,CAAA,GAAA,CAAA;AAAA,cAAA;;;YACpD+B,EAEYgD,EAAAG,CAAA,GAAA;AAAA,cAFD,MAAK;AAAA,cAAW,SAASnB,EAAA;AAAA,cAAS,SAAOY;AAAA,YAAA;yBAClD,MAAsB;AAAA,oBAAnBI,EAAA/E,CAAA,EAAC,aAAA,CAAA,GAAA,CAAA;AAAA,cAAA;;;;;;;iBAxDV,MAkDM;AAAA,QAlDNsC,EAkDM,OAlDN6C,IAkDM;AAAA,UAjDJ7C,EAgDM,OAhDN8C,IAgDM;AAAA,YA/COxF,EAAA,eAAXyF,EAAA,GAAAC,EAQM,OARNC,IAQM;AAAA,eAPJF,KAAAxD,EAME2D,GALK5F,EAAA,WAAW,GAAA;AAAA,4BACPqE,EAAA;AAAA,8DAAAA,EAAK,QAAA7B;AAAA,gBACb,OAAO2C,EAAA/E,CAAA,EAAC,eAAA;AAAA,gBACT,QAAO;AAAA,gBACN,UAAU;AAAA,cAAA;;YAIfsC,EAoCM,OApCNmD,IAoCM;AAAA,cAnCJ1D,EAEegD,EAAAW,CAAA,GAAA;AAAA,gBAFA,OAAOX,EAAA/E,CAAA,EAAC,aAAA;AAAA,gBAAkB,OAAOyE,EAAO,MAAA;AAAA,cAAA;2BACrD,MAA4C;AAAA,kBAA5C1C,EAA4CgD,EAAAY,CAAA,GAAA;AAAA,oBAAzB,YAAA3B,EAAA,MAAK;AAAA,oBAAL,uBAAAzB,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAH,MAAA4B,EAAA,MAAK,OAAI5B;AAAA,oBAAE,MAAK;AAAA,kBAAA;;;;cAGrCL,EAEegD,EAAAW,CAAA,GAAA;AAAA,gBAFA,OAAOX,EAAA/E,CAAA,EAAC,cAAA;AAAA,gBAAmB,OAAOyE,EAAO,OAAA;AAAA,cAAA;2BACtD,MAA8E;AAAA,kBAA9E1C,EAA8EgD,EAAAY,CAAA,GAAA;AAAA,oBAA3D,YAAA3B,EAAA,MAAK;AAAA,oBAAL,uBAAAzB,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAH,MAAA4B,EAAA,MAAK,QAAK5B;AAAA,oBAAE,MAAK;AAAA,oBAAQ,MAAK;AAAA,oBAAQ,cAAa;AAAA,kBAAA;;;;cAGxEL,EAWegD,EAAAW,CAAA,GAAA;AAAA,gBAVZ,OAAOX,EAAA/E,CAAA,EAAC,iBAAA;AAAA,gBACR,OAAOyE,EAAO,UAAA;AAAA,gBACd,MAAMN,EAAA,QAAUY,EAAA/E,CAAA,4BAA4B+E,EAAA/E,CAAA,EAAC,sBAAA;AAAA,cAAA;2BAE9C,MAKE;AAAA,kBALF+B,EAKEgD,EAAAY,CAAA,GAAA;AAAA,oBAJS,YAAA3B,EAAA,MAAK;AAAA,oBAAL,uBAAAzB,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAH,MAAA4B,EAAA,MAAK,WAAQ5B;AAAA,oBACtB,MAAK;AAAA,oBACL,MAAK;AAAA,oBACL,cAAa;AAAA,kBAAA;;;;cAIjBL,EAEegD,EAAAW,CAAA,GAAA;AAAA,gBAFA,OAAOX,EAAA/E,CAAA,EAAC,cAAA;AAAA,gBAAmB,OAAOyE,EAAO,OAAA;AAAA,cAAA;2BACtD,MAAkE;AAAA,kBAAlE1C,EAAkEgD,EAAAa,EAAA,GAAA;AAAA,oBAA9C,YAAA5B,EAAA,MAAK;AAAA,oBAAL,uBAAAzB,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAH,MAAA4B,EAAA,MAAK,QAAK5B;AAAA,oBAAG,SAASgC,EAAA;AAAA,oBAAa,UAAA;AAAA,kBAAA;;;;cAGzDrC,EAEegD,EAAAW,CAAA,GAAA;AAAA,gBAFA,OAAOX,EAAA/E,CAAA,EAAC,eAAA;AAAA,cAAA;2BACrB,MAA4D;AAAA,kBAA5D+B,EAA4DgD,EAAAa,EAAA,GAAA;AAAA,oBAAxC,YAAA5B,EAAA,MAAK;AAAA,oBAAL,uBAAAzB,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAH,MAAA4B,EAAA,MAAK,SAAM5B;AAAA,oBAAG,SAASkC,EAAA;AAAA,kBAAA;;;;cAG7CvC,EAGWgD,EAAAE,EAAA,GAAA,EAHD,MAAK,QAAI;AAAA,2BACjB,MAAoE;AAAA,kBAApElD,EAAoEgD,EAAAc,EAAA,GAAA;AAAA,oBAA9C,YAAA7B,EAAA,MAAK;AAAA,oBAAL,uBAAAzB,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAH,MAAA4B,EAAA,MAAK,YAAS5B;AAAA,oBAAG,OAAO2C,EAAA/E,CAAA,EAAC,eAAA;AAAA,kBAAA;kBAC/C+B,EAAkEgD,EAAAc,EAAA,GAAA;AAAA,oBAA5C,YAAA7B,EAAA,MAAK;AAAA,oBAAL,uBAAAzB,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAH,MAAA4B,EAAA,MAAK,WAAQ5B;AAAA,oBAAG,OAAO2C,EAAA/E,CAAA,EAAC,cAAA;AAAA,kBAAA;;;;cAGhD+B,EAAsEgD,EAAAe,EAAA,GAAA;AAAA,gBAA7D,MAAK;AAAA,gBAAK,MAAK;AAAA,cAAA;2BAAQ,MAA4B;AAAA,sBAAzBf,EAAA/E,CAAA,EAAC,mBAAA,CAAA,GAAA,CAAA;AAAA,gBAAA;;;;;;;;;;ICpIxC+F,KAAQ;;;;;;;;;;;AAxCd,UAAMpG,IAAQC,GA2BRC,IAAOC,GAQPjB,IAAQwE,EAAA,GACRM,IAAMd,GAAgBhE,CAAK;AACjC,IAAAsE,GAAA;AAKA,UAAM6C,IAAOjF,GAA4B,MAAM,GACzCkF,IAAQC,GAAgBF,CAAI,GAC5BG,IAAUhG,EAAS,MAAM8F,EAAM,QAAQ,KAAKA,EAAM,QAAQF,EAAK,GAE/D/F,IAAIC,EAAa,WAAW,GAG5BmG,IAAQnG,EAAa,YAAY,GAEjCoG,IAAOhG,EAAsB,IAAI,GACjCyD,IAAQzD,EAAY,EAAE,GACtBiG,IAAUjG,EAAI,EAAK,GAGnBkG,IAAOlG,EAAI,KAAK,GAChBmG,IAASnG,EAA0B,KAAK,GACxCoG,IAAWpG,EAAa,EAAE,GAC1BqG,IAAUrG,EAA4B,EAAE;AAE9C,QAAIsG,IAAmB,EAAE,MAAM,GAAG,SAAS,IAAI,MAAM,MAAM,QAAQ,GAAA;AAEnE,UAAMvC,IAAcjE,EAAS,MAAM;AAAA,MACjC,EAAE,OAAO,OAAO,OAAOH,EAAE,kBAAkB,EAAA;AAAA,MAC3C,GAAG8D,EAAM,MAAM,IAAI,CAACO,OAAS,EAAE,OAAOA,EAAI,MAAM,OAAOA,EAAI,OAAO;AAAA,IAAA,CACnE,GAEKuC,IAAezG,EAAS,MAAM;AAAA,MAClC,EAAE,OAAO,OAAO,OAAOH,EAAE,kBAAkB,EAAA;AAAA,MAC3C,EAAE,OAAO,OAAO,OAAOA,EAAE,oBAAoB,EAAA;AAAA,MAC7C,EAAE,OAAO,MAAM,OAAOA,EAAE,sBAAsB,EAAA;AAAA,IAAE,CACjD,GAQK6G,IAAU1G,EAA0B,MAAM;AAC9C,YAAM2G,IAAyB,CAAA,GAEzBC,IAASjD,EAAM,MAAM,KAAK,CAACO,MAAQA,EAAI,SAASkC,EAAK,KAAK;AAChE,aAAIQ,KACFD,EAAM,KAAK;AAAA,QACT,KAAK;AAAA,QACL,OAAO,GAAG9G,EAAE,oBAAoB,CAAC,KAAK+G,EAAO,IAAI;AAAA,QACjD,OAAO,MAAOR,EAAK,QAAQ;AAAA,MAAA,CAC5B,GAGCC,EAAO,UAAU,SACnBM,EAAM,KAAK;AAAA,QACT,KAAK;AAAA,QACL,OAAO,GAAG9G,EAAE,qBAAqB,CAAC,KAChCwG,EAAO,UAAU,QAAQxG,EAAE,oBAAoB,IAAIA,EAAE,sBAAsB,CAC7E;AAAA,QACA,OAAO,MAAOwG,EAAO,QAAQ;AAAA,MAAA,CAC9B,GAGIM;AAAA,IACT,CAAC;AAED,aAASE,KAAqB;AAC5B,MAAAT,EAAK,QAAQ,OACbC,EAAO,QAAQ;AAAA,IACjB;AASA,UAAMS,IAAU9G,EAA+B,MACzCgG,EAAQ,QAAc,CAAC,EAAE,KAAK,QAAQ,OAAO,IAAI,IAE9C;AAAA,MACL,EAAE,KAAK,QAAQ,OAAOnG,EAAE,aAAa,GAAG,UAAU,GAAA;AAAA,MAClD,EAAE,KAAK,SAAS,OAAOA,EAAE,cAAc,GAAG,UAAU,IAAM,WAAW,IAAA;AAAA,MACrE,EAAE,KAAK,SAAS,OAAOA,EAAE,cAAc,GAAG,WAAW,IAAA;AAAA,MACrD,EAAE,KAAK,aAAa,OAAOA,EAAE,eAAe,GAAG,OAAO,UAAU,WAAW,IAAA;AAAA,MAC3E;AAAA,QACE,KAAK;AAAA,QACL,OAAOA,EAAE,mBAAmB;AAAA,QAC5B,UAAU;AAAA,QACV,WAAW;AAAA;AAAA;AAAA,QAGX,aAAa;AAAA,MAAA;AAAA,MAEf;AAAA,QACE,KAAK;AAAA,QACL,OAAO;AAAA,QACP,OAAOkH;AAAA,QACP,OAAO;AAAA,QACP,QAAQ,CAACvH,EAAM;AAAA;AAAA,QAEf,aAAa;AAAA,MAAA;AAAA,IACf,CAEH;AAED,IAAKgE,EAAI,QAAQ,KAAK,CAACY,MAAST,EAAM,QAAQS,CAAI,GAElDC,GAAM,CAAC+B,GAAMC,CAAM,GAAG,MAAMW,EAAKR,CAAI,CAAC;AAEtC,mBAAeQ,EAAKC,GAAkC;AACpD,MAAAT,IAAOS,GACPd,EAAQ,QAAQ;AAEhB,UAAI;AACF,QAAAD,EAAK,QAAQ,MAAM1C,EAAI,KAAK;AAAA,UAC1B,GAAGyD,EAAM;AAAA,UACT,MAAMb,EAAK,UAAU,QAAQ,OAAOA,EAAK;AAAA,UACzC,QAAQC,EAAO,UAAU,QAAQ,OAAOA,EAAO;AAAA,UAC/C,MAAMY,EAAM,OAAO,GAAGA,EAAM,KAAK,UAAU,SAAS,MAAM,EAAE,GAAGA,EAAM,KAAK,GAAG,KAAK;AAAA,UAClF,MAAMA,EAAM;AAAA,UACZ,UAAUA,EAAM;AAAA,QAAA,CACjB,GAEIC,EAAA;AAAA,MACP,UAAA;AACE,QAAAf,EAAQ,QAAQ;AAAA,MAClB;AAAA,IACF;AAOA,mBAAee,IAAgC;AAC7C,YAAM/D,IAAU3D,EAAM;AAEtB,UAAK2D;AAEL,mBAAWgE,KAAOjB,EAAK,OAAO,QAAQ,CAAA,GAAI;AACxC,cAAI,CAACiB,EAAI,UAAUZ,EAAQ,MAAMY,EAAI,MAAM,EAAG;AAE9C,gBAAMC,IAAM,MAAMjE,EAAQgE,EAAI,MAAM;AAEpC,UAAIC,MAAKb,EAAQ,QAAQ,EAAE,GAAGA,EAAQ,OAAO,CAACY,EAAI,MAAM,GAAGC,EAAA;AAAA,QAC7D;AAAA,IACF;AAMA,aAASC,EAAWF,GAAyB;AAC3C,aAAO;AAAA,QACL;AAAA,UACE,KAAK;AAAA,UACL,MAAM;AAAA,UACN,OAAOtH,EAAE,eAAe;AAAA,UACxB,QAAQ;AAAA,UACR,KAAK,MAAMH,EAAK,UAAUyH,CAAG;AAAA,QAAA;AAAA,MAC/B;AAAA,IAEJ;AAEA,aAASG,GAAMH,GAAkB;AAC/B,UAAI3H,EAAM,SAAS;AACjB,QAAAE,EAAK,UAAU,CAACyH,CAAG,CAAC;AAEpB;AAAA,MACF;AAEA,MAAAzH,EAAK,QAAQyH,CAAG;AAAA,IAClB;AAUA,UAAMI,IAAYvH,EAAS,MAAM,EAAER,EAAM,WAAWA,EAAM,SAAS;AAEnE,aAASgI,GAAYC,GAAgBC,GAAqB;AACxD,MAAApB,EAAS,QAAQoB,GACjBhI,EAAK,aAAagI,CAAI;AAAA,IACxB;AAGA,WAAAjG,EAAa,EAAE,QAAQ,MAAMuF,EAAKR,CAAI,GAAG,QAAQ,MAAMF,EAAS,OAAO,mBAIrEnB,EA0GM,OAAA;AAAA,eA1GG;AAAA,MAAJ,KAAIU;AAAA,MAAO,OAAM;AAAA,IAAA;MACpBjE,EAwGWgD,EAAA+C,EAAA,GAAA;AAAA,QAvGR,MAAMzB,EAAA;AAAA,QACN,SAASY,EAAA;AAAA,QACV,WAAQ;AAAA,QACR,YAAA;AAAA,QACC,WAAWS,EAAA;AAAA,QACX,OAAOA,EAAA;AAAA,QACR,OAAA;AAAA,QACC,SAASpB,EAAA;AAAA,QACT,sBAAoBvB,EAAA/E,CAAA,EAAC,eAAA;AAAA,QACrB,cAAY+E,EAAA/E,CAAA,EAAC,cAAA;AAAA,QACb,YAAYJ,EAAA,WAAWA,EAAA;AAAA,QACvB,eAAamG;AAAA,QACb,iBAAec,EAAA,MAAQ;AAAA,QACvB,iBAAe9B,EAAAqB,CAAA,EAAK,eAAA;AAAA,QACpB,YAAWqB;AAAA,QACX,eAAcN;AAAA,QACd,mBAAkBQ;AAAA,MAAA;QAuBR,aAASI,EAClB,CAQW,EATW,KAAAT,QAAG;AAAA,UACzBvF,EAQWgD,EAAAE,EAAA,GAAA;AAAA,YARD,MAAK;AAAA,YAAK,OAAM;AAAA,UAAA;uBACxB,MAIE;AAAA,cAJFlD,EAIEgD,EAAAiD,EAAA,GAAA;AAAA,gBAHC,MAAMV,EAAI;AAAA,gBACV,KAAKA,EAAI,SAASZ,EAAA,MAAQY,EAAI,MAAM,IAAI;AAAA,gBACzC,MAAK;AAAA,cAAA;cAEPhF,EAA2B,QAAA,MAAAK,EAAlB2E,EAAI,IAAI,GAAA,CAAA;AAAA,cACDA,EAAI,iBAApBzF,EAA+EkD,EAAAkD,CAAA,GAAA;AAAA;gBAAjD,MAAK;AAAA,cAAA;2BAAU,MAAuB;AAAA,sBAApBlD,EAAA/E,CAAA,EAAC,cAAA,CAAA,GAAA,CAAA;AAAA,gBAAA;;;;;;;QAI1C,cAAU+H,EACnB,CAEW,EAHY,KAAAT,QAAG;AAAA,UACVA,EAAI,MAAM,SAAM,UAAhCzF,EAEWkD,EAAAE,EAAA,GAAA;AAAA;YAF2B,MAAK;AAAA,YAAK,MAAA;AAAA,UAAA;uBACpC,MAAwB;AAAA,eAAlCI,EAAA,EAAA,GAAAC,EAA0E4C,IAAA,MAAAC,GAAlDb,EAAI,QAAXjD,YAAjBxC,EAA0EkD,EAAAkD,CAAA,GAAA;AAAA,gBAAtC,KAAK5D,EAAI;AAAA,cAAA;2BAAI,MAAc;AAAA,kBAAX3B,EAAAC,EAAA0B,EAAI,IAAI,GAAA,CAAA;AAAA,gBAAA;;;;;4BAE9DxC,EAA2EkD,EAAAe,EAAA,GAAA;AAAA;YAA3D,MAAK;AAAA,YAAK,MAAK;AAAA,UAAA;uBAAQ,MAA0B;AAAA,kBAAvBf,EAAA/E,CAAA,EAAC,iBAAA,CAAA,GAAA,CAAA;AAAA,YAAA;;;;QAGlC,kBAAc+H,EACvB,CAEW,EAHgB,KAAAT,QAAG;AAAA,UAC9BvF,EAEWgD,EAAAkD,CAAA,GAAA;AAAA,YAFA,MAAMX,EAAI,YAAS,YAAA;AAAA,YAA0B,KAAA;AAAA,UAAA;uBACtD,MAAoE;AAAA,cAAjE5E,EAAAC,EAAA2E,EAAI,YAAYvC,EAAA/E,CAAA,qBAAqB+E,EAAA/E,CAAA,EAAC,sBAAA,CAAA,GAAA,CAAA;AAAA,YAAA;;;;QAIlC,gBAAY+H,EACrB,CAA6E,EADpD,KAAAT,QAAG;AAAA,UACT1H,EAAA,kBAAnBiC,EAA6EkD,EAAAqD,EAAA,GAAA;AAAA;YAA9C,SAASZ,EAAWF,CAAG;AAAA,YAAI,OAAOA,EAAI;AAAA,UAAA;;QAG5D,gBAAYS,EACrB,CAA4D,EADnC,KAAAT,QAAG;AAAA,UAC5BvF,EAA4DgD,EAAAqD,EAAA,GAAA;AAAA,YAA9C,SAASZ,EAAWF,CAAG;AAAA,YAAI,OAAOA,EAAI;AAAA,UAAA;;QAG3C,sBAAkBS,EAC3B,CAIE,EAL6B,KAAAT,QAAG;AAAA,UAClCvF,EAIEgD,EAAAsD,EAAA,GAAA;AAAA,YAHC,OAAOf,EAAI;AAAA,YACX,MAAMA,EAAI,gBAAa,YAAA;AAAA,YACxB,SAAA;AAAA,UAAA;;QAQO,aAASS,EAClB,CAgBiB,EAjBK,KAAAT,QAAG;AAAA,UACzBvF,EAgBiBgD,EAAAuD,EAAA,GAAA;AAAA,YAff,SAAQ;AAAA,YACR,OAAM;AAAA,YACL,OAAOhB,EAAI;AAAA,YACX,OAAOA,EAAI,SAASZ,EAAA,MAAQY,EAAI,MAAM,IAAI;AAAA,YAC1C,UAAUA,EAAI;AAAA,UAAA;YAEJ,QACT,MAEW;AAAA,cAFKA,EAAI,iBAApBzF,EAEWkD,EAAAkD,CAAA,GAAA;AAAA;gBAFmB,MAAK;AAAA,gBAAU,MAAK;AAAA,cAAA;2BAChD,MAAuB;AAAA,sBAApBlD,EAAA/E,CAAA,EAAC,cAAA,CAAA,GAAA,CAAA;AAAA,gBAAA;;;eAENqF,EAAA,EAAA,GAAAC,EAAoF4C,IAAA,MAAAC,GAA5Db,EAAI,QAAXjD,YAAjBxC,EAAoFkD,EAAAkD,CAAA,GAAA;AAAA,gBAAhD,KAAK5D,EAAI;AAAA,gBAAI,MAAK;AAAA,cAAA;2BAAK,MAAc;AAAA,kBAAX3B,EAAAC,EAAA0B,EAAI,IAAI,GAAA,CAAA;AAAA,gBAAA;;;cACtEtC,EAEWgD,EAAAkD,CAAA,GAAA;AAAA,gBAFA,MAAMX,EAAI,YAAS,YAAA;AAAA,gBAA0B,KAAA;AAAA,gBAAI,MAAK;AAAA,cAAA;2BAC/D,MAAoE;AAAA,kBAAjE5E,EAAAC,EAAA2E,EAAI,YAAYvC,EAAA/E,CAAA,qBAAqB+E,EAAA/E,CAAA,EAAC,sBAAA,CAAA,GAAA,CAAA;AAAA,gBAAA;;;;;;;;;QA9EjCJ,EAAA;gBAAU;AAAA,gBACxB,MAEe;AAAA,YAFfmC,EAEegD,EAAAW,CAAA,GAAA;AAAA,cAFA,OAAOX,EAAA/E,CAAA,EAAC,oBAAA;AAAA,YAAA;yBACrB,MAA6D;AAAA,gBAA7D+B,EAA6DgD,EAAAa,EAAA,GAAA;AAAA,8BAAzCW,EAAA;AAAA,gEAAAA,EAAI,QAAAnE;AAAA,kBAAG,SAASgC,EAAA;AAAA,kBAAa,MAAK;AAAA,gBAAA;;;;YAGxDrC,EAEegD,EAAAW,CAAA,GAAA;AAAA,cAFA,OAAOX,EAAA/E,CAAA,EAAC,qBAAA;AAAA,YAAA;yBACrB,MAAgE;AAAA,gBAAhE+B,EAAgEgD,EAAAa,EAAA,GAAA;AAAA,8BAA5CY,EAAA;AAAA,gEAAAA,EAAM,QAAApE;AAAA,kBAAG,SAASwE,EAAA;AAAA,kBAAc,MAAK;AAAA,gBAAA;;;;YAG1CC,EAAA,MAAQ,SAAM,UAA/BhF,EAGYkD,EAAAG,CAAA,GAAA;AAAA;cAHyB,SAAQ;AAAA,cAAO,MAAK;AAAA,cAAK,OAAA;AAAA,cAAO,SAAO8B;AAAA,YAAA;cAC/D,QAAK,MAAwB;AAAA,gBAAxBjF,EAAwBgD,EAAAwD,EAAA,GAAA,EAAf,MAAK,SAAO;AAAA,cAAA;yBAAc,MACnD;AAAA,gBADmD7F,EAAA,QAChDqC,EAAAqB,CAAA,EAAK,eAAA,CAAA,GAAA,CAAA;AAAA,cAAA;;;;;;QAIIxG,EAAA;gBAAU;AAAA,gBACxB,MAAsC;AAAA,YAAtCmC,EAAsCgD,EAAAyD,EAAA,GAAA,EAApB,SAAS3B,EAAA,SAAO,MAAA,GAAA,CAAA,SAAA,CAAA;AAAA,UAAA;;;;;;;;;;;;;AC5R1C,UAAMlH,IAAQC,GAQR8D,IAAUL,EAAA,GACVM,IAAMd,GAAgBa,CAAO;AACnC,IAAAP,GAAA;AAEA,UAAMnD,IAAIC,EAAa,WAAW,GAE5BU,IAAUkD,GAAA,GAEV4E,IAAO1H,GAAuC,MAAM,GACpDoD,IAAU9D,EAAkB,IAAI,GAEhCqI,IAAOC,GAAqEC,EAAW,GAEvFC,IAAYnF,EAAQ,IAAI,eAAe,GAGvCoF,IAAQ3I;AAAA,MACZ,MACEuD,EAAQ,MAAM,UAAU,QAAQ,KAAK,CAACqF,MAAWA,EAAO,OAAO,QAAQ,GAAG,SAC1E/I,EAAE,cAAc;AAAA,IAAA,GAIdgJ,IAAU7I;AAAA,MAAyB,MACvC0I,IACI;AAAA,QACE;AAAA,UACE,KAAK;AAAA,UACL,OAAO7I,EAAE,kBAAkB;AAAA,UAC3B,MAAM;AAAA,UACN,SAAS;AAAA,UACT,KAAK,MAAA;AAAM,YAAKwD,EAAK,IAAI;AAAA;AAAA,QAAA;AAAA,MAC3B,IAEF,CAAA;AAAA,IAAC;AAGP,mBAAeA,EAAK3E,GAAoC;AACtD,MAAAsF,EAAQ,QAAQtF,GAEF,MAAM6J,EAAK,EAAE,OAAA7J,GAAO,aAAac,EAAM,aAAa,KAGhE8I,EAAK,OAAO,OAAA;AAAA,IAEhB;AAEA,mBAAeQ,EAAOpK,GAA6B;AASjD,UARe,MAAMqK,GAAQ;AAAA,QAC3B,OAAOlJ,EAAE,uBAAuB,EAAE,MAAMnB,EAAM,MAAM;AAAA,QACpD,SAASmB,EAAE,oBAAoB;AAAA,QAC/B,aAAaA,EAAE,eAAe;AAAA,QAC9B,YAAYA,EAAE,eAAe;AAAA,QAC7B,MAAM;AAAA,MAAA,CACP;AAID,YAAI;AACF,gBAAM2D,EAAI,OAAO9E,EAAM,EAAE,GACzBiG,EAAM,QAAQ9E,EAAE,gBAAgB,CAAC,GACjCyI,EAAK,OAAO,OAAA;AAAA,QACd,SAAS1J,GAAO;AACd,UAAA+F,EAAM,OAAOnE,EAAQ5B,CAAK,CAAC;AAAA,QAC7B;AAAA,IACF;2BAIE8C,EAQiBkD,EAAAoE,EAAA,GAAA;AAAA,MARA,OAAOL,EAAA;AAAA,MAAQ,SAASE,EAAA;AAAA,IAAA;iBACvC,MAME;AAAA,QANFjH,EAMEqH,IAAA;AAAA,mBALI;AAAA,UAAJ,KAAIX;AAAA,UACH,WAAW1D,EAAA8D,CAAA;AAAA,UACX,kBAAgBlJ,EAAM;AAAA,UACtB,QAAM6D;AAAA,UACN,UAAQyF;AAAA,QAAA;;;;;;ACtFR,SAASI,GAAOC,IAAyB,IAAiB;AAC/D,QAAMC,IAAOD,EAAQ,QAAQ;AAE7B,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAAC;AAAA,IACA,QAAQ;AAAA,MACN;AAAA,QACE,MAAAA;AAAA,QACA,MAAM;AAAA,QACN,WAAW;AAAA,UACT,QAAQ,MACNC,GAAEC,IAAY;AAAA,YACZ,aAAaH,EAAQ;AAAA,YACrB,eAAeA,EAAQ;AAAA,UAAA,CACxB;AAAA,QAAA;AAAA,MACL;AAAA,IACF;AAAA,EACF;AAEJ;;;;;;;;;AC9BA,UAAM3J,IAAQC,GASR,EAAE,MAAA4D,GAAM,SAAAF,GAAS,SAAAC,EAAA,IAAYE,GAAA;AAEnC,IAAAN,GAAA;AAEA,UAAMnD,IAAIC,EAAa,WAAW,GAE5BwI,IAAO1H,GAA0C,MAAM,GACvD2I,IAAQrJ,EAAI,CAAC;AAGnB,aAASsJ,EAAON,GAAuB;AACrC,MAAA/F,EAAQ+F,CAAM;AAAA,IAChB;AAEA,aAASO,EAAMP,GAAuB;AACpC,MAAAK,EAAM,QAAQL,EAAO;AAAA,IACvB;2BAIExH,EAuBYkD,EAAAC,EAAA,GAAA;AAAA,MAtBF,MAAMD,EAAAvB,CAAA;AAAA,sDAAAA,EAAI,QAAApB,IAAA;AAAA,MACjB,OAAOxC,EAAA,SAASmF,EAAA/E,CAAA,EAAEJ,EAAA,WAAQ,uBAAA,mBAAA;AAAA,MAC1B,OAAO;AAAA,IAAA;iBAER,MAOE;AAAA,QAPFmC,EAOEqH,IAAA;AAAA,mBANI;AAAA,UAAJ,KAAIX;AAAA,UACJ,SAAA;AAAA,UACC,UAAU7I,EAAA;AAAA,UACV,kBAAgBD,EAAM;AAAA,UACtB,UAAQgK;AAAA,UACR,aAAWC;AAAA,QAAA;;;;MAGEhK,EAAA;cAAW;AAAA,cACzB,MAMW;AAAA,UANXmC,EAMWgD,EAAAE,EAAA,GAAA;AAAA,YAND,MAAK;AAAA,YAAK,OAAM;AAAA,UAAA;uBACxB,MAA6E;AAAA,cAA7ElD,EAA6EgD,EAAAe,EAAA,GAAA;AAAA,gBAApE,MAAK;AAAA,gBAAK,MAAK;AAAA,cAAA;2BAAQ,MAAmC;AAAA,kBAAhCpD,EAAAC,EAAAoC,EAAA/E,CAAA,4BAAqB0J,EAAA,MAAA,CAAK,CAAA,GAAA,CAAA;AAAA,gBAAA;;;cAC7D3H,EAAoFgD,EAAAG,CAAA,GAAA;AAAA,gBAAzE,SAAQ;AAAA,gBAAW,gCAAOH,EAAAxB,CAAA,EAAA;AAAA,cAAO;2BAAI,MAAwB;AAAA,sBAArBwB,EAAA/E,CAAA,EAAC,eAAA,CAAA,GAAA,CAAA;AAAA,gBAAA;;;cACpD+B,EAEYgD,EAAAG,CAAA,GAAA;AAAA,gBAFD,MAAK;AAAA,gBAAW,UAAUwE,EAAA,UAAK;AAAA,gBAAS,SAAKnH,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAH,MAAE2C,EAAAzB,CAAA,EAAQmF,EAAA,OAAM,OAAA,KAAM,CAAA,CAAA;AAAA,cAAA;2BAC5E,MAAsB;AAAA,sBAAnB1D,EAAA/E,CAAA,EAAC,aAAA,CAAA,GAAA,CAAA;AAAA,gBAAA;;;;;;;;;;;ICnDR6J,KAAOlB,GAAkEmB,EAAW;AAc1F,eAAsBC,GAAYT,IAA8B,IAAgC;AAG9F,UAFe,MAAMO,GAAK,EAAE,GAAGP,GAAS,UAAU,IAAO,KAEzC,CAAC;AACnB;AAQO,SAASU,GAAaV,IAA8B,IAAkC;AAC3F,SAAOO,GAAK,EAAE,GAAGP,GAAS,UAAU,IAAM;AAC5C;;;;;;;;;;ACrBA,UAAM3J,IAAQC,GAQR,EAAE,SAAA0D,GAAS,SAAAC,GAAS,MAAAC,EAAA,IAASC,GAAA,GAE7BC,IAAUL,EAAA,GACVtD,IAAOtB,GAAA;AACb,IAAA0E,GAAA;AAEA,UAAMnD,IAAIC,EAAa,WAAW,GAE5BU,IAAUkD,GAAA,GAEVE,IAAS1D,EAAI,EAAK,GAClBO,IAASP,EAA8B,EAAE,GAEzC2D,IAAO3D,EAAI;AAAA,MACf,MAAMqD,EAAQ,MAAM,MAAM,QAAQ;AAAA,MAClC,QAASA,EAAQ,MAAM,MAAM,UAAwC;AAAA,MACrE,UAAU;AAAA,MACV,kBAAkB;AAAA,IAAA,CACnB,GAGKO,IAAQ5D,EAA6B2D,EAAK,MAAM,SAAS,EAAE,MAAMA,EAAK,MAAM,OAAA,IAAW,IAAI;AAEjG,aAASS,EAAQC,GAAmC;AAClD,aAAO9D,EAAO,MAAM8D,CAAK,IAAI,CAAC;AAAA,IAChC;AAEA,mBAAeC,IAAsB;AACnC,MAAAZ,EAAO,QAAQ,IACfnD,EAAO,QAAQ,CAAA;AAEf,UAAI;AACF,cAAMb,EAAK,cAAc;AAAA,UACvB,MAAMiE,EAAK,MAAM;AAAA,UACjB,QAAQC,EAAM,OAAO,QAAQ;AAAA;AAAA;AAAA,UAG7B,GAAID,EAAK,MAAM,WACX,EAAE,UAAUA,EAAK,MAAM,UAAU,kBAAkBA,EAAK,MAAM,iBAAA,IAC9D,CAAA;AAAA,QAAC,CACN,GAEDc,EAAM,QAAQ9E,EAAE,eAAe,CAAC,GAChCsD,EAAQ,EAAI;AAAA,MACd,SAASvE,GAAO;AACd,cAAMG,IAAQH,EAA2D;AAEzE,QAAIG,GAAM,SACR0B,EAAO,QAAQ1B,EAAK,SAEpB4F,EAAM,OAAOnE,EAAQ5B,CAAK,CAAC;AAAA,MAE/B,UAAA;AACE,QAAAgF,EAAO,QAAQ;AAAA,MACjB;AAAA,IACF;2BAIElC,EAqDYkD,EAAAC,EAAA,GAAA;AAAA,MArDO,MAAMD,EAAAvB,CAAA;AAAA,sDAAAA,EAAI,QAAApB,IAAA;AAAA,MAAG,OAAO2C,EAAA/E,CAAA,EAAC,eAAA;AAAA,MAAoB,OAAO;AAAA,IAAA;MAiDtD,UACT,MAAmE;AAAA,QAAnE+B,EAAmEgD,EAAAG,CAAA,GAAA;AAAA,UAAvD,gCAAOH,EAAAxB,CAAA,EAAA;AAAA,QAAO;qBAAI,MAAyB;AAAA,gBAAtBwB,EAAA/E,CAAA,EAAC,gBAAA,CAAA,GAAA,CAAA;AAAA,UAAA;;;QAClC+B,EAA6FgD,EAAAG,CAAA,GAAA;AAAA,UAAlF,MAAK;AAAA,UAAW,SAASnB,EAAA;AAAA,UAAS,SAAOY;AAAA,QAAA;qBAAM,MAAuB;AAAA,gBAApBI,EAAA/E,CAAA,EAAC,cAAA,CAAA,GAAA,CAAA;AAAA,UAAA;;;;iBAhDhE,MA4CM;AAAA,QA5CNsC,EA4CM,OA5CN6C,IA4CM;AAAA,UA3CJ7C,EA0CM,OA1CN8C,IA0CM;AAAA,YAzCOzF,EAAM,eAAjB0F,EAAA,GAAAC,EAQM,OARNC,IAQM;AAAA,oBAPJ1D,EAME2D,GALK7F,EAAM,WAAW,GAAA;AAAA,4BACbsE,EAAA;AAAA,8DAAAA,EAAK,QAAA7B;AAAA,gBACb,OAAO2C,EAAA/E,CAAA,EAAC,gBAAA;AAAA,gBACT,QAAO;AAAA,gBACN,UAAU;AAAA,cAAA;;YAIfsC,EA8BM,OA9BNmD,IA8BM;AAAA,cA7BJ1D,EAEegD,EAAAW,CAAA,GAAA;AAAA,gBAFA,OAAOX,EAAA/E,CAAA,EAAC,cAAA;AAAA,gBAAmB,OAAOyE,EAAO,MAAA;AAAA,cAAA;2BACtD,MAA4C;AAAA,kBAA5C1C,EAA4CgD,EAAAY,CAAA,GAAA;AAAA,oBAAzB,YAAA3B,EAAA,MAAK;AAAA,oBAAL,uBAAAzB,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAH,MAAA4B,EAAA,MAAK,OAAI5B;AAAA,oBAAE,MAAK;AAAA,kBAAA;;;;cAIrCL,EAEegD,EAAAW,CAAA,GAAA;AAAA,gBAFA,OAAOX,EAAA/E,CAAA,EAAC,cAAA;AAAA,cAAA;2BACrB,MAAoE;AAAA,kBAApE+B,EAAoEgD,EAAAY,CAAA,GAAA;AAAA,oBAAzD,eAAaZ,EAAArB,CAAA,EAAQ,MAAM,MAAM,SAAK;AAAA,oBAAQ,UAAA;AAAA,kBAAA;;;;cAG3D3B,EAMegD,EAAAW,CAAA,GAAA;AAAA,gBALZ,OAAOX,EAAA/E,CAAA,EAAC,kBAAA;AAAA,gBACR,MAAM+E,EAAA/E,CAAA,EAAC,uBAAA;AAAA,gBACP,OAAOyE,EAAO,UAAA;AAAA,cAAA;2BAEf,MAAgF;AAAA,kBAAhF1C,EAAgFgD,EAAAY,CAAA,GAAA;AAAA,oBAA7D,YAAA3B,EAAA,MAAK;AAAA,oBAAL,uBAAAzB,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAH,MAAA4B,EAAA,MAAK,WAAQ5B;AAAA,oBAAE,MAAK;AAAA,oBAAW,cAAa;AAAA,kBAAA;;;;cAIzD4B,EAAA,MAAK,iBADbnC,EAWekD,EAAAW,CAAA,GAAA;AAAA;gBATZ,OAAOX,EAAA/E,CAAA,EAAC,0BAAA;AAAA,gBACR,MAAM+E,EAAA/E,CAAA,EAAC,+BAAA;AAAA,gBACP,OAAOyE,EAAO,kBAAA;AAAA,cAAA;2BAEf,MAIE;AAAA,kBAJF1C,EAIEgD,EAAAY,CAAA,GAAA;AAAA,oBAHS,YAAA3B,EAAA,MAAK;AAAA,oBAAL,uBAAAzB,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAH,MAAA4B,EAAA,MAAK,mBAAgB5B;AAAA,oBAC9B,MAAK;AAAA,oBACL,cAAa;AAAA,kBAAA;;;;;;;;;;;;;;;;;;;;;;;AChH3B,UAAMzC,IAAQC,GAmBRf,IAAQwE,EAAA,GACRtD,IAAOtB,GAAA,GACP2E,IAAOQ,GAAA,GACP5D,IAAIC,EAAa,WAAW,GAE5BgK,IAAO9J,EAAS,MAAMtB,EAAM,MAAM,IAAI,GAGtCqL,IAAY/J;AAAA,MAAS,MACzBiD,EAAK,MAAM,aAAa,SAAS,IAAIA,EAAK,MAAM,eAAe,CAAA;AAAA,IAAC,GAG5D+G,IAAWxL,GAAOU,IAAmB,IAAI,GACzC+K,IAAgBzL,GAAOW,IAAgB,IAAI,GAC3C+K,IAAYhK,EAAwB,MAAS;AAQnD,IAAAmE;AAAA,MACE,MAAO,OAAOyF,EAAK,OAAO,UAAW,WAAWA,EAAK,MAAM,SAAS;AAAA,MACpE,OAAOK,MAAQ;AACb,cAAMhH,IAAU3D,EAAM,iBAAiBwK;AAEvC,YAAIG,MAAQ,QAAQA,MAAQ,MAAMhH,MAAY,MAAM;AAClD,UAAA+G,EAAU,QAAQ;AAElB;AAAA,QACF;AAEA,cAAM9C,IAAM,MAAMjE,EAAQgH,CAAG;AAG7B,QAAIL,EAAK,OAAO,WAAWK,MACzBD,EAAU,QAAQ9C,KAAO;AAAA,MAE7B;AAAA,MACA,EAAE,WAAW,GAAA;AAAA,IAAK;AAQpB,UAAMgD,IAAc5B,GAA+C6B,EAAa;AAEhF,aAASC,IAAoB;AAC3B;AAAA,QACE,MAAA;AAAM,UAAKF,EAAY,EAAE,aAAa5K,EAAM,eAAeyK,KAAiB,QAAW;AAAA;AAAA,QACvF;AAAA,MAAA;AAAA,IAEJ;AAEA,mBAAeM,EAAOvL,GAA6B;AACjD,MAAIA,MAASiE,EAAK,MAAM,UAIxB,MAAMrD,EAAK,UAAUZ,CAAI;AAAA,IAC3B;;;aAIqB8K,EAAA,UAAI,aAAvBpI,EAuDc8I,GAAA,EAAA,KAAA,KAAA;AAAA,QAjDD,WACT,MAaS;AAAA,UAbTrI,EAaS,UAAA;AAAA,YAZP,MAAK;AAAA,YACL,OAAKsI,GAAA,CAAC,yBAAuB,EAAA,gCACahL,EAAA,SAAA,CAAQ,CAAA;AAAA,YACjD,OAAOqK,EAAA,MAAK;AAAA,UAAA;YAEblI,EAA0D8I,GAAA;AAAA,cAA9C,MAAMZ,EAAA,MAAK;AAAA,cAAO,KAAKI,EAAA;AAAA,cAAW,MAAK;AAAA,YAAA;YAMvCzK,EAAA,YAAZyF,EAAA,GAAAC,EAAuE,QAAvEF,IAAuEzC,EAAnBsH,EAAA,MAAK,IAAI,GAAA,CAAA;;;mBAIjE,MAEmB;AAAA,UAFnBlI,EAEmB+I,GAAA,EAFD,UAAA,MAAQ;AAAA,uBACxB,MAA6C;AAAA,cAA7C/I,EAA6CgJ,GAAA,EAApC,MAAK,QAAI;AAAA,2BAAC,MAAgB;AAAA,kBAAbrI,EAAAC,EAAAsH,EAAA,MAAK,KAAK,GAAA,CAAA;AAAA,gBAAA;;;;;;UAGlClI,EAA2BiJ,GAAA,EAAf,SAAQ,MAAI;AAAA,UAExBjJ,EAEmB+I,GAAA;AAAA,YAFD,MAAK;AAAA,YAAQ,SAAOL;AAAA,UAAA;uBACpC,MAAuB;AAAA,kBAApB1F,EAAA/E,CAAA,EAAC,cAAA,CAAA,GAAA,CAAA;AAAA,YAAA;;;UAGUkK,EAAA,MAAU,SAAM,UAAhC5E,EAiBW4C,IAAA,EAAA,KAAA,KAAA;AAAA,YAhBTnG,EAA2BiJ,GAAA,EAAf,SAAQ,MAAI;AAAA,YAExBjJ,EAEmB+I,GAAA,EAFD,UAAA,MAAQ;AAAA,yBACxB,MAAkE;AAAA,gBAAlE/I,EAAkEgJ,GAAA;AAAA,kBAAzD,MAAK;AAAA,kBAAK,MAAK;AAAA,gBAAA;6BAAQ,MAAwB;AAAA,wBAArBhG,EAAA/E,CAAA,EAAC,eAAA,CAAA,GAAA,CAAA;AAAA,kBAAA;;;;;;oBAGtCsF,EAOmB4C,IAAA,MAAAC,GANE+B,EAAA,OAAS,CAArBe,YADTpJ,EAOmBiJ,GAAA;AAAA,cALhB,KAAKG,EAAS;AAAA,cACd,MAAMA,EAAS,SAASlG,EAAA3B,CAAA,EAAK,MAAM,SAAM,UAAa;AAAA,cACtD,SAAK,CAAAhB,MAAEsI,EAAOO,EAAS,IAAI;AAAA,YAAA;yBAE5B,MAAyB;AAAA,gBAAtBvI,EAAAC,EAAAsI,EAAS,UAAU,GAAA,CAAA;AAAA,cAAA;;;YAGxBlJ,EAA2BiJ,GAAA,EAAf,SAAQ,MAAI;AAAA,UAAA;UAG1BjJ,EAEmB+I,GAAA;AAAA,YAFD,MAAK;AAAA,YAAU,SAAKvI,EAAA,CAAA,MAAAA,EAAA,CAAA,IAAA,CAAAH,MAAE2C,EAAAhF,CAAA,EAAK,OAAA;AAAA,UAAM;uBACjD,MAAwC;AAAA,cAArC2C,EAAAC,EAAA/C,EAAA,gBAAgBmF,EAAA/E,CAAA,EAAC,eAAA,CAAA,GAAA,CAAA;AAAA,YAAA;;;;;;;;;ACzHnB,SAASD,GAAKuJ,IAAuB,IAAiB;AAC3D,QAAMC,IAAOD,EAAQ,QAAQ;AAE7B,SAAO;AAAA,IACL,QAAQzK,GAAc;AACpB,YAAMH,IAAUE,GAAkBC,EAAM,OAAO;AAI/C,MAAAA,EAAM,KAAK,SAAS,aAAa+D,EAAY,GAE7CrD,GAAYV,EAAM,KAAKH,GAAS4K,EAAQ,iBAAiB,MAAMA,EAAQ,eAAe,IAAI,GAC1FzK,EAAM,QAAQ,iBAAiB,MAAMH,EAAQ,IAAI,GAEjDG,EAAM,OAAO,SAAS;AAAA,QACpB,MAAA0K;AAAA,QACA,MAAM;AAAA,QACN,MAAM,EAAE,QAAQ,GAAA;AAAA,QAChB,WAAW,EAAE,QAAQ,MAAMC,GAAE0B,IAAW,EAAE,GAAG5B,EAAQ,MAAM,EAAA;AAAA,MAAE,CAC9D,GAEDzK,EAAM,OAAO,WAAW,CAACsM,MACnBA,EAAG,KAAK,WAAW,MAInBtM,EAAM,QAAQ,MAAM,WAAW,oBAC1B,KAGF,EAAE,MAAA0K,GAAM,OAAO4B,EAAG,aAAa,MAAM,CAAA,IAAK,EAAE,MAAMA,EAAG,SAAA,EAAS,CACtE,GAMD3G;AAAA,QACE,MAAM3F,EAAM,QAAQ,MAAM;AAAA,QAC1B,CAACuM,MAAW;AACV,gBAAMC,IAAUxM,EAAM,OAAO,aAAa;AAK1C,cAAIuM,MAAW,qBAAqBC,EAAQ,SAAS9B,GAAM;AACzD,YAAK1K,EAAM,OAAO,QAAQ,EAAE,MAAA0K,GAAM,OAAO,EAAE,MAAM8B,EAAQ,SAAA,GAAY;AAErE;AAAA,UACF;AAEA,cAAID,MAAW,WAAWC,EAAQ,SAAS9B,GAAM;AAC/C,kBAAM+B,IAAOD,EAAQ,MAAM;AAE3B,YAAKxM,EAAM,OAAO,QAAQ,OAAOyM,KAAS,WAAWA,IAAO,GAAG;AAAA,UACjE;AAAA,QACF;AAAA,MAAA;AAAA,IAEJ;AAAA,EAAA;AAEJ;"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/session.ts","../src/LoginCard.vue","../src/messages.ts","../src/admins.ts","../src/i18n.ts","../src/AdminDialog.vue","../src/AdminList.vue","../src/CallList.vue","../src/connections.ts","../src/ConnectionList.vue","../src/AdminsPage.vue","../src/ConnectPage.vue","../src/module.ts","../src/AdminPicker.vue","../src/selectAdmin.ts","../src/ProfileDialog.vue","../src/UserMenu.vue","../src/index.ts"],"sourcesContent":["import { inject, type App, type Component, type InjectionKey } from 'vue'\nimport type { AdminContext, AdminUser, ThemePreference } from '@webx-ui/module-admin'\n\nexport interface Credentials {\n email: string\n password: string\n remember?: boolean\n}\n\nexport interface AuthSession {\n /** Who is signed in, or null. A 401 is an answer, not a failure. */\n me(): Promise<AdminUser | null>\n /** Signs in and tells the panel to load itself. Throws `HttpError` on a refusal. */\n login(credentials: Credentials): Promise<AdminUser>\n logout(): Promise<void>\n /**\n * Change the language this administrator reads the panel in.\n *\n * Stored on the person rather than in this browser, so it follows them to the next machine\n * and so the server writes its own messages — a 422 under a field — in the same language as\n * the label above it.\n */\n setLocale(code: string): Promise<void>\n /**\n * Write down which theme this administrator reads the panel in.\n *\n * Only writes it down: the screen has already changed by the time this is called. Painting\n * is instant and local, and an account that has not caught up yet costs nobody anything —\n * whereas a panel that waits for the network before it changes colour looks broken.\n */\n setTheme(preference: ThemePreference): Promise<void>\n /**\n * Change what is yours to change about yourself: your name, your photograph, your password.\n *\n * Not the administrators endpoint. That one is behind `admins.manage`, which is a permission\n * about other people — an editor who may not manage anybody still has a name to spell and\n * a password to rotate.\n */\n updateProfile(profile: ProfileInput): Promise<AdminUser>\n}\n\n/** What somebody may send about themselves. A blank password is \"leave it alone\". */\nexport interface ProfileInput {\n name: string\n avatar: string | null\n password?: string\n /** Required by the server whenever `password` is filled in. */\n current_password?: string\n}\n\nexport const authKey: InjectionKey<AuthSession> = Symbol('webx-auth')\n\nexport function useAuth(): AuthSession {\n const session = inject(authKey, null)\n\n if (session === null) {\n throw new Error('useAuth() was called outside a panel that installed the auth plugin.')\n }\n\n return session\n}\n\n/**\n * The three calls `webx-ui/module-auth` answers, and nothing else.\n *\n * The session lives in a cookie, so there is nothing to keep here: the source of truth is the\n * server, and the panel asks it rather than remembering.\n */\nexport function createAuthSession(admin: AdminContext): AuthSession {\n const base = `${admin.apiPath}/auth`\n\n return {\n async me() {\n try {\n const body = await admin.http.get<{ data: AdminUser | null }>(`${base}/me`)\n\n return body.data\n } catch (error) {\n if (isUnauthenticated(error)) {\n return null\n }\n\n throw error\n }\n },\n\n async login(credentials) {\n const body = await admin.http.post<{ data: AdminUser }>(`${base}/login`, {\n email: credentials.email,\n password: credentials.password,\n remember: credentials.remember ?? false,\n })\n\n admin.setUser(body.data)\n\n // Signing in is the moment the panel becomes knowable: the manifest needs a session,\n // so it could not have been loaded before now.\n await admin.reload()\n\n return body.data\n },\n\n async setLocale(code) {\n // The panel redraws only after the server has accepted the choice: a language that\n // half took — menus switched, error messages not — is worse than one that did not.\n const body = await admin.http.put<{ data: AdminUser }>(`${base}/locale`, { locale: code })\n\n admin.setUser(body.data)\n\n await admin.setLocale(code)\n },\n\n async setTheme(preference) {\n // Null rather than the word: \"follow the machine\" is the absence of a choice, and the\n // column says so the same way the language column does.\n const body = await admin.http.put<{ data: AdminUser }>(`${base}/theme`, {\n theme: preference === 'system' ? null : preference,\n })\n\n admin.setUser(body.data)\n },\n\n async updateProfile(profile) {\n const body = await admin.http.put<{ data: AdminUser }>(\n `${base}/me`,\n profile as unknown as Record<string, unknown>,\n )\n\n // The corner of the panel is drawn from this, so the new name and the new photograph\n // are on screen before the dialog has finished closing.\n admin.setUser(body.data)\n\n return body.data\n },\n\n async logout() {\n try {\n await admin.http.post(`${base}/logout`)\n } finally {\n // Whatever the server said, this browser is done: leaving the panel drawn behind a\n // failed sign-out would be worse than signing out optimistically.\n admin.setUser(null)\n admin.state.status = 'unauthenticated'\n admin.state.manifest = null\n }\n },\n }\n}\n\n/** Turns the key an avatar is stored under into an address the browser can load. */\nexport type AvatarResolver = (key: string) => Promise<string | null>\n\n/**\n * The resolver the panel handed to `auth()`, for whatever draws the signed-in person — the\n * menu in the corner, first of all. Null in a panel that has no library to resolve against,\n * which is what initials are for.\n */\nexport const avatarResolverKey: InjectionKey<AvatarResolver | null> = Symbol('webx-auth-avatar')\n\n/**\n * The field the panel picks photographs with, for the profile dialog behind the corner menu.\n *\n * Provided rather than passed: the shell renders the user menu itself, and a component it\n * builds is not a place to hand a component down through. Null in a panel with no library —\n * the profile still opens, with everything but the photograph.\n */\nexport const avatarFieldKey: InjectionKey<Component | null> = Symbol('webx-auth-avatar-field')\n\nexport function provideAuth(\n app: App,\n session: AuthSession,\n resolveAvatar: AvatarResolver | null = null,\n avatarField: Component | null = null,\n): void {\n app.provide(authKey, session)\n app.provide(avatarResolverKey, resolveAvatar)\n app.provide(avatarFieldKey, avatarField)\n}\n\nfunction isUnauthenticated(error: unknown): boolean {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'status' in error &&\n (error as { status: unknown }).status === 401\n )\n}\n","<script setup lang=\"ts\">\nimport { computed, onBeforeUnmount, ref, useTemplateRef } from 'vue'\nimport { HttpError, useTranslate } from '@webx-ui/module-admin'\nimport { useAuth } from './session'\n\n/**\n * The card a panel opens on.\n *\n * No logo, no product name, no \"welcome back\" — an admin panel's front door should say as\n * little as possible to somebody who has no business behind it, and the people who do have\n * business behind it already know where they are.\n *\n * Every string is still a prop and a prop given still wins. What changed is what happens when\n * one is not: the label comes from the panel's dictionary, which the server assembles from the\n * same `lang` files it answers its own messages from. A panel gets its language without being\n * told every label twice, and a card placed outside a panel still speaks English.\n */\nconst props = withDefaults(\n defineProps<{\n emailLabel?: string\n passwordLabel?: string\n rememberLabel?: string\n submitLabel?: string\n capsLockWarning?: string\n /** `:seconds` is replaced with however long the server said to wait. */\n throttleMessage?: string\n revealLabel?: string\n hideLabel?: string\n /** Offer to stay signed in. The server honours it with a long-lived cookie. */\n remember?: boolean\n /** Put the cursor in the email field. Off for a card that is not the only thing on screen. */\n autofocus?: boolean\n }>(),\n {\n // Undefined rather than a string, so \"not given\" is tellable from \"given as English\" and\n // the dictionary gets its turn.\n emailLabel: undefined,\n passwordLabel: undefined,\n rememberLabel: undefined,\n submitLabel: undefined,\n capsLockWarning: undefined,\n throttleMessage: undefined,\n revealLabel: undefined,\n hideLabel: undefined,\n remember: true,\n autofocus: true,\n },\n)\n\nconst emit = defineEmits<{ success: [] }>()\n\nconst auth = useAuth()\nconst t = useTranslate('webx-auth')\n\nconst labels = computed(() => ({\n email: props.emailLabel ?? t('card.email'),\n password: props.passwordLabel ?? t('card.password'),\n remember: props.rememberLabel ?? t('card.remember'),\n submit: props.submitLabel ?? t('card.submit'),\n capsLock: props.capsLockWarning ?? t('card.caps-lock'),\n reveal: props.revealLabel ?? t('card.reveal'),\n hide: props.hideLabel ?? t('card.hide'),\n}))\n\nconst email = ref('')\nconst password = ref('')\nconst rememberMe = ref(false)\n\nconst busy = ref(false)\nconst revealed = ref(false)\nconst capsLock = ref(false)\nconst message = ref('')\nconst errors = ref<Record<string, string[]>>({})\nconst secondsLeft = ref(0)\n\nconst emailField = useTemplateRef<HTMLInputElement>('emailField')\n\nlet countdown: ReturnType<typeof setInterval> | null = null\n\nonBeforeUnmount(stopCountdown)\n\nconst throttled = computed(() => secondsLeft.value > 0)\n\nconst throttleNotice = computed(() =>\n props.throttleMessage === undefined\n ? t('card.throttled', { seconds: secondsLeft.value })\n : props.throttleMessage.replace(':seconds', String(secondsLeft.value)),\n)\n\nasync function submit(): Promise<void> {\n if (busy.value || throttled.value) {\n return\n }\n\n busy.value = true\n message.value = ''\n errors.value = {}\n\n try {\n await auth.login({\n email: email.value,\n password: password.value,\n remember: rememberMe.value,\n })\n\n password.value = ''\n emit('success')\n } catch (error) {\n handle(error)\n } finally {\n busy.value = false\n }\n}\n\nfunction handle(error: unknown): void {\n if (!(error instanceof HttpError)) {\n message.value = error instanceof Error ? error.message : String(error)\n\n return\n }\n\n // A bad password comes back as a 422 against the email field, the same as an address\n // nobody owns — the server refuses to say which, and so does this.\n if (error.isValidation) {\n errors.value = error.errors\n\n return\n }\n\n if (error.isThrottled) {\n startCountdown(error.retryAfter ?? 60)\n\n return\n }\n\n message.value = error.message\n}\n\nfunction startCountdown(seconds: number): void {\n stopCountdown()\n secondsLeft.value = seconds\n\n countdown = setInterval(() => {\n secondsLeft.value -= 1\n\n if (secondsLeft.value <= 0) {\n stopCountdown()\n }\n }, 1000)\n}\n\nfunction stopCountdown(): void {\n if (countdown !== null) {\n clearInterval(countdown)\n countdown = null\n }\n\n secondsLeft.value = 0\n}\n\n/**\n * Worth saying out loud: a password typed in capitals fails with the same unhelpful message\n * as a wrong one, and people retype it three times before noticing.\n */\nfunction trackCapsLock(event: KeyboardEvent): void {\n capsLock.value = event.getModifierState('CapsLock')\n}\n\ndefineExpose({ focus: () => emailField.value?.focus() })\n</script>\n\n<template>\n <wx-card class=\"wx-login\" :padding=\"24\">\n <wx-form :errors=\"errors\" :disabled=\"busy\" gap=\"md\" @submit.prevent=\"submit\">\n <wx-alert\n v-if=\"message !== ''\"\n type=\"danger\"\n variant=\"soft\"\n live\n :description=\"message\"\n class=\"wx-login__message\"\n />\n\n <wx-alert\n v-else-if=\"throttled\"\n type=\"warning\"\n variant=\"soft\"\n live\n :description=\"throttleNotice\"\n class=\"wx-login__message\"\n />\n\n <wx-form-item name=\"email\">\n <wx-input\n ref=\"emailField\"\n v-model=\"email\"\n type=\"email\"\n :placeholder=\"labels.email\"\n :aria-label=\"labels.email\"\n autocomplete=\"username\"\n :autofocus=\"autofocus\"\n inputmode=\"email\"\n size=\"lg\"\n >\n <template #prefix><wx-icon name=\"mail\" /></template>\n </wx-input>\n </wx-form-item>\n\n <wx-form-item name=\"password\" :help=\"capsLock ? labels.capsLock : undefined\">\n <wx-input\n v-model=\"password\"\n :type=\"revealed ? 'text' : 'password'\"\n :placeholder=\"labels.password\"\n :aria-label=\"labels.password\"\n autocomplete=\"current-password\"\n size=\"lg\"\n @keydown=\"trackCapsLock\"\n @keyup=\"trackCapsLock\"\n >\n <template #prefix><wx-icon name=\"lock\" /></template>\n <template #suffix>\n <!-- A bare icon rather than a button with a surface: it sits inside the field, and\n a second box in there reads as a second control. Out of the tab order because\n it is a convenience, not a step. -->\n <button\n type=\"button\"\n class=\"wx-login__reveal\"\n :title=\"revealed ? labels.hide : labels.reveal\"\n :aria-label=\"revealed ? labels.hide : labels.reveal\"\n :aria-pressed=\"revealed\"\n tabindex=\"-1\"\n @click=\"revealed = !revealed\"\n >\n <wx-icon :name=\"revealed ? 'eye-off' : 'eye'\" />\n </button>\n </template>\n </wx-input>\n </wx-form-item>\n\n <wx-checkbox\n v-if=\"remember\"\n v-model=\"rememberMe\"\n :label=\"labels.remember\"\n class=\"wx-login__remember\"\n />\n\n <wx-button\n type=\"primary\"\n native-type=\"submit\"\n size=\"lg\"\n block\n :loading=\"busy\"\n :disabled=\"throttled\"\n >\n {{ labels.submit }}\n </wx-button>\n </wx-form>\n </wx-card>\n</template>\n\n<style scoped>\n.wx-login {\n width: 100%;\n /* Wide enough for an email address, narrow enough to stay a card rather than a page. */\n max-width: 26rem;\n}\n\n.wx-login__message {\n margin-block-end: var(--wx-space-4);\n}\n\n/* The checkbox belongs to the password above it, not to the form's row of fields: pulled up\n out of the form's gap so it reads as an option under the field rather than as a field of its\n own. */\n.wx-login__remember {\n margin-block-start: calc(-1 * var(--wx-space-12));\n}\n\n.wx-login__reveal {\n display: inline-flex;\n align-items: center;\n padding: 0;\n /* A button has a font size of its own — 13.3px in every browser — and the icon inside is\n sized in em, so without this it came out smaller than the lock at the other end. */\n font: inherit;\n color: var(--wx-text-muted);\n background: none;\n border: 0;\n cursor: pointer;\n}\n\n.wx-login__reveal:hover,\n.wx-login__reveal[aria-pressed='true'] {\n color: var(--wx-text-default);\n}\n</style>\n","import type { Messages } from '@webx-ui/module-admin'\n\n/**\n * What this package says, in English.\n *\n * The same keys `webx-ui/module-auth` ships as `lang/en/*.php`. Kept here so a card placed by\n * hand, with no WebX UI server behind it, still has words — and so a key never reaches the\n * screen when a translation is missing.\n */\nexport const authMessages: Record<string, Messages> = {\n card: {\n email: 'Email',\n password: 'Password',\n remember: 'Stay signed in',\n submit: 'Sign in',\n 'caps-lock': 'Caps Lock is on.',\n reveal: 'Show the password',\n hide: 'Hide the password',\n throttled: 'Too many attempts. Try again in :seconds s.',\n failed: 'Those details do not match an account.',\n },\n admins: {\n title: 'Administrators',\n 'filter-role': 'Role',\n 'filter-state': 'State',\n search: 'Search administrators',\n new: 'New administrator',\n edit: 'Edit administrator',\n // The same two, short enough for a button on a phone and for a dialog's heading.\n 'new-short': 'Add',\n 'edit-short': 'Edit',\n name: 'Name',\n email: 'Email',\n password: 'Password',\n 'password-hint': 'At least 12 characters',\n 'password-keep': 'Leave blank to keep the current one',\n avatar: 'Photo',\n roles: 'Roles',\n 'no-roles': 'No roles',\n active: 'Active',\n super: 'Super administrator',\n 'super-hint': 'Answers yes to everything, whatever the roles say',\n locale: 'Panel language',\n 'locale-auto': 'Follow the browser',\n 'last-login': 'Last signed in',\n save: 'Save',\n cancel: 'Cancel',\n delete: 'Delete',\n 'delete-title': 'Delete :name?',\n 'delete-text': 'They lose access at once. What they have written stays.',\n 'all-roles': 'All roles',\n 'any-state': 'Active and not',\n 'only-active': 'Active only',\n 'only-inactive': 'Switched off only',\n empty: 'Nobody matches that',\n 'select-one': 'Choose an administrator',\n 'select-many': 'Choose administrators',\n chosen: 'Chosen: :count',\n created: 'Administrator created',\n updated: 'Administrator saved',\n deleted: 'Administrator deleted',\n },\n // The trail of what agents did through the panel: the view next to the administrators.\n calls: {\n title: 'Agent calls',\n when: 'When',\n who: 'Who',\n client: 'Client',\n tool: 'Tool',\n outcome: 'Outcome',\n took: 'Took',\n ms: ':count ms',\n answered: 'Answered',\n refused: 'Refused',\n 'dry-run': 'Dry run',\n local: 'Local server',\n deleted: 'Administrator #:id',\n 'filter-user': 'Administrator',\n 'filter-tool': 'Tool',\n 'filter-outcome': 'Outcome',\n everybody: 'Everybody',\n 'all-tools': 'All tools',\n 'any-outcome': 'Any outcome',\n 'only-answered': 'Answered only',\n 'only-refused': 'Refused only',\n 'only-dry': 'Dry runs only',\n arguments: 'Arguments',\n 'no-arguments': 'Called with no arguments',\n error: 'Refused with',\n empty: 'No calls match',\n },\n // The connections tab and the page that tells somebody how to make one.\n connections: {\n title: 'Connections',\n client: 'Client',\n allowed: 'What is allowed',\n connected: 'Connected',\n 'last-used': 'Last used',\n who: 'Administrator',\n full: 'Everything :name can do',\n 'full-you': 'Everything you can do',\n 'read-only': 'Reading only',\n 'returns-to': 'Answers go to :host',\n disconnect: 'Disconnect',\n disconnected: 'Disconnected',\n 'never-used': 'Never',\n empty: 'No agents are connected',\n 'empty-all': 'Nobody has connected an agent',\n 'confirm-title': 'Disconnect :client?',\n 'confirm-text':\n 'It loses access at once, including the month of refreshing it was given. Connecting it again takes three clicks.',\n confirm: 'Disconnect',\n cancel: 'Cancel',\n done: ':client is disconnected',\n deleted: 'Administrator #:id',\n },\n connect: {\n lead: 'An AI agent — Claude, ChatGPT, Cursor — can work in this panel as you. Give it the address below; it will ask you to sign in and then ask what it may do.',\n title: 'Connect an agent',\n address: 'The address of this panel for agents',\n copy: 'Copy the address',\n copied: 'Copied',\n 'copy-failed': 'Could not reach the clipboard. Select the address and copy it by hand.',\n 'copy-line': 'Copy',\n how: 'How to add it',\n 'claude-title': 'Claude',\n 'claude-1': 'Open Settings and go to Connectors.',\n 'claude-2': 'Press \"Add custom connector\".',\n 'claude-3': 'Paste the address and press Add.',\n 'chatgpt-title': 'ChatGPT',\n 'chatgpt-1': 'Open Settings and go to Connectors.',\n 'chatgpt-2': 'Press \"Add\" and choose a custom MCP server.',\n 'chatgpt-3': 'Paste the address, leave the authentication as OAuth, and confirm.',\n 'claude-code-title': 'Claude Code',\n 'claude-code-hint': 'One line in a terminal, from any project:',\n 'codex-title': 'Codex',\n 'codex-hint': 'These lines in ~/.codex/config.toml:',\n 'one-click': 'One click',\n 'one-click-hint':\n 'These two take the whole thing from a link: the editor opens, asks once, and the server is there.',\n 'install-in': 'Add to :client',\n 'cursor-title': 'Cursor',\n 'vscode-title': 'VS Code',\n next: 'What happens next',\n 'next-1':\n 'The client opens this site in a browser and asks you to sign in to the panel, with your usual password. It never sees the password.',\n 'next-2':\n 'The panel shows who is asking and where the answer goes. There is a \"read only\" switch on that screen, and turning it on is the safe way to begin.',\n 'next-3':\n 'From then on the agent acts as you: it can do what you can do and nothing more, and every call it makes is written down.',\n mine: 'Your connections',\n },\n errors: {\n unauthenticated: 'Unauthenticated.',\n inactive: 'This account is no longer active.',\n forbidden: 'This account may not do that.',\n 'not-yourself': 'You cannot do that to your own account.',\n 'last-super': 'There has to be one super administrator left.',\n },\n // Somebody editing themselves, which is a shorter list than an administrator editing them.\n profile: {\n title: 'Your profile',\n menu: 'Edit your profile',\n name: 'Name',\n avatar: 'Photo',\n password: 'New password',\n 'password-hint': 'At least 12 characters. Leave blank to keep the current one.',\n 'current-password': 'Current password',\n 'current-password-hint': 'Asked for before the password is changed.',\n 'wrong-password': 'That is not the current password.',\n saved: 'Your profile is saved.',\n save: 'Save',\n cancel: 'Cancel',\n },\n menu: {\n 'sign-out': 'Sign out',\n language: 'Interface language',\n },\n}\n","import type { AdminContext } from '@webx-ui/module-admin'\nimport type {\n Admin,\n AdminInput,\n AdminPage,\n AdminQuery,\n AgentCall,\n AgentCallFilters,\n AgentCallPage,\n AgentCallQuery,\n Role,\n} from './types'\n\nexport interface AdminsApi {\n list(query?: AdminQuery): Promise<AdminPage>\n get(id: number): Promise<Admin>\n create(input: AdminInput): Promise<Admin>\n update(id: number, input: AdminInput): Promise<Admin>\n remove(id: number): Promise<void>\n roles(): Promise<Role[]>\n /** What agents did, newest first. Behind `admins.audit`. */\n calls(query?: AgentCallQuery): Promise<AgentCallPage>\n}\n\n/**\n * The calls behind the administrators screen, and behind the picker that opens from code.\n *\n * Reading is separate from managing on the server, so a module that only wants to say who\n * wrote something — or offer a list of people to assign work to — can use `list` and `roles`\n * without being allowed anywhere near the rest.\n */\nexport function createAdminsApi(admin: AdminContext): AdminsApi {\n const base = `${admin.apiPath}/auth`\n const data = <T>(body: { data: T }): T => body.data\n\n return {\n list: (query = {}) =>\n admin.http\n .get<{ data: Admin[]; meta: Omit<AdminPage, 'data'> }>(`${base}/admins`, {\n query: {\n q: query.q || undefined,\n role: query.role ?? undefined,\n active: query.active ?? undefined,\n sort: query.sort,\n page: query.page,\n per_page: query.per_page,\n },\n })\n .then((body) => ({ ...body.meta, data: body.data })),\n\n get: (id) => admin.http.get<{ data: Admin }>(`${base}/admins/${id}`).then(data),\n\n create: (input) => admin.http.post<{ data: Admin }>(`${base}/admins`, input).then(data),\n\n update: (id, input) =>\n admin.http.patch<{ data: Admin }>(`${base}/admins/${id}`, input).then(data),\n\n remove: (id) => admin.http.delete<void>(`${base}/admins/${id}`),\n\n roles: () => admin.http.get<{ data: Role[] }>(`${base}/roles`).then(data),\n\n calls: (query = {}) =>\n admin.http\n .get<{\n data: AgentCall[]\n meta: Omit<AgentCallPage, 'data' | 'filters'>\n filters: AgentCallFilters\n }>(`${base}/mcp-calls`, {\n query: {\n user: query.user ?? undefined,\n tool: query.tool ?? undefined,\n outcome: query.outcome ?? undefined,\n page: query.page,\n per_page: query.per_page,\n },\n })\n .then((body) => ({ ...body.meta, data: body.data, filters: body.filters })),\n }\n}\n","import { useAdmin } from '@webx-ui/module-admin'\nimport { authMessages } from './messages'\n\nconst seeded = new WeakSet<object>()\n\n/**\n * Put this package's English under the panel's dictionary, once per panel.\n *\n * The plugin does this too, but a panel can use the administrators screen — or just the picker\n * — without installing the sign-in plugin at all, and a screen showing `admins.title` instead\n * of a word is not a thing to leave to chance.\n */\nexport function useAuthMessages(): void {\n try {\n const { i18n } = useAdmin()\n\n if (!seeded.has(i18n)) {\n i18n.defaults('webx-auth', authMessages)\n seeded.add(i18n)\n }\n } catch {\n // Outside a panel there is no dictionary to seed, and `useTranslate` falls back on its own.\n }\n}\n","<script setup lang=\"ts\">\nimport { computed, ref, watch, type Component } from 'vue'\nimport { useAdmin, useErrorText, useI18n, useTranslate } from '@webx-ui/module-admin'\nimport {\n toast,\n useModal,\n WxButton,\n WxCheckbox,\n WxDialog,\n WxFormItem,\n WxInput,\n WxSelect,\n WxSpace,\n WxText,\n} from '@webx-ui/core'\n\nimport { createAdminsApi } from './admins'\nimport { useAuthMessages } from './i18n'\nimport type { Admin, AdminInput, Role } from './types'\n\n/**\n * Creating an administrator and editing one — the same form, because they differ in one field.\n *\n * The password is required the first time and optional afterwards, and a blank one on an edit\n * means \"leave it alone\" rather than \"set it to nothing\". That is the only conditional here.\n */\nconst props = withDefaults(\n defineProps<{\n /** Absent creates somebody; present edits them. */\n admin?: Admin | null\n /**\n * The field to pick the photograph with — `WxMediaField` in a panel that has the library.\n *\n * Handed in rather than imported: this package does not depend on the media one, and a\n * panel without a library still edits everything else about a person.\n */\n avatarField?: Component\n }>(),\n { admin: null, avatarField: undefined },\n)\n\nconst { resolve, dismiss, open } = useModal<Admin>()\n\nconst context = useAdmin()\nconst api = createAdminsApi(context)\nconst i18n = useI18n()\nuseAuthMessages()\n\nconst t = useTranslate('webx-auth')\n/* Not the server's `message`: the panel says how a request failed in its own words (§13.3). */\nconst message = useErrorText()\n\nconst roles = ref<Role[]>([])\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\n\nconst form = ref<AdminInput & { avatar: string | null }>({\n name: '',\n email: '',\n password: '',\n avatar: null,\n is_active: true,\n is_super: false,\n locale: null,\n roles: [],\n})\n\n/** The field hands back `{ path, … }`; what is stored is the key. */\nconst photo = computed({\n get: () => (form.value.avatar ? { path: form.value.avatar } : null),\n set: (value: { path: string } | null) => {\n form.value.avatar = value?.path ?? null\n },\n})\n\nconst editing = computed(() => props.admin !== null)\n\nconst roleOptions = computed(() => roles.value.map((one) => ({ value: one.id, label: one.name })))\n\n/* A word, not an empty string: a select reads \"\" as nothing chosen and shows a blank control. */\nconst localeOptions = computed(() => [\n { value: 'auto', label: t('admins.locale-auto') },\n ...i18n.state.panelLocales.map((one) => ({ value: one.code, label: one.nativeName })),\n])\n\nvoid api.roles().then((all) => (roles.value = all))\n\nwatch(\n () => props.admin,\n (admin) => {\n form.value = {\n name: admin?.name ?? '',\n email: admin?.email ?? '',\n password: '',\n avatar: admin?.avatar ?? null,\n is_active: admin?.is_active ?? true,\n is_super: admin?.is_super ?? false,\n locale: admin?.locale ?? 'auto',\n roles: admin?.roles.map((one) => one.id) ?? [],\n }\n errors.value = {}\n },\n { immediate: true },\n)\n\nfunction errorOf(field: string): string | undefined {\n return errors.value[field]?.[0]\n}\n\nasync function save(): Promise<void> {\n saving.value = true\n errors.value = {}\n\n const payload: AdminInput = {\n name: form.value.name,\n email: form.value.email,\n avatar: form.value.avatar,\n is_active: form.value.is_active,\n is_super: form.value.is_super,\n locale: form.value.locale === 'auto' ? null : form.value.locale,\n roles: form.value.roles ?? [],\n }\n\n if (form.value.password) {\n payload.password = form.value.password\n }\n\n try {\n const saved = props.admin\n ? await api.update(props.admin.id, payload)\n : await api.create(payload)\n\n toast.success(t(editing.value ? 'admins.updated' : 'admins.created'))\n resolve(saved)\n } catch (error) {\n const body = (error as { body?: { errors?: Record<string, string[]>; message?: string } }).body\n\n if (body?.errors) {\n errors.value = body.errors\n } else {\n // The two refusals that keep a panel reachable — the last super administrator, and doing\n // it to yourself — arrive as a 422 with a message and no field, which is the one shape\n // the panel repeats back word for word.\n toast.danger(message(error))\n }\n } finally {\n saving.value = false\n }\n}\n</script>\n\n<template>\n <wx-dialog\n v-model:open=\"open\"\n :title=\"editing ? t('admins.edit-short') : t('admins.new-short')\"\n :width=\"720\"\n >\n <!--\n The photograph beside the fields rather than above them: it is square, the fields are a\n column, and stacking the two makes somebody scroll past a picture to reach a name. Under\n `--wx-admin-form-wrap` the sidebar goes back on top, which is the only shape that fits a\n phone.\n -->\n <div class=\"wx-admin-form\">\n <div class=\"wx-admin-form__grid\">\n <div v-if=\"avatarField\" class=\"wx-admin-form__aside\">\n <component\n :is=\"avatarField\"\n v-model=\"photo\"\n :label=\"t('admins.avatar')\"\n aspect=\"1/1\"\n :captions=\"false\"\n />\n </div>\n\n <div class=\"wx-admin-form__fields\">\n <wx-form-item :label=\"t('admins.name')\" :error=\"errorOf('name')\">\n <wx-input v-model=\"form.name\" name=\"name\" />\n </wx-form-item>\n\n <wx-form-item :label=\"t('admins.email')\" :error=\"errorOf('email')\">\n <wx-input v-model=\"form.email\" type=\"email\" name=\"email\" autocomplete=\"off\" />\n </wx-form-item>\n\n <wx-form-item\n :label=\"t('admins.password')\"\n :error=\"errorOf('password')\"\n :help=\"editing ? t('admins.password-keep') : t('admins.password-hint')\"\n >\n <wx-input\n v-model=\"form.password\"\n type=\"password\"\n name=\"password\"\n autocomplete=\"new-password\"\n />\n </wx-form-item>\n\n <wx-form-item :label=\"t('admins.roles')\" :error=\"errorOf('roles')\">\n <wx-select v-model=\"form.roles\" :options=\"roleOptions\" multiple />\n </wx-form-item>\n\n <wx-form-item :label=\"t('admins.locale')\">\n <wx-select v-model=\"form.locale\" :options=\"localeOptions\" />\n </wx-form-item>\n\n <wx-space size=\"md\">\n <wx-checkbox v-model=\"form.is_active\" :label=\"t('admins.active')\" />\n <wx-checkbox v-model=\"form.is_super\" :label=\"t('admins.super')\" />\n </wx-space>\n\n <wx-text size=\"sm\" tone=\"muted\">{{ t('admins.super-hint') }}</wx-text>\n </div>\n </div>\n </div>\n\n <template #footer>\n <wx-space size=\"sm\">\n <wx-button variant=\"outline\" @click=\"dismiss()\">{{ t('admins.cancel') }}</wx-button>\n <wx-button type=\"primary\" :loading=\"saving\" @click=\"save\">\n {{ t('admins.save') }}\n </wx-button>\n </wx-space>\n </template>\n </wx-dialog>\n</template>\n\n<style>\n/* The container is the wrapper, not the grid: an element cannot query its own width. */\n.wx-admin-form {\n container-type: inline-size;\n}\n\n.wx-admin-form__grid {\n display: grid;\n grid-template-columns: 200px minmax(0, 1fr);\n gap: var(--wx-space-24);\n}\n\n.wx-admin-form__fields {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-12);\n min-width: 0;\n}\n\n/* The dialog's own width decides this, not the window's. */\n@container (max-width: 520px) {\n .wx-admin-form__grid {\n grid-template-columns: minmax(0, 1fr);\n }\n\n .wx-admin-form__aside {\n max-width: 200px;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, useTemplateRef, watch } from 'vue'\nimport {\n useAdmin,\n useTranslate,\n WxDate,\n WxFilterChips,\n rowMenuWidth,\n WxRowMenu,\n type AppliedFilter,\n type RowAction,\n} from '@webx-ui/module-admin'\nimport {\n WxAvatar,\n WxBadge,\n WxButton,\n WxEntityCard,\n WxFormItem,\n WxIcon,\n WxSelect,\n WxSpace,\n WxTable,\n WxText,\n useElementWidth,\n type TableColumn,\n type TableState,\n} from '@webx-ui/core'\nimport { createAdminsApi } from './admins'\nimport { useAuthMessages } from './i18n'\nimport type { Admin, AdminPage, Role } from './types'\n\n/**\n * The list of administrators — the screen, and the inside of the picker.\n *\n * One component for both so that searching, filtering and paging behave the same wherever\n * somebody is looking at these people. What changes is only what a row is for: opening it, or\n * choosing it.\n */\nconst props = withDefaults(\n defineProps<{\n /** Rows are chosen rather than opened. */\n picking?: boolean\n /** More than one may be chosen at a time. */\n multiple?: boolean\n /** Adds the column that removes somebody. */\n removable?: boolean\n /**\n * Offers the role and state filters.\n *\n * Off by default: a panel has half a dozen administrators, and a filter over six rows is a\n * control to read and decide about where a glance would have done.\n */\n filters?: boolean\n /** Turns an avatar key into an address. The panel wires this; this package cannot. */\n resolveAvatar?: (key: string) => Promise<string | null>\n }>(),\n {\n picking: false,\n multiple: false,\n removable: false,\n filters: false,\n resolveAvatar: undefined,\n },\n)\n\nconst emit = defineEmits<{\n open: [admin: Admin]\n chosen: [admins: Admin[]]\n /** Which rows are ticked, for a footer that counts them. */\n selection: [admins: Admin[]]\n remove: [admin: Admin]\n}>()\n\nconst admin = useAdmin()\nconst api = createAdminsApi(admin)\nuseAuthMessages()\n\n/** Where a row stops being a row. The table and the columns read the same number. */\nconst CARDS = 480\n\nconst root = useTemplateRef<HTMLElement>('root')\nconst width = useElementWidth(root)\nconst asCards = computed(() => width.value > 0 && width.value < CARDS)\n\nconst t = useTranslate('webx-auth')\n/* The funnel and 'reset all' are the panel's words, not this module's. `admin` is taken:\n in this file that is the panel context. */\nconst panel = useTranslate('webx-admin')\n\nconst page = ref<AdminPage | null>(null)\nconst roles = ref<Role[]>([])\nconst loading = ref(false)\n/* A word rather than an empty string: a select reads \"\" as nothing chosen and shows a\n blank control where the option said \"all roles\". */\nconst role = ref('all')\nconst active = ref<'any' | 'yes' | 'no'>('any')\nconst selected = ref<Admin[]>([])\nconst avatars = ref<Record<string, string>>({})\n\nlet last: TableState = { page: 1, perPage: 20, sort: null, search: '' }\n\nconst roleOptions = computed(() => [\n { value: 'all', label: t('admins.all-roles') },\n ...roles.value.map((one) => ({ value: one.slug, label: one.name })),\n])\n\nconst stateOptions = computed(() => [\n { value: 'any', label: t('admins.any-state') },\n { value: 'yes', label: t('admins.only-active') },\n { value: 'no', label: t('admins.only-inactive') },\n])\n\n/**\n * Which of the two are on, in the reader's words.\n *\n * They stand behind the funnel now, and a shut panel says nothing about itself — a list of six\n * people showing two of them looks broken until something says why.\n */\nconst applied = computed<AppliedFilter[]>(() => {\n const chips: AppliedFilter[] = []\n\n const picked = roles.value.find((one) => one.slug === role.value)\n if (picked) {\n chips.push({\n key: 'role',\n label: `${t('admins.filter-role')}: ${picked.name}`,\n clear: () => (role.value = 'all'),\n })\n }\n\n if (active.value !== 'any') {\n chips.push({\n key: 'state',\n label: `${t('admins.filter-state')}: ${\n active.value === 'yes' ? t('admins.only-active') : t('admins.only-inactive')\n }`,\n clear: () => (active.value = 'any'),\n })\n }\n\n return chips\n})\n\nfunction clearFilters(): void {\n role.value = 'all'\n active.value = 'any'\n}\n\n/*\n * No widths but the one the buttons need.\n *\n * A declared width is a promise the table has to keep, and five of them add up to more than the\n * screen — so it scrolls sideways while half the row is whitespace. Left alone, the columns take\n * what their contents need. What does not fit is dropped instead, in the order it can be spared.\n */\nconst columns = computed<TableColumn<Admin>[]>(() => {\n if (asCards.value) return [{ key: 'card', label: '' }]\n\n return [\n { key: 'name', label: t('admins.name'), sortable: true },\n { key: 'email', label: t('admins.email'), sortable: true, hideBelow: 560 },\n { key: 'roles', label: t('admins.roles'), hideBelow: 760 },\n { key: 'is_active', label: t('admins.active'), align: 'center', hideBelow: 660 },\n {\n key: 'last_login_at',\n label: t('admins.last-login'),\n sortable: true,\n hideBelow: 900,\n // The first thing to go when a row becomes a card: it is a date somebody scans down a\n // column, and a card has no column to scan.\n hideOnCards: true,\n },\n {\n key: 'actions',\n label: '',\n width: rowMenuWidth,\n align: 'right',\n hidden: !props.removable,\n // A card puts them along its top instead, through the `card-actions` slot.\n hideOnCards: true,\n },\n ]\n})\n\nvoid api.roles().then((all) => (roles.value = all))\n\nwatch([role, active], () => load(last))\n\nasync function load(state: TableState): Promise<void> {\n last = state\n loading.value = true\n\n try {\n page.value = await api.list({\n q: state.search,\n role: role.value === 'all' ? null : role.value,\n active: active.value === 'any' ? null : active.value,\n sort: state.sort ? `${state.sort.order === 'desc' ? '-' : ''}${state.sort.key}` : 'name',\n page: state.page,\n per_page: state.perPage,\n })\n\n void resolveAvatars()\n } finally {\n loading.value = false\n }\n}\n\n/**\n * A row stores the library's key, not an address, and this package has no business knowing how\n * to turn one into the other. The panel hands that in; without it a row shows initials, which\n * is what it would have shown anyway for everybody who never uploaded a photograph.\n */\nasync function resolveAvatars(): Promise<void> {\n const resolve = props.resolveAvatar\n\n if (!resolve) return\n\n for (const row of page.value?.data ?? []) {\n if (!row.avatar || avatars.value[row.avatar]) continue\n\n const url = await resolve(row.avatar)\n\n if (url) avatars.value = { ...avatars.value, [row.avatar]: url }\n }\n}\n\n/**\n * One line, and the same `···` every other list of the panel puts a record's actions behind.\n * A bare red bin in every row says deleting somebody is the thing this list is for (§20).\n */\nfunction actionsFor(row: Admin): RowAction[] {\n return [\n {\n key: 'delete',\n icon: 'trash',\n label: t('admins.delete'),\n danger: true,\n run: () => emit('remove', row),\n },\n ]\n}\n\nfunction onRow(row: Admin): void {\n if (props.picking) {\n emit('chosen', [row])\n\n return\n }\n\n emit('open', row)\n}\n\n/**\n * The listeners the table gets, rather than a handler that decides to do nothing (§13).\n *\n * Picking several is the one case where a row leads nowhere: the tick box is what chooses, and\n * a click on the row itself would have to either choose one and drop the rest or do nothing at\n * all. So it does nothing and says so — no cursor, no highlight, which is what `WxTable` reads\n * out of the missing listener. `v-bind`, not `v-on`: only that reaches it (CLAUDE.md §4).\n */\nconst clickable = computed(() => !(props.picking && props.multiple))\n\nfunction onSelection(_keys: unknown, rows: Admin[]): void {\n selected.value = rows\n emit('selection', rows)\n}\n\n/** Reload without losing where somebody was, for a caller that changed something. */\ndefineExpose({ reload: () => load(last), chosen: () => selected.value })\n</script>\n\n<template>\n <div ref=\"root\" class=\"wx-admin-list\">\n <wx-table\n :data=\"page\"\n :columns=\"columns\"\n row-key=\"id\"\n searchable\n :clickable=\"clickable\"\n :hover=\"clickable\"\n flush\n :loading=\"loading\"\n :search-placeholder=\"t('admins.search')\"\n :empty-text=\"t('admins.empty')\"\n :selectable=\"picking && multiple\"\n :cards-below=\"CARDS\"\n :filters-count=\"applied.length\"\n :filters-label=\"panel('filters.title')\"\n @row-click=\"onRow\"\n @state-change=\"load\"\n @selection-change=\"onSelection\"\n >\n <!-- Behind the funnel, and what they are set to comes back as chips beside it: two\n dropdowns standing open over six people are two controls to read before the rows. -->\n <template v-if=\"filters\" #filters>\n <wx-form-item :label=\"t('admins.filter-role')\">\n <wx-select v-model=\"role\" :options=\"roleOptions\" size=\"sm\" />\n </wx-form-item>\n\n <wx-form-item :label=\"t('admins.filter-state')\">\n <wx-select v-model=\"active\" :options=\"stateOptions\" size=\"sm\" />\n </wx-form-item>\n\n <wx-button v-if=\"applied.length > 0\" variant=\"text\" size=\"sm\" block @click=\"clearFilters\">\n <template #icon><wx-icon name=\"close\" /></template>\n {{ panel('filters.reset') }}\n </wx-button>\n </template>\n\n <template v-if=\"filters\" #applied>\n <wx-filter-chips :filters=\"applied\" />\n </template>\n\n <template #cell-name=\"{ row }\">\n <wx-space size=\"sm\" align=\"center\">\n <wx-avatar\n :name=\"row.name\"\n :src=\"row.avatar ? avatars[row.avatar] : undefined\"\n size=\"sm\"\n />\n <span>{{ row.name }}</span>\n <wx-badge v-if=\"row.is_super\" type=\"warning\">{{ t('admins.super') }}</wx-badge>\n </wx-space>\n </template>\n\n <template #cell-roles=\"{ row }\">\n <wx-space v-if=\"row.roles.length > 0\" size=\"xs\" wrap>\n <wx-badge v-for=\"one in row.roles\" :key=\"one.id\">{{ one.name }}</wx-badge>\n </wx-space>\n <wx-text v-else size=\"sm\" tone=\"muted\">{{ t('admins.no-roles') }}</wx-text>\n </template>\n\n <template #cell-is_active=\"{ row }\">\n <wx-badge :type=\"row.is_active ? 'success' : 'default'\" dot>\n {{ row.is_active ? t('admins.active') : t('admins.only-inactive') }}\n </wx-badge>\n </template>\n\n <template #card-actions=\"{ row }\">\n <wx-row-menu v-if=\"removable\" :actions=\"actionsFor(row)\" :label=\"row.name\" />\n </template>\n\n <template #cell-actions=\"{ row }\">\n <wx-row-menu :actions=\"actionsFor(row)\" :label=\"row.name\" />\n </template>\n\n <template #cell-last_login_at=\"{ row }\">\n <wx-date\n :value=\"row.last_login_at\"\n :tone=\"row.last_login_at ? 'default' : 'muted'\"\n compact\n />\n </template>\n\n <!--\n Narrow, a row is one entity rather than four labelled lines: a face, a name, the address\n that identifies them and what they are allowed. `circle`, because these are people.\n -->\n <template #cell-card=\"{ row }\">\n <wx-entity-card\n variant=\"plain\"\n shape=\"circle\"\n :title=\"row.name\"\n :image=\"row.avatar ? avatars[row.avatar] : undefined\"\n :subtitle=\"row.email\"\n >\n <template #meta>\n <wx-badge v-if=\"row.is_super\" type=\"warning\" size=\"sm\">\n {{ t('admins.super') }}\n </wx-badge>\n <wx-badge v-for=\"one in row.roles\" :key=\"one.id\" size=\"sm\">{{ one.name }}</wx-badge>\n <wx-badge :type=\"row.is_active ? 'success' : 'default'\" dot size=\"sm\">\n {{ row.is_active ? t('admins.active') : t('admins.only-inactive') }}\n </wx-badge>\n </template>\n </wx-entity-card>\n </template>\n </wx-table>\n </div>\n</template>\n\n<style>\n.wx-admin-list {\n min-width: 0;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, useTemplateRef, watch } from 'vue'\nimport {\n useAdmin,\n useTranslate,\n WxDate,\n WxFilterChips,\n type AppliedFilter,\n} from '@webx-ui/module-admin'\nimport {\n WxBadge,\n WxButton,\n WxEntityCard,\n WxFormItem,\n WxIcon,\n WxSelect,\n WxSpace,\n WxTable,\n WxText,\n useElementWidth,\n type TableColumn,\n type TableState,\n} from '@webx-ui/core'\nimport { createAdminsApi } from './admins'\nimport { useAuthMessages } from './i18n'\nimport type { AgentCall, AgentCallFilters, AgentCallOutcome, AgentCallPage } from './types'\n\n/**\n * What agents did through the panel: one row per tool call, newest first.\n *\n * A row is who, which tool, and how it went; what it was called with and why it was refused\n * open underneath, because arguments are a block of JSON and a column of those is a column\n * nobody reads. There is no link from a row to what the call was about — tools are about\n * different things, and a link that was right for one in three would be wrong for the rest.\n *\n * The filters offer the people and the tools that actually appear in the log, as the server\n * lists them, rather than every administrator and every tool on offer.\n */\nconst admin = useAdmin()\nconst api = createAdminsApi(admin)\nuseAuthMessages()\n\n/** Where a row stops being a row. */\nconst CARDS = 480\n\nconst root = useTemplateRef<HTMLElement>('root')\nconst width = useElementWidth(root)\nconst asCards = computed(() => width.value > 0 && width.value < CARDS)\n\nconst t = useTranslate('webx-auth')\n/* The funnel and 'reset all' are the panel's words, not this module's. */\nconst panel = useTranslate('webx-admin')\n\nconst page = ref<AgentCallPage | null>(null)\nconst filters = ref<AgentCallFilters>({ users: [], tools: [] })\nconst loading = ref(false)\n\n/* Words rather than empty strings: a select reads \"\" as nothing chosen and shows a blank\n control where the option said \"everybody\". `none` is the server's own word for the stdio\n server, so it travels as it is. */\nconst user = ref('all')\nconst tool = ref('all')\nconst outcome = ref<'any' | AgentCallOutcome>('any')\n\nlet last: TableState = { page: 1, perPage: 30, sort: null, search: '' }\n\nfunction nameOf(who: AgentCall['user']): string {\n if (who === null) return t('calls.local')\n\n return who.name ?? t('calls.deleted', { id: who.id })\n}\n\nconst userOptions = computed(() => [\n { value: 'all', label: t('calls.everybody') },\n ...filters.value.users.map((one) => ({\n value: one.id === null ? 'none' : String(one.id),\n label: nameOf(one.id === null ? null : { id: one.id, name: one.name }),\n })),\n])\n\nconst toolOptions = computed(() => [\n { value: 'all', label: t('calls.all-tools') },\n ...filters.value.tools.map((one) => ({ value: one, label: one })),\n])\n\nconst outcomeOptions = computed(() => [\n { value: 'any', label: t('calls.any-outcome') },\n { value: 'ok', label: t('calls.only-answered') },\n { value: 'failed', label: t('calls.only-refused') },\n { value: 'dry', label: t('calls.only-dry') },\n])\n\n/** Which filters are on, in the reader's words — the chips beside the funnel. */\nconst applied = computed<AppliedFilter[]>(() => {\n const chips: AppliedFilter[] = []\n\n const who = userOptions.value.find((one) => one.value === user.value)\n if (user.value !== 'all' && who) {\n chips.push({\n key: 'user',\n label: `${t('calls.filter-user')}: ${who.label}`,\n clear: () => (user.value = 'all'),\n })\n }\n\n if (tool.value !== 'all') {\n chips.push({\n key: 'tool',\n label: `${t('calls.filter-tool')}: ${tool.value}`,\n clear: () => (tool.value = 'all'),\n })\n }\n\n const how = outcomeOptions.value.find((one) => one.value === outcome.value)\n if (outcome.value !== 'any' && how) {\n chips.push({\n key: 'outcome',\n label: `${t('calls.filter-outcome')}: ${how.label}`,\n clear: () => (outcome.value = 'any'),\n })\n }\n\n return chips\n})\n\nfunction clearFilters(): void {\n user.value = 'all'\n tool.value = 'all'\n outcome.value = 'any'\n}\n\n/*\n * The date and the outcome are the two a reader scans down; the rest give way as the screen\n * narrows, the client first — it is the same word on every row of one connection.\n */\nconst columns = computed<TableColumn<AgentCall>[]>(() => {\n if (asCards.value) return [{ key: 'card', label: '' }]\n\n return [\n { key: 'at', label: t('calls.when') },\n { key: 'user', label: t('calls.who'), hideBelow: 640 },\n { key: 'tool', label: t('calls.tool') },\n { key: 'outcome', label: t('calls.outcome') },\n { key: 'duration_ms', label: t('calls.took'), align: 'right', hideBelow: 820 },\n { key: 'client', label: t('calls.client'), hideBelow: 960 },\n ]\n})\n\nwatch([user, tool, outcome], () => load({ ...last, page: 1 }))\n\nasync function load(state: TableState): Promise<void> {\n last = state\n loading.value = true\n\n try {\n page.value = await api.calls({\n user: user.value === 'all' ? null : user.value,\n tool: tool.value === 'all' ? null : tool.value,\n outcome: outcome.value === 'any' ? null : outcome.value,\n page: state.page,\n per_page: state.perPage,\n })\n\n filters.value = page.value.filters\n } finally {\n loading.value = false\n }\n}\n\n/** A refusal is a refusal whether or not it was a dry run; a dry run that went through is one. */\nfunction outcomeOf(row: AgentCall): { type: 'success' | 'danger' | 'info'; label: string } {\n if (!row.ok) return { type: 'danger', label: t('calls.refused') }\n if (row.dry_run) return { type: 'info', label: t('calls.dry-run') }\n\n return { type: 'success', label: t('calls.answered') }\n}\n\n/** Only a row with something underneath gets the disclosure. */\nfunction hasDetails(row: AgentCall): boolean {\n return row.arguments !== null || row.error !== null\n}\n\ndefineExpose({ reload: () => load(last) })\n</script>\n\n<template>\n <div ref=\"root\" class=\"wx-call-list\">\n <wx-table\n :data=\"page\"\n :columns=\"columns\"\n row-key=\"id\"\n flush\n :loading=\"loading\"\n :clickable=\"false\"\n :expandable=\"!asCards\"\n :expandable-if=\"hasDetails\"\n :empty-text=\"t('calls.empty')\"\n :cards-below=\"CARDS\"\n :filters-count=\"applied.length\"\n :filters-label=\"panel('filters.title')\"\n @state-change=\"load\"\n >\n <template #filters>\n <wx-form-item :label=\"t('calls.filter-user')\">\n <wx-select v-model=\"user\" :options=\"userOptions\" size=\"sm\" />\n </wx-form-item>\n\n <wx-form-item :label=\"t('calls.filter-tool')\">\n <wx-select v-model=\"tool\" :options=\"toolOptions\" size=\"sm\" />\n </wx-form-item>\n\n <wx-form-item :label=\"t('calls.filter-outcome')\">\n <wx-select v-model=\"outcome\" :options=\"outcomeOptions\" size=\"sm\" />\n </wx-form-item>\n\n <wx-button v-if=\"applied.length > 0\" variant=\"text\" size=\"sm\" block @click=\"clearFilters\">\n <template #icon><wx-icon name=\"close\" /></template>\n {{ panel('filters.reset') }}\n </wx-button>\n </template>\n\n <template #applied>\n <wx-filter-chips :filters=\"applied\" />\n </template>\n\n <template #cell-at=\"{ row }\">\n <wx-date :value=\"row.at\" tone=\"default\" compact />\n </template>\n\n <template #cell-user=\"{ row }\">\n <wx-text :tone=\"row.user === null || row.user.name === null ? 'muted' : 'default'\">\n {{ nameOf(row.user) }}\n </wx-text>\n </template>\n\n <template #cell-tool=\"{ row }\">\n <wx-text mono size=\"sm\">{{ row.tool }}</wx-text>\n </template>\n\n <template #cell-outcome=\"{ row }\">\n <wx-badge :type=\"outcomeOf(row).type\" dot>{{ outcomeOf(row).label }}</wx-badge>\n </template>\n\n <template #cell-duration_ms=\"{ row }\">\n <wx-text size=\"sm\" tone=\"muted\">{{ t('calls.ms', { count: row.duration_ms }) }}</wx-text>\n </template>\n\n <template #cell-client=\"{ row }\">\n <wx-text v-if=\"row.client\" size=\"sm\">{{ row.client }}</wx-text>\n <wx-text v-else size=\"sm\" tone=\"muted\">—</wx-text>\n </template>\n\n <!-- What it was called with, and why it was refused: a block of text, not a column. -->\n <template #expanded=\"{ row }\">\n <div class=\"wx-call-list__details\">\n <wx-text v-if=\"row.error\" tone=\"danger\" size=\"sm\" class=\"wx-call-list__error\">\n <strong>{{ t('calls.error') }}:</strong> {{ row.error }}\n </wx-text>\n\n <pre v-if=\"row.arguments\" class=\"wx-call-list__arguments\">{{ row.arguments }}</pre>\n <wx-text v-else size=\"sm\" tone=\"muted\">{{ t('calls.no-arguments') }}</wx-text>\n </div>\n </template>\n\n <!--\n Narrow, a row is the tool and who ran it, with the outcome as a badge and the details\n underneath: there is no disclosure column on a card, so what would open is simply shown.\n -->\n <template #cell-card=\"{ row }\">\n <wx-entity-card variant=\"plain\" :title=\"row.tool\" :subtitle=\"nameOf(row.user)\">\n <!-- Not the first letter of the tool's name, which is what the card draws for a\n title with no picture: a call is not a thing with a face. -->\n <template #media>\n <span class=\"wx-call-list__glyph\" aria-hidden=\"true\"><wx-icon name=\"sliders\" /></span>\n </template>\n\n <template #meta>\n <wx-badge :type=\"outcomeOf(row).type\" dot size=\"sm\">{{\n outcomeOf(row).label\n }}</wx-badge>\n <wx-date :value=\"row.at\" compact />\n <wx-text v-if=\"row.client\" size=\"xs\" tone=\"muted\">{{ row.client }}</wx-text>\n </template>\n\n <wx-space\n v-if=\"hasDetails(row)\"\n direction=\"vertical\"\n size=\"xs\"\n class=\"wx-call-list__card-details\"\n >\n <wx-text v-if=\"row.error\" tone=\"danger\" size=\"sm\">{{ row.error }}</wx-text>\n <pre v-if=\"row.arguments\" class=\"wx-call-list__arguments\">{{ row.arguments }}</pre>\n </wx-space>\n </wx-entity-card>\n </template>\n </wx-table>\n </div>\n</template>\n\n<style>\n.wx-call-list {\n min-width: 0;\n}\n\n.wx-call-list__details {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-8);\n padding: var(--wx-space-8) var(--wx-space-16);\n}\n\n.wx-call-list__card-details {\n margin-top: var(--wx-space-8);\n}\n\n.wx-call-list__glyph {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n color: var(--wx-text-muted);\n background: var(--wx-bg-subtle);\n}\n\n.wx-call-list__arguments {\n margin: 0;\n padding: var(--wx-space-8) var(--wx-space-12);\n max-height: 320px;\n overflow: auto;\n border-radius: var(--wx-radius-sm);\n background: var(--wx-bg-subtle);\n color: var(--wx-text-default);\n font-family: var(--wx-font-family-mono);\n font-size: var(--wx-font-size-xs);\n line-height: var(--wx-font-line-height-relaxed);\n white-space: pre-wrap;\n word-break: break-word;\n}\n</style>\n","import type { AdminContext } from '@webx-ui/module-admin'\nimport type { Connection, ConnectionScope, Connections } from './types'\n\nexport interface ConnectionsApi {\n /** The agents connected — this person's, or everybody's for whoever may see them. */\n list(scope?: ConnectionScope): Promise<Connections>\n /** End one. The row survives; the tokens behind it do not. */\n disconnect(id: number): Promise<Connection>\n}\n\n/**\n * The two calls behind the connections list.\n *\n * Separate from the administrators API because the audiences are: everybody signed in reads\n * their own connections and ends them, and nothing about that is managing other people's\n * accounts. The server decides which list it hands back — the scope here is a request, not a\n * claim.\n */\nexport function createConnectionsApi(admin: AdminContext): ConnectionsApi {\n const base = `${admin.apiPath}/auth/connections`\n\n return {\n list: (scope = 'mine') =>\n admin.http\n .get<{\n data: Connection[]\n meta: { scope: ConnectionScope; can_see_everybody: boolean }\n }>(base, { query: { all: scope === 'all' ? 1 : undefined } })\n .then((body) => ({\n data: body.data,\n scope: body.meta.scope,\n canSeeEverybody: body.meta.can_see_everybody,\n })),\n\n disconnect: (id) =>\n admin.http.delete<{ data: Connection }>(`${base}/${id}`).then((body) => body.data),\n }\n}\n","<script setup lang=\"ts\">\nimport { computed, ref, useTemplateRef, watch } from 'vue'\nimport { useAdmin, useErrorText, useTranslate, WxDate } from '@webx-ui/module-admin'\nimport {\n confirm,\n toast,\n WxBadge,\n WxButton,\n WxEntityCard,\n WxIcon,\n WxSpace,\n WxTable,\n WxText,\n useElementWidth,\n type TableColumn,\n} from '@webx-ui/core'\nimport { createConnectionsApi } from './connections'\nimport { useAuthMessages } from './i18n'\nimport type { Connection, ConnectionScope } from './types'\n\n/**\n * The agents somebody let in, and the one button that ends one.\n *\n * A row is who is connected, on what terms, and when it was last heard from. The terms are\n * the part worth reading: \"read only\" is not a scope the client asked for, it is what the\n * person chose on the consent screen, and it outranks their own permissions.\n *\n * Ending a connection is not paged, sorted or searched, and the list is not either. A person\n * has one agent, or three; a table of them that needed a search would mean something else had\n * gone wrong.\n *\n * A connection that was ended stays in the list, greyed, because it is what the call log\n * points at — the rows an agent left behind outlive the connection that made them.\n */\nconst props = withDefaults(\n defineProps<{\n /** Whose connections: this person's, or everybody's (`admins.manage`). */\n scope?: ConnectionScope\n }>(),\n { scope: 'mine' },\n)\n\n/** Where a row stops being a row. */\nconst CARDS = 560\n\nconst admin = useAdmin()\nconst api = createConnectionsApi(admin)\nuseAuthMessages()\n\nconst t = useTranslate('webx-auth')\n/* Not the server's `message`: the panel says how a request failed in its own words. */\nconst message = useErrorText()\n\nconst root = useTemplateRef<HTMLElement>('root')\nconst width = useElementWidth(root)\nconst asCards = computed(() => width.value > 0 && width.value < CARDS)\n\nconst rows = ref<Connection[]>([])\nconst loading = ref(false)\n/** Which one is being ended, so its own button is the one that spins. */\nconst ending = ref<number | null>(null)\n\nconst columns = computed<TableColumn<Connection>[]>(() => {\n if (asCards.value) return [{ key: 'card', label: '' }]\n\n return [\n { key: 'client', label: t('connections.client'), minWidth: 210 },\n ...(props.scope === 'all' ? [{ key: 'user', label: t('connections.who'), minWidth: 140 }] : []),\n { key: 'read_only', label: t('connections.allowed'), minWidth: 160 },\n { key: 'connected_at', label: t('connections.connected'), hideBelow: 860 },\n { key: 'last_used_at', label: t('connections.last-used'), minWidth: 130 },\n { key: 'end', label: '', align: 'right', width: 140 },\n ]\n})\n\n/** Who the agent acts as — a name, or the id alone for somebody deleted since. */\nfunction nameOf(row: Connection): string {\n return row.user.name ?? t('calls.deleted', { id: row.user.id })\n}\n\n/**\n * What the connection may do, in words.\n *\n * \"Everything you can do\" rather than a list of modules, for the reason the consent screen\n * gives: the answer is the reader's own permissions, and spelling them out here would be a\n * second copy of them, wrong the day somebody's role changes.\n */\nfunction allowed(row: Connection): string {\n if (row.read_only) return t('connections.read-only')\n\n if (props.scope !== 'all') return t('connections.full-you')\n\n // The given name: the column beside this one already says who, in full, and a badge\n // repeating it whole is what pushed the client's address onto three lines.\n const name = row.user.name?.split(' ')[0]\n\n return t('connections.full', { name: name || nameOf(row) })\n}\n\nasync function load(): Promise<void> {\n loading.value = true\n\n try {\n rows.value = (await api.list(props.scope)).data\n } catch (error) {\n toast.danger(message(error))\n } finally {\n loading.value = false\n }\n}\n\nasync function end(row: Connection): Promise<void> {\n const agreed = await confirm({\n title: t('connections.confirm-title', { client: row.client }),\n message: t('connections.confirm-text'),\n confirmText: t('connections.confirm'),\n cancelText: t('connections.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n ending.value = row.id\n\n try {\n const ended = await api.disconnect(row.id)\n\n // The row comes back rather than disappearing: what it did is still in the log.\n rows.value = rows.value.map((one) => (one.id === ended.id ? ended : one))\n toast.success(t('connections.done', { client: row.client }))\n } catch (error) {\n toast.danger(message(error))\n } finally {\n ending.value = null\n }\n}\n\nwatch(() => props.scope, load, { immediate: true })\n\ndefineExpose({ reload: load })\n</script>\n\n<template>\n <div ref=\"root\" class=\"wx-connection-list\">\n <wx-table\n :data=\"rows\"\n :columns=\"columns\"\n row-key=\"id\"\n flush\n :loading=\"loading\"\n :clickable=\"false\"\n :empty-text=\"scope === 'all' ? t('connections.empty-all') : t('connections.empty')\"\n :cards-below=\"CARDS\"\n :row-class=\"(row: Connection) => (row.revoked_at ? 'is-ended' : '')\"\n >\n <template #cell-client=\"{ row }\">\n <wx-space direction=\"vertical\" size=\"none\">\n <wx-text :tone=\"row.revoked_at ? 'muted' : 'default'\" weight=\"medium\">\n {{ row.client }}\n </wx-text>\n <!-- The name is the client's own choice; the host is the part that cannot lie. -->\n <wx-text size=\"xs\" tone=\"muted\">{{\n t('connections.returns-to', { host: row.host })\n }}</wx-text>\n </wx-space>\n </template>\n\n <template #cell-user=\"{ row }\">\n <wx-text :tone=\"row.user.name === null ? 'muted' : 'default'\">{{ nameOf(row) }}</wx-text>\n </template>\n\n <template #cell-read_only=\"{ row }\">\n <wx-badge v-if=\"row.revoked_at\" type=\"default\" dot>{{\n t('connections.disconnected')\n }}</wx-badge>\n <wx-badge v-else :type=\"row.read_only ? 'info' : 'success'\" dot>{{\n allowed(row)\n }}</wx-badge>\n </template>\n\n <template #cell-connected_at=\"{ row }\">\n <wx-date :value=\"row.connected_at\" tone=\"muted\" compact />\n </template>\n\n <template #cell-last_used_at=\"{ row }\">\n <wx-date v-if=\"row.last_used_at\" :value=\"row.last_used_at\" tone=\"default\" compact />\n <wx-text v-else size=\"sm\" tone=\"muted\">{{ t('connections.never-used') }}</wx-text>\n </template>\n\n <template #cell-end=\"{ row }\">\n <wx-button\n v-if=\"!row.revoked_at\"\n type=\"danger\"\n variant=\"text\"\n size=\"sm\"\n :loading=\"ending === row.id\"\n @click=\"end(row)\"\n >\n {{ t('connections.disconnect') }}\n </wx-button>\n </template>\n\n <!-- Narrow, the client is the title and everything else is a line under it. -->\n <template #cell-card=\"{ row }\">\n <wx-entity-card\n variant=\"plain\"\n :title=\"row.client\"\n :subtitle=\"t('connections.returns-to', { host: row.host })\"\n >\n <!-- Not the first letter of the client's name, which is what the card draws for a\n title with no picture: Claude, Cursor, Codex and ChatGPT are one letter between\n them, and three grey Cs in a column read as a broken photograph. -->\n <template #media>\n <span class=\"wx-connection-list__glyph\" aria-hidden=\"true\"\n ><wx-icon name=\"link\"\n /></span>\n </template>\n\n <template #meta>\n <wx-badge v-if=\"row.revoked_at\" type=\"default\" dot size=\"sm\">{{\n t('connections.disconnected')\n }}</wx-badge>\n <wx-badge v-else :type=\"row.read_only ? 'info' : 'success'\" dot size=\"sm\">{{\n allowed(row)\n }}</wx-badge>\n <wx-text v-if=\"scope === 'all'\" size=\"xs\" tone=\"muted\">{{ nameOf(row) }}</wx-text>\n <wx-date v-if=\"row.last_used_at\" :value=\"row.last_used_at\" compact />\n <wx-text v-else size=\"xs\" tone=\"muted\">{{ t('connections.never-used') }}</wx-text>\n </template>\n\n <template #actions>\n <wx-button\n v-if=\"!row.revoked_at\"\n type=\"danger\"\n variant=\"text\"\n size=\"sm\"\n :loading=\"ending === row.id\"\n @click=\"end(row)\"\n >\n {{ t('connections.disconnect') }}\n </wx-button>\n </template>\n </wx-entity-card>\n </template>\n </wx-table>\n </div>\n</template>\n\n<style>\n.wx-connection-list {\n min-width: 0;\n}\n\n.wx-connection-list__glyph {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n color: var(--wx-text-muted);\n background: var(--wx-bg-subtle);\n}\n\n/* Ended, and still here: the log points at it, so it is greyed rather than taken away.\n Both shapes of a row, because the table draws cards below its own threshold. */\n.wx-connection-list .wx-table__row.is-ended,\n.wx-connection-list .wx-table__card.is-ended {\n opacity: 0.6;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, ref, useTemplateRef, type Component } from 'vue'\nimport { useRoute, useRouter } from 'vue-router'\nimport {\n useAdmin,\n useErrorText,\n useTranslate,\n WxListScreen,\n type ScreenAction,\n} from '@webx-ui/module-admin'\nimport { confirm, createModal, toast, type TabItem, type TabValue } from '@webx-ui/core'\nimport AdminDialog from './AdminDialog.vue'\nimport AdminList from './AdminList.vue'\nimport CallList from './CallList.vue'\nimport ConnectionList from './ConnectionList.vue'\nimport { createAdminsApi } from './admins'\nimport type { AdminsView } from './module'\nimport { useAuthMessages } from './i18n'\nimport type { Admin } from './types'\n\n/**\n * The administrators section: the list, the form over it, the trail of what their agents did,\n * and the agents themselves.\n *\n * A dialog rather than a second route for the form. Editing somebody is half a dozen fields,\n * and a screen that takes over the page for that loses the list you were reading — which is\n * where you decide who to open next.\n *\n * The calls and the connections are further views of the same section rather than sections of\n * their own: \"who did what\" is a question about these people, whether a hand or a program was\n * at the other end. A route each rather than panels of one, because each has its own paging\n * and filters, and a view that quietly resets both when you come back to it is worse than a\n * second address. The calls are behind `admins.audit`, the permission the sign-in trail is\n * behind; everybody's connections are behind `admins.manage`, the one that is about other\n * people's accounts.\n *\n * The heading and `Add` used to live inside the card, which made this the one section of the\n * panel with no line of its own at the top. They stand outside it now, where every other list\n * keeps them (§19).\n */\nconst props = withDefaults(\n defineProps<{\n /** The section's own path, for the address of the second view. */\n base?: string\n /** Which view is open. */\n current?: AdminsView\n avatarField?: Component\n resolveAvatar?: (key: string) => Promise<string | null>\n }>(),\n { base: '/admins', current: 'admins', avatarField: undefined, resolveAvatar: undefined },\n)\n\nconst context = useAdmin()\nconst api = createAdminsApi(context)\nconst router = useRouter()\nconst route = useRoute()\nuseAuthMessages()\n\nconst t = useTranslate('webx-auth')\n/* Not the server's `message`: the panel says how a request failed in its own words (§13.3). */\nconst message = useErrorText()\n\nconst list = useTemplateRef<{ reload: () => void }>('list')\nconst editing = ref<Admin | null>(null)\n\nconst edit = createModal<Admin, { admin: Admin | null; avatarField?: Component }>(AdminDialog)\n\nconst canManage = context.can('admins.manage')\nconst canAudit = context.can('admins.audit')\n\n/** The section's name as the server translated it; the built-in English until it arrives. */\nconst title = computed(\n () =>\n context.state.manifest?.modules.find((module) => module.id === 'admins')?.title ??\n t('admins.title'),\n)\n\n/*\n * One view is no view: the strip is drawn only for somebody who can see a second one.\n *\n * The connections here are everybody's, which is `admins.manage` — the permission that is\n * already about other people's accounts. Your own live on the connect page, where anybody\n * signed in reaches them without a permission at all.\n */\nconst views = computed<TabItem[] | undefined>(() => {\n const items: TabItem[] = [{ value: 'admins', label: title.value }]\n\n if (canAudit) items.push({ value: 'calls', label: t('calls.title') })\n if (canManage) items.push({ value: 'connections', label: t('connections.title') })\n\n return items.length > 1 ? items : undefined\n})\n\nconst where = computed<TabValue>({\n get: () => props.current,\n set: (next) => {\n const path = next === 'admins' ? props.base : `${props.base}/${String(next)}`\n\n if (path !== route.path) void router.push(path)\n },\n})\n\n/* Short enough for a phone, where the name and the button share one line. `Add` belongs to\n the people, not to the log. */\nconst actions = computed<ScreenAction[]>(() =>\n canManage && props.current === 'admins'\n ? [\n {\n key: 'new',\n label: t('admins.new-short'),\n icon: 'plus',\n primary: true,\n run: () => void open(null),\n },\n ]\n : [],\n)\n\nasync function open(admin: Admin | null): Promise<void> {\n editing.value = admin\n\n const saved = await edit({ admin, avatarField: props.avatarField })\n\n if (saved) {\n list.value?.reload()\n }\n}\n\nasync function remove(admin: Admin): Promise<void> {\n const agreed = await confirm({\n title: t('admins.delete-title', { name: admin.name }),\n message: t('admins.delete-text'),\n confirmText: t('admins.delete'),\n cancelText: t('admins.cancel'),\n tone: 'danger',\n })\n\n if (!agreed) return\n\n try {\n await api.remove(admin.id)\n toast.success(t('admins.deleted'))\n list.value?.reload()\n } catch (error) {\n toast.danger(message(error))\n }\n}\n</script>\n\n<template>\n <wx-list-screen v-model:view=\"where\" :title=\"title\" :views=\"views\" :actions=\"actions\">\n <call-list v-if=\"current === 'calls'\" />\n <connection-list v-else-if=\"current === 'connections'\" scope=\"all\" />\n <admin-list\n v-else\n ref=\"list\"\n :removable=\"canManage\"\n :resolve-avatar=\"props.resolveAvatar\"\n @open=\"open\"\n @remove=\"remove\"\n />\n </wx-list-screen>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from 'vue'\nimport { useAdmin, useTranslate, WxListScreen } from '@webx-ui/module-admin'\nimport {\n toast,\n WxAlert,\n WxButton,\n WxCard,\n WxHeading,\n WxIcon,\n WxTab,\n WxTabs,\n WxText,\n} from '@webx-ui/core'\nimport ConnectionList from './ConnectionList.vue'\nimport { useAuthMessages } from './i18n'\n\n/**\n * How a person connects their own agent, on one page.\n *\n * It exists because the alternative is explaining it — over a call, to somebody who has never\n * heard of MCP and has no reason to. So the page is written for that call: the address large\n * enough to read out, a button that copies it, and three steps for each client, in the order\n * they appear on that client's screen.\n *\n * The address carries no secret, which is the whole point of the OAuth path (§2.3 of the\n * spec): it can be printed in a letter, read aloud, or left on this page for anybody signed\n * in. What a person has to do themselves is sign in, and that happens on our own screen.\n *\n * Under it are their own connections, because the first thing somebody does after connecting\n * is look for proof that it worked — and the second, weeks later, is come back to end it.\n */\nconst admin = useAdmin()\nuseAuthMessages()\n\nconst t = useTranslate('webx-auth')\n\nconst copied = ref(false)\nlet copiedTimer: ReturnType<typeof setTimeout> | undefined\n\n/**\n * The address agents connect to, as the server printed it — absolute, because it is pasted\n * into a program on another machine.\n */\nconst url = computed<string>(() => {\n const meta = admin.state.manifest?.modules.find((module) => module.id === 'connect')?.meta\n\n return typeof meta?.url === 'string' ? meta.url : ''\n})\n\n/**\n * What the server is called in the client's own list of servers.\n *\n * The site's own name, taken off the address: somebody with three sites connected reads this\n * word and nothing else, and \"webx\" three times over would be three servers they cannot tell\n * apart.\n */\nconst name = computed<string>(() => {\n try {\n return new URL(url.value).hostname.split('.')[0] || 'webx'\n } catch {\n return 'webx'\n }\n})\n\nconst command = computed(() => `claude mcp add --transport http ${name.value} ${url.value}`)\n\nconst toml = computed(() => `[mcp_servers.${name.value}]\\nurl = \"${url.value}\"`)\n\n/**\n * The links Cursor and VS Code take a whole server from. Both are documented shapes, and both\n * carry only the address — there is nothing secret to leak into a link.\n */\nconst cursorLink = computed(() => {\n const config = encode(JSON.stringify({ url: url.value }))\n\n return `cursor://anysphere.cursor-deeplink/mcp/install?name=${encodeURIComponent(name.value)}&config=${config}`\n})\n\nconst vscodeLink = computed(() => {\n const config = JSON.stringify({ name: name.value, type: 'http', url: url.value })\n\n return `vscode:mcp/install?${encodeURIComponent(config)}`\n})\n\n/** Base64 of a UTF-8 string: `btoa` alone throws on anything above ASCII. */\nfunction encode(value: string): string {\n const bytes = new TextEncoder().encode(value)\n let binary = ''\n\n for (const byte of bytes) binary += String.fromCharCode(byte)\n\n return encodeURIComponent(btoa(binary))\n}\n\n/**\n * Copying, with the old way behind it.\n *\n * The clipboard API is missing outside a secure context and refused inside some frames, and\n * this page is exactly the page where \"it did not work and said nothing\" is worst: the\n * address is the one thing the person came here for.\n */\nasync function copy(value: string): Promise<void> {\n try {\n if (navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(value)\n } else if (!byHand(value)) {\n throw new Error('no clipboard')\n }\n\n copied.value = true\n clearTimeout(copiedTimer)\n copiedTimer = setTimeout(() => (copied.value = false), 2000)\n } catch {\n if (byHand(value)) return\n\n toast.danger(t('connect.copy-failed'))\n }\n}\n\nfunction byHand(value: string): boolean {\n const field = document.createElement('textarea')\n\n field.value = value\n field.setAttribute('readonly', '')\n field.style.position = 'fixed'\n field.style.opacity = '0'\n\n document.body.append(field)\n field.select()\n\n try {\n return document.execCommand('copy')\n } catch {\n return false\n } finally {\n field.remove()\n }\n}\n</script>\n\n<template>\n <wx-list-screen :title=\"t('connect.title')\" :card=\"false\">\n <div class=\"wx-connect\">\n <wx-text class=\"wx-connect__lead\">{{ t('connect.lead') }}</wx-text>\n\n <!-- The address, large. Everything else on this page is about what to do with it. -->\n <wx-card bordered>\n <wx-text size=\"xs\" tone=\"muted\" class=\"wx-connect__label\">{{\n t('connect.address')\n }}</wx-text>\n\n <div class=\"wx-connect__row\">\n <code class=\"wx-connect__url\">{{ url }}</code>\n\n <wx-button type=\"primary\" :disabled=\"url === ''\" @click=\"copy(url)\">\n <template #icon><wx-icon :name=\"copied ? 'check' : 'copy'\" /></template>\n {{ copied ? t('connect.copied') : t('connect.copy') }}\n </wx-button>\n </div>\n </wx-card>\n\n <section class=\"wx-connect__section\">\n <wx-heading :level=\"2\" size=\"sm\">{{ t('connect.how') }}</wx-heading>\n\n <wx-tabs variant=\"line\" :aria-label=\"t('connect.how')\">\n <wx-tab value=\"claude\" :label=\"t('connect.claude-title')\">\n <ol class=\"wx-connect__steps\">\n <li>{{ t('connect.claude-1') }}</li>\n <li>{{ t('connect.claude-2') }}</li>\n <li>{{ t('connect.claude-3') }}</li>\n </ol>\n </wx-tab>\n\n <wx-tab value=\"chatgpt\" :label=\"t('connect.chatgpt-title')\">\n <ol class=\"wx-connect__steps\">\n <li>{{ t('connect.chatgpt-1') }}</li>\n <li>{{ t('connect.chatgpt-2') }}</li>\n <li>{{ t('connect.chatgpt-3') }}</li>\n </ol>\n </wx-tab>\n\n <wx-tab value=\"claude-code\" :label=\"t('connect.claude-code-title')\">\n <wx-text size=\"sm\">{{ t('connect.claude-code-hint') }}</wx-text>\n\n <div class=\"wx-connect__snippet\">\n <pre>{{ command }}</pre>\n <wx-button variant=\"text\" size=\"sm\" @click=\"copy(command)\">\n <template #icon><wx-icon name=\"copy\" /></template>\n {{ t('connect.copy-line') }}\n </wx-button>\n </div>\n </wx-tab>\n\n <wx-tab value=\"codex\" :label=\"t('connect.codex-title')\">\n <wx-text size=\"sm\">{{ t('connect.codex-hint') }}</wx-text>\n\n <div class=\"wx-connect__snippet\">\n <pre>{{ toml }}</pre>\n <wx-button variant=\"text\" size=\"sm\" @click=\"copy(toml)\">\n <template #icon><wx-icon name=\"copy\" /></template>\n {{ t('connect.copy-line') }}\n </wx-button>\n </div>\n </wx-tab>\n </wx-tabs>\n </section>\n\n <section class=\"wx-connect__section\">\n <wx-heading :level=\"2\" size=\"sm\">{{ t('connect.one-click') }}</wx-heading>\n <wx-text size=\"sm\" tone=\"muted\">{{ t('connect.one-click-hint') }}</wx-text>\n\n <div class=\"wx-connect__links\">\n <wx-button\n :href=\"cursorLink\"\n variant=\"outline\"\n :disabled=\"url === ''\"\n class=\"wx-connect__link\"\n >\n <template #icon><wx-icon name=\"external-link\" /></template>\n {{ t('connect.install-in', { client: t('connect.cursor-title') }) }}\n </wx-button>\n\n <wx-button\n :href=\"vscodeLink\"\n variant=\"outline\"\n :disabled=\"url === ''\"\n class=\"wx-connect__link\"\n >\n <template #icon><wx-icon name=\"external-link\" /></template>\n {{ t('connect.install-in', { client: t('connect.vscode-title') }) }}\n </wx-button>\n </div>\n </section>\n\n <wx-alert type=\"info\" :title=\"t('connect.next')\">\n <ol class=\"wx-connect__steps\">\n <li>{{ t('connect.next-1') }}</li>\n <li>{{ t('connect.next-2') }}</li>\n <li>{{ t('connect.next-3') }}</li>\n </ol>\n </wx-alert>\n\n <section class=\"wx-connect__section\">\n <wx-heading :level=\"2\" size=\"sm\">{{ t('connect.mine') }}</wx-heading>\n\n <wx-card padding=\"none\">\n <connection-list scope=\"mine\" />\n </wx-card>\n </section>\n </div>\n </wx-list-screen>\n</template>\n\n<style scoped>\n.wx-connect {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-24);\n min-width: 0;\n}\n\n.wx-connect__lead {\n max-width: 68ch;\n}\n\n.wx-connect__label {\n display: block;\n margin-bottom: var(--wx-space-8);\n}\n\n/* The address and its button on one line, and under each other as soon as they do not fit:\n the address is long, and shrinking it is what would make it unreadable. */\n.wx-connect__row {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--wx-space-12);\n}\n\n.wx-connect__url {\n flex: 1 1 260px;\n min-width: 0;\n padding: var(--wx-space-10) var(--wx-space-12);\n border-radius: var(--wx-radius-sm);\n background: var(--wx-bg-subtle);\n color: var(--wx-text-default);\n font-family: var(--wx-font-family-mono);\n font-size: var(--wx-font-size-md);\n /* Broken anywhere rather than pushed off the card: it is read, not clicked. */\n word-break: break-all;\n}\n\n.wx-connect__section {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-12);\n min-width: 0;\n}\n\n.wx-connect__steps {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-8);\n margin: 0;\n padding-left: var(--wx-space-24);\n color: var(--wx-text-default);\n font-size: var(--wx-font-size-sm);\n line-height: var(--wx-font-line-height-relaxed);\n}\n\n.wx-connect__snippet {\n display: flex;\n flex-wrap: wrap;\n align-items: flex-start;\n justify-content: space-between;\n gap: var(--wx-space-8);\n padding: var(--wx-space-12);\n border-radius: var(--wx-radius-sm);\n background: var(--wx-bg-subtle);\n}\n\n.wx-connect__snippet pre {\n flex: 1 1 240px;\n margin: 0;\n min-width: 0;\n color: var(--wx-text-default);\n font-family: var(--wx-font-family-mono);\n font-size: var(--wx-font-size-xs);\n line-height: var(--wx-font-line-height-relaxed);\n white-space: pre-wrap;\n word-break: break-all;\n}\n\n.wx-connect__links {\n display: flex;\n flex-wrap: wrap;\n gap: var(--wx-space-12);\n}\n</style>\n","import { h, type Component } from 'vue'\nimport type { AdminModule } from '@webx-ui/module-admin'\nimport AdminsPage from './AdminsPage.vue'\nimport ConnectPage from './ConnectPage.vue'\n\nexport interface AdminsOptions {\n /** Where the section lives inside the panel. */\n path?: string\n /**\n * The field the photograph is picked with — `WxMediaField` in a panel that has the library.\n *\n * Handed in rather than imported: this package does not depend on the media one, and a panel\n * without a library still edits everything else about a person.\n */\n avatarField?: Component\n /** Turns an avatar key into an address. Omitted, rows and forms show initials. */\n resolveAvatar?: (key: string) => Promise<string | null>\n}\n\n/** Which of the three views of the administrators section a route opens. */\nexport type AdminsView = 'admins' | 'calls' | 'connections'\n\n/**\n * The administrators, as a section of the panel.\n *\n * The id matches the module the server reports, which is what makes the entry appear in the\n * navigation: a section shows up when both halves are installed.\n */\nexport function admins(options: AdminsOptions = {}): AdminModule {\n const path = options.path ?? '/admins'\n\n const page = (current: AdminsView) => ({\n render: () =>\n h(AdminsPage, {\n base: path,\n current,\n avatarField: options.avatarField,\n resolveAvatar: options.resolveAvatar,\n }),\n })\n\n return {\n id: 'admins',\n path,\n routes: [\n { path, name: 'webx.admins', component: page('admins') },\n // What their agents did, and which agents they let in: two more views of the same\n // section, each with an address of its own so that its filters survive a trip to the\n // people and back.\n { path: `${path}/calls`, name: 'webx.admins.calls', component: page('calls') },\n {\n path: `${path}/connections`,\n name: 'webx.admins.connections',\n component: page('connections'),\n },\n ],\n }\n}\n\nexport interface ConnectOptions {\n /** Where the page lives inside the panel. */\n path?: string\n}\n\n/**\n * The page that tells a person how to connect their own agent.\n *\n * A section of its own rather than a corner of the profile dialog, because it is the page\n * somebody is sent a link to — a client who has never opened this panel, on a call, with\n * somebody reading the steps out. The server reports it only where there is a door to connect\n * to, so a panel with the agent server switched off simply has no such entry.\n *\n * It carries no permission: whoever got into the panel may connect an agent, and the agent\n * will not be able to do anything they cannot.\n */\nexport function connect(options: ConnectOptions = {}): AdminModule {\n const path = options.path ?? '/connect'\n\n return {\n id: 'connect',\n path,\n routes: [{ path, name: 'webx.connect', component: ConnectPage }],\n }\n}\n","<script setup lang=\"ts\">\nimport { ref, useTemplateRef } from 'vue'\nimport { useTranslate } from '@webx-ui/module-admin'\nimport { useModal, WxButton, WxDialog, WxSpace, WxText } from '@webx-ui/core'\nimport AdminList from './AdminList.vue'\nimport { useAuthMessages } from './i18n'\nimport type { Admin } from './types'\n\n/**\n * The list of administrators as a dialog, resolving with whoever was chosen.\n *\n * The same list as the screen — one component, so that searching and filtering behave the same\n * wherever these people are being looked at.\n */\nconst props = withDefaults(\n defineProps<{\n multiple?: boolean\n title?: string\n resolveAvatar?: (key: string) => Promise<string | null>\n }>(),\n { multiple: false, title: undefined, resolveAvatar: undefined },\n)\n\nconst { open, resolve, dismiss } = useModal<Admin[]>()\n\nuseAuthMessages()\n\nconst t = useTranslate('webx-auth')\n\nconst list = useTemplateRef<{ chosen: () => Admin[] }>('list')\nconst count = ref(0)\n\n/** One is chosen by clicking the row; several are ticked and then confirmed. */\nfunction chosen(admins: Admin[]): void {\n resolve(admins)\n}\n\nfunction track(admins: Admin[]): void {\n count.value = admins.length\n}\n</script>\n\n<template>\n <wx-dialog\n v-model:open=\"open\"\n :title=\"title ?? t(multiple ? 'admins.select-many' : 'admins.select-one')\"\n :width=\"880\"\n >\n <admin-list\n ref=\"list\"\n picking\n :multiple=\"multiple\"\n :resolve-avatar=\"props.resolveAvatar\"\n @chosen=\"chosen\"\n @selection=\"track\"\n />\n\n <template v-if=\"multiple\" #footer>\n <wx-space size=\"sm\" align=\"center\">\n <wx-text size=\"sm\" tone=\"muted\">{{ t('admins.chosen', { count }) }}</wx-text>\n <wx-button variant=\"outline\" @click=\"dismiss()\">{{ t('admins.cancel') }}</wx-button>\n <wx-button type=\"primary\" :disabled=\"count === 0\" @click=\"resolve(list?.chosen() ?? [])\">\n {{ t('admins.save') }}\n </wx-button>\n </wx-space>\n </template>\n </wx-dialog>\n</template>\n","import { createModal } from '@webx-ui/core'\nimport AdminPicker from './AdminPicker.vue'\nimport type { Admin } from './types'\n\nexport interface AdminPickerOptions {\n /** Heading of the dialog, when the default is not specific enough. */\n title?: string\n /** Turns an avatar key into an address. Omitted, rows show initials. */\n resolveAvatar?: (key: string) => Promise<string | null>\n}\n\nconst pick = createModal<Admin[], AdminPickerOptions & { multiple?: boolean }>(AdminPicker)\n\n/**\n * One administrator, chosen from a dialog.\n *\n * ```ts\n * const owner = await selectAdmin()\n * if (owner) task.assignedTo = owner.id\n * ```\n *\n * Resolves with `undefined` when the dialog was closed without choosing. Needs `admins.view`,\n * which is deliberately separate from managing them: assigning work to somebody is not the\n * same as being allowed to edit their account.\n */\nexport async function selectAdmin(options: AdminPickerOptions = {}): Promise<Admin | undefined> {\n const chosen = await pick({ ...options, multiple: false })\n\n return chosen?.[0]\n}\n\n/**\n * Several administrators, ticked and confirmed.\n *\n * Resolves with `undefined` when the dialog was closed, and never with an empty list — the\n * confirming button is disabled until somebody is ticked, so \"none\" can only mean \"cancelled\".\n */\nexport function selectAdmins(options: AdminPickerOptions = {}): Promise<Admin[] | undefined> {\n return pick({ ...options, multiple: true })\n}\n","<script setup lang=\"ts\">\nimport { ref, type Component } from 'vue'\nimport { useAdmin, useErrorText, useTranslate } from '@webx-ui/module-admin'\nimport { toast, useModal, WxButton, WxDialog, WxFormItem, WxInput } from '@webx-ui/core'\nimport { useAuth } from './session'\nimport { useAuthMessages } from './i18n'\n\n/**\n * Somebody editing themselves.\n *\n * Not `AdminDialog` with the fields taken away. What a person may change about themselves and\n * what an administrator may change about them are different lists, and the difference is the\n * whole point: roles, super, active and the sign-in address are not here, which is what makes\n * this reachable by everybody rather than by whoever holds `admins.manage`.\n *\n * The address is shown and not editable: changing where somebody signs in has to be proved at\n * the new address first, and nothing here can do that yet.\n */\nconst props = withDefaults(\n defineProps<{\n /** The field to pick the photograph with — `WxMediaField` in a panel with the library. */\n avatarField?: Component\n }>(),\n { avatarField: undefined },\n)\n\nconst { resolve, dismiss, open } = useModal<true>()\n\nconst context = useAdmin()\nconst auth = useAuth()\nuseAuthMessages()\n\nconst t = useTranslate('webx-auth')\n/* Not the server's `message`: the panel says how a request failed in its own words (§13.3). */\nconst message = useErrorText()\n\nconst saving = ref(false)\nconst errors = ref<Record<string, string[]>>({})\n\nconst form = ref({\n name: context.state.user?.name ?? '',\n avatar: (context.state.user?.avatar as string | null | undefined) ?? null,\n password: '',\n current_password: '',\n})\n\n/** The field hands back `{ path, … }`; what is stored is the key. */\nconst photo = ref<{ path: string } | null>(form.value.avatar ? { path: form.value.avatar } : null)\n\nfunction errorOf(field: string): string | undefined {\n return errors.value[field]?.[0]\n}\n\nasync function save(): Promise<void> {\n saving.value = true\n errors.value = {}\n\n try {\n await auth.updateProfile({\n name: form.value.name,\n avatar: photo.value?.path ?? null,\n // Blank is left out entirely rather than sent as an empty string: the server reads a\n // filled password as \"change it\", and an empty one would be a change to nothing.\n ...(form.value.password\n ? { password: form.value.password, current_password: form.value.current_password }\n : {}),\n })\n\n toast.success(t('profile.saved'))\n resolve(true)\n } catch (error) {\n const body = (error as { body?: { errors?: Record<string, string[]> } }).body\n\n if (body?.errors) {\n errors.value = body.errors\n } else {\n toast.danger(message(error))\n }\n } finally {\n saving.value = false\n }\n}\n</script>\n\n<template>\n <wx-dialog v-model:open=\"open\" :title=\"t('profile.title')\" :width=\"560\">\n <!-- The host is what the query below measures: a container query on an element's own\n class resolves against its nearest ancestor container, never against itself. -->\n <div class=\"wx-profile-host\">\n <div class=\"wx-profile\">\n <div v-if=\"props.avatarField\" class=\"wx-profile__aside\">\n <component\n :is=\"props.avatarField\"\n v-model=\"photo\"\n :label=\"t('profile.avatar')\"\n aspect=\"1/1\"\n :captions=\"false\"\n />\n </div>\n\n <div class=\"wx-profile__fields\">\n <wx-form-item :label=\"t('profile.name')\" :error=\"errorOf('name')\">\n <wx-input v-model=\"form.name\" name=\"name\" />\n </wx-form-item>\n\n <!-- Shown, not offered: see the note on the component. -->\n <wx-form-item :label=\"t('admins.email')\">\n <wx-input :model-value=\"context.state.user?.email ?? ''\" readonly />\n </wx-form-item>\n\n <wx-form-item\n :label=\"t('profile.password')\"\n :help=\"t('profile.password-hint')\"\n :error=\"errorOf('password')\"\n >\n <wx-input v-model=\"form.password\" type=\"password\" autocomplete=\"new-password\" />\n </wx-form-item>\n\n <wx-form-item\n v-if=\"form.password\"\n :label=\"t('profile.current-password')\"\n :help=\"t('profile.current-password-hint')\"\n :error=\"errorOf('current_password')\"\n >\n <wx-input\n v-model=\"form.current_password\"\n type=\"password\"\n autocomplete=\"current-password\"\n />\n </wx-form-item>\n </div>\n </div>\n </div>\n\n <template #footer>\n <wx-button @click=\"dismiss()\">{{ t('profile.cancel') }}</wx-button>\n <wx-button type=\"primary\" :loading=\"saving\" @click=\"save\">{{ t('profile.save') }}</wx-button>\n </template>\n </wx-dialog>\n</template>\n\n<style scoped>\n/*\n * The photograph beside the fields, the same shape as the administrators form — and on a\n * narrow dialog it goes back on top, which is the only arrangement that fits a phone.\n */\n.wx-profile-host {\n container-type: inline-size;\n}\n\n.wx-profile {\n display: flex;\n gap: var(--wx-space-16);\n}\n\n.wx-profile__aside {\n flex: 0 0 180px;\n}\n\n.wx-profile__fields {\n display: flex;\n flex-direction: column;\n gap: var(--wx-space-12);\n flex: 1 1 auto;\n min-width: 0;\n}\n\n@container (max-width: 480px) {\n .wx-profile {\n flex-direction: column;\n }\n\n .wx-profile__aside {\n flex: none;\n }\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, inject, ref, watch, type Component } from 'vue'\nimport {\n useAdmin,\n useI18n,\n useTheme,\n useTranslate,\n type ThemePreference,\n} from '@webx-ui/module-admin'\nimport { createModal, WxThemeSwitch } from '@webx-ui/core'\nimport ProfileDialog from './ProfileDialog.vue'\nimport { avatarFieldKey, avatarResolverKey, useAuth, type AvatarResolver } from './session'\n\n/**\n * The corner of the header: who this is, which language they read the panel in, and the way\n * out.\n *\n * The language picker lives here rather than in a settings screen because it is a property of\n * the person, not of the site — and because somebody who has landed in a language they cannot\n * read needs it within reach, not three clicks into a section they cannot navigate.\n */\nconst props = withDefaults(\n defineProps<{\n signOutLabel?: string\n /** Overrides the resolver `auth()` was given. Without either, initials. */\n resolveAvatar?: AvatarResolver\n /**\n * The field to pick a photograph with, handed down from the plugin. Without one the\n * profile still opens — a panel with no library edits everything else about a person.\n */\n avatarField?: Component\n /**\n * There is room beside the face for a name. The shell says so: the foot of an open\n * sidebar and the foot of the drawer have it, the icon rail and a phone's bar do not.\n */\n expanded?: boolean\n }>(),\n { signOutLabel: undefined, resolveAvatar: undefined, avatarField: undefined, expanded: false },\n)\n\nconst admin = useAdmin()\nconst auth = useAuth()\nconst i18n = useI18n()\nconst theme = useTheme()\nconst t = useTranslate('webx-auth')\n// The switch is the panel's own control, so its three words are the panel's own too.\nconst panel = i18n.scope('webx-admin')\n\nconst user = computed(() => admin.state.user)\n\n// One language is not a choice, and a menu that offers it is noise.\nconst languages = computed(() =>\n i18n.state.panelLocales.length > 1 ? i18n.state.panelLocales : [],\n)\n\nconst provided = inject(avatarResolverKey, null)\nconst providedField = inject(avatarFieldKey, null)\nconst avatarUrl = ref<string | undefined>(undefined)\n\n/*\n * The server sends the key the photograph is stored under, not a picture: turning one into\n * the other is the library's job, and the panel says which library. Resolved again whenever\n * the key changes — somebody editing their own photograph is told about it by the row, and\n * the corner should agree with the row.\n */\nwatch(\n () => (typeof user.value?.avatar === 'string' ? user.value.avatar : null),\n async (key) => {\n const resolve = props.resolveAvatar ?? provided\n\n if (key === null || key === '' || resolve === null) {\n avatarUrl.value = undefined\n\n return\n }\n\n const url = await resolve(key)\n\n // Only the answer to the key still on screen: a slow answer to a previous one is stale.\n if (user.value?.avatar === key) {\n avatarUrl.value = url ?? undefined\n }\n },\n { immediate: true },\n)\n\n/*\n * Opened from a menu, which is itself a dismissable layer: Reka builds the dialog's own layer\n * in the middle of handling this very click, then sees the click end outside it and treats\n * that as a click away. So it opens on the next turn of the loop instead.\n */\nconst editProfile = createModal<true, { avatarField?: Component }>(ProfileDialog)\n\nfunction openProfile(): void {\n setTimeout(\n () => void editProfile({ avatarField: props.avatarField ?? providedField ?? undefined }),\n 0,\n )\n}\n\n/*\n * The screen changes first and the account catches up: repainting is local and instant, and\n * a control that waits for a round trip before it moves feels broken. If the write fails the\n * panel is still the colour that was asked for, and this browser remembers it — only the\n * other machines have not heard yet, and the next change tells them.\n */\nconst preference = computed<ThemePreference>({\n get: () => theme.state.preference,\n set: (value) => {\n theme.set(value)\n void auth.setTheme(value)\n },\n})\n\nasync function choose(code: string): Promise<void> {\n if (code === i18n.state.locale) {\n return\n }\n\n await auth.setLocale(code)\n}\n</script>\n\n<template>\n <wx-dropdown v-if=\"user !== null\">\n <!--\n The avatar is the button. Wrapped in an action it was a small picture inside a box the\n same size as every icon beside it, which reads as one more tool rather than as who is\n signed in.\n -->\n <template #trigger>\n <button\n type=\"button\"\n class=\"wx-user-menu__trigger\"\n :class=\"{ 'wx-user-menu__trigger--named': expanded }\"\n :title=\"user.name\"\n >\n <wx-avatar :name=\"user.name\" :src=\"avatarUrl\" size=\"lg\" />\n\n <!--\n Initials are a way of telling two people apart, not of saying who somebody is. Where\n the corner is wide enough, it says it.\n -->\n <span v-if=\"expanded\" class=\"wx-user-menu__name\">{{ user.name }}</span>\n </button>\n </template>\n\n <wx-dropdown-item disabled>\n <wx-text size=\"sm\">{{ user.email }}</wx-text>\n </wx-dropdown-item>\n\n <wx-divider spacing=\"sm\" />\n\n <wx-dropdown-item icon=\"user\" @click=\"openProfile\">\n {{ t('profile.menu') }}\n </wx-dropdown-item>\n\n <wx-divider spacing=\"sm\" />\n\n <wx-dropdown-item disabled>\n <wx-text size=\"sm\" tone=\"muted\">{{ panel('theme.label') }}</wx-text>\n </wx-dropdown-item>\n\n <!--\n The click is stopped here: every click inside the panel closes the menu, and a switch\n that shuts the thing it lives in can only ever be thrown once at a time.\n -->\n <div class=\"wx-user-menu__theme\" @click.stop>\n <wx-theme-switch\n v-model=\"preference\"\n size=\"sm\"\n block\n :aria-label=\"panel('theme.label')\"\n :light-label=\"panel('theme.light')\"\n :dark-label=\"panel('theme.dark')\"\n :system-label=\"panel('theme.system')\"\n />\n </div>\n\n <template v-if=\"languages.length > 0\">\n <wx-divider spacing=\"sm\" />\n\n <wx-dropdown-item disabled>\n <wx-text size=\"sm\" tone=\"muted\">{{ t('menu.language') }}</wx-text>\n </wx-dropdown-item>\n\n <wx-dropdown-item\n v-for=\"language in languages\"\n :key=\"language.code\"\n :icon=\"language.code === i18n.state.locale ? 'check' : undefined\"\n @click=\"choose(language.code)\"\n >\n {{ language.nativeName }}\n </wx-dropdown-item>\n\n <wx-divider spacing=\"sm\" />\n </template>\n\n <wx-dropdown-item icon=\"logout\" @click=\"auth.logout()\">\n {{ signOutLabel ?? t('menu.sign-out') }}\n </wx-dropdown-item>\n </wx-dropdown>\n</template>\n\n<style scoped>\n/* The row the switch sits on, lined up with the text of the items above it. */\n.wx-user-menu__theme {\n padding: var(--wx-space-2) var(--wx-space-8) var(--wx-space-6);\n}\n\n/*\n * The avatar is the whole button: no border, no background, nothing of the browser's own —\n * a bordered box around a round picture reads as one more tool in the row of icons, which is\n * what wrapping it in an action looked like in the first place.\n */\n.wx-user-menu__trigger {\n display: inline-flex;\n padding: 0;\n border: none;\n border-radius: var(--wx-radius-full);\n background: transparent;\n cursor: pointer;\n transition: opacity var(--wx-duration-fast) var(--wx-easing-standard);\n}\n\n.wx-user-menu__trigger:hover {\n opacity: 0.85;\n}\n\n.wx-user-menu__trigger:focus-visible {\n outline: 2px solid var(--wx-border-focus);\n outline-offset: 2px;\n}\n\n/*\n * With a name beside it the button is a row rather than a circle, so it takes the width it is\n * given and rounds like the menu items above it instead of like the picture inside it.\n */\n.wx-user-menu__trigger--named {\n align-items: center;\n gap: var(--wx-space-8);\n width: 100%;\n min-width: 0;\n padding: var(--wx-space-4);\n border-radius: var(--wx-radius-control);\n text-align: start;\n}\n\n.wx-user-menu__trigger--named:hover {\n opacity: 1;\n background: var(--wx-bg-subtle);\n}\n\n.wx-user-menu__name {\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n color: var(--wx-text-default);\n font-family: var(--wx-font-family-sans);\n font-size: var(--wx-font-size-sm);\n font-weight: var(--wx-font-weight-medium);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n</style>\n","import { h, watch, type Component } from 'vue'\nimport type { Admin, AdminPlugin } from '@webx-ui/module-admin'\nimport LoginCard from './LoginCard.vue'\nimport { authMessages } from './messages'\nimport { createAuthSession, provideAuth, type AvatarResolver } from './session'\n\nexport interface AuthOptions {\n /** Where the sign-in screen lives, inside the panel. */\n path?: string\n /** Props handed to the card — the labels, mostly. */\n card?: Record<string, unknown>\n /**\n * Turns the key the signed-in person's photograph is stored under into an address, so the\n * menu in the corner shows the picture rather than initials. The same function `admins()`\n * takes: this package does not depend on the media one, and the panel is the only place\n * that knows both are installed.\n */\n resolveAvatar?: AvatarResolver\n /**\n * The field that picks a photograph — `WxMediaField` in a panel with the library. Handed\n * in for the same reason as the resolver, and used by the profile dialog in the corner\n * menu. Without it a person edits everything about themselves except their picture.\n */\n avatarField?: Component\n}\n\n/**\n * Signing in, as a plugin rather than a section: it owns a route and a guard, and it has no\n * place in the menu.\n *\n * Installing it also tells the panel how to find out who is signed in, which is why the panel\n * itself knows nothing about authentication — it holds the answer, not the question.\n */\nexport function auth(options: AuthOptions = {}): AdminPlugin {\n const path = options.path ?? '/login'\n\n return {\n install(admin: Admin) {\n const session = createAuthSession(admin.context)\n\n // The floor under this module's labels: what the server has not translated, and what\n // a panel assembled without a server has to fall back on.\n admin.i18n.defaults('webx-auth', authMessages)\n\n provideAuth(admin.app, session, options.resolveAvatar ?? null, options.avatarField ?? null)\n admin.context.useSessionLoader(() => session.me())\n\n admin.router.addRoute({\n path,\n name: 'webx.login',\n meta: { public: true },\n component: { render: () => h(LoginCard, { ...options.card }) },\n })\n\n admin.router.beforeEach((to) => {\n if (to.meta.public === true) {\n return true\n }\n\n if (admin.context.state.status !== 'unauthenticated') {\n return true\n }\n\n return { path, query: to.fullPath === '/' ? {} : { next: to.fullPath } }\n })\n\n // Both directions are driven by the state rather than by an event, because the card\n // cannot be relied on to announce its own success: signing in flips the panel to\n // `ready`, the shell swaps the branch it renders, and the card is unmounted before the\n // line after `await` runs.\n watch(\n () => admin.context.state.status,\n (status) => {\n const current = admin.router.currentRoute.value\n\n // A session can also end between requests — a sign-out in another tab, an account\n // switched off. The panel learns that from a 401 on whatever it asked for next,\n // which is not a navigation, so nothing would move without this.\n if (status === 'unauthenticated' && current.path !== path) {\n void admin.router.replace({ path, query: { next: current.fullPath } })\n\n return\n }\n\n if (status === 'ready' && current.path === path) {\n const next = current.query.next\n\n // A whole address rather than a path is a page outside the panel that sent the\n // person here to sign in — the consent screen an agent opens — and it is a page\n // to load, not a route to resolve. Only on this site: an address elsewhere would\n // make the sign-in form a way to send people anywhere.\n if (typeof next === 'string' && isPageOnThisSite(next)) {\n location.assign(next)\n\n return\n }\n\n void admin.router.replace(typeof next === 'string' ? next : '/')\n }\n },\n )\n },\n }\n}\n\n/** An absolute address on the same origin as the panel — the only kind worth following whole. */\nfunction isPageOnThisSite(next: string): boolean {\n if (!/^https?:\\/\\//i.test(next)) {\n return false\n }\n\n try {\n return new URL(next).origin === location.origin\n } catch {\n return false\n }\n}\n\nexport { admins, connect, type AdminsOptions, type AdminsView, type ConnectOptions } from './module'\nexport { createAdminsApi, type AdminsApi } from './admins'\nexport { createConnectionsApi, type ConnectionsApi } from './connections'\nexport { selectAdmin, selectAdmins, type AdminPickerOptions } from './selectAdmin'\nexport { default as WxAdminsPage } from './AdminsPage.vue'\nexport { default as WxAdminList } from './AdminList.vue'\nexport { default as WxAgentCallList } from './CallList.vue'\nexport { default as WxConnectionList } from './ConnectionList.vue'\nexport { default as WxConnectPage } from './ConnectPage.vue'\nexport type {\n Admin,\n AdminInput,\n AdminPage,\n AdminQuery,\n AdminRole,\n AgentCall,\n AgentCallFilters,\n AgentCallOutcome,\n AgentCallPage,\n AgentCallQuery,\n Connection,\n ConnectionScope,\n Connections,\n Role,\n} from './types'\nexport {\n createAuthSession,\n provideAuth,\n useAuth,\n authKey,\n avatarFieldKey,\n avatarResolverKey,\n type ProfileInput,\n} from './session'\nexport { default as WxProfileDialog } from './ProfileDialog.vue'\nexport type { AuthSession, AvatarResolver, Credentials } from './session'\nexport { default as WxLoginCard } from './LoginCard.vue'\nexport { default as WxUserMenu } from './UserMenu.vue'\nexport { authMessages } from './messages'\n"],"names":["authKey","useAuth","session","inject","createAuthSession","admin","base","error","isUnauthenticated","credentials","body","code","preference","profile","avatarResolverKey","avatarFieldKey","provideAuth","app","resolveAvatar","avatarField","props","__props","emit","__emit","auth","t","useTranslate","labels","computed","email","ref","password","rememberMe","busy","revealed","capsLock","message","errors","secondsLeft","emailField","useTemplateRef","countdown","onBeforeUnmount","stopCountdown","throttled","throttleNotice","submit","handle","HttpError","startCountdown","seconds","trackCapsLock","event","__expose","_createBlock","_component_wx_card","_createVNode","_component_wx_form","_component_wx_alert","_component_wx_form_item","_component_wx_input","$event","_component_wx_icon","_createElementVNode","_cache","_component_wx_checkbox","_component_wx_button","_createTextVNode","_toDisplayString","authMessages","createAdminsApi","data","query","id","input","seeded","useAuthMessages","i18n","useAdmin","resolve","dismiss","open","useModal","context","api","useI18n","useErrorText","roles","saving","form","photo","value","editing","roleOptions","one","localeOptions","all","watch","errorOf","field","save","payload","saved","toast","_unref","WxDialog","WxSpace","WxButton","_hoisted_1","_hoisted_2","_openBlock","_createElementBlock","_hoisted_3","_resolveDynamicComponent","_hoisted_4","WxFormItem","WxInput","WxSelect","WxCheckbox","WxText","CARDS","root","width","useElementWidth","asCards","panel","page","loading","role","active","selected","avatars","last","stateOptions","applied","chips","picked","clearFilters","columns","rowMenuWidth","load","state","resolveAvatars","row","url","actionsFor","onRow","clickable","onSelection","_keys","rows","WxTable","_withCtx","WxAvatar","WxBadge","_Fragment","_renderList","WxRowMenu","WxDate","WxEntityCard","WxIcon","WxFilterChips","filters","user","tool","outcome","nameOf","who","userOptions","toolOptions","outcomeOptions","how","outcomeOf","hasDetails","createConnectionsApi","scope","ending","allowed","name","end","confirm","ended","router","useRouter","route","useRoute","list","edit","createModal","AdminDialog","canManage","canAudit","title","module","views","items","where","next","path","actions","remove","WxListScreen","CallList","ConnectionList","AdminList","copied","copiedTimer","meta","command","toml","cursorLink","config","encode","vscodeLink","bytes","binary","byte","copy","byHand","WxCard","WxHeading","WxTabs","WxTab","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","WxAlert","_hoisted_11","_hoisted_12","admins","options","current","h","AdminsPage","connect","ConnectPage","count","chosen","track","pick","AdminPicker","selectAdmin","selectAdmins","theme","useTheme","languages","provided","providedField","avatarUrl","key","editProfile","ProfileDialog","openProfile","choose","_component_wx_dropdown","_normalizeClass","_component_wx_avatar","_component_wx_dropdown_item","_component_wx_text","_component_wx_divider","WxThemeSwitch","language","LoginCard","to","status","isPageOnThisSite"],"mappings":"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"}
|