@adea-ai/ui 0.95.7 → 0.97.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/components/composites/about-dialog/about-dialog.d.ts +17 -0
- package/dist/components/composites/about-dialog/about-dialog.d.ts.map +1 -0
- package/dist/components/composites/about-dialog/about-dialog.js +167 -0
- package/dist/components/composites/about-dialog/about-dialog.js.map +1 -0
- package/dist/components/composites/about-dialog/index.d.ts +3 -0
- package/dist/components/composites/about-dialog/index.d.ts.map +1 -0
- package/dist/components/composites/about-dialog/index.js +2 -0
- package/dist/components/composites/account-menu/account-menu.d.ts +12 -0
- package/dist/components/composites/account-menu/account-menu.d.ts.map +1 -1
- package/dist/components/composites/account-menu/account-menu.js +91 -49
- package/dist/components/composites/account-menu/account-menu.js.map +1 -1
- package/dist/components/composites/account-menu/index.d.ts +1 -1
- package/dist/components/composites/account-menu/index.d.ts.map +1 -1
- package/dist/components/composites/account-menu/index.js +2 -2
- package/dist/components/composites/catalog-browser/catalog-browser-group.d.ts +0 -1
- package/dist/components/composites/catalog-browser/catalog-browser-group.d.ts.map +1 -1
- package/dist/components/composites/catalog-browser/catalog-browser-group.js +16 -18
- package/dist/components/composites/catalog-browser/catalog-browser-group.js.map +1 -1
- package/dist/components/composites/catalog-browser/catalog-browser.d.ts.map +1 -1
- package/dist/components/composites/catalog-browser/catalog-browser.js +7 -7
- package/dist/components/composites/catalog-browser/catalog-browser.js.map +1 -1
- package/dist/components/composites/catalog-browser/catalog-detail.d.ts +1 -1
- package/dist/components/composites/catalog-browser/catalog-detail.d.ts.map +1 -1
- package/dist/components/composites/catalog-browser/catalog-detail.js +57 -73
- package/dist/components/composites/catalog-browser/catalog-detail.js.map +1 -1
- package/dist/components/composites/catalog-browser/catalog-types.d.ts +2 -1
- package/dist/components/composites/catalog-browser/catalog-types.d.ts.map +1 -1
- package/dist/components/composites/help-center/help-center.d.ts +19 -0
- package/dist/components/composites/help-center/help-center.d.ts.map +1 -0
- package/dist/components/composites/help-center/help-center.js +135 -0
- package/dist/components/composites/help-center/help-center.js.map +1 -0
- package/dist/components/composites/help-center/index.d.ts +3 -0
- package/dist/components/composites/help-center/index.d.ts.map +1 -0
- package/dist/components/composites/help-center/index.js +2 -0
- package/dist/components/composites/update-dialog/update-dialog.d.ts +11 -1
- package/dist/components/composites/update-dialog/update-dialog.d.ts.map +1 -1
- package/dist/components/composites/update-dialog/update-dialog.js +209 -78
- package/dist/components/composites/update-dialog/update-dialog.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/r/about-dialog.json +26 -0
- package/dist/r/catalog-browser.json +0 -1
- package/dist/r/help-center.json +28 -0
- package/dist/r/registry.json +55 -1
- package/dist/r/src/components/composites/about-dialog/about-dialog.tsx +161 -0
- package/dist/r/src/components/composites/about-dialog/index.ts +2 -0
- package/dist/r/src/components/composites/account-menu/account-menu.tsx +64 -12
- package/dist/r/src/components/composites/account-menu/index.ts +6 -1
- package/dist/r/src/components/composites/catalog-browser/catalog-browser-group.tsx +19 -8
- package/dist/r/src/components/composites/catalog-browser/catalog-browser.tsx +5 -3
- package/dist/r/src/components/composites/catalog-browser/catalog-detail.tsx +36 -35
- package/dist/r/src/components/composites/catalog-browser/catalog-types.ts +2 -1
- package/dist/r/src/components/composites/help-center/help-center.tsx +104 -0
- package/dist/r/src/components/composites/help-center/index.ts +2 -0
- package/dist/r/src/components/composites/update-dialog/update-dialog.tsx +200 -27
- package/dist/r/update-dialog.json +1 -0
- package/package.json +1 -1
- package/registry.json +55 -1
- package/src/components/composites/about-dialog/about-dialog.tsx +161 -0
- package/src/components/composites/about-dialog/index.ts +2 -0
- package/src/components/composites/account-menu/account-menu.tsx +64 -12
- package/src/components/composites/account-menu/index.ts +6 -1
- package/src/components/composites/catalog-browser/catalog-browser-group.tsx +19 -8
- package/src/components/composites/catalog-browser/catalog-browser.tsx +5 -3
- package/src/components/composites/catalog-browser/catalog-detail.tsx +36 -35
- package/src/components/composites/catalog-browser/catalog-types.ts +2 -1
- package/src/components/composites/help-center/help-center.tsx +104 -0
- package/src/components/composites/help-center/index.ts +2 -0
- package/src/components/composites/update-dialog/update-dialog.tsx +200 -27
- package/src/index.ts +2 -0
|
@@ -64,6 +64,8 @@ export type AccountMenuProps = {
|
|
|
64
64
|
platform?: 'desktop' | 'web'
|
|
65
65
|
/** Whether a session exists. Decides the sign-in/sign-out row. */
|
|
66
66
|
authenticated: boolean
|
|
67
|
+
/** Local-only applications can omit the session actions altogether. */
|
|
68
|
+
showSession?: boolean
|
|
67
69
|
/** Disable the session row while a sign-in or sign-out is in flight. */
|
|
68
70
|
busy?: boolean
|
|
69
71
|
/** The name shown on the trigger's accessible label. */
|
|
@@ -90,6 +92,7 @@ export function AccountMenu(props: AccountMenuProps) {
|
|
|
90
92
|
'items',
|
|
91
93
|
'platform',
|
|
92
94
|
'authenticated',
|
|
95
|
+
'showSession',
|
|
93
96
|
'busy',
|
|
94
97
|
'label',
|
|
95
98
|
'onSignIn',
|
|
@@ -174,24 +177,73 @@ export function AccountMenu(props: AccountMenuProps) {
|
|
|
174
177
|
)}
|
|
175
178
|
</For>
|
|
176
179
|
</DropdownMenuGroup>
|
|
177
|
-
<
|
|
178
|
-
|
|
179
|
-
<
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
<
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
180
|
+
<Show when={local.showSession !== false}>
|
|
181
|
+
<DropdownMenuSeparator />
|
|
182
|
+
<DropdownMenuGroup>
|
|
183
|
+
<DropdownMenuItem
|
|
184
|
+
disabled={local.busy}
|
|
185
|
+
onSelect={() => (local.authenticated ? local.onSignOut?.() : local.onSignIn?.())}
|
|
186
|
+
>
|
|
187
|
+
<Show when={local.authenticated} fallback={<LogIn aria-hidden="true" />}>
|
|
188
|
+
<LogOut aria-hidden="true" />
|
|
189
|
+
</Show>
|
|
190
|
+
<span>{local.authenticated ? 'Sign out' : 'Sign in'}</span>
|
|
191
|
+
</DropdownMenuItem>
|
|
192
|
+
</DropdownMenuGroup>
|
|
193
|
+
</Show>
|
|
189
194
|
</DropdownMenuContent>
|
|
190
195
|
</DropdownMenu>
|
|
191
196
|
</Tooltip>
|
|
192
197
|
)
|
|
193
198
|
}
|
|
194
199
|
|
|
200
|
+
/** The common support menu, with one app-specific entry before the shared items. */
|
|
201
|
+
export function createAppMenuItems(options: {
|
|
202
|
+
primaryItem?: AccountMenuItem
|
|
203
|
+
settingsShortcut?: string
|
|
204
|
+
onAbout?: AccountMenuItem['onSelectAfterClose']
|
|
205
|
+
onHelp?: AccountMenuItem['onSelectAfterClose']
|
|
206
|
+
onFeedback?: AccountMenuItem['onSelectAfterClose']
|
|
207
|
+
onUpdates?: AccountMenuItem['onSelectAfterClose']
|
|
208
|
+
onSettings?: AccountMenuItem['onSelectAfterClose']
|
|
209
|
+
}): AccountMenuItem[] {
|
|
210
|
+
return [
|
|
211
|
+
...(options.primaryItem ? [options.primaryItem] : []),
|
|
212
|
+
{
|
|
213
|
+
id: 'about',
|
|
214
|
+
label: 'About',
|
|
215
|
+
disabled: !options.onAbout,
|
|
216
|
+
onSelectAfterClose: options.onAbout,
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
id: 'help',
|
|
220
|
+
label: 'Help Center',
|
|
221
|
+
disabled: !options.onHelp,
|
|
222
|
+
onSelectAfterClose: options.onHelp,
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
id: 'feedback',
|
|
226
|
+
label: 'Send Feedback',
|
|
227
|
+
disabled: !options.onFeedback,
|
|
228
|
+
onSelectAfterClose: options.onFeedback,
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
id: 'updates',
|
|
232
|
+
label: 'Updates',
|
|
233
|
+
platform: 'desktop',
|
|
234
|
+
disabled: !options.onUpdates,
|
|
235
|
+
onSelectAfterClose: options.onUpdates,
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
id: 'settings',
|
|
239
|
+
label: 'Settings',
|
|
240
|
+
shortcut: options.settingsShortcut,
|
|
241
|
+
disabled: !options.onSettings,
|
|
242
|
+
onSelectAfterClose: options.onSettings,
|
|
243
|
+
},
|
|
244
|
+
]
|
|
245
|
+
}
|
|
246
|
+
|
|
195
247
|
function AccountMenuButton(props: ComponentProps<typeof Button>) {
|
|
196
248
|
return <DropdownMenuTrigger as={Button} {...props} />
|
|
197
249
|
}
|
|
@@ -33,7 +33,6 @@ function CatalogBrowserRow<Value>(props: {
|
|
|
33
33
|
entry: CatalogBrowserEntry<Value>
|
|
34
34
|
groupId: string
|
|
35
35
|
installedLabel: string
|
|
36
|
-
publishedByLabel: (publisher: string) => string
|
|
37
36
|
disabled: boolean
|
|
38
37
|
renderIcon: (value: Value) => JSX.Element
|
|
39
38
|
onSelect: (entry: CatalogBrowserEntry<Value>, key: string) => void
|
|
@@ -59,7 +58,7 @@ function CatalogBrowserRow<Value>(props: {
|
|
|
59
58
|
// unimportant `h-auto` loses the stylesheet sort and clamps this
|
|
60
59
|
// multi-line row to one control line, spilling its content over the
|
|
61
60
|
// neighbouring rows.
|
|
62
|
-
class="h-auto! min-h-row-lg w-full min-w-0 justify-start gap-3 whitespace-normal"
|
|
61
|
+
class="h-auto! min-h-row-lg w-full min-w-0 justify-start gap-3 py-2.5 whitespace-normal"
|
|
63
62
|
disabled={props.disabled}
|
|
64
63
|
onClick={() => props.onSelect(props.entry, key)}
|
|
65
64
|
>
|
|
@@ -71,9 +70,18 @@ function CatalogBrowserRow<Value>(props: {
|
|
|
71
70
|
<Badge variant="secondary">{props.installedLabel}</Badge>
|
|
72
71
|
</Show>
|
|
73
72
|
</span>
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
{/* Exactly one content line: the description truncates and the
|
|
74
|
+
publisher and category ride the same line as accent chips. */}
|
|
75
|
+
<span class="flex min-w-0 items-center gap-1.5 text-xs">
|
|
76
|
+
<span class="text-muted-foreground min-w-0 truncate">{props.entry.description}</span>
|
|
77
|
+
<span class="ms-auto flex shrink-0 items-center gap-1">
|
|
78
|
+
<span class="rounded bg-primary-subtle px-1.5 py-0.5 text-2xs font-medium text-primary">
|
|
79
|
+
{props.entry.publisher}
|
|
80
|
+
</span>
|
|
81
|
+
<span class="rounded bg-primary-subtle px-1.5 py-0.5 text-2xs font-medium text-primary">
|
|
82
|
+
{props.entry.category}
|
|
83
|
+
</span>
|
|
84
|
+
</span>
|
|
77
85
|
</span>
|
|
78
86
|
</span>
|
|
79
87
|
<ChevronRight aria-hidden="true" />
|
|
@@ -87,7 +95,6 @@ export function CatalogBrowserGroup<Value>(props: {
|
|
|
87
95
|
previewCount: number
|
|
88
96
|
disabled: boolean
|
|
89
97
|
installedLabel: string
|
|
90
|
-
publishedByLabel: (publisher: string) => string
|
|
91
98
|
showMoreLabel: (entries: readonly CatalogBrowserEntry<Value>[], remainingCount: number) => string
|
|
92
99
|
showLessLabel: string
|
|
93
100
|
renderIcon: (value: Value) => JSX.Element
|
|
@@ -108,7 +115,12 @@ export function CatalogBrowserGroup<Value>(props: {
|
|
|
108
115
|
<h3 id={headingId} class="min-w-0 flex-1 truncate text-sm font-semibold">
|
|
109
116
|
{props.group.label}
|
|
110
117
|
</h3>
|
|
111
|
-
<Badge
|
|
118
|
+
<Badge
|
|
119
|
+
variant="outline"
|
|
120
|
+
class="border-primary/25 bg-primary-subtle tabular-nums text-primary"
|
|
121
|
+
>
|
|
122
|
+
{props.group.entries.length}
|
|
123
|
+
</Badge>
|
|
112
124
|
</header>
|
|
113
125
|
<div id={listId} class="grid min-w-0 grid-cols-1 gap-2 md:grid-cols-2">
|
|
114
126
|
<For each={rows()}>
|
|
@@ -117,7 +129,6 @@ export function CatalogBrowserGroup<Value>(props: {
|
|
|
117
129
|
entry={row.item()}
|
|
118
130
|
groupId={props.group.id}
|
|
119
131
|
installedLabel={props.installedLabel}
|
|
120
|
-
publishedByLabel={props.publishedByLabel}
|
|
121
132
|
disabled={props.disabled}
|
|
122
133
|
renderIcon={props.renderIcon}
|
|
123
134
|
onSelect={props.onSelect}
|
|
@@ -131,7 +131,7 @@ export function CatalogBrowser<Value>(props: CatalogBrowserProps<Value>) {
|
|
|
131
131
|
<div class="flex min-w-0 shrink-0 flex-wrap items-center gap-2 px-4 pt-2">
|
|
132
132
|
{props.filterControl}
|
|
133
133
|
<InputGroup class="min-w-0 flex-1 sm:max-w-md">
|
|
134
|
-
<InputGroupAddon>
|
|
134
|
+
<InputGroupAddon class="text-primary">
|
|
135
135
|
<Search aria-hidden="true" />
|
|
136
136
|
</InputGroupAddon>
|
|
137
137
|
<InputGroupInput
|
|
@@ -143,7 +143,10 @@ export function CatalogBrowser<Value>(props: CatalogBrowserProps<Value>) {
|
|
|
143
143
|
onInput={(event) => props.onQueryChange(event.currentTarget.value)}
|
|
144
144
|
/>
|
|
145
145
|
</InputGroup>
|
|
146
|
-
<span
|
|
146
|
+
<span
|
|
147
|
+
class="text-primary ms-auto text-sm font-medium tabular-nums"
|
|
148
|
+
role="status"
|
|
149
|
+
>
|
|
147
150
|
{countLabel()}
|
|
148
151
|
</span>
|
|
149
152
|
</div>
|
|
@@ -200,7 +203,6 @@ export function CatalogBrowser<Value>(props: CatalogBrowserProps<Value>) {
|
|
|
200
203
|
previewCount={previewCount()}
|
|
201
204
|
disabled={props.interactionDisabled ?? false}
|
|
202
205
|
installedLabel={props.installedLabel}
|
|
203
|
-
publishedByLabel={props.publishedByLabel}
|
|
204
206
|
showMoreLabel={props.showMoreLabel}
|
|
205
207
|
showLessLabel={props.showLessLabel}
|
|
206
208
|
renderIcon={props.renderIcon}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { createUniqueId, Show, type JSX } from 'solid-js'
|
|
2
2
|
import { Badge } from '../../ui/badge'
|
|
3
|
-
import { Card, CardContent, CardFooter, CardHeader } from '../../ui/card'
|
|
4
3
|
|
|
5
4
|
/** Shared item summary and action frame for catalog-owned detail content. */
|
|
6
5
|
export function CatalogDetail(props: {
|
|
@@ -17,48 +16,50 @@ export function CatalogDetail(props: {
|
|
|
17
16
|
children?: JSX.Element
|
|
18
17
|
}) {
|
|
19
18
|
return (
|
|
20
|
-
<article class="grid min-w-0 gap-
|
|
21
|
-
<
|
|
22
|
-
<
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
<div class="
|
|
27
|
-
<
|
|
28
|
-
<
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
{props.badges}
|
|
33
|
-
</div>
|
|
34
|
-
<h2 class="text-base leading-tight font-semibold tracking-tight">{props.title}</h2>
|
|
35
|
-
<p class="text-muted-foreground text-sm">{props.description}</p>
|
|
36
|
-
<p class="text-muted-foreground text-xs">{props.publishedByLabel(props.publisher)}</p>
|
|
19
|
+
<article class="grid min-w-0 gap-2" data-catalog-detail>
|
|
20
|
+
<div class="flex min-w-0 items-start gap-3 rounded-xl border border-border p-3">
|
|
21
|
+
<Show when={props.leading}>
|
|
22
|
+
<span class="shrink-0">{props.leading}</span>
|
|
23
|
+
</Show>
|
|
24
|
+
<div class="grid min-w-0 flex-1 gap-1">
|
|
25
|
+
<div class="flex min-w-0 flex-wrap items-center gap-1.5">
|
|
26
|
+
<Show when={props.eyebrow}>
|
|
27
|
+
<Badge variant="outline">{props.eyebrow}</Badge>
|
|
28
|
+
</Show>
|
|
29
|
+
<Badge variant="secondary">{props.category}</Badge>
|
|
30
|
+
{props.badges}
|
|
37
31
|
</div>
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
{props.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
32
|
+
<h2 class="text-base leading-tight font-semibold tracking-tight">{props.title}</h2>
|
|
33
|
+
<p class="text-muted-foreground text-sm">{props.description}</p>
|
|
34
|
+
<p class="text-muted-foreground text-xs">{props.publishedByLabel(props.publisher)}</p>
|
|
35
|
+
</div>
|
|
36
|
+
<Show when={props.action || props.status}>
|
|
37
|
+
<div class="flex shrink-0 flex-col items-end gap-2">
|
|
38
|
+
{props.action}
|
|
39
|
+
{props.status}
|
|
40
|
+
</div>
|
|
41
|
+
</Show>
|
|
42
|
+
</div>
|
|
43
|
+
<div class="grid min-w-0 gap-2">{props.children}</div>
|
|
45
44
|
</article>
|
|
46
45
|
)
|
|
47
46
|
}
|
|
48
47
|
|
|
49
|
-
/** A shared
|
|
48
|
+
/** A shared compact row for host-specific fields, permissions, and copy. */
|
|
50
49
|
export function CatalogDetailSection(props: { title: string; children?: JSX.Element }) {
|
|
51
50
|
const headingId = `catalog-detail-section-${createUniqueId()}`
|
|
52
51
|
return (
|
|
53
|
-
<section
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
52
|
+
<section
|
|
53
|
+
class="grid min-w-0 gap-1.5 rounded-xl border border-border p-3"
|
|
54
|
+
aria-labelledby={headingId}
|
|
55
|
+
>
|
|
56
|
+
<h3
|
|
57
|
+
id={headingId}
|
|
58
|
+
class="text-2xs font-semibold tracking-wider text-muted-foreground uppercase"
|
|
59
|
+
>
|
|
60
|
+
{props.title}
|
|
61
|
+
</h3>
|
|
62
|
+
<div class="grid min-w-0 gap-1.5">{props.children}</div>
|
|
62
63
|
</section>
|
|
63
64
|
)
|
|
64
65
|
}
|
|
@@ -73,7 +73,8 @@ export type CatalogBrowserProps<Value> = Readonly<{
|
|
|
73
73
|
backLabel: string
|
|
74
74
|
detailRegionLabel: string
|
|
75
75
|
installedLabel: string
|
|
76
|
-
|
|
76
|
+
/** Optional legacy label: rows now show the bare publisher as an accent chip. */
|
|
77
|
+
publishedByLabel?: (publisher: string) => string
|
|
77
78
|
showMoreLabel: (entries: readonly CatalogBrowserEntry<Value>[], remainingCount: number) => string
|
|
78
79
|
showLessLabel: string
|
|
79
80
|
/** Temporarily disables catalog controls while a host menu owns interaction. */
|
|
@@ -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
|
+
}
|