@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.
- 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 +20 -1
- package/dist/components/composites/account-menu/account-menu.d.ts.map +1 -1
- package/dist/components/composites/account-menu/account-menu.js +103 -50
- 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/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/help-center.json +28 -0
- package/dist/r/registry.json +55 -0
- 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 +76 -13
- package/dist/r/src/components/composites/account-menu/index.ts +6 -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 -0
- 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 +76 -13
- package/src/components/composites/account-menu/index.ts +6 -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
|
@@ -8,9 +8,19 @@ import {
|
|
|
8
8
|
splitProps,
|
|
9
9
|
} from 'solid-js'
|
|
10
10
|
import type { Accessor } from 'solid-js'
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
Check,
|
|
13
|
+
CircleStop,
|
|
14
|
+
Download,
|
|
15
|
+
ExternalLink,
|
|
16
|
+
LoaderCircle,
|
|
17
|
+
Monitor,
|
|
18
|
+
RefreshCw,
|
|
19
|
+
Sparkles,
|
|
20
|
+
} from 'lucide-solid'
|
|
12
21
|
import { Badge } from '../../ui/badge'
|
|
13
22
|
import { Button } from '../../ui/button'
|
|
23
|
+
import { ActionButton } from '../action-button'
|
|
14
24
|
import {
|
|
15
25
|
Dialog,
|
|
16
26
|
DialogClose,
|
|
@@ -79,6 +89,9 @@ export type UpdatePhase =
|
|
|
79
89
|
| 'downloading'
|
|
80
90
|
| 'installing'
|
|
81
91
|
| 'installed'
|
|
92
|
+
| 'cancelling'
|
|
93
|
+
| 'cancelled'
|
|
94
|
+
| 'unavailable'
|
|
82
95
|
| 'failed'
|
|
83
96
|
|
|
84
97
|
/** A snapshot of the updater. Every field the dialog renders, and nothing else. */
|
|
@@ -124,6 +137,12 @@ export type UpdateAdapter = Readonly<{
|
|
|
124
137
|
* wrong one.
|
|
125
138
|
*/
|
|
126
139
|
install(expectedVersion: string): Promise<UpdateState>
|
|
140
|
+
/** Optional native cancellation, available only while work is in flight. */
|
|
141
|
+
cancel?(): Promise<UpdateState>
|
|
142
|
+
/** Enable status polling during native work; omit for push-driven adapters. */
|
|
143
|
+
pollIntervalMs?: number
|
|
144
|
+
/** Route external links through the native shell when needed. */
|
|
145
|
+
openExternal?(url: string): Promise<void>
|
|
127
146
|
/** False in a browser build, where there is no installer to drive. */
|
|
128
147
|
isDesktopRuntime(): boolean
|
|
129
148
|
}>
|
|
@@ -132,6 +151,10 @@ export type UpdateDialogProps = {
|
|
|
132
151
|
adapter: UpdateAdapter
|
|
133
152
|
/** The application's name, used in the title and the copy. */
|
|
134
153
|
appName?: string
|
|
154
|
+
/** The actual desktop application icon, supplied by its host. */
|
|
155
|
+
appIcon?: string
|
|
156
|
+
/** Complete build-time history, also available before the native status arrives. */
|
|
157
|
+
changelog?: string
|
|
135
158
|
/** Shown before the first status arrives, so the dialog is never blank. */
|
|
136
159
|
fallbackVersion?: string
|
|
137
160
|
/** Copy for the adapter's non-desktop case. */
|
|
@@ -181,7 +204,10 @@ function errorMessage(caught: unknown, fallback: string): string {
|
|
|
181
204
|
*/
|
|
182
205
|
function isBusy(state: UpdateState | null): boolean {
|
|
183
206
|
return (
|
|
184
|
-
state?.phase === 'checking' ||
|
|
207
|
+
state?.phase === 'checking' ||
|
|
208
|
+
state?.phase === 'downloading' ||
|
|
209
|
+
state?.phase === 'installing' ||
|
|
210
|
+
state?.phase === 'cancelling'
|
|
185
211
|
)
|
|
186
212
|
}
|
|
187
213
|
|
|
@@ -189,6 +215,8 @@ export function UpdateDialog(props: UpdateDialogProps) {
|
|
|
189
215
|
const [local] = splitProps(props, [
|
|
190
216
|
'adapter',
|
|
191
217
|
'appName',
|
|
218
|
+
'appIcon',
|
|
219
|
+
'changelog',
|
|
192
220
|
'fallbackVersion',
|
|
193
221
|
'desktopOnlyMessage',
|
|
194
222
|
'open',
|
|
@@ -216,6 +244,7 @@ export function UpdateDialog(props: UpdateDialogProps) {
|
|
|
216
244
|
const [busy, setBusy] = createSignal(false)
|
|
217
245
|
const [error, setError] = createSignal('')
|
|
218
246
|
const [checkFailed, setCheckFailed] = createSignal(false)
|
|
247
|
+
const [cancelPending, setCancelPending] = createSignal(false)
|
|
219
248
|
let lifecycleEpoch = 0
|
|
220
249
|
let activeOpenEpoch: number | undefined
|
|
221
250
|
let disposed = false
|
|
@@ -309,6 +338,7 @@ export function UpdateDialog(props: UpdateDialogProps) {
|
|
|
309
338
|
*/
|
|
310
339
|
createEffect(() => {
|
|
311
340
|
if (!open() || !desktop()) {
|
|
341
|
+
setCancelPending(false)
|
|
312
342
|
if (activeOpenEpoch !== undefined) {
|
|
313
343
|
activeOpenEpoch = undefined
|
|
314
344
|
lifecycleEpoch += 1
|
|
@@ -329,6 +359,7 @@ export function UpdateDialog(props: UpdateDialogProps) {
|
|
|
329
359
|
})
|
|
330
360
|
setError('')
|
|
331
361
|
setCheckFailed(false)
|
|
362
|
+
setCancelPending(false)
|
|
332
363
|
setBusy(true)
|
|
333
364
|
void (async () => {
|
|
334
365
|
try {
|
|
@@ -357,10 +388,17 @@ export function UpdateDialog(props: UpdateDialogProps) {
|
|
|
357
388
|
if (!version || !isCurrent()) return
|
|
358
389
|
setBusy(true)
|
|
359
390
|
setError('')
|
|
391
|
+
setState((current) =>
|
|
392
|
+
current ? { ...current, phase: 'downloading', downloadedBytes: 0, totalBytes: null } : current
|
|
393
|
+
)
|
|
360
394
|
try {
|
|
361
395
|
const next = await local.adapter.install(version)
|
|
362
396
|
if (!isCurrent()) return
|
|
363
|
-
setState(
|
|
397
|
+
setState(
|
|
398
|
+
cancelPending() && ['downloading', 'installing'].includes(next.phase)
|
|
399
|
+
? { ...next, phase: 'cancelling' }
|
|
400
|
+
: next
|
|
401
|
+
)
|
|
364
402
|
// A refused install answers with a normal payload whose phase is `failed`.
|
|
365
403
|
// Without this the click looked like nothing happened.
|
|
366
404
|
if (next.phase === 'failed') {
|
|
@@ -377,12 +415,87 @@ export function UpdateDialog(props: UpdateDialogProps) {
|
|
|
377
415
|
}
|
|
378
416
|
}
|
|
379
417
|
|
|
380
|
-
|
|
418
|
+
// Poll only an open, active native operation. Epoch ownership prevents a late
|
|
419
|
+
// status response from replacing a reopened dialog's newer snapshot.
|
|
420
|
+
createEffect(() => {
|
|
421
|
+
const phase = state()?.phase
|
|
422
|
+
if (
|
|
423
|
+
!open() ||
|
|
424
|
+
!desktop() ||
|
|
425
|
+
!local.adapter.pollIntervalMs ||
|
|
426
|
+
!['downloading', 'installing', 'cancelling'].includes(phase ?? '')
|
|
427
|
+
)
|
|
428
|
+
return
|
|
429
|
+
const epoch = activeOpenEpoch
|
|
430
|
+
let active = true
|
|
431
|
+
let requestInFlight = false
|
|
432
|
+
const timer = window.setInterval(
|
|
433
|
+
() => {
|
|
434
|
+
if (requestInFlight) return
|
|
435
|
+
requestInFlight = true
|
|
436
|
+
void (async () => {
|
|
437
|
+
try {
|
|
438
|
+
const next = await local.adapter.getStatus()
|
|
439
|
+
if (!active || disposed || !open() || activeOpenEpoch !== epoch) return
|
|
440
|
+
setState(
|
|
441
|
+
cancelPending() && ['downloading', 'installing'].includes(next.phase)
|
|
442
|
+
? { ...next, phase: 'cancelling' }
|
|
443
|
+
: next
|
|
444
|
+
)
|
|
445
|
+
if (next.phase === 'failed')
|
|
446
|
+
setError(errorMessage(next.error, 'Update installation failed'))
|
|
447
|
+
} catch (caught) {
|
|
448
|
+
if (active && !disposed && activeOpenEpoch === epoch)
|
|
449
|
+
setError(errorMessage(caught, 'Update progress is unavailable'))
|
|
450
|
+
} finally {
|
|
451
|
+
requestInFlight = false
|
|
452
|
+
}
|
|
453
|
+
})()
|
|
454
|
+
},
|
|
455
|
+
Math.max(250, local.adapter.pollIntervalMs)
|
|
456
|
+
)
|
|
457
|
+
onCleanup(() => {
|
|
458
|
+
active = false
|
|
459
|
+
window.clearInterval(timer)
|
|
460
|
+
})
|
|
461
|
+
})
|
|
462
|
+
|
|
463
|
+
const cancel = async () => {
|
|
464
|
+
if (!local.adapter.cancel || cancelPending() || state()?.phase === 'cancelling') return
|
|
465
|
+
const epoch = activeOpenEpoch
|
|
466
|
+
setError('')
|
|
467
|
+
setCancelPending(true)
|
|
468
|
+
setState((current) => (current ? { ...current, phase: 'cancelling' } : current))
|
|
469
|
+
try {
|
|
470
|
+
const next = await local.adapter.cancel()
|
|
471
|
+
if (!disposed && open() && activeOpenEpoch === epoch) setState(next)
|
|
472
|
+
} catch (caught) {
|
|
473
|
+
if (!disposed && open() && activeOpenEpoch === epoch) {
|
|
474
|
+
setError(errorMessage(caught, 'Could not cancel the update'))
|
|
475
|
+
await loadStatus(() => !disposed && open() && activeOpenEpoch === epoch)
|
|
476
|
+
}
|
|
477
|
+
} finally {
|
|
478
|
+
if (!disposed && open() && activeOpenEpoch === epoch) setCancelPending(false)
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const openRelease = async (url: string) => {
|
|
483
|
+
try {
|
|
484
|
+
if (local.adapter.openExternal) await local.adapter.openExternal(url)
|
|
485
|
+
else window.open(url, '_blank', 'noopener,noreferrer')
|
|
486
|
+
} catch (caught) {
|
|
487
|
+
if (!disposed) setError(errorMessage(caught, 'Could not open the release page'))
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const changelog = createMemo(() =>
|
|
492
|
+
plainTextFromMarkdown(state()?.changelog || local.changelog || '')
|
|
493
|
+
)
|
|
381
494
|
const notes = () => {
|
|
382
495
|
const value = state()?.releaseNotes
|
|
383
496
|
return value ? plainTextFromMarkdown(value) : ''
|
|
384
497
|
}
|
|
385
|
-
const working = () => busy() || isBusy(state())
|
|
498
|
+
const working = () => busy() || cancelPending() || isBusy(state())
|
|
386
499
|
const isCurrent = () => state()?.phase === 'current' && !error() && !checkFailed() && !working()
|
|
387
500
|
|
|
388
501
|
const triggerLabel = () => {
|
|
@@ -432,14 +545,32 @@ export function UpdateDialog(props: UpdateDialogProps) {
|
|
|
432
545
|
|
|
433
546
|
<DialogContent
|
|
434
547
|
class={cn('max-w-3xl', local.class)}
|
|
435
|
-
|
|
548
|
+
onKeyDown={(event: KeyboardEvent) => {
|
|
549
|
+
// An explanatory tooltip must not consume the popup's close shortcut,
|
|
550
|
+
// including while its exit animation still owns Kobalte's top layer.
|
|
551
|
+
if (event.key === 'Escape' && !event.defaultPrevented) {
|
|
552
|
+
event.preventDefault()
|
|
553
|
+
setOpen(false)
|
|
554
|
+
}
|
|
555
|
+
}}
|
|
556
|
+
onOpenAutoFocus={(event) => {
|
|
557
|
+
focusRestoration.onOpenAutoFocus(event)
|
|
558
|
+
// Start at the readable panel rather than opening an action's tooltip.
|
|
559
|
+
if (!event.defaultPrevented && event.currentTarget instanceof HTMLElement) {
|
|
560
|
+
event.preventDefault()
|
|
561
|
+
event.currentTarget.focus({ preventScroll: true })
|
|
562
|
+
}
|
|
563
|
+
}}
|
|
436
564
|
onCloseAutoFocus={focusRestoration.onCloseAutoFocus}
|
|
437
565
|
>
|
|
438
566
|
<DialogHeader>
|
|
439
567
|
<div class="flex items-center gap-3">
|
|
440
|
-
<
|
|
441
|
-
|
|
442
|
-
|
|
568
|
+
<Show
|
|
569
|
+
when={local.appIcon}
|
|
570
|
+
fallback={<Monitor class="size-10 text-primary" aria-hidden="true" />}
|
|
571
|
+
>
|
|
572
|
+
{(icon) => <img src={icon()} alt="" class="size-10" aria-hidden="true" />}
|
|
573
|
+
</Show>
|
|
443
574
|
<div>
|
|
444
575
|
<DialogTitle>Version & updates</DialogTitle>
|
|
445
576
|
<DialogDescription>
|
|
@@ -459,7 +590,7 @@ export function UpdateDialog(props: UpdateDialogProps) {
|
|
|
459
590
|
<p class="text-2xs font-semibold tracking-widest text-muted-foreground uppercase">
|
|
460
591
|
Installed version
|
|
461
592
|
</p>
|
|
462
|
-
<p class="text-xl font-semibold tracking-tight">
|
|
593
|
+
<p class="text-xl font-semibold tracking-tight text-primary">
|
|
463
594
|
v{state()?.currentVersion || local.fallbackVersion || '0.1.0'}
|
|
464
595
|
</p>
|
|
465
596
|
<p class="text-sm text-muted-foreground">
|
|
@@ -469,10 +600,14 @@ export function UpdateDialog(props: UpdateDialogProps) {
|
|
|
469
600
|
<Show
|
|
470
601
|
when={state()?.phase === 'available' && state()?.availableVersion}
|
|
471
602
|
fallback={
|
|
472
|
-
|
|
473
|
-
? '
|
|
474
|
-
: (
|
|
475
|
-
|
|
603
|
+
state()?.phase === 'unavailable'
|
|
604
|
+
? 'No signed package is published for this platform.'
|
|
605
|
+
: state()?.phase === 'cancelled'
|
|
606
|
+
? 'Update cancelled. You can retry when ready.'
|
|
607
|
+
: desktop()
|
|
608
|
+
? 'Check the release channel for the latest signed build.'
|
|
609
|
+
: (local.desktopOnlyMessage ??
|
|
610
|
+
`Open this dialog inside the desktop app to check for updates.`)
|
|
476
611
|
}
|
|
477
612
|
>
|
|
478
613
|
{`A newer release, v${state()?.availableVersion}, is ready.`}
|
|
@@ -483,10 +618,11 @@ export function UpdateDialog(props: UpdateDialogProps) {
|
|
|
483
618
|
</Show>
|
|
484
619
|
</p>
|
|
485
620
|
</div>
|
|
486
|
-
<
|
|
621
|
+
<ActionButton
|
|
487
622
|
type="button"
|
|
488
623
|
variant="outline"
|
|
489
624
|
size="sm"
|
|
625
|
+
tooltip="Check the release channel for the latest signed app version."
|
|
490
626
|
disabled={!desktop() || working()}
|
|
491
627
|
onClick={() => void check()}
|
|
492
628
|
>
|
|
@@ -494,11 +630,17 @@ export function UpdateDialog(props: UpdateDialogProps) {
|
|
|
494
630
|
<LoaderCircle class="animate-spin" aria-hidden="true" />
|
|
495
631
|
</Show>
|
|
496
632
|
{checkFailed() ? 'Retry update check' : 'Check latest version'}
|
|
497
|
-
</
|
|
633
|
+
</ActionButton>
|
|
498
634
|
</div>
|
|
499
635
|
</section>
|
|
500
636
|
|
|
501
|
-
<Show
|
|
637
|
+
<Show
|
|
638
|
+
when={
|
|
639
|
+
state()?.availableVersion &&
|
|
640
|
+
state()?.phase !== 'installed' &&
|
|
641
|
+
state()?.phase !== 'unavailable'
|
|
642
|
+
}
|
|
643
|
+
>
|
|
502
644
|
<section
|
|
503
645
|
class="rounded-xl border border-primary/35 bg-primary-subtle p-4"
|
|
504
646
|
aria-label="Available update"
|
|
@@ -507,7 +649,10 @@ export function UpdateDialog(props: UpdateDialogProps) {
|
|
|
507
649
|
<div class="flex flex-col gap-1">
|
|
508
650
|
<p class="flex items-center gap-2 text-sm font-semibold">
|
|
509
651
|
<Sparkles class="size-4 text-primary" aria-hidden="true" />
|
|
510
|
-
Version {state()?.availableVersion}
|
|
652
|
+
Version {state()?.availableVersion}{' '}
|
|
653
|
+
{['downloading', 'installing', 'cancelling'].includes(state()?.phase ?? '')
|
|
654
|
+
? 'is being prepared'
|
|
655
|
+
: 'is ready'}
|
|
511
656
|
</p>
|
|
512
657
|
<p class="text-sm text-muted-foreground">
|
|
513
658
|
The signed installer is verified before {appName()} restarts.
|
|
@@ -518,12 +663,18 @@ export function UpdateDialog(props: UpdateDialogProps) {
|
|
|
518
663
|
)}
|
|
519
664
|
</Show>
|
|
520
665
|
</div>
|
|
521
|
-
<
|
|
666
|
+
<ActionButton
|
|
667
|
+
type="button"
|
|
668
|
+
size="sm"
|
|
669
|
+
tooltip={`Download the signed update and restart ${appName()}.`}
|
|
670
|
+
disabled={working()}
|
|
671
|
+
onClick={() => void install()}
|
|
672
|
+
>
|
|
522
673
|
<Show when={working()} fallback={<Download aria-hidden="true" />}>
|
|
523
674
|
<LoaderCircle class="animate-spin" aria-hidden="true" />
|
|
524
675
|
</Show>
|
|
525
676
|
Install and restart
|
|
526
|
-
</
|
|
677
|
+
</ActionButton>
|
|
527
678
|
</div>
|
|
528
679
|
</section>
|
|
529
680
|
</Show>
|
|
@@ -532,7 +683,13 @@ export function UpdateDialog(props: UpdateDialogProps) {
|
|
|
532
683
|
Progress with both numbers: a bar alone does not distinguish a stalled
|
|
533
684
|
download from a slow one, and "12.4 MB of 48.1 MB" does.
|
|
534
685
|
*/}
|
|
535
|
-
<Show
|
|
686
|
+
<Show
|
|
687
|
+
when={
|
|
688
|
+
state()?.phase === 'downloading' ||
|
|
689
|
+
state()?.phase === 'installing' ||
|
|
690
|
+
state()?.phase === 'cancelling'
|
|
691
|
+
}
|
|
692
|
+
>
|
|
536
693
|
<section class="grid gap-2" aria-label="Update progress">
|
|
537
694
|
<Progress
|
|
538
695
|
value={progressValue() ?? undefined}
|
|
@@ -554,6 +711,21 @@ export function UpdateDialog(props: UpdateDialogProps) {
|
|
|
554
711
|
Applying the update…
|
|
555
712
|
</Show>
|
|
556
713
|
</p>
|
|
714
|
+
<Show when={local.adapter.cancel}>
|
|
715
|
+
<ActionButton
|
|
716
|
+
type="button"
|
|
717
|
+
variant="outline"
|
|
718
|
+
size="sm"
|
|
719
|
+
tooltip="Stop the current update download or installation."
|
|
720
|
+
disabled={cancelPending() || state()?.phase === 'cancelling'}
|
|
721
|
+
onClick={() => void cancel()}
|
|
722
|
+
>
|
|
723
|
+
<CircleStop aria-hidden="true" />
|
|
724
|
+
{cancelPending() || state()?.phase === 'cancelling'
|
|
725
|
+
? 'Cancelling…'
|
|
726
|
+
: 'Cancel update'}
|
|
727
|
+
</ActionButton>
|
|
728
|
+
</Show>
|
|
557
729
|
</section>
|
|
558
730
|
</Show>
|
|
559
731
|
|
|
@@ -585,7 +757,7 @@ export function UpdateDialog(props: UpdateDialogProps) {
|
|
|
585
757
|
<Show when={notes()}>
|
|
586
758
|
<div class="flex flex-col gap-2">
|
|
587
759
|
<div class="flex items-center gap-2">
|
|
588
|
-
<h2 id="update-release-notes" class="text-sm font-semibold">
|
|
760
|
+
<h2 id="update-release-notes" class="text-sm font-semibold text-primary">
|
|
589
761
|
What changed in this release
|
|
590
762
|
</h2>
|
|
591
763
|
<Badge variant="subtle" size="sm">
|
|
@@ -608,11 +780,11 @@ export function UpdateDialog(props: UpdateDialogProps) {
|
|
|
608
780
|
|
|
609
781
|
<Show when={changelog()}>
|
|
610
782
|
<div class="flex flex-col gap-2">
|
|
611
|
-
<h2 id="update-changelog" class="text-sm font-semibold">
|
|
783
|
+
<h2 id="update-changelog" class="text-sm font-semibold text-primary">
|
|
612
784
|
Installed changelog
|
|
613
785
|
</h2>
|
|
614
786
|
<p class="text-xs text-muted-foreground">
|
|
615
|
-
|
|
787
|
+
All versions and updates included with this installation.
|
|
616
788
|
</p>
|
|
617
789
|
{/* A long text block with no focusable descendant: it needs its own tab
|
|
618
790
|
stop, named by the heading above it, or a keyboard user cannot
|
|
@@ -632,17 +804,18 @@ export function UpdateDialog(props: UpdateDialogProps) {
|
|
|
632
804
|
<DialogFooter>
|
|
633
805
|
<Show when={state()?.releaseUrl}>
|
|
634
806
|
{(url) => (
|
|
635
|
-
<
|
|
807
|
+
<ActionButton
|
|
636
808
|
type="button"
|
|
637
809
|
variant="ghost"
|
|
638
810
|
size="sm"
|
|
811
|
+
tooltip="Open the release history in your browser."
|
|
639
812
|
// `noopener,noreferrer` because the release page is a different
|
|
640
813
|
// origin and must not get a handle on this window.
|
|
641
|
-
onClick={() =>
|
|
814
|
+
onClick={() => void openRelease(url())}
|
|
642
815
|
>
|
|
643
816
|
<ExternalLink aria-hidden="true" />
|
|
644
817
|
View releases
|
|
645
|
-
</
|
|
818
|
+
</ActionButton>
|
|
646
819
|
)}
|
|
647
820
|
</Show>
|
|
648
821
|
<DialogClose as={Button} variant="outline" size="sm">
|
package/src/index.ts
CHANGED
|
@@ -108,6 +108,8 @@ export * from './components/composites/scene-controls'
|
|
|
108
108
|
export * from './components/composites/stat'
|
|
109
109
|
export * from './components/composites/appearance-editor'
|
|
110
110
|
export * from './components/composites/update-dialog'
|
|
111
|
+
export * from './components/composites/about-dialog'
|
|
112
|
+
export * from './components/composites/help-center'
|
|
111
113
|
export * from './components/composites/workspace-mark'
|
|
112
114
|
|
|
113
115
|
/* --- Library ------------------------------------------------------------- */
|