@exegia/corpora-ui 0.10.0 → 0.12.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/dist-lib/components/agents/__tests__/ai-sidebar.test.d.ts +1 -0
- package/dist-lib/components/agents/ai-sidebar.d.ts +2 -0
- package/dist-lib/components/agents/index.d.ts +12 -0
- package/dist-lib/components/agents/marquee-label.d.ts +4 -0
- package/dist-lib/components/agents/sidebar-icon.d.ts +2 -0
- package/dist-lib/components/agents/sidebar-row.d.ts +2 -0
- package/dist-lib/components/agents/type.d.ts +83 -0
- package/dist-lib/components/agents/use-ai-sidebar.d.ts +31 -0
- package/dist-lib/components/agents/utils.d.ts +16 -0
- package/dist-lib/components/beste/piece/browser-frame.d.ts +2 -0
- package/dist-lib/components/beste/piece/type.d.ts +9 -0
- package/dist-lib/components/beste/piece/utils.d.ts +3 -0
- package/dist-lib/components/blocks/auth/signup-block.d.ts +5 -5
- package/dist-lib/components/blocks/nav/sidebar-block.d.ts +3 -56
- package/dist-lib/components/blocks/nav/sidebar-nav-row.d.ts +3 -0
- package/dist-lib/components/blocks/nav/types.d.ts +60 -0
- package/dist-lib/components/blocks/nav/utils.d.ts +1 -0
- package/dist-lib/components/blocks/profile/profile-card-block.d.ts +6 -1
- package/dist-lib/components/blocks/shell/index.d.ts +1 -0
- package/dist-lib/components/blocks/shell/shell-layout.d.ts +3 -0
- package/dist-lib/components/blocks/shell/type.d.ts +26 -0
- package/dist-lib/components/blocks/shell/utils.d.ts +1 -0
- package/dist-lib/components/composed/social-providers.d.ts +9 -1
- package/dist-lib/components/docs/demo-controls.d.ts +4 -4
- package/dist-lib/components/motion/animated-sidebar.d.ts +12 -1
- package/dist-lib/components/motion/popover-morph.d.ts +35 -0
- package/dist-lib/components/motion/popover-position.d.ts +15 -0
- package/dist-lib/components/ui/badge.d.ts +7 -0
- package/dist-lib/components/ui/select.d.ts +30 -0
- package/dist-lib/index.d.ts +1 -0
- package/dist-lib/index.js +644 -524
- package/dist-lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/agents/__tests__/ai-sidebar.test.tsx +93 -0
- package/src/components/agents/ai-sidebar.tsx +118 -0
- package/src/components/agents/index.ts +14 -0
- package/src/components/agents/marquee-label.tsx +59 -0
- package/src/components/agents/sidebar-icon.tsx +14 -0
- package/src/components/agents/sidebar-row.tsx +309 -0
- package/src/components/agents/type.ts +95 -0
- package/src/components/agents/use-ai-sidebar.ts +386 -0
- package/src/components/agents/utils.ts +142 -0
- package/src/components/beste/piece/browser-frame.tsx +55 -0
- package/src/components/beste/piece/type.ts +11 -0
- package/src/components/beste/piece/utils.ts +21 -0
- package/src/components/blocks/auth/linked-accounts-block.tsx +55 -57
- package/src/components/blocks/auth/passkey-manager-block.tsx +88 -81
- package/src/components/blocks/auth/signup-block.tsx +6 -6
- package/src/components/blocks/nav/sidebar-block.tsx +25 -155
- package/src/components/blocks/nav/sidebar-nav-row.tsx +79 -0
- package/src/components/blocks/nav/types.ts +65 -0
- package/src/components/blocks/nav/utils.ts +4 -0
- package/src/components/blocks/profile/profile-card-block.tsx +17 -6
- package/src/components/blocks/shell/index.ts +1 -0
- package/src/components/blocks/shell/shell-layout.tsx +115 -0
- package/src/components/blocks/shell/type.ts +50 -0
- package/src/components/blocks/shell/utils.ts +2 -0
- package/src/components/composed/social-providers.tsx +83 -27
- package/src/components/docs/component-preview.tsx +13 -12
- package/src/components/docs/demo-controls.tsx +37 -21
- package/src/components/motion/animated-sidebar.tsx +392 -364
- package/src/components/motion/popover-morph.tsx +292 -0
- package/src/components/motion/popover-position.ts +86 -0
- package/src/components/ui/badge.tsx +52 -0
- package/src/components/ui/select.tsx +257 -0
- package/src/index.css +18 -0
- package/src/index.ts +1 -0
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
"use client"
|
|
1
|
+
"use client"
|
|
2
2
|
|
|
3
|
-
import { AnimatePresence, MotionConfig, motion } from "motion/react"
|
|
4
|
-
import * as React from "react"
|
|
3
|
+
import { AnimatePresence, MotionConfig, motion } from "motion/react"
|
|
4
|
+
import * as React from "react"
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
7
|
SOCIAL_PROVIDERS,
|
|
8
8
|
type SocialProvider,
|
|
9
|
-
} from "@/components/composed/social-providers"
|
|
10
|
-
import { Button } from "@/components/ui/button"
|
|
9
|
+
} from "@/components/composed/social-providers"
|
|
10
|
+
import { Button } from "@/components/ui/button"
|
|
11
11
|
import {
|
|
12
12
|
Card,
|
|
13
13
|
CardDescription,
|
|
14
14
|
CardHeader,
|
|
15
15
|
CardPanel,
|
|
16
16
|
CardTitle,
|
|
17
|
-
} from "@/components/ui/card"
|
|
18
|
-
import { Spinner } from "@/components/ui/spinner"
|
|
19
|
-
import { cn } from "@/lib/utils"
|
|
20
|
-
import { AuthError, EASE } from "./auth-shell"
|
|
17
|
+
} from "@/components/ui/card"
|
|
18
|
+
import { Spinner } from "@/components/ui/spinner"
|
|
19
|
+
import { cn } from "@/lib/utils"
|
|
20
|
+
import { AuthError, EASE } from "./auth-shell"
|
|
21
21
|
|
|
22
22
|
/** One sign-in identity attached to the account. */
|
|
23
23
|
export interface LinkedIdentity {
|
|
24
|
-
id: string
|
|
25
|
-
provider: SocialProvider
|
|
24
|
+
id: string
|
|
25
|
+
provider: SocialProvider
|
|
26
26
|
/** Account address shown under the provider name, when known. */
|
|
27
|
-
email?: string | null
|
|
27
|
+
email?: string | null
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export interface LinkedAccountsBlockProps {
|
|
31
|
-
title?: string
|
|
32
|
-
description?: string
|
|
31
|
+
title?: string
|
|
32
|
+
description?: string
|
|
33
33
|
/** Identities already attached to the account. */
|
|
34
|
-
identities?: LinkedIdentity[]
|
|
34
|
+
identities?: LinkedIdentity[]
|
|
35
35
|
/** Providers offered as connect candidates; connected ones are filtered out. */
|
|
36
|
-
providers?: SocialProvider[]
|
|
36
|
+
providers?: SocialProvider[]
|
|
37
37
|
/** Shows the loading row instead of the list. */
|
|
38
|
-
loading?: boolean
|
|
38
|
+
loading?: boolean
|
|
39
39
|
/**
|
|
40
40
|
* The account can also sign in through a method that has no row in this
|
|
41
41
|
* list — an email/password credential, say. Lifts the last-method guard,
|
|
42
42
|
* which otherwise refuses to disconnect the final listed identity.
|
|
43
43
|
*/
|
|
44
|
-
hasOtherSignInMethods?: boolean
|
|
44
|
+
hasOtherSignInMethods?: boolean
|
|
45
45
|
/** Reject (or throw) to show the inline error. */
|
|
46
|
-
onLink?: (provider: SocialProvider) => Promise<void> | void
|
|
47
|
-
onUnlink?: (id: string) => Promise<void> | void
|
|
48
|
-
className?: string
|
|
46
|
+
onLink?: (provider: SocialProvider) => Promise<void> | void
|
|
47
|
+
onUnlink?: (id: string) => Promise<void> | void
|
|
48
|
+
className?: string
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
const LAST_METHOD_EXPLANATION =
|
|
52
|
-
"This is your only way to sign in, so it can't be disconnected."
|
|
52
|
+
"This is your only way to sign in, so it can't be disconnected."
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
55
|
* Settings panel for the sign-in identities attached to an account: lists the
|
|
@@ -68,48 +68,46 @@ export function LinkedAccountsBlock({
|
|
|
68
68
|
onUnlink,
|
|
69
69
|
className,
|
|
70
70
|
}: LinkedAccountsBlockProps) {
|
|
71
|
-
const noteId = React.useId()
|
|
72
|
-
const [error, setError] = React.useState<string | null>(null)
|
|
73
|
-
const [linking, setLinking] = React.useState<SocialProvider | null>(null)
|
|
74
|
-
const [unlinking, setUnlinking] = React.useState<string | null>(null)
|
|
71
|
+
const noteId = React.useId()
|
|
72
|
+
const [error, setError] = React.useState<string | null>(null)
|
|
73
|
+
const [linking, setLinking] = React.useState<SocialProvider | null>(null)
|
|
74
|
+
const [unlinking, setUnlinking] = React.useState<string | null>(null)
|
|
75
75
|
|
|
76
|
-
const connected = new Set(identities.map((identity) => identity.provider))
|
|
77
|
-
const connectable = providers.filter((provider) => !connected.has(provider))
|
|
78
|
-
const busy = linking !== null || unlinking !== null
|
|
76
|
+
const connected = new Set(identities.map((identity) => identity.provider))
|
|
77
|
+
const connectable = providers.filter((provider) => !connected.has(provider))
|
|
78
|
+
const busy = linking !== null || unlinking !== null
|
|
79
79
|
// The backend rule, enforced here so we never fire a request that would
|
|
80
80
|
// remove the account's only way in. An off-list method (a password) keeps
|
|
81
81
|
// the account reachable, so it lifts the guard.
|
|
82
|
-
const lastMethod = !hasOtherSignInMethods && identities.length <= 1
|
|
82
|
+
const lastMethod = !hasOtherSignInMethods && identities.length <= 1
|
|
83
83
|
|
|
84
84
|
async function connect(provider: SocialProvider) {
|
|
85
|
-
if (busy) return
|
|
86
|
-
setError(null)
|
|
87
|
-
setLinking(provider)
|
|
85
|
+
if (busy) return
|
|
86
|
+
setError(null)
|
|
87
|
+
setLinking(provider)
|
|
88
88
|
try {
|
|
89
|
-
await onLink?.(provider)
|
|
89
|
+
await onLink?.(provider)
|
|
90
90
|
} catch (cause) {
|
|
91
91
|
setError(
|
|
92
|
-
cause instanceof Error ? cause.message : "Unable to connect account."
|
|
93
|
-
)
|
|
92
|
+
cause instanceof Error ? cause.message : "Unable to connect account."
|
|
93
|
+
)
|
|
94
94
|
} finally {
|
|
95
|
-
setLinking(null)
|
|
95
|
+
setLinking(null)
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
async function disconnect(id: string) {
|
|
100
|
-
if (busy || lastMethod) return
|
|
101
|
-
setError(null)
|
|
102
|
-
setUnlinking(id)
|
|
100
|
+
if (busy || lastMethod) return
|
|
101
|
+
setError(null)
|
|
102
|
+
setUnlinking(id)
|
|
103
103
|
try {
|
|
104
|
-
await onUnlink?.(id)
|
|
104
|
+
await onUnlink?.(id)
|
|
105
105
|
} catch (cause) {
|
|
106
106
|
setError(
|
|
107
|
-
cause instanceof Error
|
|
108
|
-
|
|
109
|
-
: "Unable to disconnect account.",
|
|
110
|
-
);
|
|
107
|
+
cause instanceof Error ? cause.message : "Unable to disconnect account."
|
|
108
|
+
)
|
|
111
109
|
} finally {
|
|
112
|
-
setUnlinking(null)
|
|
110
|
+
setUnlinking(null)
|
|
113
111
|
}
|
|
114
112
|
}
|
|
115
113
|
|
|
@@ -141,9 +139,8 @@ export function LinkedAccountsBlock({
|
|
|
141
139
|
>
|
|
142
140
|
<AnimatePresence initial={false}>
|
|
143
141
|
{identities.map((identity, index) => {
|
|
144
|
-
const { label,
|
|
145
|
-
identity.provider
|
|
146
|
-
];
|
|
142
|
+
const { label, BrandIcon, brandClassName } =
|
|
143
|
+
SOCIAL_PROVIDERS[identity.provider]
|
|
147
144
|
return (
|
|
148
145
|
<motion.li
|
|
149
146
|
key={identity.id}
|
|
@@ -161,12 +158,12 @@ export function LinkedAccountsBlock({
|
|
|
161
158
|
}}
|
|
162
159
|
>
|
|
163
160
|
<div className="flex min-w-0 items-center gap-2.5">
|
|
164
|
-
<
|
|
161
|
+
<BrandIcon
|
|
165
162
|
aria-hidden="true"
|
|
166
|
-
className="size-4
|
|
163
|
+
className={cn("size-4", brandClassName)}
|
|
167
164
|
/>
|
|
168
165
|
<div className="flex min-w-0 flex-col">
|
|
169
|
-
<span className="font-medium
|
|
166
|
+
<span className="text-sm font-medium">
|
|
170
167
|
{label}
|
|
171
168
|
</span>
|
|
172
169
|
{identity.email && (
|
|
@@ -189,7 +186,7 @@ export function LinkedAccountsBlock({
|
|
|
189
186
|
Disconnect
|
|
190
187
|
</Button>
|
|
191
188
|
</motion.li>
|
|
192
|
-
)
|
|
189
|
+
)
|
|
193
190
|
})}
|
|
194
191
|
</AnimatePresence>
|
|
195
192
|
</ul>
|
|
@@ -206,7 +203,8 @@ export function LinkedAccountsBlock({
|
|
|
206
203
|
)}
|
|
207
204
|
|
|
208
205
|
{connectable.map((provider) => {
|
|
209
|
-
const { label,
|
|
206
|
+
const { label, BrandIcon, brandClassName } =
|
|
207
|
+
SOCIAL_PROVIDERS[provider]
|
|
210
208
|
return (
|
|
211
209
|
<Button
|
|
212
210
|
key={provider}
|
|
@@ -217,15 +215,15 @@ export function LinkedAccountsBlock({
|
|
|
217
215
|
loading={linking === provider}
|
|
218
216
|
onClick={() => void connect(provider)}
|
|
219
217
|
>
|
|
220
|
-
<
|
|
218
|
+
<BrandIcon aria-hidden="true" className={brandClassName} />
|
|
221
219
|
<span className="flex-1">Connect {label}</span>
|
|
222
220
|
</Button>
|
|
223
|
-
)
|
|
221
|
+
)
|
|
224
222
|
})}
|
|
225
223
|
</motion.div>
|
|
226
224
|
)}
|
|
227
225
|
</CardPanel>
|
|
228
226
|
</Card>
|
|
229
227
|
</MotionConfig>
|
|
230
|
-
)
|
|
228
|
+
)
|
|
231
229
|
}
|
|
@@ -1,60 +1,61 @@
|
|
|
1
|
-
"use client"
|
|
1
|
+
"use client"
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
3
|
+
import { KeyRoundIcon } from "lucide-react"
|
|
4
|
+
import { AnimatePresence, MotionConfig, motion } from "motion/react"
|
|
5
|
+
import * as React from "react"
|
|
5
6
|
|
|
6
|
-
import { Button } from "@/components/ui/button"
|
|
7
|
+
import { Button } from "@/components/ui/button"
|
|
7
8
|
import {
|
|
8
9
|
Card,
|
|
9
10
|
CardDescription,
|
|
10
11
|
CardHeader,
|
|
11
12
|
CardPanel,
|
|
12
13
|
CardTitle,
|
|
13
|
-
} from "@/components/ui/card"
|
|
14
|
-
import { Field, FieldError, FieldLabel } from "@/components/ui/field"
|
|
15
|
-
import { Input } from "@/components/ui/input"
|
|
16
|
-
import { Spinner } from "@/components/ui/spinner"
|
|
17
|
-
import { cn } from "@/lib/utils"
|
|
18
|
-
import { AuthError, EASE, Reveal } from "./auth-shell"
|
|
14
|
+
} from "@/components/ui/card"
|
|
15
|
+
import { Field, FieldError, FieldLabel } from "@/components/ui/field"
|
|
16
|
+
import { Input } from "@/components/ui/input"
|
|
17
|
+
import { Spinner } from "@/components/ui/spinner"
|
|
18
|
+
import { cn } from "@/lib/utils"
|
|
19
|
+
import { AuthError, EASE, Reveal } from "./auth-shell"
|
|
19
20
|
|
|
20
21
|
/** One registered passkey, as rendered by {@link PasskeyManagerBlock}. */
|
|
21
22
|
export interface PasskeyRecord {
|
|
22
|
-
id: string
|
|
23
|
+
id: string
|
|
23
24
|
/** Server-derived name; falls back to "Passkey" when absent. */
|
|
24
|
-
name?: string | null
|
|
25
|
+
name?: string | null
|
|
25
26
|
/** ISO timestamps. Unparseable values are simply not shown. */
|
|
26
|
-
createdAt?: string | null
|
|
27
|
-
lastUsedAt?: string | null
|
|
27
|
+
createdAt?: string | null
|
|
28
|
+
lastUsedAt?: string | null
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
export interface PasskeyManagerBlockProps {
|
|
31
|
-
title?: string
|
|
32
|
-
description?: string
|
|
32
|
+
title?: string
|
|
33
|
+
description?: string
|
|
33
34
|
/** The account's passkeys, newest first. */
|
|
34
|
-
passkeys?: PasskeyRecord[]
|
|
35
|
+
passkeys?: PasskeyRecord[]
|
|
35
36
|
/** Whether this device can register passkeys at all. */
|
|
36
|
-
available?: boolean
|
|
37
|
+
available?: boolean
|
|
37
38
|
/** Shows the loading row instead of the list. */
|
|
38
|
-
loading?: boolean
|
|
39
|
+
loading?: boolean
|
|
39
40
|
/**
|
|
40
41
|
* Reject (or throw) to show the error. Resolving with `{ cancelled: true }`
|
|
41
42
|
* returns silently to idle — a dismissed OS prompt is not a failure.
|
|
42
43
|
*/
|
|
43
|
-
onRegister?: () => Promise<{ cancelled?: boolean } | void> | void
|
|
44
|
-
onRename?: (id: string, name: string) => Promise<void> | void
|
|
45
|
-
onDelete?: (id: string) => Promise<void> | void
|
|
46
|
-
className?: string
|
|
44
|
+
onRegister?: () => Promise<{ cancelled?: boolean } | void> | void
|
|
45
|
+
onRename?: (id: string, name: string) => Promise<void> | void
|
|
46
|
+
onDelete?: (id: string) => Promise<void> | void
|
|
47
|
+
className?: string
|
|
47
48
|
}
|
|
48
49
|
|
|
49
|
-
const NAME_MAX = 120
|
|
50
|
+
const NAME_MAX = 120
|
|
50
51
|
|
|
51
52
|
const LAST_PASSKEY_WARNING =
|
|
52
|
-
"This is your last passkey. After deleting it you won't be able to sign in with a passkey until you register a new one."
|
|
53
|
+
"This is your last passkey. After deleting it you won't be able to sign in with a passkey until you register a new one."
|
|
53
54
|
|
|
54
55
|
function formatDate(iso: string | null | undefined): string | null {
|
|
55
|
-
if (!iso) return null
|
|
56
|
-
const date = new Date(iso)
|
|
57
|
-
return Number.isNaN(date.getTime()) ? null : date.toLocaleDateString()
|
|
56
|
+
if (!iso) return null
|
|
57
|
+
const date = new Date(iso)
|
|
58
|
+
return Number.isNaN(date.getTime()) ? null : date.toLocaleDateString()
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
/**
|
|
@@ -75,63 +76,61 @@ export function PasskeyManagerBlock({
|
|
|
75
76
|
onDelete,
|
|
76
77
|
className,
|
|
77
78
|
}: PasskeyManagerBlockProps) {
|
|
78
|
-
const [error, setError] = React.useState<string | null>(null)
|
|
79
|
-
const [busy, setBusy] = React.useState(false)
|
|
80
|
-
const [registering, setRegistering] = React.useState(false)
|
|
81
|
-
const [renamingId, setRenamingId] = React.useState<string | null>(null)
|
|
82
|
-
const [renameValue, setRenameValue] = React.useState("")
|
|
83
|
-
const [renameError, setRenameError] = React.useState<string | null>(null)
|
|
84
|
-
const [confirmingId, setConfirmingId] = React.useState<string | null>(null)
|
|
79
|
+
const [error, setError] = React.useState<string | null>(null)
|
|
80
|
+
const [busy, setBusy] = React.useState(false)
|
|
81
|
+
const [registering, setRegistering] = React.useState(false)
|
|
82
|
+
const [renamingId, setRenamingId] = React.useState<string | null>(null)
|
|
83
|
+
const [renameValue, setRenameValue] = React.useState("")
|
|
84
|
+
const [renameError, setRenameError] = React.useState<string | null>(null)
|
|
85
|
+
const [confirmingId, setConfirmingId] = React.useState<string | null>(null)
|
|
85
86
|
|
|
86
|
-
const lastPasskey = passkeys.length === 1
|
|
87
|
+
const lastPasskey = passkeys.length === 1
|
|
87
88
|
|
|
88
89
|
async function run(action: () => Promise<unknown> | unknown) {
|
|
89
|
-
if (busy) return { ok: false } as const
|
|
90
|
-
setError(null)
|
|
91
|
-
setBusy(true)
|
|
90
|
+
if (busy) return { ok: false } as const
|
|
91
|
+
setError(null)
|
|
92
|
+
setBusy(true)
|
|
92
93
|
try {
|
|
93
|
-
const result = await action()
|
|
94
|
-
return { ok: true, result } as const
|
|
94
|
+
const result = await action()
|
|
95
|
+
return { ok: true, result } as const
|
|
95
96
|
} catch (cause) {
|
|
96
|
-
setError(
|
|
97
|
-
|
|
98
|
-
);
|
|
99
|
-
return { ok: false } as const;
|
|
97
|
+
setError(cause instanceof Error ? cause.message : "Something went wrong.")
|
|
98
|
+
return { ok: false } as const
|
|
100
99
|
} finally {
|
|
101
|
-
setBusy(false)
|
|
100
|
+
setBusy(false)
|
|
102
101
|
}
|
|
103
102
|
}
|
|
104
103
|
|
|
105
104
|
async function handleRegister() {
|
|
106
|
-
setRegistering(true)
|
|
107
|
-
await run(() => onRegister?.())
|
|
108
|
-
setRegistering(false)
|
|
105
|
+
setRegistering(true)
|
|
106
|
+
await run(() => onRegister?.())
|
|
107
|
+
setRegistering(false)
|
|
109
108
|
}
|
|
110
109
|
|
|
111
110
|
function startRename(passkey: PasskeyRecord) {
|
|
112
|
-
setConfirmingId(null)
|
|
113
|
-
setRenamingId(passkey.id)
|
|
114
|
-
setRenameValue(passkey.name ?? "")
|
|
115
|
-
setRenameError(null)
|
|
111
|
+
setConfirmingId(null)
|
|
112
|
+
setRenamingId(passkey.id)
|
|
113
|
+
setRenameValue(passkey.name ?? "")
|
|
114
|
+
setRenameError(null)
|
|
116
115
|
}
|
|
117
116
|
|
|
118
117
|
async function submitRename(id: string) {
|
|
119
|
-
const name = renameValue.trim()
|
|
118
|
+
const name = renameValue.trim()
|
|
120
119
|
if (name.length < 1 || name.length > NAME_MAX) {
|
|
121
|
-
setRenameError(`Name must be between 1 and ${NAME_MAX} characters.`)
|
|
122
|
-
return
|
|
120
|
+
setRenameError(`Name must be between 1 and ${NAME_MAX} characters.`)
|
|
121
|
+
return
|
|
123
122
|
}
|
|
124
|
-
const outcome = await run(() => onRename?.(id, name))
|
|
125
|
-
if (!outcome.ok) return
|
|
126
|
-
setRenamingId(null)
|
|
127
|
-
setRenameError(null)
|
|
123
|
+
const outcome = await run(() => onRename?.(id, name))
|
|
124
|
+
if (!outcome.ok) return
|
|
125
|
+
setRenamingId(null)
|
|
126
|
+
setRenameError(null)
|
|
128
127
|
}
|
|
129
128
|
|
|
130
129
|
async function confirmDelete(id: string) {
|
|
131
|
-
const outcome = await run(() => onDelete?.(id))
|
|
132
|
-
setConfirmingId(null)
|
|
133
|
-
if (!outcome.ok) return
|
|
134
|
-
if (renamingId === id) setRenamingId(null)
|
|
130
|
+
const outcome = await run(() => onDelete?.(id))
|
|
131
|
+
setConfirmingId(null)
|
|
132
|
+
if (!outcome.ok) return
|
|
133
|
+
if (renamingId === id) setRenamingId(null)
|
|
135
134
|
}
|
|
136
135
|
|
|
137
136
|
return (
|
|
@@ -164,9 +163,9 @@ export function PasskeyManagerBlock({
|
|
|
164
163
|
<ul aria-label="Passkeys" className="flex flex-col gap-2">
|
|
165
164
|
<AnimatePresence initial={false}>
|
|
166
165
|
{passkeys.map((passkey, index) => {
|
|
167
|
-
const name = passkey.name ?? "Passkey"
|
|
168
|
-
const created = formatDate(passkey.createdAt)
|
|
169
|
-
const lastUsed = formatDate(passkey.lastUsedAt)
|
|
166
|
+
const name = passkey.name ?? "Passkey"
|
|
167
|
+
const created = formatDate(passkey.createdAt)
|
|
168
|
+
const lastUsed = formatDate(passkey.lastUsedAt)
|
|
170
169
|
return (
|
|
171
170
|
<motion.li
|
|
172
171
|
key={passkey.id}
|
|
@@ -184,15 +183,23 @@ export function PasskeyManagerBlock({
|
|
|
184
183
|
}}
|
|
185
184
|
>
|
|
186
185
|
<div className="flex items-center justify-between gap-3">
|
|
187
|
-
<div className="flex min-w-0
|
|
188
|
-
<span
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
{created && lastUsed ? " · " : null}
|
|
194
|
-
{lastUsed ? `Last used ${lastUsed}` : null}
|
|
186
|
+
<div className="flex min-w-0 items-center gap-2.5">
|
|
187
|
+
<span
|
|
188
|
+
aria-hidden="true"
|
|
189
|
+
className="grid size-8 shrink-0 place-items-center rounded-md bg-muted text-muted-foreground"
|
|
190
|
+
>
|
|
191
|
+
<KeyRoundIcon className="size-4" />
|
|
195
192
|
</span>
|
|
193
|
+
<div className="flex min-w-0 flex-col">
|
|
194
|
+
<span className="truncate text-sm font-medium">
|
|
195
|
+
{name}
|
|
196
|
+
</span>
|
|
197
|
+
<span className="text-xs text-muted-foreground">
|
|
198
|
+
{created ? `Added ${created}` : null}
|
|
199
|
+
{created && lastUsed ? " · " : null}
|
|
200
|
+
{lastUsed ? `Last used ${lastUsed}` : null}
|
|
201
|
+
</span>
|
|
202
|
+
</div>
|
|
196
203
|
</div>
|
|
197
204
|
<div className="flex shrink-0 gap-2">
|
|
198
205
|
<Button
|
|
@@ -212,8 +219,8 @@ export function PasskeyManagerBlock({
|
|
|
212
219
|
variant="destructive-outline"
|
|
213
220
|
disabled={busy}
|
|
214
221
|
onClick={() => {
|
|
215
|
-
setRenamingId(null)
|
|
216
|
-
setConfirmingId(passkey.id)
|
|
222
|
+
setRenamingId(null)
|
|
223
|
+
setConfirmingId(passkey.id)
|
|
217
224
|
}}
|
|
218
225
|
>
|
|
219
226
|
Delete
|
|
@@ -225,8 +232,8 @@ export function PasskeyManagerBlock({
|
|
|
225
232
|
<form
|
|
226
233
|
className="flex items-start gap-2 pt-1"
|
|
227
234
|
onSubmit={(event) => {
|
|
228
|
-
event.preventDefault()
|
|
229
|
-
void submitRename(passkey.id)
|
|
235
|
+
event.preventDefault()
|
|
236
|
+
void submitRename(passkey.id)
|
|
230
237
|
}}
|
|
231
238
|
>
|
|
232
239
|
<Field
|
|
@@ -304,7 +311,7 @@ export function PasskeyManagerBlock({
|
|
|
304
311
|
</div>
|
|
305
312
|
</Reveal>
|
|
306
313
|
</motion.li>
|
|
307
|
-
)
|
|
314
|
+
)
|
|
308
315
|
})}
|
|
309
316
|
</AnimatePresence>
|
|
310
317
|
</ul>
|
|
@@ -330,5 +337,5 @@ export function PasskeyManagerBlock({
|
|
|
330
337
|
</CardPanel>
|
|
331
338
|
</Card>
|
|
332
339
|
</MotionConfig>
|
|
333
|
-
)
|
|
340
|
+
)
|
|
334
341
|
}
|
|
@@ -38,19 +38,19 @@ export interface SignupBlockProps {
|
|
|
38
38
|
termsComponent?: React.ReactNode;
|
|
39
39
|
/** Brand mark rendered above the title. Omit for no logo row at all. */
|
|
40
40
|
logo?: React.ReactNode;
|
|
41
|
-
/** Brand accent for the primary action. Omit
|
|
41
|
+
/** Brand accent for the primary action. Omit keeping the default primary. */
|
|
42
42
|
accent?: AuthAccent;
|
|
43
43
|
providers?: SocialProvider[];
|
|
44
44
|
showNameField?: boolean;
|
|
45
|
-
/** Require the
|
|
45
|
+
/** Require the term checkbox before submitting. */
|
|
46
46
|
showTerms?: boolean;
|
|
47
47
|
/**
|
|
48
|
-
* Controls the
|
|
48
|
+
* Controls the term checkbox. Pass it with `onTermsCheckedChange` when
|
|
49
49
|
* something outside the block has to tick the box — an "I agree" action in
|
|
50
|
-
* your own terms dialog, say. Omit
|
|
50
|
+
* your own terms dialog, say. Omit letting the block own the state.
|
|
51
51
|
*/
|
|
52
52
|
termsChecked?: boolean;
|
|
53
|
-
/** Starting state of the
|
|
53
|
+
/** Starting state of the term checkbox while it is uncontrolled. */
|
|
54
54
|
defaultTermsChecked?: boolean;
|
|
55
55
|
/** Fires on every change, controlled or not. */
|
|
56
56
|
onTermsCheckedChange?: (checked: boolean) => void;
|
|
@@ -122,7 +122,7 @@ export function SignupBlock({
|
|
|
122
122
|
const emailPathComplete =
|
|
123
123
|
(!showNameField || nameValid) && emailValid && passwordValid;
|
|
124
124
|
|
|
125
|
-
async function handleSubmit(event: React.
|
|
125
|
+
async function handleSubmit(event: React.SubmitEvent<HTMLFormElement>) {
|
|
126
126
|
event.preventDefault();
|
|
127
127
|
setError(null);
|
|
128
128
|
if (!strongEnough) {
|