@adea-ai/ui 0.96.0 → 0.97.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/components/composites/about-dialog/about-dialog.d.ts +17 -0
  2. package/dist/components/composites/about-dialog/about-dialog.d.ts.map +1 -0
  3. package/dist/components/composites/about-dialog/about-dialog.js +167 -0
  4. package/dist/components/composites/about-dialog/about-dialog.js.map +1 -0
  5. package/dist/components/composites/about-dialog/index.d.ts +3 -0
  6. package/dist/components/composites/about-dialog/index.d.ts.map +1 -0
  7. package/dist/components/composites/about-dialog/index.js +2 -0
  8. package/dist/components/composites/account-menu/account-menu.d.ts +20 -1
  9. package/dist/components/composites/account-menu/account-menu.d.ts.map +1 -1
  10. package/dist/components/composites/account-menu/account-menu.js +103 -50
  11. package/dist/components/composites/account-menu/account-menu.js.map +1 -1
  12. package/dist/components/composites/account-menu/index.d.ts +1 -1
  13. package/dist/components/composites/account-menu/index.d.ts.map +1 -1
  14. package/dist/components/composites/account-menu/index.js +2 -2
  15. package/dist/components/composites/help-center/help-center.d.ts +19 -0
  16. package/dist/components/composites/help-center/help-center.d.ts.map +1 -0
  17. package/dist/components/composites/help-center/help-center.js +135 -0
  18. package/dist/components/composites/help-center/help-center.js.map +1 -0
  19. package/dist/components/composites/help-center/index.d.ts +3 -0
  20. package/dist/components/composites/help-center/index.d.ts.map +1 -0
  21. package/dist/components/composites/help-center/index.js +2 -0
  22. package/dist/components/composites/update-dialog/update-dialog.d.ts +11 -1
  23. package/dist/components/composites/update-dialog/update-dialog.d.ts.map +1 -1
  24. package/dist/components/composites/update-dialog/update-dialog.js +209 -78
  25. package/dist/components/composites/update-dialog/update-dialog.js.map +1 -1
  26. package/dist/index.d.ts +2 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +4 -2
  29. package/dist/r/about-dialog.json +26 -0
  30. package/dist/r/help-center.json +28 -0
  31. package/dist/r/registry.json +55 -0
  32. package/dist/r/src/components/composites/about-dialog/about-dialog.tsx +161 -0
  33. package/dist/r/src/components/composites/about-dialog/index.ts +2 -0
  34. package/dist/r/src/components/composites/account-menu/account-menu.tsx +76 -13
  35. package/dist/r/src/components/composites/account-menu/index.ts +6 -1
  36. package/dist/r/src/components/composites/help-center/help-center.tsx +104 -0
  37. package/dist/r/src/components/composites/help-center/index.ts +2 -0
  38. package/dist/r/src/components/composites/update-dialog/update-dialog.tsx +200 -27
  39. package/dist/r/update-dialog.json +1 -0
  40. package/package.json +1 -1
  41. package/registry.json +55 -0
  42. package/src/components/composites/about-dialog/about-dialog.tsx +161 -0
  43. package/src/components/composites/about-dialog/index.ts +2 -0
  44. package/src/components/composites/account-menu/account-menu.tsx +76 -13
  45. package/src/components/composites/account-menu/index.ts +6 -1
  46. package/src/components/composites/help-center/help-center.tsx +104 -0
  47. package/src/components/composites/help-center/index.ts +2 -0
  48. package/src/components/composites/update-dialog/update-dialog.tsx +200 -27
  49. package/src/index.ts +2 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adea-ai/ui",
3
- "version": "0.96.0",
3
+ "version": "0.97.1",
4
4
  "description": "The Adea design system: Kobalte-backed Solid components, semantic OKLCH tokens, and the app-shell layout primitives shared by Adea and Cortana.",
5
5
  "keywords": [
6
6
  "adea",
package/registry.json CHANGED
@@ -1783,6 +1783,32 @@
1783
1783
  }
1784
1784
  ]
1785
1785
  },
1786
+ {
1787
+ "name": "about-dialog",
1788
+ "type": "registry:ui",
1789
+ "title": "About Dialog",
1790
+ "description": "Shared app identity and version support dialog.",
1791
+ "dependencies": [
1792
+ "lucide-solid@^1.48.0"
1793
+ ],
1794
+ "registryDependencies": [
1795
+ "@adea-ai/ui/action-button",
1796
+ "@adea-ai/ui/alert",
1797
+ "@adea-ai/ui/dialog"
1798
+ ],
1799
+ "files": [
1800
+ {
1801
+ "path": "src/components/composites/about-dialog/about-dialog.tsx",
1802
+ "type": "registry:ui",
1803
+ "target": "components/composites/about-dialog/about-dialog.tsx"
1804
+ },
1805
+ {
1806
+ "path": "src/components/composites/about-dialog/index.ts",
1807
+ "type": "registry:ui",
1808
+ "target": "components/composites/about-dialog/index.ts"
1809
+ }
1810
+ ]
1811
+ },
1786
1812
  {
1787
1813
  "name": "account-menu",
1788
1814
  "type": "registry:ui",
@@ -1931,6 +1957,34 @@
1931
1957
  }
1932
1958
  ]
1933
1959
  },
1960
+ {
1961
+ "name": "help-center",
1962
+ "type": "registry:ui",
1963
+ "title": "Help Center",
1964
+ "description": "Shared keyboard shortcuts and project resource page.",
1965
+ "dependencies": [
1966
+ "lucide-solid@^1.48.0"
1967
+ ],
1968
+ "registryDependencies": [
1969
+ "@adea-ai/ui/action-button",
1970
+ "@adea-ai/ui/alert",
1971
+ "@adea-ai/ui/card",
1972
+ "@adea-ai/ui/kbd",
1973
+ "lib"
1974
+ ],
1975
+ "files": [
1976
+ {
1977
+ "path": "src/components/composites/help-center/help-center.tsx",
1978
+ "type": "registry:ui",
1979
+ "target": "components/composites/help-center/help-center.tsx"
1980
+ },
1981
+ {
1982
+ "path": "src/components/composites/help-center/index.ts",
1983
+ "type": "registry:ui",
1984
+ "target": "components/composites/help-center/index.ts"
1985
+ }
1986
+ ]
1987
+ },
1934
1988
  {
1935
1989
  "name": "list-row",
1936
1990
  "type": "registry:ui",
@@ -2081,6 +2135,7 @@
2081
2135
  "lucide-solid@^1.48.0"
2082
2136
  ],
2083
2137
  "registryDependencies": [
2138
+ "@adea-ai/ui/action-button",
2084
2139
  "@adea-ai/ui/badge",
2085
2140
  "@adea-ai/ui/button",
2086
2141
  "@adea-ai/ui/dialog",
@@ -0,0 +1,161 @@
1
+ import {
2
+ createEffect,
3
+ createMemo,
4
+ createSignal,
5
+ on,
6
+ onCleanup,
7
+ Show,
8
+ type Accessor,
9
+ } from 'solid-js'
10
+ import { ExternalLink } from 'lucide-solid'
11
+ import { ActionButton } from '../action-button'
12
+ import { Alert, AlertDescription } from '../../ui/alert'
13
+ import { Dialog, DialogContent, DialogTitle } from '../../ui/dialog'
14
+ import { createDialogFocusRestoration } from '../../ui/dialog/dialog'
15
+
16
+ export type AboutDialogProps = {
17
+ appName: string
18
+ appIcon: string
19
+ version?: string
20
+ platform: string
21
+ copyright: string
22
+ sourceUrl: string
23
+ open: boolean
24
+ onOpenChange: (open: boolean) => void
25
+ copyVersionInfo?: (text: string) => Promise<void>
26
+ openExternal?: (url: string) => Promise<void>
27
+ restoreFocusRef?: Accessor<HTMLElement | undefined>
28
+ }
29
+
30
+ /** Compact app identity and support details, with clipboard and native-browser adapters. */
31
+ export function AboutDialog(props: AboutDialogProps) {
32
+ const versionLabel = () => (props.version ? `Version ${props.version}` : 'Version unavailable')
33
+ const versionInfo = createMemo(() =>
34
+ [props.appName, versionLabel(), `Platform: ${props.platform}`].join('\n')
35
+ )
36
+ const [copied, setCopied] = createSignal(false)
37
+ const [error, setError] = createSignal('')
38
+ const [copying, setCopying] = createSignal(false)
39
+ let disposed = false
40
+ let epoch = 0
41
+ let timer: ReturnType<typeof setTimeout> | undefined
42
+ onCleanup(() => {
43
+ disposed = true
44
+ epoch += 1
45
+ clearTimeout(timer)
46
+ })
47
+ createEffect(
48
+ on(
49
+ () => props.open,
50
+ () => {
51
+ epoch += 1
52
+ clearTimeout(timer)
53
+ setCopied(false)
54
+ setError('')
55
+ setCopying(false)
56
+ }
57
+ )
58
+ )
59
+ const focus = createDialogFocusRestoration({
60
+ open: () => props.open,
61
+ restoreFocusRef: props.restoreFocusRef,
62
+ })
63
+ const copy = async () => {
64
+ const owner = epoch
65
+ const active = () => !disposed && props.open && owner === epoch
66
+ setError('')
67
+ setCopied(false)
68
+ setCopying(true)
69
+ try {
70
+ if (props.copyVersionInfo) await props.copyVersionInfo(versionInfo())
71
+ else await navigator.clipboard.writeText(versionInfo())
72
+ if (!active()) return
73
+ setCopied(true)
74
+ clearTimeout(timer)
75
+ timer = setTimeout(() => {
76
+ if (active()) setCopied(false)
77
+ }, 2000)
78
+ } catch {
79
+ if (active()) setError('Could not copy version info. Please try again.')
80
+ } finally {
81
+ if (active()) setCopying(false)
82
+ }
83
+ }
84
+ const openSource = async () => {
85
+ const owner = epoch
86
+ setError('')
87
+ try {
88
+ await props.openExternal?.(props.sourceUrl)
89
+ } catch {
90
+ if (!disposed && props.open && owner === epoch)
91
+ setError('Could not open the source repository. Please try again.')
92
+ }
93
+ }
94
+ return (
95
+ <Dialog open={props.open} onOpenChange={props.onOpenChange}>
96
+ <DialogContent
97
+ onKeyDown={(event: KeyboardEvent) => {
98
+ // Tooltip help on the identity actions cannot trap Escape in this popup.
99
+ if (event.key === 'Escape' && !event.defaultPrevented) {
100
+ event.preventDefault()
101
+ props.onOpenChange(false)
102
+ }
103
+ }}
104
+ aria-label={`About ${props.appName}`}
105
+ aria-labelledby=""
106
+ aria-describedby={undefined}
107
+ onOpenAutoFocus={(event) => {
108
+ focus.onOpenAutoFocus(event)
109
+ // Let the identity be read before focus opens explanatory action help.
110
+ if (!event.defaultPrevented && event.currentTarget instanceof HTMLElement) {
111
+ event.preventDefault()
112
+ event.currentTarget.focus({ preventScroll: true })
113
+ }
114
+ }}
115
+ onCloseAutoFocus={focus.onCloseAutoFocus}
116
+ >
117
+ <div class="flex flex-col items-center gap-3 px-6 py-8 text-center">
118
+ <img src={props.appIcon} alt="" class="size-16" aria-hidden="true" />
119
+ <DialogTitle>{props.appName}</DialogTitle>
120
+ <p class="text-sm text-primary">{versionLabel()}</p>
121
+ <p class="text-xs text-muted-foreground">{props.copyright}</p>
122
+ <div class="flex flex-wrap items-center justify-center gap-3">
123
+ <ActionButton
124
+ type="button"
125
+ variant="outline"
126
+ size="sm"
127
+ busy={copying()}
128
+ tooltip="Copy the app version and runtime details for a support report."
129
+ onClick={() => void copy()}
130
+ >
131
+ {copied() ? 'Copied' : 'Copy version info'}
132
+ </ActionButton>
133
+ <ActionButton
134
+ as="a"
135
+ variant="link"
136
+ size="sm"
137
+ href={props.sourceUrl}
138
+ target="_blank"
139
+ rel="noopener noreferrer"
140
+ tooltip={`Open the ${props.appName} source repository in your browser.`}
141
+ onClick={(event) => {
142
+ if (props.openExternal) {
143
+ event.preventDefault()
144
+ void openSource()
145
+ }
146
+ }}
147
+ >
148
+ <ExternalLink aria-hidden="true" />
149
+ View source
150
+ </ActionButton>
151
+ </div>
152
+ <Show when={error()}>
153
+ <Alert variant="destructive" role="alert">
154
+ <AlertDescription>{error()}</AlertDescription>
155
+ </Alert>
156
+ </Show>
157
+ </div>
158
+ </DialogContent>
159
+ </Dialog>
160
+ )
161
+ }
@@ -0,0 +1,2 @@
1
+ /** Shared app identity and version support dialog. */
2
+ export { AboutDialog, type AboutDialogProps } from './about-dialog'
@@ -56,6 +56,12 @@ export type AccountMenuItem = {
56
56
 
57
57
  export type AccountMenuProps = {
58
58
  class?: string
59
+ /** Anchor a rail menu beside its trigger, or keep the default upward placement. */
60
+ placement?: ComponentProps<typeof DropdownMenu>['placement']
61
+ /** Space between the trigger and menu, in pixels. */
62
+ gutter?: ComponentProps<typeof DropdownMenu>['gutter']
63
+ /** Hide the menu pointer when aligning it beside a compact rail. */
64
+ hideArrow?: boolean
59
65
  /** The trigger size. Icon-only menus default to the standard icon size. */
60
66
  size?: ButtonProps['size']
61
67
  /** The entries above the separator. */
@@ -64,6 +70,8 @@ export type AccountMenuProps = {
64
70
  platform?: 'desktop' | 'web'
65
71
  /** Whether a session exists. Decides the sign-in/sign-out row. */
66
72
  authenticated: boolean
73
+ /** Local-only applications can omit the session actions altogether. */
74
+ showSession?: boolean
67
75
  /** Disable the session row while a sign-in or sign-out is in flight. */
68
76
  busy?: boolean
69
77
  /** The name shown on the trigger's accessible label. */
@@ -86,10 +94,14 @@ const FALLBACK_ICONS: Record<string, typeof Settings2> = {
86
94
  export function AccountMenu(props: AccountMenuProps) {
87
95
  const [local, rest] = splitProps(props, [
88
96
  'class',
97
+ 'placement',
98
+ 'gutter',
99
+ 'hideArrow',
89
100
  'size',
90
101
  'items',
91
102
  'platform',
92
103
  'authenticated',
104
+ 'showSession',
93
105
  'busy',
94
106
  'label',
95
107
  'onSignIn',
@@ -119,6 +131,8 @@ export function AccountMenu(props: AccountMenuProps) {
119
131
  <Tooltip open={tooltipOpen()} onOpenChange={(open) => setTooltipOpen(open && !menuOpen())}>
120
132
  <DropdownMenu
121
133
  modal={false}
134
+ placement={local.placement ?? 'top-start'}
135
+ gutter={local.gutter}
122
136
  onOpenChange={(open) => {
123
137
  setMenuOpen(open)
124
138
  if (open) setTooltipOpen(false)
@@ -139,7 +153,7 @@ export function AccountMenu(props: AccountMenuProps) {
139
153
  </TooltipTrigger>
140
154
  <TooltipContent placement="top">{local.label ?? 'Account and settings'}</TooltipContent>
141
155
  <DropdownMenuContent
142
- placement="top-start"
156
+ hideArrow={local.hideArrow}
143
157
  class="min-w-56 max-h-(--kb-popper-available-height) overflow-x-hidden overflow-y-auto"
144
158
  onCloseAutoFocus={(event) => {
145
159
  const selection = pendingAfterClose
@@ -174,24 +188,73 @@ export function AccountMenu(props: AccountMenuProps) {
174
188
  )}
175
189
  </For>
176
190
  </DropdownMenuGroup>
177
- <DropdownMenuSeparator />
178
- <DropdownMenuGroup>
179
- <DropdownMenuItem
180
- disabled={local.busy}
181
- onSelect={() => (local.authenticated ? local.onSignOut?.() : local.onSignIn?.())}
182
- >
183
- <Show when={local.authenticated} fallback={<LogIn aria-hidden="true" />}>
184
- <LogOut aria-hidden="true" />
185
- </Show>
186
- <span>{local.authenticated ? 'Sign out' : 'Sign in'}</span>
187
- </DropdownMenuItem>
188
- </DropdownMenuGroup>
191
+ <Show when={local.showSession !== false}>
192
+ <DropdownMenuSeparator />
193
+ <DropdownMenuGroup>
194
+ <DropdownMenuItem
195
+ disabled={local.busy}
196
+ onSelect={() => (local.authenticated ? local.onSignOut?.() : local.onSignIn?.())}
197
+ >
198
+ <Show when={local.authenticated} fallback={<LogIn aria-hidden="true" />}>
199
+ <LogOut aria-hidden="true" />
200
+ </Show>
201
+ <span>{local.authenticated ? 'Sign out' : 'Sign in'}</span>
202
+ </DropdownMenuItem>
203
+ </DropdownMenuGroup>
204
+ </Show>
189
205
  </DropdownMenuContent>
190
206
  </DropdownMenu>
191
207
  </Tooltip>
192
208
  )
193
209
  }
194
210
 
211
+ /** The common support menu, with one app-specific entry before the shared items. */
212
+ export function createAppMenuItems(options: {
213
+ primaryItem?: AccountMenuItem
214
+ settingsShortcut?: string
215
+ onAbout?: AccountMenuItem['onSelectAfterClose']
216
+ onHelp?: AccountMenuItem['onSelectAfterClose']
217
+ onFeedback?: AccountMenuItem['onSelectAfterClose']
218
+ onUpdates?: AccountMenuItem['onSelectAfterClose']
219
+ onSettings?: AccountMenuItem['onSelectAfterClose']
220
+ }): AccountMenuItem[] {
221
+ return [
222
+ ...(options.primaryItem ? [options.primaryItem] : []),
223
+ {
224
+ id: 'about',
225
+ label: 'About',
226
+ disabled: !options.onAbout,
227
+ onSelectAfterClose: options.onAbout,
228
+ },
229
+ {
230
+ id: 'help',
231
+ label: 'Help Center',
232
+ disabled: !options.onHelp,
233
+ onSelectAfterClose: options.onHelp,
234
+ },
235
+ {
236
+ id: 'feedback',
237
+ label: 'Send Feedback',
238
+ disabled: !options.onFeedback,
239
+ onSelectAfterClose: options.onFeedback,
240
+ },
241
+ {
242
+ id: 'updates',
243
+ label: 'Updates',
244
+ platform: 'desktop',
245
+ disabled: !options.onUpdates,
246
+ onSelectAfterClose: options.onUpdates,
247
+ },
248
+ {
249
+ id: 'settings',
250
+ label: 'Settings',
251
+ shortcut: options.settingsShortcut,
252
+ disabled: !options.onSettings,
253
+ onSelectAfterClose: options.onSettings,
254
+ },
255
+ ]
256
+ }
257
+
195
258
  function AccountMenuButton(props: ComponentProps<typeof Button>) {
196
259
  return <DropdownMenuTrigger as={Button} {...props} />
197
260
  }
@@ -1 +1,6 @@
1
- export { AccountMenu, type AccountMenuItem, type AccountMenuProps } from './account-menu'
1
+ export {
2
+ AccountMenu,
3
+ createAppMenuItems,
4
+ type AccountMenuItem,
5
+ type AccountMenuProps,
6
+ } from './account-menu'
@@ -0,0 +1,104 @@
1
+ import { createSignal, For, onCleanup, Show } from 'solid-js'
2
+ import { ExternalLink } from 'lucide-solid'
3
+ import { cn } from '#lib/utils'
4
+ import { ActionButton } from '../action-button'
5
+ import { Alert, AlertDescription } from '../../ui/alert'
6
+ import { Card, CardContent, CardHeader } from '../../ui/card'
7
+ import { Kbd, KbdGroup } from '../../ui/kbd'
8
+
9
+ export type HelpShortcut = { label: string; keys: readonly string[] }
10
+ export type HelpLink = { label: string; description?: string; url: string }
11
+ export type HelpCenterProps = {
12
+ appName: string
13
+ shortcuts: readonly HelpShortcut[]
14
+ links: readonly HelpLink[]
15
+ openExternal?: (url: string) => Promise<void>
16
+ class?: string
17
+ }
18
+
19
+ /** A shared help page: real keyboard shortcuts and the application's project resources. */
20
+ export function HelpCenter(props: HelpCenterProps) {
21
+ const [error, setError] = createSignal('')
22
+ let disposed = false
23
+ onCleanup(() => {
24
+ disposed = true
25
+ })
26
+ const openLink = async (url: string) => {
27
+ setError('')
28
+ try {
29
+ await props.openExternal?.(url)
30
+ } catch {
31
+ if (!disposed) setError('Could not open this help resource. Please try again.')
32
+ }
33
+ }
34
+ return (
35
+ <div class={cn('flex min-w-0 flex-col gap-5', props.class)}>
36
+ <div class="flex flex-col gap-2">
37
+ <h1 class="text-xl font-semibold tracking-tight">Help Center</h1>
38
+ <p class="text-sm text-muted-foreground">
39
+ Keyboard shortcuts and resources for {props.appName}.
40
+ </p>
41
+ </div>
42
+ <Card>
43
+ <CardHeader>
44
+ <h2 class="text-base font-semibold text-primary">Keyboard shortcuts</h2>
45
+ </CardHeader>
46
+ <CardContent>
47
+ <ul class="flex flex-col gap-3">
48
+ <For each={props.shortcuts}>
49
+ {(shortcut) => (
50
+ <li class="flex flex-wrap items-center justify-between gap-2">
51
+ <span class="text-sm">{shortcut.label}</span>
52
+ <KbdGroup>
53
+ <For each={shortcut.keys}>{(key) => <Kbd>{key}</Kbd>}</For>
54
+ </KbdGroup>
55
+ </li>
56
+ )}
57
+ </For>
58
+ </ul>
59
+ </CardContent>
60
+ </Card>
61
+ <Card>
62
+ <CardHeader>
63
+ <h2 class="text-base font-semibold text-primary">Project links</h2>
64
+ </CardHeader>
65
+ <CardContent>
66
+ <ul class="flex flex-col gap-4">
67
+ <For each={props.links}>
68
+ {(link) => (
69
+ <li class="flex min-w-0 flex-col items-start gap-1">
70
+ <ActionButton
71
+ as="a"
72
+ variant="link"
73
+ size="sm"
74
+ href={link.url}
75
+ target="_blank"
76
+ rel="noopener noreferrer"
77
+ tooltip={`Open ${link.label} in your browser.`}
78
+ onClick={(event) => {
79
+ if (props.openExternal) {
80
+ event.preventDefault()
81
+ void openLink(link.url)
82
+ }
83
+ }}
84
+ >
85
+ {link.label}
86
+ <ExternalLink aria-hidden="true" />
87
+ </ActionButton>
88
+ <Show when={link.description}>
89
+ <p class="text-sm text-muted-foreground">{link.description}</p>
90
+ </Show>
91
+ </li>
92
+ )}
93
+ </For>
94
+ </ul>
95
+ </CardContent>
96
+ </Card>
97
+ <Show when={error()}>
98
+ <Alert variant="destructive" role="alert">
99
+ <AlertDescription>{error()}</AlertDescription>
100
+ </Alert>
101
+ </Show>
102
+ </div>
103
+ )
104
+ }
@@ -0,0 +1,2 @@
1
+ /** Shared keyboard shortcuts and project resource page. */
2
+ export { HelpCenter, type HelpCenterProps, type HelpShortcut, type HelpLink } from './help-center'