@adea-ai/ui 0.112.1 → 0.113.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 (27) hide show
  1. package/dist/components/composites/update-dialog/channel-control.d.ts +30 -0
  2. package/dist/components/composites/update-dialog/channel-control.d.ts.map +1 -0
  3. package/dist/components/composites/update-dialog/channel-control.js +119 -0
  4. package/dist/components/composites/update-dialog/channel-control.js.map +1 -0
  5. package/dist/components/composites/update-dialog/index.d.ts +2 -1
  6. package/dist/components/composites/update-dialog/index.d.ts.map +1 -1
  7. package/dist/components/composites/update-dialog/index.js +2 -1
  8. package/dist/components/composites/update-dialog/update-dialog.d.ts +1 -2
  9. package/dist/components/composites/update-dialog/update-dialog.d.ts.map +1 -1
  10. package/dist/components/composites/update-dialog/update-dialog.js.map +1 -1
  11. package/dist/components/ui/toast/toast.d.ts +13 -0
  12. package/dist/components/ui/toast/toast.d.ts.map +1 -1
  13. package/dist/components/ui/toast/toast.js +22 -7
  14. package/dist/components/ui/toast/toast.js.map +1 -1
  15. package/dist/index.js +2 -1
  16. package/dist/r/registry.json +7 -0
  17. package/dist/r/src/components/composites/update-dialog/channel-control.tsx +128 -0
  18. package/dist/r/src/components/composites/update-dialog/index.ts +6 -0
  19. package/dist/r/src/components/composites/update-dialog/update-dialog.tsx +1 -1
  20. package/dist/r/src/components/ui/toast/toast.tsx +20 -1
  21. package/dist/r/update-dialog.json +7 -0
  22. package/package.json +1 -1
  23. package/registry.json +7 -0
  24. package/src/components/composites/update-dialog/channel-control.tsx +128 -0
  25. package/src/components/composites/update-dialog/index.ts +6 -0
  26. package/src/components/composites/update-dialog/update-dialog.tsx +1 -1
  27. package/src/components/ui/toast/toast.tsx +20 -1
@@ -0,0 +1,30 @@
1
+ import { type Accessor } from 'solid-js';
2
+ import type { ChannelControlActions } from './update-dialog';
3
+ export type UpdateChannel = 'stable' | 'pre-release' | 'dev';
4
+ export type UpdateChannelControlProps = {
5
+ /** The dialog's channel actions; every save flows through `recheck`. */
6
+ controls: ChannelControlActions;
7
+ /** Read the persisted channel. */
8
+ read: () => Promise<UpdateChannel>;
9
+ /** Persist a selection; resolves with the channel that actually stuck. */
10
+ persist: (value: UpdateChannel) => Promise<UpdateChannel>;
11
+ /** Disable beyond the dialog's own working state, e.g. a host save in flight. */
12
+ disabled?: Accessor<boolean>;
13
+ /** Per-channel row descriptions, when the defaults don't fit the app. */
14
+ descriptions?: Partial<Record<UpdateChannel, string>>;
15
+ /**
16
+ * Re-read the persisted channel whenever this value changes. Supply it when
17
+ * the channel can move while the dialog is closed — a service that gets torn
18
+ * down, or a CLI writing the same setting — and hand it the dialog's `open`.
19
+ */
20
+ reloadOn?: Accessor<unknown>;
21
+ };
22
+ /**
23
+ * The updater dialog's release-channel row: one setting, a select trailing.
24
+ * Every desktop application that ships channels presents the same three of
25
+ * them with the same copy, which is why the row lives here rather than being
26
+ * redrawn per app. A save always flows through the dialog's `recheck`, so a
27
+ * channel switch re-asks the feed and the offer never outlives its channel.
28
+ */
29
+ export declare function UpdateChannelControl(props: UpdateChannelControlProps): import("solid-js").JSX.Element;
30
+ //# sourceMappingURL=channel-control.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-control.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/update-dialog/channel-control.tsx"],"names":[],"mappings":"AAEA,OAAO,EAA+C,KAAK,QAAQ,EAAE,MAAM,UAAU,CAAA;AAErF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAA;AAE5D,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,aAAa,GAAG,KAAK,CAAA;AAc5D,MAAM,MAAM,yBAAyB,GAAG;IACtC,wEAAwE;IACxE,QAAQ,EAAE,qBAAqB,CAAA;IAC/B,kCAAkC;IAClC,IAAI,EAAE,MAAM,OAAO,CAAC,aAAa,CAAC,CAAA;IAClC,0EAA0E;IAC1E,OAAO,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC,aAAa,CAAC,CAAA;IACzD,iFAAiF;IACjF,QAAQ,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;IAC5B,yEAAyE;IACzE,YAAY,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAA;IACrD;;;;OAIG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;CAC7B,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,yBAAyB,kCAiFpE"}
@@ -0,0 +1,119 @@
1
+ import { NativeSelect } from "../../ui/native-select/native-select.js";
2
+ import { SettingsRow } from "../settings/settings.js";
3
+ import { createComponent, insert, template } from "solid-js/web";
4
+ import { Show, createEffect, createSignal, onCleanup } from "solid-js";
5
+ //#region src/components/composites/update-dialog/channel-control.tsx
6
+ var _tmpl$ = /*#__PURE__*/ template(`<div class="flex flex-col gap-2">`);
7
+ var _tmpl$2 = /*#__PURE__*/ template(`<p role=alert>`);
8
+ var CHANNEL_OPTIONS = [
9
+ {
10
+ value: "stable",
11
+ label: "Stable"
12
+ },
13
+ {
14
+ value: "pre-release",
15
+ label: "Pre-release"
16
+ },
17
+ {
18
+ value: "dev",
19
+ label: "Dev"
20
+ }
21
+ ];
22
+ var CHANNEL_DESCRIPTIONS = {
23
+ stable: "Tested releases after about four days in pre-release.",
24
+ "pre-release": "Daily pre-release builds arrive early, with rough edges included.",
25
+ dev: "Follows every dev build from main. Intended for development machines."
26
+ };
27
+ /**
28
+ * The updater dialog's release-channel row: one setting, a select trailing.
29
+ * Every desktop application that ships channels presents the same three of
30
+ * them with the same copy, which is why the row lives here rather than being
31
+ * redrawn per app. A save always flows through the dialog's `recheck`, so a
32
+ * channel switch re-asks the feed and the offer never outlives its channel.
33
+ */
34
+ function UpdateChannelControl(props) {
35
+ const [channel, setChannel] = createSignal("stable");
36
+ const [loaded, setLoaded] = createSignal(false);
37
+ const [saving, setSaving] = createSignal(false);
38
+ const [error, setError] = createSignal(null);
39
+ createEffect(() => {
40
+ props.reloadOn?.();
41
+ let active = true;
42
+ onCleanup(() => {
43
+ active = false;
44
+ });
45
+ props.read().then((value) => {
46
+ if (active) setChannel(value);
47
+ }).catch(() => {
48
+ if (active) setError("The current update channel could not be read.");
49
+ }).finally(() => {
50
+ if (active) setLoaded(true);
51
+ });
52
+ });
53
+ const save = async (value) => {
54
+ const next = value;
55
+ if (!loaded() || saving() || props.controls.disabled() || next === channel()) return;
56
+ const previous = channel();
57
+ let persisted = false;
58
+ setChannel(next);
59
+ setSaving(true);
60
+ setError(null);
61
+ try {
62
+ await props.controls.recheck(async () => {
63
+ try {
64
+ const saved = await props.persist(next);
65
+ setChannel(saved);
66
+ persisted = true;
67
+ } catch (caught) {
68
+ setChannel(previous);
69
+ setError(caught instanceof Error ? caught.message : "The update channel could not be saved.");
70
+ throw caught;
71
+ }
72
+ });
73
+ } catch (caught) {
74
+ if (!persisted) {
75
+ setChannel(previous);
76
+ setError(caught instanceof Error ? caught.message : "The update channel could not be saved.");
77
+ }
78
+ } finally {
79
+ setSaving(false);
80
+ }
81
+ };
82
+ return (() => {
83
+ var _el$ = _tmpl$();
84
+ insert(_el$, createComponent(SettingsRow, {
85
+ label: "Update channel",
86
+ get description() {
87
+ return props.descriptions?.[channel()] ?? CHANNEL_DESCRIPTIONS[channel()];
88
+ },
89
+ get children() {
90
+ return createComponent(NativeSelect, {
91
+ "aria-label": "Update channel",
92
+ get disabled() {
93
+ return !loaded() || saving() || props.controls.disabled() || (props.disabled?.() ?? false);
94
+ },
95
+ get value() {
96
+ return channel();
97
+ },
98
+ options: CHANNEL_OPTIONS,
99
+ onChange: (event) => void save(event.currentTarget.value)
100
+ });
101
+ }
102
+ }), null);
103
+ insert(_el$, createComponent(Show, {
104
+ get when() {
105
+ return error();
106
+ },
107
+ children: (message) => (() => {
108
+ var _el$2 = _tmpl$2();
109
+ insert(_el$2, message);
110
+ return _el$2;
111
+ })()
112
+ }), null);
113
+ return _el$;
114
+ })();
115
+ }
116
+ //#endregion
117
+ export { UpdateChannelControl };
118
+
119
+ //# sourceMappingURL=channel-control.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-control.js","names":["SettingsRow","NativeSelect","createEffect","createSignal","onCleanup","Show","Accessor","ChannelControlActions","UpdateChannel","CHANNEL_OPTIONS","ReadonlyArray","value","label","CHANNEL_DESCRIPTIONS","Record","stable","dev","UpdateChannelControlProps","controls","read","Promise","persist","disabled","descriptions","Partial","reloadOn","UpdateChannelControl","props","channel","setChannel","loaded","setLoaded","saving","setSaving","error","setError","active","then","catch","finally","save","next","previous","persisted","recheck","saved","caught","Error","message","_el$","_tmpl$","_$insert","_$createComponent","description","children","options","onChange","event","currentTarget","when","_el$2","_tmpl$2","_$createComponent"],"sources":["../../../../src/components/composites/update-dialog/channel-control.tsx"],"sourcesContent":["import { SettingsRow } from '../settings'\nimport { NativeSelect } from '../../ui/native-select'\nimport { createEffect, createSignal, onCleanup, Show, type Accessor } from 'solid-js'\n\nimport type { ChannelControlActions } from './update-dialog'\n\nexport type UpdateChannel = 'stable' | 'pre-release' | 'dev'\n\nconst CHANNEL_OPTIONS: ReadonlyArray<{ value: UpdateChannel; label: string }> = [\n { value: 'stable', label: 'Stable' },\n { value: 'pre-release', label: 'Pre-release' },\n { value: 'dev', label: 'Dev' },\n]\n\nconst CHANNEL_DESCRIPTIONS: Record<UpdateChannel, string> = {\n stable: 'Tested releases after about four days in pre-release.',\n 'pre-release': 'Daily pre-release builds arrive early, with rough edges included.',\n dev: 'Follows every dev build from main. Intended for development machines.',\n}\n\nexport type UpdateChannelControlProps = {\n /** The dialog's channel actions; every save flows through `recheck`. */\n controls: ChannelControlActions\n /** Read the persisted channel. */\n read: () => Promise<UpdateChannel>\n /** Persist a selection; resolves with the channel that actually stuck. */\n persist: (value: UpdateChannel) => Promise<UpdateChannel>\n /** Disable beyond the dialog's own working state, e.g. a host save in flight. */\n disabled?: Accessor<boolean>\n /** Per-channel row descriptions, when the defaults don't fit the app. */\n descriptions?: Partial<Record<UpdateChannel, string>>\n /**\n * Re-read the persisted channel whenever this value changes. Supply it when\n * the channel can move while the dialog is closed — a service that gets torn\n * down, or a CLI writing the same setting — and hand it the dialog's `open`.\n */\n reloadOn?: Accessor<unknown>\n}\n\n/**\n * The updater dialog's release-channel row: one setting, a select trailing.\n * Every desktop application that ships channels presents the same three of\n * them with the same copy, which is why the row lives here rather than being\n * redrawn per app. A save always flows through the dialog's `recheck`, so a\n * channel switch re-asks the feed and the offer never outlives its channel.\n */\nexport function UpdateChannelControl(props: UpdateChannelControlProps) {\n const [channel, setChannel] = createSignal<UpdateChannel>('stable')\n const [loaded, setLoaded] = createSignal(false)\n const [saving, setSaving] = createSignal(false)\n const [error, setError] = createSignal<string | null>(null)\n\n createEffect(() => {\n props.reloadOn?.()\n let active = true\n onCleanup(() => {\n active = false\n })\n void props\n .read()\n .then((value) => {\n if (active) setChannel(value)\n })\n .catch(() => {\n if (active) setError('The current update channel could not be read.')\n })\n .finally(() => {\n if (active) setLoaded(true)\n })\n })\n\n const save = async (value: string) => {\n const next = value as UpdateChannel\n if (!loaded() || saving() || props.controls.disabled() || next === channel()) return\n const previous = channel()\n let persisted = false\n setChannel(next)\n setSaving(true)\n setError(null)\n\n try {\n await props.controls.recheck(async () => {\n try {\n const saved = await props.persist(next)\n setChannel(saved)\n persisted = true\n } catch (caught) {\n setChannel(previous)\n setError(\n caught instanceof Error ? caught.message : 'The update channel could not be saved.'\n )\n throw caught\n }\n })\n } catch (caught) {\n // The shared dialog renders the recheck failure. Roll back only if the\n // channel write itself failed; a failed feed check keeps the saved choice.\n if (!persisted) {\n setChannel(previous)\n setError(\n caught instanceof Error ? caught.message : 'The update channel could not be saved.'\n )\n }\n } finally {\n setSaving(false)\n }\n }\n\n return (\n <div class=\"flex flex-col gap-2\">\n <SettingsRow\n label=\"Update channel\"\n description={props.descriptions?.[channel()] ?? CHANNEL_DESCRIPTIONS[channel()]}\n >\n <NativeSelect\n aria-label=\"Update channel\"\n disabled={\n !loaded() || saving() || props.controls.disabled() || (props.disabled?.() ?? false)\n }\n value={channel()}\n options={CHANNEL_OPTIONS}\n onChange={(event) => void save(event.currentTarget.value)}\n />\n </SettingsRow>\n <Show when={error()}>{(message) => <p role=\"alert\">{message()}</p>}</Show>\n </div>\n )\n}\n"],"mappings":";;;;;;;AAQA,IAAMS,kBAA0E;CAC9E;EAAEE,OAAO;EAAUC,OAAO;CAAS;CACnC;EAAED,OAAO;EAAeC,OAAO;CAAc;CAC7C;EAAED,OAAO;EAAOC,OAAO;CAAM;AAAC;AAGhC,IAAMC,uBAAsD;CAC1DE,QAAQ;CACR,eAAe;CACfC,KAAK;AACP;;;;;;;;AA4BA,SAAgBU,qBAAqBC,OAAkC;CACrE,MAAM,CAACC,SAASC,cAAc1B,aAA4B,QAAQ;CAClE,MAAM,CAAC2B,QAAQC,aAAa5B,aAAa,KAAK;CAC9C,MAAM,CAAC6B,QAAQC,aAAa9B,aAAa,KAAK;CAC9C,MAAM,CAAC+B,OAAOC,YAAYhC,aAA4B,IAAI;CAE1DD,mBAAmB;EACjByB,MAAMF,WAAW;EACjB,IAAIW,SAAS;EACbhC,gBAAgB;GACdgC,SAAS;EACX,CAAC;EACD,MACGjB,KAAK,CAAC,CACNkB,MAAM1B,UAAU;GACf,IAAIyB,QAAQP,WAAWlB,KAAK;EAC9B,CAAC,CAAC,CACD2B,YAAY;GACX,IAAIF,QAAQD,SAAS,+CAA+C;EACtE,CAAC,CAAC,CACDI,cAAc;GACb,IAAIH,QAAQL,UAAU,IAAI;EAC5B,CAAC;CACL,CAAC;CAED,MAAMS,OAAO,OAAO7B,UAAkB;EACpC,MAAM8B,OAAO9B;EACb,IAAI,CAACmB,OAAO,KAAKE,OAAO,KAAKL,MAAMT,SAASI,SAAS,KAAKmB,SAASb,QAAQ,GAAG;EAC9E,MAAMc,WAAWd,QAAQ;EACzB,IAAIe,YAAY;EAChBd,WAAWY,IAAI;EACfR,UAAU,IAAI;EACdE,SAAS,IAAI;EAEb,IAAI;GACF,MAAMR,MAAMT,SAAS0B,QAAQ,YAAY;IACvC,IAAI;KACF,MAAMC,QAAQ,MAAMlB,MAAMN,QAAQoB,IAAI;KACtCZ,WAAWgB,KAAK;KAChBF,YAAY;IACd,SAASG,QAAQ;KACfjB,WAAWa,QAAQ;KACnBP,SACEW,kBAAkBC,QAAQD,OAAOE,UAAU,wCAC7C;KACA,MAAMF;IACR;GACF,CAAC;EACH,SAASA,QAAQ;GAGf,IAAI,CAACH,WAAW;IACdd,WAAWa,QAAQ;IACnBP,SACEW,kBAAkBC,QAAQD,OAAOE,UAAU,wCAC7C;GACF;EACF,UAAU;GACRf,UAAU,KAAK;EACjB;CACF;CAEA,cAAA;EAAA,IAAAgB,OAAAC,OAAA;EAAAC,OAAAF,MAAAG,gBAEKpD,aAAW;GACVY,OAAK;GAAA,IACLyC,cAAW;IAAA,OAAE1B,MAAMJ,eAAeK,QAAQ,MAAMf,qBAAqBe,QAAQ;GAAE;GAAA,IAAA0B,WAAA;IAAA,OAAAF,gBAE9EnD,cAAY;KAAA,cAAA;KAAA,IAEXqB,WAAQ;MAAA,OACN,CAACQ,OAAO,KAAKE,OAAO,KAAKL,MAAMT,SAASI,SAAS,MAAMK,MAAML,WAAW,KAAK;KAAM;KAAA,IAErFX,QAAK;MAAA,OAAEiB,QAAQ;KAAC;KAChB2B,SAAS9C;KACT+C,WAAWC,UAAU,KAAKjB,KAAKiB,MAAMC,cAAc/C,KAAK;IAAC,CAAA;GAAA;EAAA,CAAA,GAAA,IAAA;EAAAwC,OAAAF,MAAAG,gBAG5D/C,MAAI;GAAA,IAACsD,OAAI;IAAA,OAAEzB,MAAM;GAAC;GAAAoB,WAAIN,mBAAO;IAAA,IAAAY,QAAAC,QAAA;IAAAV,OAAAS,OAAsBZ,OAAO;IAAA,OAAAY;GAAA,EAAA,CAAA;EAAO,CAAA,GAAA,IAAA;EAAA,OAAAX;CAAA,EAAA,CAAA;AAGxE"}
@@ -1,2 +1,3 @@
1
- export { UpdateDialog, type UpdateAdapter, type UpdateDialogProps, type UpdatePhase, type UpdateState, } from './update-dialog';
1
+ export { UpdateDialog, type UpdateAdapter, type ChannelControlActions, type UpdateDialogProps, type UpdatePhase, type UpdateState, } from './update-dialog';
2
+ export { UpdateChannelControl, type UpdateChannel, type UpdateChannelControlProps, } from './channel-control';
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/update-dialog/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,WAAW,GACjB,MAAM,iBAAiB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/update-dialog/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,WAAW,GACjB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,oBAAoB,EACpB,KAAK,aAAa,EAClB,KAAK,yBAAyB,GAC/B,MAAM,mBAAmB,CAAA"}
@@ -1,2 +1,3 @@
1
1
  import { UpdateDialog } from "./update-dialog.js";
2
- export { UpdateDialog };
2
+ import { UpdateChannelControl } from "./channel-control.js";
3
+ export { UpdateChannelControl, UpdateDialog };
@@ -95,7 +95,7 @@ export type UpdateAdapter = Readonly<{
95
95
  /** False in a browser build, where there is no installer to drive. */
96
96
  isDesktopRuntime(): boolean;
97
97
  }>;
98
- type ChannelControlActions = Readonly<{
98
+ export type ChannelControlActions = Readonly<{
99
99
  /** Disabled while the app is checking, downloading, installing, or saving a channel. */
100
100
  disabled: Accessor<boolean>;
101
101
  /** Persist a channel change, then check that channel without retaining a stale offer. */
@@ -138,5 +138,4 @@ export type UpdateDialogProps = {
138
138
  class?: string;
139
139
  };
140
140
  export declare function UpdateDialog(props: UpdateDialogProps): JSX.Element;
141
- export {};
142
141
  //# sourceMappingURL=update-dialog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"update-dialog.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/update-dialog/update-dialog.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"update-dialog.d.ts","sourceRoot":"","sources":["../../../../src/components/composites/update-dialog/update-dialog.tsx"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"update-dialog.js","names":["createEffect","createMemo","createSignal","onCleanup","onMount","Show","splitProps","Accessor","JSX","Check","CircleStop","Download","ExternalLink","LoaderCircle","Monitor","RefreshCw","Sparkles","Badge","Button","ActionButton","Dialog","DialogClose","DialogContent","DialogDescription","DialogFooter","DialogHeader","DialogTitle","DialogTrigger","createDialogFocusRestoration","Progress","Heading","cn","formatBytes","formatReleaseDate","plainTextFromMarkdown","UpdatePhase","UpdateState","Readonly","phase","currentVersion","availableVersion","changelog","releaseNotes","releaseDate","downloadedBytes","totalBytes","error","releaseUrl","restartRequired","UpdateAdapter","getStatus","Promise","check","install","expectedVersion","cancel","pollIntervalMs","openExternal","url","isDesktopRuntime","ChannelControlActions","disabled","recheck","beforeCheck","UpdateDialogProps","adapter","appName","appIcon","fallbackVersion","desktopOnlyMessage","channelControl","actions","Element","open","defaultOpen","onOpenChange","restoreFocusRef","HTMLElement","class","messageText","value","undefined","message","trim","errorMessage","caught","fallback","Error","plainMessage","record","safe","nestedError","isBusy","state","UpdateDialog","props","local","uncontrolledOpen","setUncontrolledOpen","setOpen","next","focusRestoration","desktop","setState","busy","setBusy","setError","checkFailed","setCheckFailed","cancelPending","setCancelPending","lifecycleEpoch","activeOpenEpoch","channelCheckUnconfirmed","disposed","loadStatus","isCurrent","status","previous","hasActionableSnapshot","candidate","ignoresUnconfirmedChannelSnapshot","preservesSnapshot","epoch","failCheck","applyCheckResult","preserveOnFailure","current","clearStaleOffer","working","discardStaleOffer","channelSaved","checked","active","version","includes","requestInFlight","timer","window","setInterval","Math","max","clearInterval","openRelease","notes","channelControlDisabled","recheckChannel","triggerLabel","progressValue","min","round","_$createComponent","children","when","as","variant","size","_$memo","positioner","onKeyDown","event","KeyboardEvent","key","defaultPrevented","preventDefault","onOpenAutoFocus","currentTarget","focus","preventScroll","onCloseAutoFocus","_el$","_tmpl$","_el$2","firstChild","_$insert","icon","_el$34","_tmpl$0","_$effect","_$setAttribute","_el$3","_tmpl$9","_el$4","_el$5","_el$6","_el$7","_el$8","nextSibling","_el$9","tone","type","tooltip","onClick","_el$0","_tmpl$2","_el$1","_el$35","_tmpl$1","_el$10","_tmpl$3","_el$11","_el$12","_el$13","_el$14","_el$16","_el$15","_el$17","_el$18","_el$20","_el$19","released","_el$36","_tmpl$10","_el$37","_el$21","_tmpl$4","_el$22","aria-label","_el$23","_tmpl$5","_el$24","_tmpl$6","_el$25","_tmpl$7","_el$26","_el$27","_el$28","_el$29","_tmpl$8","_el$30","_el$31","_el$32","_el$33","_$createComponent","_$memo","_$setAttribute"],"sources":["../../../../src/components/composites/update-dialog/update-dialog.tsx"],"sourcesContent":["import {\n createEffect,\n createMemo,\n createSignal,\n onCleanup,\n onMount,\n Show,\n splitProps,\n} from 'solid-js'\nimport type { Accessor, JSX } from 'solid-js'\nimport {\n Check,\n CircleStop,\n Download,\n ExternalLink,\n LoaderCircle,\n Monitor,\n RefreshCw,\n Sparkles,\n} from 'lucide-solid'\nimport { Badge } from '../../ui/badge'\nimport { Button } from '../../ui/button'\nimport { ActionButton } from '../action-button'\nimport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from '../../ui/dialog'\nimport { createDialogFocusRestoration } from '../../ui/dialog/dialog'\nimport { Progress } from '../../ui/progress'\nimport { Heading } from '../../ui/typography'\nimport { cn } from '#lib/utils'\nimport { formatBytes, formatReleaseDate, plainTextFromMarkdown } from '#lib/version-notes'\n\n/**\n * UpdateDialog.\n *\n * A software-update surface: the installed version, whether a newer release\n * exists, a download that shows its progress, and the release notes for what is\n * about to arrive.\n *\n * The transport is an adapter, because an update is the one thing a component\n * library genuinely cannot implement: it is signed, downloaded and applied by the\n * application's own updater, and the mechanism differs per shell. What *is*\n * general is the phase machine and the way each phase is presented — and that is\n * where the hard-won detail lives:\n *\n * **A refused install answers with a normal payload, not a rejection.** adea's\n * shell returns a status whose phase is `failed` when a download or an extract\n * fails, rather than throwing. Without handling that, clicking install looked like\n * nothing happened at all — the button spun, the phase never changed, and the\n * error sat unread in a field nobody rendered. The `failed` phase after an install\n * is therefore surfaced as an error, explicitly.\n *\n * **Busy is derived from the snapshot as well as from the click.** An update can\n * be downloading while the dialog is open — started before it opened, or by the\n * shell's own poll — so a local `busy` flag alone would offer an enabled Install\n * button mid-download.\n *\n * **Opening the dialog loads status first, then checks.** Status paints the\n * installed version immediately; the check is a network round trip. Doing them in\n * the other order leaves the dialog blank for as long as the check takes, on a\n * dialog whose first question is always \"what am I running?\".\n *\n * **A failed check is not a current result.** A cached `current` status can only\n * be shown as up to date after the latest check succeeds; a failed check remains\n * retryable and suppresses that claim.\n *\n * **Errors are not always `Error`s.** An adapter can reject with a string or a\n * structured safe message. Unknown object fields are never serialized into the\n * UI; a generic fallback is shown instead.\n *\n * The adapter's `isDesktopRuntime` gate exists because an update is a desktop\n * concern: a browser build has no installer, and the honest thing to show is that\n * updates are available in the desktop application rather than a disabled button\n * with no reason.\n */\n\n/** The phase an update is in. The union is the state machine. */\nexport type UpdatePhase =\n | 'idle'\n | 'checking'\n | 'current'\n | 'available'\n | 'downloading'\n | 'installing'\n | 'installed'\n | 'cancelling'\n | 'cancelled'\n | 'unavailable'\n | 'failed'\n\n/** A snapshot of the updater. Every field the dialog renders, and nothing else. */\nexport type UpdateState = Readonly<{\n phase: UpdatePhase\n /** The version currently installed. */\n currentVersion: string\n /** The version that can be installed, when one can. */\n availableVersion?: string | null\n /** A changelog for the installed channel, as markdown. */\n changelog?: string\n /** Notes for the available release, as markdown. */\n releaseNotes?: string | null\n /** The available release's date, as an ISO string. */\n releaseDate?: string | null\n /** Bytes downloaded so far, during `downloading`. */\n downloadedBytes?: number\n /** Total bytes, when the server sent a length. */\n totalBytes?: number | null\n /** Set when `phase` is `failed`, and sometimes when it is not. */\n error?: string | null\n /** A URL for the release's page. */\n releaseUrl?: string | null\n /** True when the application must restart to finish. */\n restartRequired?: boolean\n}>\n\n/**\n * The updater, as the application exposes it. Three calls, each answering with a\n * full snapshot rather than a delta — an updater's state is small, and a snapshot\n * cannot drift from the shell's own view of it.\n */\nexport type UpdateAdapter = Readonly<{\n /** The current state, without contacting the release channel. */\n getStatus(): Promise<UpdateState>\n /** Ask the release channel, and answer with what it found. */\n check(): Promise<UpdateState>\n /**\n * Download and apply `expectedVersion`.\n *\n * The argument is the version the caller believes is available, so the updater\n * can refuse a request that raced a newer release rather than installing the\n * wrong one.\n */\n install(expectedVersion: string): Promise<UpdateState>\n /** Optional native cancellation, available only while work is in flight. */\n cancel?(): Promise<UpdateState>\n /** Enable status polling during native work; omit for push-driven adapters. */\n pollIntervalMs?: number\n /** Route external links through the native shell when needed. */\n openExternal?(url: string): Promise<void>\n /** False in a browser build, where there is no installer to drive. */\n isDesktopRuntime(): boolean\n}>\n\ntype ChannelControlActions = Readonly<{\n /** Disabled while the app is checking, downloading, installing, or saving a channel. */\n disabled: Accessor<boolean>\n /** Persist a channel change, then check that channel without retaining a stale offer. */\n recheck(beforeCheck?: () => Promise<void>): Promise<void>\n}>\n\nexport type UpdateDialogProps = {\n adapter: UpdateAdapter\n /** The application's name, used in the title and the copy. */\n appName?: string\n /** The actual desktop application icon, supplied by its host. */\n appIcon?: string\n /** Complete build-time history, also available before the native status arrives. */\n changelog?: string\n /** Shown before the first status arrives, so the dialog is never blank. */\n fallbackVersion?: string\n /** Copy for the adapter's non-desktop case. */\n desktopOnlyMessage?: string\n /**\n * Optional host-owned release-channel control, rendered in the version status\n * card. The host owns its accessible label and calls `recheck` after a selection;\n * the optional callback persists that selection before the dialog checks it.\n */\n channelControl?: (actions: ChannelControlActions) => JSX.Element\n /** A controlled open state. Omit for the trigger to manage its own. */\n open?: boolean\n /**\n * Start open, uncontrolled — for a caller that opens the dialog because it found\n * something (\"an update is available\") rather than because the user asked. The\n * trigger is still rendered, so the dialog is reachable again after closing.\n */\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n /**\n * Supplies a stable external control to focus when a controlled dialog closes.\n * Useful when a menu or other layer closes before this dialog opens; when omitted,\n * the element focused immediately before the dialog opens is used when available.\n */\n restoreFocusRef?: Accessor<HTMLElement | undefined>\n /** Render only the panel, for a caller that provides its own dialog. */\n class?: string\n}\n\nfunction messageText(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined\n const message = value.trim()\n return message && message !== '[object Object]' ? message : undefined\n}\n\nfunction errorMessage(caught: unknown, fallback: string): string {\n if (caught instanceof Error) return messageText(caught.message) ?? fallback\n const plainMessage = messageText(caught)\n if (plainMessage) return plainMessage\n if (!caught || typeof caught !== 'object') return fallback\n\n const record = caught as { error?: unknown; safe?: unknown }\n const safe = record.safe as { message?: unknown } | null | undefined\n const nestedError = record.error as { safe?: { message?: unknown } | null } | null | undefined\n return messageText(safe?.message) ?? messageText(nestedError?.safe?.message) ?? fallback\n}\n\n/**\n * The phases during which the updater is working. `downloading` and `installing`\n * are included because the shell can be in either while the dialog is open, and\n * offering an enabled Install button during a download is how a double-install\n * happens.\n */\nfunction isBusy(state: UpdateState | null): boolean {\n return (\n state?.phase === 'checking' ||\n state?.phase === 'downloading' ||\n state?.phase === 'installing' ||\n state?.phase === 'cancelling'\n )\n}\n\nexport function UpdateDialog(props: UpdateDialogProps) {\n const [local] = splitProps(props, [\n 'adapter',\n 'appName',\n 'appIcon',\n 'changelog',\n 'fallbackVersion',\n 'desktopOnlyMessage',\n 'channelControl',\n 'open',\n 'defaultOpen',\n 'onOpenChange',\n 'restoreFocusRef',\n 'class',\n ])\n\n const [uncontrolledOpen, setUncontrolledOpen] = createSignal(local.defaultOpen ?? false)\n const open = () => local.open ?? uncontrolledOpen()\n const setOpen = (next: boolean) => {\n if (local.open === undefined) setUncontrolledOpen(next)\n local.onOpenChange?.(next)\n }\n\n const focusRestoration = createDialogFocusRestoration({\n open,\n restoreFocusRef: local.restoreFocusRef,\n })\n\n const appName = () => local.appName ?? 'the application'\n const desktop = () => local.adapter.isDesktopRuntime()\n const [state, setState] = createSignal<UpdateState | null>(null)\n const [busy, setBusy] = createSignal(false)\n const [error, setError] = createSignal('')\n const [checkFailed, setCheckFailed] = createSignal(false)\n const [cancelPending, setCancelPending] = createSignal(false)\n let lifecycleEpoch = 0\n let activeOpenEpoch: number | undefined\n let channelCheckUnconfirmed = false\n let disposed = false\n\n onCleanup(() => {\n disposed = true\n activeOpenEpoch = undefined\n lifecycleEpoch += 1\n })\n\n const loadStatus = async (isCurrent: () => boolean = () => !disposed) => {\n if (!desktop() || !isCurrent()) return\n try {\n const status = await local.adapter.getStatus()\n const previous = state()\n const hasActionableSnapshot = (candidate: UpdateState | null) =>\n candidate?.phase === 'available' || isBusy(candidate)\n const ignoresUnconfirmedChannelSnapshot =\n channelCheckUnconfirmed && hasActionableSnapshot(status)\n const preservesSnapshot =\n previous?.phase === 'available'\n ? status.phase === 'available' || isBusy(status)\n : isBusy(previous) && isBusy(status)\n if (\n isCurrent() &&\n !ignoresUnconfirmedChannelSnapshot &&\n !(checkFailed() && hasActionableSnapshot(previous) && !preservesSnapshot)\n ) {\n setState(status)\n }\n } catch (caught) {\n if (isCurrent()) setError(errorMessage(caught, 'Version status is unavailable'))\n }\n }\n\n onMount(() => {\n if (desktop() && !open()) {\n const epoch = lifecycleEpoch\n void loadStatus(() => !disposed && lifecycleEpoch === epoch)\n }\n })\n\n const failCheck = (caught: unknown, fallback: string) => {\n const message = errorMessage(caught, fallback)\n setCheckFailed(true)\n setError(message)\n }\n\n const applyCheckResult = (next: UpdateState, preserveOnFailure = true) => {\n if (next.phase === 'failed') {\n const current = state()\n if (!current || !preserveOnFailure) setState(next)\n failCheck(next.error, 'Could not check for updates')\n return\n }\n setState(next)\n setCheckFailed(false)\n setError('')\n }\n\n const check = async (\n beforeCheck?: () => Promise<void>,\n clearStaleOffer = beforeCheck !== undefined\n ) => {\n if (!desktop()) {\n setError(local.desktopOnlyMessage ?? `Update checks are available from the desktop app.`)\n return\n }\n const epoch = activeOpenEpoch\n const isCurrent = () =>\n !disposed &&\n epoch !== undefined &&\n activeOpenEpoch === epoch &&\n lifecycleEpoch === epoch &&\n open()\n if (!isCurrent()) return\n if (clearStaleOffer && working()) return\n if (clearStaleOffer) channelCheckUnconfirmed = true\n const discardStaleOffer = clearStaleOffer || channelCheckUnconfirmed\n setBusy(true)\n setError('')\n setCheckFailed(false)\n if (discardStaleOffer) {\n const current = state()\n if (current) setState({ phase: 'checking', currentVersion: current.currentVersion })\n }\n let channelSaved = beforeCheck === undefined\n try {\n await beforeCheck?.()\n channelSaved = true\n if (!isCurrent()) return\n const checked = await local.adapter.check()\n if (isCurrent()) {\n applyCheckResult(checked, !discardStaleOffer)\n if (discardStaleOffer && checked.phase !== 'failed') channelCheckUnconfirmed = false\n }\n } catch (caught) {\n if (isCurrent()) {\n if (discardStaleOffer) {\n const fallback = channelSaved\n ? 'Could not check for updates'\n : 'Could not save the update channel'\n const message = errorMessage(caught, fallback)\n setState({\n phase: 'failed',\n currentVersion: state()?.currentVersion ?? '',\n error: message,\n })\n failCheck(message, fallback)\n } else {\n failCheck(caught, 'Could not check for updates')\n await loadStatus(isCurrent)\n }\n }\n } finally {\n if (isCurrent()) setBusy(false)\n }\n }\n\n /**\n * Status first, then the check — and an `active` flag, because the dialog can\n * close while the check is in flight and a late `setState` would write into a\n * disposed scope.\n */\n createEffect(() => {\n if (!open() || !desktop()) {\n setCancelPending(false)\n if (activeOpenEpoch !== undefined) {\n activeOpenEpoch = undefined\n lifecycleEpoch += 1\n }\n return\n }\n const epoch = ++lifecycleEpoch\n activeOpenEpoch = epoch\n let active = true\n const isCurrent = () =>\n active && !disposed && activeOpenEpoch === epoch && lifecycleEpoch === epoch && open()\n onCleanup(() => {\n active = false\n if (activeOpenEpoch === epoch) {\n activeOpenEpoch = undefined\n lifecycleEpoch += 1\n }\n })\n setError('')\n setCheckFailed(false)\n setCancelPending(false)\n setBusy(true)\n void (async () => {\n try {\n const current = await local.adapter.getStatus()\n if (!isCurrent()) return\n const discardStaleOffer = channelCheckUnconfirmed\n setState(\n discardStaleOffer\n ? { phase: 'checking', currentVersion: current.currentVersion }\n : current\n )\n const checked = await local.adapter.check()\n if (isCurrent()) {\n applyCheckResult(checked, !discardStaleOffer)\n if (discardStaleOffer && checked.phase !== 'failed') channelCheckUnconfirmed = false\n }\n } catch (caught) {\n if (isCurrent()) {\n if (channelCheckUnconfirmed) {\n const message = errorMessage(caught, 'Could not check for updates')\n setState({\n phase: 'failed',\n currentVersion: state()?.currentVersion ?? '',\n error: message,\n })\n failCheck(message, 'Could not check for updates')\n } else {\n failCheck(caught, 'Could not check for updates')\n }\n }\n } finally {\n if (isCurrent()) setBusy(false)\n }\n })()\n })\n\n const install = async () => {\n const version = state()?.availableVersion\n const epoch = activeOpenEpoch\n const isCurrent = () =>\n !disposed &&\n epoch !== undefined &&\n activeOpenEpoch === epoch &&\n lifecycleEpoch === epoch &&\n open()\n if (!version || !isCurrent()) return\n setBusy(true)\n setError('')\n setState((current) =>\n current ? { ...current, phase: 'downloading', downloadedBytes: 0, totalBytes: null } : current\n )\n try {\n const next = await local.adapter.install(version)\n if (!isCurrent()) return\n setState(\n cancelPending() && ['downloading', 'installing'].includes(next.phase)\n ? { ...next, phase: 'cancelling' }\n : next\n )\n // A refused install answers with a normal payload whose phase is `failed`.\n // Without this the click looked like nothing happened.\n if (next.phase === 'failed') {\n setError(errorMessage(next.error, 'Update installation failed'))\n } else {\n setCheckFailed(false)\n }\n } catch (caught) {\n if (!isCurrent()) return\n setError(errorMessage(caught, 'Update installation failed'))\n await loadStatus(isCurrent)\n } finally {\n if (isCurrent()) setBusy(false)\n }\n }\n\n // Poll only an open, active native operation. Epoch ownership prevents a late\n // status response from replacing a reopened dialog's newer snapshot.\n createEffect(() => {\n const phase = state()?.phase\n if (\n !open() ||\n !desktop() ||\n !local.adapter.pollIntervalMs ||\n !['downloading', 'installing', 'cancelling'].includes(phase ?? '')\n )\n return\n const epoch = activeOpenEpoch\n let active = true\n let requestInFlight = false\n const timer = window.setInterval(\n () => {\n if (requestInFlight) return\n requestInFlight = true\n void (async () => {\n try {\n const next = await local.adapter.getStatus()\n if (!active || disposed || !open() || activeOpenEpoch !== epoch) return\n setState(\n cancelPending() && ['downloading', 'installing'].includes(next.phase)\n ? { ...next, phase: 'cancelling' }\n : next\n )\n if (next.phase === 'failed')\n setError(errorMessage(next.error, 'Update installation failed'))\n } catch (caught) {\n if (active && !disposed && activeOpenEpoch === epoch)\n setError(errorMessage(caught, 'Update progress is unavailable'))\n } finally {\n requestInFlight = false\n }\n })()\n },\n Math.max(250, local.adapter.pollIntervalMs)\n )\n onCleanup(() => {\n active = false\n window.clearInterval(timer)\n })\n })\n\n const cancel = async () => {\n if (!local.adapter.cancel || cancelPending() || state()?.phase === 'cancelling') return\n const epoch = activeOpenEpoch\n setError('')\n setCancelPending(true)\n setState((current) => (current ? { ...current, phase: 'cancelling' } : current))\n try {\n const next = await local.adapter.cancel()\n if (!disposed && open() && activeOpenEpoch === epoch) setState(next)\n } catch (caught) {\n if (!disposed && open() && activeOpenEpoch === epoch) {\n setError(errorMessage(caught, 'Could not cancel the update'))\n await loadStatus(() => !disposed && open() && activeOpenEpoch === epoch)\n }\n } finally {\n if (!disposed && open() && activeOpenEpoch === epoch) setCancelPending(false)\n }\n }\n\n const openRelease = async (url: string) => {\n try {\n if (local.adapter.openExternal) await local.adapter.openExternal(url)\n else window.open(url, '_blank', 'noopener,noreferrer')\n } catch (caught) {\n if (!disposed) setError(errorMessage(caught, 'Could not open the release page'))\n }\n }\n\n const changelog = createMemo(() =>\n plainTextFromMarkdown(state()?.changelog || local.changelog || '')\n )\n const notes = () => {\n const value = state()?.releaseNotes\n return value ? plainTextFromMarkdown(value) : ''\n }\n const working = () => busy() || cancelPending() || isBusy(state())\n const channelControlDisabled = () => !desktop() || working()\n const recheckChannel = (beforeCheck?: () => Promise<void>) => check(beforeCheck, true)\n const isCurrent = () => state()?.phase === 'current' && !error() && !checkFailed() && !working()\n\n const triggerLabel = () => {\n const current = state()\n const fallback = `v${local.fallbackVersion ?? '0.1.0'}`\n if (checkFailed()) return `Version ${current?.currentVersion || fallback} · Retry`\n if (!current) return `${appName()} ${fallback}`\n switch (current.phase) {\n case 'checking':\n return 'Checking for updates…'\n case 'available':\n return current.availableVersion\n ? `Update v${current.availableVersion} available`\n : 'Update available'\n case 'downloading':\n case 'installing':\n return 'Installing update…'\n case 'failed':\n return `Version ${current.currentVersion || fallback} · Retry`\n default:\n return `${appName()} v${current.currentVersion || fallback}`\n }\n }\n\n const progressValue = () => {\n const current = state()\n if (!current?.totalBytes || !current.downloadedBytes) return null\n return Math.min(100, Math.round((current.downloadedBytes / current.totalBytes) * 100))\n }\n\n return (\n <Dialog open={open()} onOpenChange={setOpen}>\n <Show when={local.open === undefined}>\n <DialogTrigger\n as={Button}\n variant=\"ghost\"\n size=\"sm\"\n aria-label=\"Open version and updates\"\n aria-haspopup=\"dialog\"\n >\n <Show when={state()?.phase === 'available'} fallback={<RefreshCw aria-hidden=\"true\" />}>\n <Sparkles aria-hidden=\"true\" />\n </Show>\n {triggerLabel()}\n </DialogTrigger>\n </Show>\n\n <DialogContent\n positioner=\"inset\"\n class={cn('flex max-h-full min-h-0 max-w-3xl flex-col overflow-hidden', local.class)}\n onKeyDown={(event: KeyboardEvent) => {\n // An explanatory tooltip must not consume the popup's close shortcut,\n // including while its exit animation still owns Kobalte's top layer.\n if (event.key === 'Escape' && !event.defaultPrevented) {\n event.preventDefault()\n setOpen(false)\n }\n }}\n onOpenAutoFocus={(event) => {\n focusRestoration.onOpenAutoFocus(event)\n // Start at the readable panel rather than opening an action's tooltip.\n if (!event.defaultPrevented && event.currentTarget instanceof HTMLElement) {\n event.preventDefault()\n event.currentTarget.focus({ preventScroll: true })\n }\n }}\n onCloseAutoFocus={focusRestoration.onCloseAutoFocus}\n >\n <DialogHeader class=\"shrink-0\">\n <div class=\"flex items-center gap-3\">\n <Show\n when={local.appIcon}\n fallback={<Monitor class=\"size-10 text-primary\" aria-hidden=\"true\" />}\n >\n {(icon) => <img src={icon()} alt=\"\" class=\"size-10\" aria-hidden=\"true\" />}\n </Show>\n <div>\n <DialogTitle>Version &amp; updates</DialogTitle>\n <DialogDescription>\n Keep {appName()} current and review what changed in each release.\n </DialogDescription>\n </div>\n </div>\n </DialogHeader>\n\n <div\n data-slot=\"update-dialog-scroll-region\"\n class=\"flex min-h-0 min-w-0 flex-1 flex-col gap-5 overflow-y-auto p-6\"\n >\n <section\n class=\"rounded-xl border border-border bg-background/45 p-4\"\n aria-label=\"Version status\"\n >\n <div class=\"flex flex-wrap items-start justify-between gap-4\">\n <div class=\"flex flex-col gap-1\">\n <p class=\"text-2xs font-semibold tracking-widest text-muted-foreground uppercase\">\n Installed version\n </p>\n <Heading as=\"p\" size=\"page\" tone=\"foreground\">\n v{state()?.currentVersion || local.fallbackVersion || '0.1.0'}\n </Heading>\n <p class=\"text-sm text-muted-foreground\">\n <Show\n when={isCurrent()}\n fallback={\n <Show\n when={state()?.phase === 'available' && state()?.availableVersion}\n fallback={\n state()?.phase === 'unavailable'\n ? 'No signed package is published for this platform.'\n : state()?.phase === 'cancelled'\n ? 'Update cancelled. You can retry when ready.'\n : desktop()\n ? 'Check the release channel for the latest signed build.'\n : (local.desktopOnlyMessage ??\n `Open this dialog inside the desktop app to check for updates.`)\n }\n >\n {`A newer release, v${state()?.availableVersion}, is ready.`}\n </Show>\n }\n >\n You are running the latest release.\n </Show>\n </p>\n </div>\n <div class=\"flex flex-col items-end gap-2\">\n <ActionButton\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n tooltip=\"Check the release channel for the latest signed app version.\"\n disabled={!desktop() || working()}\n onClick={() => void check()}\n >\n <Show when={working()} fallback={<RefreshCw aria-hidden=\"true\" />}>\n <LoaderCircle class=\"animate-spin\" aria-hidden=\"true\" />\n </Show>\n {checkFailed() ? 'Retry update check' : 'Check latest version'}\n </ActionButton>\n {/* The up-to-date confirmation reads as the button's outcome,\n so it sits directly beneath it rather than floating as a\n full-width row between sections. */}\n <Show when={isCurrent()}>\n <p class=\"text-foreground flex items-center gap-2 text-sm\" role=\"status\">\n <Check class=\"size-4 text-success\" aria-hidden=\"true\" />\n {appName()} is up to date.\n </p>\n </Show>\n </div>\n </div>\n <Show when={local.channelControl}>\n {(channelControl) => (\n <div class=\"mt-4 border-t border-border pt-4\">\n {channelControl()({\n disabled: channelControlDisabled,\n recheck: recheckChannel,\n })}\n </div>\n )}\n </Show>\n </section>\n\n <Show\n when={\n state()?.availableVersion &&\n state()?.phase !== 'installed' &&\n state()?.phase !== 'unavailable'\n }\n >\n <section\n class=\"rounded-xl border border-primary/35 bg-primary-subtle p-4\"\n aria-label=\"Available update\"\n >\n <div class=\"flex flex-wrap items-start justify-between gap-4\">\n <div class=\"flex flex-col gap-1\">\n <p class=\"flex items-center gap-2 text-sm font-semibold\">\n <Sparkles class=\"size-4 text-primary\" aria-hidden=\"true\" />\n Version {state()?.availableVersion}{' '}\n {['downloading', 'installing', 'cancelling'].includes(state()?.phase ?? '')\n ? 'is being prepared'\n : 'is ready'}\n </p>\n <p class=\"text-sm text-muted-foreground\">\n The signed installer is verified before {appName()} restarts.\n </p>\n <Show when={formatReleaseDate(state()?.releaseDate ?? null)}>\n {(released) => (\n <p class=\"text-xs text-muted-foreground\">Released {released()}</p>\n )}\n </Show>\n </div>\n <ActionButton\n type=\"button\"\n size=\"sm\"\n tooltip={`Download the signed update and restart ${appName()}.`}\n disabled={working()}\n onClick={() => void install()}\n >\n <Show when={working()} fallback={<Download aria-hidden=\"true\" />}>\n <LoaderCircle class=\"animate-spin\" aria-hidden=\"true\" />\n </Show>\n Install and restart\n </ActionButton>\n </div>\n </section>\n </Show>\n\n {/*\n Progress with both numbers: a bar alone does not distinguish a stalled\n download from a slow one, and \"12.4 MB of 48.1 MB\" does.\n */}\n <Show\n when={\n state()?.phase === 'downloading' ||\n state()?.phase === 'installing' ||\n state()?.phase === 'cancelling'\n }\n >\n <section class=\"grid gap-2\" aria-label=\"Update progress\">\n <Progress\n value={progressValue() ?? undefined}\n aria-label={\n state()?.phase === 'installing' ? 'Installing update' : 'Downloading update'\n }\n />\n <p class=\"text-xs text-muted-foreground\" role=\"status\">\n <Show\n when={state()?.phase === 'installing'}\n fallback={\n <Show when={progressValue() !== null} fallback=\"Downloading…\">\n {`${formatBytes(state()?.downloadedBytes ?? 0)} of ${formatBytes(\n state()?.totalBytes ?? 0\n )} · ${progressValue()}%`}\n </Show>\n }\n >\n Applying the update…\n </Show>\n </p>\n <Show when={local.adapter.cancel}>\n <ActionButton\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n tooltip=\"Stop the current update download or installation.\"\n disabled={cancelPending() || state()?.phase === 'cancelling'}\n onClick={() => void cancel()}\n >\n <CircleStop aria-hidden=\"true\" />\n {cancelPending() || state()?.phase === 'cancelling'\n ? 'Cancelling…'\n : 'Cancel update'}\n </ActionButton>\n </Show>\n </section>\n </Show>\n\n <Show when={state()?.phase === 'installed'}>\n <p class=\"text-foreground flex items-center gap-2 text-sm\" role=\"status\">\n <Check class=\"size-4 text-success\" aria-hidden=\"true\" />\n <Show when={state()?.restartRequired} fallback=\"The update is applied.\">\n Restart {appName()} to finish the update.\n </Show>\n </p>\n </Show>\n\n <Show when={error()}>\n <p\n class=\"text-foreground rounded-lg border border-destructive/35 bg-destructive-subtle px-3 py-2 text-sm\"\n role=\"alert\"\n >\n {error()}\n </p>\n </Show>\n\n <Show when={notes()}>\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex items-center gap-2\">\n <h2 id=\"update-release-notes\" class=\"text-sm font-semibold text-foreground\">\n What changed in this release\n </h2>\n <Badge variant=\"subtle\" size=\"sm\">\n v{state()?.availableVersion}\n </Badge>\n </div>\n {/* A long text block with no focusable descendant: it needs its own tab\n stop, named by the heading above it, or a keyboard user cannot\n scroll it (`scrollable-region-focusable`). */}\n <div\n class=\"max-h-52 overflow-y-auto rounded-xl border border-border bg-background/45 p-4 font-code text-code leading-5 whitespace-pre-wrap text-muted-foreground\"\n role=\"region\"\n aria-labelledby=\"update-release-notes\"\n tabindex=\"0\"\n >\n {notes()}\n </div>\n </div>\n </Show>\n\n <Show when={changelog()}>\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex items-center gap-2\">\n <h2 id=\"update-changelog\" class=\"text-sm font-semibold text-foreground\">\n Installed changelog\n </h2>\n {/* The version pill identifies which build's history this is —\n the same treatment the available-update heading carries. */}\n <Badge variant=\"subtle\" size=\"sm\">\n v{state()?.currentVersion || local.fallbackVersion || '0.1.0'}\n </Badge>\n </div>\n <p class=\"text-xs text-muted-foreground\">\n All versions and updates included with this installation.\n </p>\n {/* A long text block with no focusable descendant: it needs its own tab\n stop, named by the heading above it, or a keyboard user cannot\n scroll it (`scrollable-region-focusable`). */}\n <div\n class=\"max-h-72 overflow-y-auto rounded-xl border border-border bg-background/45 p-4 font-code text-code leading-5 whitespace-pre-wrap text-muted-foreground\"\n role=\"region\"\n aria-labelledby=\"update-changelog\"\n tabindex=\"0\"\n >\n {changelog()}\n </div>\n </div>\n </Show>\n </div>\n\n <DialogFooter>\n <Show when={state()?.releaseUrl}>\n {(url) => (\n <ActionButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n tooltip=\"Open the release history in your browser.\"\n // `noopener,noreferrer` because the release page is a different\n // origin and must not get a handle on this window.\n onClick={() => void openRelease(url())}\n >\n <ExternalLink aria-hidden=\"true\" />\n View releases\n </ActionButton>\n )}\n </Show>\n <DialogClose as={Button} variant=\"outline\" size=\"sm\">\n Close\n </DialogClose>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"update-dialog.js","names":["createEffect","createMemo","createSignal","onCleanup","onMount","Show","splitProps","Accessor","JSX","Check","CircleStop","Download","ExternalLink","LoaderCircle","Monitor","RefreshCw","Sparkles","Badge","Button","ActionButton","Dialog","DialogClose","DialogContent","DialogDescription","DialogFooter","DialogHeader","DialogTitle","DialogTrigger","createDialogFocusRestoration","Progress","Heading","cn","formatBytes","formatReleaseDate","plainTextFromMarkdown","UpdatePhase","UpdateState","Readonly","phase","currentVersion","availableVersion","changelog","releaseNotes","releaseDate","downloadedBytes","totalBytes","error","releaseUrl","restartRequired","UpdateAdapter","getStatus","Promise","check","install","expectedVersion","cancel","pollIntervalMs","openExternal","url","isDesktopRuntime","ChannelControlActions","disabled","recheck","beforeCheck","UpdateDialogProps","adapter","appName","appIcon","fallbackVersion","desktopOnlyMessage","channelControl","actions","Element","open","defaultOpen","onOpenChange","restoreFocusRef","HTMLElement","class","messageText","value","undefined","message","trim","errorMessage","caught","fallback","Error","plainMessage","record","safe","nestedError","isBusy","state","UpdateDialog","props","local","uncontrolledOpen","setUncontrolledOpen","setOpen","next","focusRestoration","desktop","setState","busy","setBusy","setError","checkFailed","setCheckFailed","cancelPending","setCancelPending","lifecycleEpoch","activeOpenEpoch","channelCheckUnconfirmed","disposed","loadStatus","isCurrent","status","previous","hasActionableSnapshot","candidate","ignoresUnconfirmedChannelSnapshot","preservesSnapshot","epoch","failCheck","applyCheckResult","preserveOnFailure","current","clearStaleOffer","working","discardStaleOffer","channelSaved","checked","active","version","includes","requestInFlight","timer","window","setInterval","Math","max","clearInterval","openRelease","notes","channelControlDisabled","recheckChannel","triggerLabel","progressValue","min","round","_$createComponent","children","when","as","variant","size","_$memo","positioner","onKeyDown","event","KeyboardEvent","key","defaultPrevented","preventDefault","onOpenAutoFocus","currentTarget","focus","preventScroll","onCloseAutoFocus","_el$","_tmpl$","_el$2","firstChild","_$insert","icon","_el$34","_tmpl$0","_$effect","_$setAttribute","_el$3","_tmpl$9","_el$4","_el$5","_el$6","_el$7","_el$8","nextSibling","_el$9","tone","type","tooltip","onClick","_el$0","_tmpl$2","_el$1","_el$35","_tmpl$1","_el$10","_tmpl$3","_el$11","_el$12","_el$13","_el$14","_el$16","_el$15","_el$17","_el$18","_el$20","_el$19","released","_el$36","_tmpl$10","_el$37","_el$21","_tmpl$4","_el$22","aria-label","_el$23","_tmpl$5","_el$24","_tmpl$6","_el$25","_tmpl$7","_el$26","_el$27","_el$28","_el$29","_tmpl$8","_el$30","_el$31","_el$32","_el$33","_$createComponent","_$memo","_$setAttribute"],"sources":["../../../../src/components/composites/update-dialog/update-dialog.tsx"],"sourcesContent":["import {\n createEffect,\n createMemo,\n createSignal,\n onCleanup,\n onMount,\n Show,\n splitProps,\n} from 'solid-js'\nimport type { Accessor, JSX } from 'solid-js'\nimport {\n Check,\n CircleStop,\n Download,\n ExternalLink,\n LoaderCircle,\n Monitor,\n RefreshCw,\n Sparkles,\n} from 'lucide-solid'\nimport { Badge } from '../../ui/badge'\nimport { Button } from '../../ui/button'\nimport { ActionButton } from '../action-button'\nimport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from '../../ui/dialog'\nimport { createDialogFocusRestoration } from '../../ui/dialog/dialog'\nimport { Progress } from '../../ui/progress'\nimport { Heading } from '../../ui/typography'\nimport { cn } from '#lib/utils'\nimport { formatBytes, formatReleaseDate, plainTextFromMarkdown } from '#lib/version-notes'\n\n/**\n * UpdateDialog.\n *\n * A software-update surface: the installed version, whether a newer release\n * exists, a download that shows its progress, and the release notes for what is\n * about to arrive.\n *\n * The transport is an adapter, because an update is the one thing a component\n * library genuinely cannot implement: it is signed, downloaded and applied by the\n * application's own updater, and the mechanism differs per shell. What *is*\n * general is the phase machine and the way each phase is presented — and that is\n * where the hard-won detail lives:\n *\n * **A refused install answers with a normal payload, not a rejection.** adea's\n * shell returns a status whose phase is `failed` when a download or an extract\n * fails, rather than throwing. Without handling that, clicking install looked like\n * nothing happened at all — the button spun, the phase never changed, and the\n * error sat unread in a field nobody rendered. The `failed` phase after an install\n * is therefore surfaced as an error, explicitly.\n *\n * **Busy is derived from the snapshot as well as from the click.** An update can\n * be downloading while the dialog is open — started before it opened, or by the\n * shell's own poll — so a local `busy` flag alone would offer an enabled Install\n * button mid-download.\n *\n * **Opening the dialog loads status first, then checks.** Status paints the\n * installed version immediately; the check is a network round trip. Doing them in\n * the other order leaves the dialog blank for as long as the check takes, on a\n * dialog whose first question is always \"what am I running?\".\n *\n * **A failed check is not a current result.** A cached `current` status can only\n * be shown as up to date after the latest check succeeds; a failed check remains\n * retryable and suppresses that claim.\n *\n * **Errors are not always `Error`s.** An adapter can reject with a string or a\n * structured safe message. Unknown object fields are never serialized into the\n * UI; a generic fallback is shown instead.\n *\n * The adapter's `isDesktopRuntime` gate exists because an update is a desktop\n * concern: a browser build has no installer, and the honest thing to show is that\n * updates are available in the desktop application rather than a disabled button\n * with no reason.\n */\n\n/** The phase an update is in. The union is the state machine. */\nexport type UpdatePhase =\n | 'idle'\n | 'checking'\n | 'current'\n | 'available'\n | 'downloading'\n | 'installing'\n | 'installed'\n | 'cancelling'\n | 'cancelled'\n | 'unavailable'\n | 'failed'\n\n/** A snapshot of the updater. Every field the dialog renders, and nothing else. */\nexport type UpdateState = Readonly<{\n phase: UpdatePhase\n /** The version currently installed. */\n currentVersion: string\n /** The version that can be installed, when one can. */\n availableVersion?: string | null\n /** A changelog for the installed channel, as markdown. */\n changelog?: string\n /** Notes for the available release, as markdown. */\n releaseNotes?: string | null\n /** The available release's date, as an ISO string. */\n releaseDate?: string | null\n /** Bytes downloaded so far, during `downloading`. */\n downloadedBytes?: number\n /** Total bytes, when the server sent a length. */\n totalBytes?: number | null\n /** Set when `phase` is `failed`, and sometimes when it is not. */\n error?: string | null\n /** A URL for the release's page. */\n releaseUrl?: string | null\n /** True when the application must restart to finish. */\n restartRequired?: boolean\n}>\n\n/**\n * The updater, as the application exposes it. Three calls, each answering with a\n * full snapshot rather than a delta — an updater's state is small, and a snapshot\n * cannot drift from the shell's own view of it.\n */\nexport type UpdateAdapter = Readonly<{\n /** The current state, without contacting the release channel. */\n getStatus(): Promise<UpdateState>\n /** Ask the release channel, and answer with what it found. */\n check(): Promise<UpdateState>\n /**\n * Download and apply `expectedVersion`.\n *\n * The argument is the version the caller believes is available, so the updater\n * can refuse a request that raced a newer release rather than installing the\n * wrong one.\n */\n install(expectedVersion: string): Promise<UpdateState>\n /** Optional native cancellation, available only while work is in flight. */\n cancel?(): Promise<UpdateState>\n /** Enable status polling during native work; omit for push-driven adapters. */\n pollIntervalMs?: number\n /** Route external links through the native shell when needed. */\n openExternal?(url: string): Promise<void>\n /** False in a browser build, where there is no installer to drive. */\n isDesktopRuntime(): boolean\n}>\n\nexport type ChannelControlActions = Readonly<{\n /** Disabled while the app is checking, downloading, installing, or saving a channel. */\n disabled: Accessor<boolean>\n /** Persist a channel change, then check that channel without retaining a stale offer. */\n recheck(beforeCheck?: () => Promise<void>): Promise<void>\n}>\n\nexport type UpdateDialogProps = {\n adapter: UpdateAdapter\n /** The application's name, used in the title and the copy. */\n appName?: string\n /** The actual desktop application icon, supplied by its host. */\n appIcon?: string\n /** Complete build-time history, also available before the native status arrives. */\n changelog?: string\n /** Shown before the first status arrives, so the dialog is never blank. */\n fallbackVersion?: string\n /** Copy for the adapter's non-desktop case. */\n desktopOnlyMessage?: string\n /**\n * Optional host-owned release-channel control, rendered in the version status\n * card. The host owns its accessible label and calls `recheck` after a selection;\n * the optional callback persists that selection before the dialog checks it.\n */\n channelControl?: (actions: ChannelControlActions) => JSX.Element\n /** A controlled open state. Omit for the trigger to manage its own. */\n open?: boolean\n /**\n * Start open, uncontrolled — for a caller that opens the dialog because it found\n * something (\"an update is available\") rather than because the user asked. The\n * trigger is still rendered, so the dialog is reachable again after closing.\n */\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n /**\n * Supplies a stable external control to focus when a controlled dialog closes.\n * Useful when a menu or other layer closes before this dialog opens; when omitted,\n * the element focused immediately before the dialog opens is used when available.\n */\n restoreFocusRef?: Accessor<HTMLElement | undefined>\n /** Render only the panel, for a caller that provides its own dialog. */\n class?: string\n}\n\nfunction messageText(value: unknown): string | undefined {\n if (typeof value !== 'string') return undefined\n const message = value.trim()\n return message && message !== '[object Object]' ? message : undefined\n}\n\nfunction errorMessage(caught: unknown, fallback: string): string {\n if (caught instanceof Error) return messageText(caught.message) ?? fallback\n const plainMessage = messageText(caught)\n if (plainMessage) return plainMessage\n if (!caught || typeof caught !== 'object') return fallback\n\n const record = caught as { error?: unknown; safe?: unknown }\n const safe = record.safe as { message?: unknown } | null | undefined\n const nestedError = record.error as { safe?: { message?: unknown } | null } | null | undefined\n return messageText(safe?.message) ?? messageText(nestedError?.safe?.message) ?? fallback\n}\n\n/**\n * The phases during which the updater is working. `downloading` and `installing`\n * are included because the shell can be in either while the dialog is open, and\n * offering an enabled Install button during a download is how a double-install\n * happens.\n */\nfunction isBusy(state: UpdateState | null): boolean {\n return (\n state?.phase === 'checking' ||\n state?.phase === 'downloading' ||\n state?.phase === 'installing' ||\n state?.phase === 'cancelling'\n )\n}\n\nexport function UpdateDialog(props: UpdateDialogProps) {\n const [local] = splitProps(props, [\n 'adapter',\n 'appName',\n 'appIcon',\n 'changelog',\n 'fallbackVersion',\n 'desktopOnlyMessage',\n 'channelControl',\n 'open',\n 'defaultOpen',\n 'onOpenChange',\n 'restoreFocusRef',\n 'class',\n ])\n\n const [uncontrolledOpen, setUncontrolledOpen] = createSignal(local.defaultOpen ?? false)\n const open = () => local.open ?? uncontrolledOpen()\n const setOpen = (next: boolean) => {\n if (local.open === undefined) setUncontrolledOpen(next)\n local.onOpenChange?.(next)\n }\n\n const focusRestoration = createDialogFocusRestoration({\n open,\n restoreFocusRef: local.restoreFocusRef,\n })\n\n const appName = () => local.appName ?? 'the application'\n const desktop = () => local.adapter.isDesktopRuntime()\n const [state, setState] = createSignal<UpdateState | null>(null)\n const [busy, setBusy] = createSignal(false)\n const [error, setError] = createSignal('')\n const [checkFailed, setCheckFailed] = createSignal(false)\n const [cancelPending, setCancelPending] = createSignal(false)\n let lifecycleEpoch = 0\n let activeOpenEpoch: number | undefined\n let channelCheckUnconfirmed = false\n let disposed = false\n\n onCleanup(() => {\n disposed = true\n activeOpenEpoch = undefined\n lifecycleEpoch += 1\n })\n\n const loadStatus = async (isCurrent: () => boolean = () => !disposed) => {\n if (!desktop() || !isCurrent()) return\n try {\n const status = await local.adapter.getStatus()\n const previous = state()\n const hasActionableSnapshot = (candidate: UpdateState | null) =>\n candidate?.phase === 'available' || isBusy(candidate)\n const ignoresUnconfirmedChannelSnapshot =\n channelCheckUnconfirmed && hasActionableSnapshot(status)\n const preservesSnapshot =\n previous?.phase === 'available'\n ? status.phase === 'available' || isBusy(status)\n : isBusy(previous) && isBusy(status)\n if (\n isCurrent() &&\n !ignoresUnconfirmedChannelSnapshot &&\n !(checkFailed() && hasActionableSnapshot(previous) && !preservesSnapshot)\n ) {\n setState(status)\n }\n } catch (caught) {\n if (isCurrent()) setError(errorMessage(caught, 'Version status is unavailable'))\n }\n }\n\n onMount(() => {\n if (desktop() && !open()) {\n const epoch = lifecycleEpoch\n void loadStatus(() => !disposed && lifecycleEpoch === epoch)\n }\n })\n\n const failCheck = (caught: unknown, fallback: string) => {\n const message = errorMessage(caught, fallback)\n setCheckFailed(true)\n setError(message)\n }\n\n const applyCheckResult = (next: UpdateState, preserveOnFailure = true) => {\n if (next.phase === 'failed') {\n const current = state()\n if (!current || !preserveOnFailure) setState(next)\n failCheck(next.error, 'Could not check for updates')\n return\n }\n setState(next)\n setCheckFailed(false)\n setError('')\n }\n\n const check = async (\n beforeCheck?: () => Promise<void>,\n clearStaleOffer = beforeCheck !== undefined\n ) => {\n if (!desktop()) {\n setError(local.desktopOnlyMessage ?? `Update checks are available from the desktop app.`)\n return\n }\n const epoch = activeOpenEpoch\n const isCurrent = () =>\n !disposed &&\n epoch !== undefined &&\n activeOpenEpoch === epoch &&\n lifecycleEpoch === epoch &&\n open()\n if (!isCurrent()) return\n if (clearStaleOffer && working()) return\n if (clearStaleOffer) channelCheckUnconfirmed = true\n const discardStaleOffer = clearStaleOffer || channelCheckUnconfirmed\n setBusy(true)\n setError('')\n setCheckFailed(false)\n if (discardStaleOffer) {\n const current = state()\n if (current) setState({ phase: 'checking', currentVersion: current.currentVersion })\n }\n let channelSaved = beforeCheck === undefined\n try {\n await beforeCheck?.()\n channelSaved = true\n if (!isCurrent()) return\n const checked = await local.adapter.check()\n if (isCurrent()) {\n applyCheckResult(checked, !discardStaleOffer)\n if (discardStaleOffer && checked.phase !== 'failed') channelCheckUnconfirmed = false\n }\n } catch (caught) {\n if (isCurrent()) {\n if (discardStaleOffer) {\n const fallback = channelSaved\n ? 'Could not check for updates'\n : 'Could not save the update channel'\n const message = errorMessage(caught, fallback)\n setState({\n phase: 'failed',\n currentVersion: state()?.currentVersion ?? '',\n error: message,\n })\n failCheck(message, fallback)\n } else {\n failCheck(caught, 'Could not check for updates')\n await loadStatus(isCurrent)\n }\n }\n } finally {\n if (isCurrent()) setBusy(false)\n }\n }\n\n /**\n * Status first, then the check — and an `active` flag, because the dialog can\n * close while the check is in flight and a late `setState` would write into a\n * disposed scope.\n */\n createEffect(() => {\n if (!open() || !desktop()) {\n setCancelPending(false)\n if (activeOpenEpoch !== undefined) {\n activeOpenEpoch = undefined\n lifecycleEpoch += 1\n }\n return\n }\n const epoch = ++lifecycleEpoch\n activeOpenEpoch = epoch\n let active = true\n const isCurrent = () =>\n active && !disposed && activeOpenEpoch === epoch && lifecycleEpoch === epoch && open()\n onCleanup(() => {\n active = false\n if (activeOpenEpoch === epoch) {\n activeOpenEpoch = undefined\n lifecycleEpoch += 1\n }\n })\n setError('')\n setCheckFailed(false)\n setCancelPending(false)\n setBusy(true)\n void (async () => {\n try {\n const current = await local.adapter.getStatus()\n if (!isCurrent()) return\n const discardStaleOffer = channelCheckUnconfirmed\n setState(\n discardStaleOffer\n ? { phase: 'checking', currentVersion: current.currentVersion }\n : current\n )\n const checked = await local.adapter.check()\n if (isCurrent()) {\n applyCheckResult(checked, !discardStaleOffer)\n if (discardStaleOffer && checked.phase !== 'failed') channelCheckUnconfirmed = false\n }\n } catch (caught) {\n if (isCurrent()) {\n if (channelCheckUnconfirmed) {\n const message = errorMessage(caught, 'Could not check for updates')\n setState({\n phase: 'failed',\n currentVersion: state()?.currentVersion ?? '',\n error: message,\n })\n failCheck(message, 'Could not check for updates')\n } else {\n failCheck(caught, 'Could not check for updates')\n }\n }\n } finally {\n if (isCurrent()) setBusy(false)\n }\n })()\n })\n\n const install = async () => {\n const version = state()?.availableVersion\n const epoch = activeOpenEpoch\n const isCurrent = () =>\n !disposed &&\n epoch !== undefined &&\n activeOpenEpoch === epoch &&\n lifecycleEpoch === epoch &&\n open()\n if (!version || !isCurrent()) return\n setBusy(true)\n setError('')\n setState((current) =>\n current ? { ...current, phase: 'downloading', downloadedBytes: 0, totalBytes: null } : current\n )\n try {\n const next = await local.adapter.install(version)\n if (!isCurrent()) return\n setState(\n cancelPending() && ['downloading', 'installing'].includes(next.phase)\n ? { ...next, phase: 'cancelling' }\n : next\n )\n // A refused install answers with a normal payload whose phase is `failed`.\n // Without this the click looked like nothing happened.\n if (next.phase === 'failed') {\n setError(errorMessage(next.error, 'Update installation failed'))\n } else {\n setCheckFailed(false)\n }\n } catch (caught) {\n if (!isCurrent()) return\n setError(errorMessage(caught, 'Update installation failed'))\n await loadStatus(isCurrent)\n } finally {\n if (isCurrent()) setBusy(false)\n }\n }\n\n // Poll only an open, active native operation. Epoch ownership prevents a late\n // status response from replacing a reopened dialog's newer snapshot.\n createEffect(() => {\n const phase = state()?.phase\n if (\n !open() ||\n !desktop() ||\n !local.adapter.pollIntervalMs ||\n !['downloading', 'installing', 'cancelling'].includes(phase ?? '')\n )\n return\n const epoch = activeOpenEpoch\n let active = true\n let requestInFlight = false\n const timer = window.setInterval(\n () => {\n if (requestInFlight) return\n requestInFlight = true\n void (async () => {\n try {\n const next = await local.adapter.getStatus()\n if (!active || disposed || !open() || activeOpenEpoch !== epoch) return\n setState(\n cancelPending() && ['downloading', 'installing'].includes(next.phase)\n ? { ...next, phase: 'cancelling' }\n : next\n )\n if (next.phase === 'failed')\n setError(errorMessage(next.error, 'Update installation failed'))\n } catch (caught) {\n if (active && !disposed && activeOpenEpoch === epoch)\n setError(errorMessage(caught, 'Update progress is unavailable'))\n } finally {\n requestInFlight = false\n }\n })()\n },\n Math.max(250, local.adapter.pollIntervalMs)\n )\n onCleanup(() => {\n active = false\n window.clearInterval(timer)\n })\n })\n\n const cancel = async () => {\n if (!local.adapter.cancel || cancelPending() || state()?.phase === 'cancelling') return\n const epoch = activeOpenEpoch\n setError('')\n setCancelPending(true)\n setState((current) => (current ? { ...current, phase: 'cancelling' } : current))\n try {\n const next = await local.adapter.cancel()\n if (!disposed && open() && activeOpenEpoch === epoch) setState(next)\n } catch (caught) {\n if (!disposed && open() && activeOpenEpoch === epoch) {\n setError(errorMessage(caught, 'Could not cancel the update'))\n await loadStatus(() => !disposed && open() && activeOpenEpoch === epoch)\n }\n } finally {\n if (!disposed && open() && activeOpenEpoch === epoch) setCancelPending(false)\n }\n }\n\n const openRelease = async (url: string) => {\n try {\n if (local.adapter.openExternal) await local.adapter.openExternal(url)\n else window.open(url, '_blank', 'noopener,noreferrer')\n } catch (caught) {\n if (!disposed) setError(errorMessage(caught, 'Could not open the release page'))\n }\n }\n\n const changelog = createMemo(() =>\n plainTextFromMarkdown(state()?.changelog || local.changelog || '')\n )\n const notes = () => {\n const value = state()?.releaseNotes\n return value ? plainTextFromMarkdown(value) : ''\n }\n const working = () => busy() || cancelPending() || isBusy(state())\n const channelControlDisabled = () => !desktop() || working()\n const recheckChannel = (beforeCheck?: () => Promise<void>) => check(beforeCheck, true)\n const isCurrent = () => state()?.phase === 'current' && !error() && !checkFailed() && !working()\n\n const triggerLabel = () => {\n const current = state()\n const fallback = `v${local.fallbackVersion ?? '0.1.0'}`\n if (checkFailed()) return `Version ${current?.currentVersion || fallback} · Retry`\n if (!current) return `${appName()} ${fallback}`\n switch (current.phase) {\n case 'checking':\n return 'Checking for updates…'\n case 'available':\n return current.availableVersion\n ? `Update v${current.availableVersion} available`\n : 'Update available'\n case 'downloading':\n case 'installing':\n return 'Installing update…'\n case 'failed':\n return `Version ${current.currentVersion || fallback} · Retry`\n default:\n return `${appName()} v${current.currentVersion || fallback}`\n }\n }\n\n const progressValue = () => {\n const current = state()\n if (!current?.totalBytes || !current.downloadedBytes) return null\n return Math.min(100, Math.round((current.downloadedBytes / current.totalBytes) * 100))\n }\n\n return (\n <Dialog open={open()} onOpenChange={setOpen}>\n <Show when={local.open === undefined}>\n <DialogTrigger\n as={Button}\n variant=\"ghost\"\n size=\"sm\"\n aria-label=\"Open version and updates\"\n aria-haspopup=\"dialog\"\n >\n <Show when={state()?.phase === 'available'} fallback={<RefreshCw aria-hidden=\"true\" />}>\n <Sparkles aria-hidden=\"true\" />\n </Show>\n {triggerLabel()}\n </DialogTrigger>\n </Show>\n\n <DialogContent\n positioner=\"inset\"\n class={cn('flex max-h-full min-h-0 max-w-3xl flex-col overflow-hidden', local.class)}\n onKeyDown={(event: KeyboardEvent) => {\n // An explanatory tooltip must not consume the popup's close shortcut,\n // including while its exit animation still owns Kobalte's top layer.\n if (event.key === 'Escape' && !event.defaultPrevented) {\n event.preventDefault()\n setOpen(false)\n }\n }}\n onOpenAutoFocus={(event) => {\n focusRestoration.onOpenAutoFocus(event)\n // Start at the readable panel rather than opening an action's tooltip.\n if (!event.defaultPrevented && event.currentTarget instanceof HTMLElement) {\n event.preventDefault()\n event.currentTarget.focus({ preventScroll: true })\n }\n }}\n onCloseAutoFocus={focusRestoration.onCloseAutoFocus}\n >\n <DialogHeader class=\"shrink-0\">\n <div class=\"flex items-center gap-3\">\n <Show\n when={local.appIcon}\n fallback={<Monitor class=\"size-10 text-primary\" aria-hidden=\"true\" />}\n >\n {(icon) => <img src={icon()} alt=\"\" class=\"size-10\" aria-hidden=\"true\" />}\n </Show>\n <div>\n <DialogTitle>Version &amp; updates</DialogTitle>\n <DialogDescription>\n Keep {appName()} current and review what changed in each release.\n </DialogDescription>\n </div>\n </div>\n </DialogHeader>\n\n <div\n data-slot=\"update-dialog-scroll-region\"\n class=\"flex min-h-0 min-w-0 flex-1 flex-col gap-5 overflow-y-auto p-6\"\n >\n <section\n class=\"rounded-xl border border-border bg-background/45 p-4\"\n aria-label=\"Version status\"\n >\n <div class=\"flex flex-wrap items-start justify-between gap-4\">\n <div class=\"flex flex-col gap-1\">\n <p class=\"text-2xs font-semibold tracking-widest text-muted-foreground uppercase\">\n Installed version\n </p>\n <Heading as=\"p\" size=\"page\" tone=\"foreground\">\n v{state()?.currentVersion || local.fallbackVersion || '0.1.0'}\n </Heading>\n <p class=\"text-sm text-muted-foreground\">\n <Show\n when={isCurrent()}\n fallback={\n <Show\n when={state()?.phase === 'available' && state()?.availableVersion}\n fallback={\n state()?.phase === 'unavailable'\n ? 'No signed package is published for this platform.'\n : state()?.phase === 'cancelled'\n ? 'Update cancelled. You can retry when ready.'\n : desktop()\n ? 'Check the release channel for the latest signed build.'\n : (local.desktopOnlyMessage ??\n `Open this dialog inside the desktop app to check for updates.`)\n }\n >\n {`A newer release, v${state()?.availableVersion}, is ready.`}\n </Show>\n }\n >\n You are running the latest release.\n </Show>\n </p>\n </div>\n <div class=\"flex flex-col items-end gap-2\">\n <ActionButton\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n tooltip=\"Check the release channel for the latest signed app version.\"\n disabled={!desktop() || working()}\n onClick={() => void check()}\n >\n <Show when={working()} fallback={<RefreshCw aria-hidden=\"true\" />}>\n <LoaderCircle class=\"animate-spin\" aria-hidden=\"true\" />\n </Show>\n {checkFailed() ? 'Retry update check' : 'Check latest version'}\n </ActionButton>\n {/* The up-to-date confirmation reads as the button's outcome,\n so it sits directly beneath it rather than floating as a\n full-width row between sections. */}\n <Show when={isCurrent()}>\n <p class=\"text-foreground flex items-center gap-2 text-sm\" role=\"status\">\n <Check class=\"size-4 text-success\" aria-hidden=\"true\" />\n {appName()} is up to date.\n </p>\n </Show>\n </div>\n </div>\n <Show when={local.channelControl}>\n {(channelControl) => (\n <div class=\"mt-4 border-t border-border pt-4\">\n {channelControl()({\n disabled: channelControlDisabled,\n recheck: recheckChannel,\n })}\n </div>\n )}\n </Show>\n </section>\n\n <Show\n when={\n state()?.availableVersion &&\n state()?.phase !== 'installed' &&\n state()?.phase !== 'unavailable'\n }\n >\n <section\n class=\"rounded-xl border border-primary/35 bg-primary-subtle p-4\"\n aria-label=\"Available update\"\n >\n <div class=\"flex flex-wrap items-start justify-between gap-4\">\n <div class=\"flex flex-col gap-1\">\n <p class=\"flex items-center gap-2 text-sm font-semibold\">\n <Sparkles class=\"size-4 text-primary\" aria-hidden=\"true\" />\n Version {state()?.availableVersion}{' '}\n {['downloading', 'installing', 'cancelling'].includes(state()?.phase ?? '')\n ? 'is being prepared'\n : 'is ready'}\n </p>\n <p class=\"text-sm text-muted-foreground\">\n The signed installer is verified before {appName()} restarts.\n </p>\n <Show when={formatReleaseDate(state()?.releaseDate ?? null)}>\n {(released) => (\n <p class=\"text-xs text-muted-foreground\">Released {released()}</p>\n )}\n </Show>\n </div>\n <ActionButton\n type=\"button\"\n size=\"sm\"\n tooltip={`Download the signed update and restart ${appName()}.`}\n disabled={working()}\n onClick={() => void install()}\n >\n <Show when={working()} fallback={<Download aria-hidden=\"true\" />}>\n <LoaderCircle class=\"animate-spin\" aria-hidden=\"true\" />\n </Show>\n Install and restart\n </ActionButton>\n </div>\n </section>\n </Show>\n\n {/*\n Progress with both numbers: a bar alone does not distinguish a stalled\n download from a slow one, and \"12.4 MB of 48.1 MB\" does.\n */}\n <Show\n when={\n state()?.phase === 'downloading' ||\n state()?.phase === 'installing' ||\n state()?.phase === 'cancelling'\n }\n >\n <section class=\"grid gap-2\" aria-label=\"Update progress\">\n <Progress\n value={progressValue() ?? undefined}\n aria-label={\n state()?.phase === 'installing' ? 'Installing update' : 'Downloading update'\n }\n />\n <p class=\"text-xs text-muted-foreground\" role=\"status\">\n <Show\n when={state()?.phase === 'installing'}\n fallback={\n <Show when={progressValue() !== null} fallback=\"Downloading…\">\n {`${formatBytes(state()?.downloadedBytes ?? 0)} of ${formatBytes(\n state()?.totalBytes ?? 0\n )} · ${progressValue()}%`}\n </Show>\n }\n >\n Applying the update…\n </Show>\n </p>\n <Show when={local.adapter.cancel}>\n <ActionButton\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n tooltip=\"Stop the current update download or installation.\"\n disabled={cancelPending() || state()?.phase === 'cancelling'}\n onClick={() => void cancel()}\n >\n <CircleStop aria-hidden=\"true\" />\n {cancelPending() || state()?.phase === 'cancelling'\n ? 'Cancelling…'\n : 'Cancel update'}\n </ActionButton>\n </Show>\n </section>\n </Show>\n\n <Show when={state()?.phase === 'installed'}>\n <p class=\"text-foreground flex items-center gap-2 text-sm\" role=\"status\">\n <Check class=\"size-4 text-success\" aria-hidden=\"true\" />\n <Show when={state()?.restartRequired} fallback=\"The update is applied.\">\n Restart {appName()} to finish the update.\n </Show>\n </p>\n </Show>\n\n <Show when={error()}>\n <p\n class=\"text-foreground rounded-lg border border-destructive/35 bg-destructive-subtle px-3 py-2 text-sm\"\n role=\"alert\"\n >\n {error()}\n </p>\n </Show>\n\n <Show when={notes()}>\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex items-center gap-2\">\n <h2 id=\"update-release-notes\" class=\"text-sm font-semibold text-foreground\">\n What changed in this release\n </h2>\n <Badge variant=\"subtle\" size=\"sm\">\n v{state()?.availableVersion}\n </Badge>\n </div>\n {/* A long text block with no focusable descendant: it needs its own tab\n stop, named by the heading above it, or a keyboard user cannot\n scroll it (`scrollable-region-focusable`). */}\n <div\n class=\"max-h-52 overflow-y-auto rounded-xl border border-border bg-background/45 p-4 font-code text-code leading-5 whitespace-pre-wrap text-muted-foreground\"\n role=\"region\"\n aria-labelledby=\"update-release-notes\"\n tabindex=\"0\"\n >\n {notes()}\n </div>\n </div>\n </Show>\n\n <Show when={changelog()}>\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex items-center gap-2\">\n <h2 id=\"update-changelog\" class=\"text-sm font-semibold text-foreground\">\n Installed changelog\n </h2>\n {/* The version pill identifies which build's history this is —\n the same treatment the available-update heading carries. */}\n <Badge variant=\"subtle\" size=\"sm\">\n v{state()?.currentVersion || local.fallbackVersion || '0.1.0'}\n </Badge>\n </div>\n <p class=\"text-xs text-muted-foreground\">\n All versions and updates included with this installation.\n </p>\n {/* A long text block with no focusable descendant: it needs its own tab\n stop, named by the heading above it, or a keyboard user cannot\n scroll it (`scrollable-region-focusable`). */}\n <div\n class=\"max-h-72 overflow-y-auto rounded-xl border border-border bg-background/45 p-4 font-code text-code leading-5 whitespace-pre-wrap text-muted-foreground\"\n role=\"region\"\n aria-labelledby=\"update-changelog\"\n tabindex=\"0\"\n >\n {changelog()}\n </div>\n </div>\n </Show>\n </div>\n\n <DialogFooter>\n <Show when={state()?.releaseUrl}>\n {(url) => (\n <ActionButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n tooltip=\"Open the release history in your browser.\"\n // `noopener,noreferrer` because the release page is a different\n // origin and must not get a handle on this window.\n onClick={() => void openRelease(url())}\n >\n <ExternalLink aria-hidden=\"true\" />\n View releases\n </ActionButton>\n )}\n </Show>\n <DialogClose as={Button} variant=\"outline\" size=\"sm\">\n Close\n </DialogClose>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n )\n}\n"],"mappings":"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"}
@@ -34,6 +34,12 @@ export type ToastOptions = {
34
34
  /** Which Toaster region to render into. */
35
35
  region?: string;
36
36
  };
37
+ /**
38
+ * The error contract, resolved: destructive tone, persistent unless the caller
39
+ * says otherwise. Exported because this decision is the part a caller depends
40
+ * on and the part testable without a browser — the card render itself needs one.
41
+ */
42
+ export declare function errorToastOptions(title: string, options?: Omit<ToastOptions, 'title' | 'tone'>): ToastOptions;
37
43
  /**
38
44
  * The toast API. `show` is the general form; the tone helpers are shorthand for
39
45
  * the four cases that come up constantly.
@@ -41,6 +47,13 @@ export type ToastOptions = {
41
47
  export declare const toast: {
42
48
  show(options: ToastOptions): number;
43
49
  success(title: string, options?: Omit<ToastOptions, "title" | "tone">): number;
50
+ /**
51
+ * A failure the user caused. Persistent by default — an error that times out
52
+ * while its reader is mid-sentence has not been reported — but `persistent`
53
+ * stays caller-owned: a failure inside a retry loop (a failed "check again",
54
+ * a background poll) passes `persistent: false` and a `duration`, because a
55
+ * dismiss-only toast there is one more thing to close by hand.
56
+ */
44
57
  error(title: string, options?: Omit<ToastOptions, "title" | "tone">): number;
45
58
  warning(title: string, options?: Omit<ToastOptions, "title" | "tone">): number;
46
59
  info(title: string, options?: Omit<ToastOptions, "title" | "tone">): number;
@@ -1 +1 @@
1
- {"version":3,"file":"toast.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/toast/toast.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,YAAY,EAA4B,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAE9F,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAInD;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,KAAK,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,GAAG,MAAM,CAAA;AAgB3E,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACpB,iFAAiF;IACjF,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAoDD;;;GAGG;AACH,eAAO,MAAM,KAAK;kBACF,YAAY,GAAG,MAAM;mBAKpB,MAAM,YAAY,IAAI,CAAC,YAAY,EAAE,OAAO,GAAG,MAAM,CAAC,GAAG,MAAM;iBAGjE,MAAM,YAAY,IAAI,CAAC,YAAY,EAAE,OAAO,GAAG,MAAM,CAAC,GAAG,MAAM;mBAG7D,MAAM,YAAY,IAAI,CAAC,YAAY,EAAE,OAAO,GAAG,MAAM,CAAC,GAAG,MAAM;gBAGlE,MAAM,YAAY,IAAI,CAAC,YAAY,EAAE,OAAO,GAAG,MAAM,CAAC,GAAG,MAAM;IAG3E;;;;OAIG;YACK,CAAC,WACE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC,YAC9B;QACR,OAAO,EAAE,MAAM,CAAA;QACf,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC,CAAA;QACvC,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC,CAAA;KAC7C,GACA,OAAO,CAAC,CAAC,CAAC;;;CAgCd,CAAA;AAED,MAAM,MAAM,YAAY,GAAG,cAAc,CAAC,OAAO,YAAY,CAAC,MAAM,CAAC,GAAG;IACtE,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,cAAc,CAAA;IACpE,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAyBD,iFAAiF;AACjF,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,eA4B1C;AAED,OAAO,EAAE,OAAO,EAAE,CAAA"}
1
+ {"version":3,"file":"toast.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/toast/toast.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,YAAY,EAA4B,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAE9F,OAAO,KAAK,EAAE,cAAc,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAInD;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,KAAK,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,GAAG,MAAM,CAAA;AAgB3E,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACpB,iFAAiF;IACjF,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAoDD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,OAAO,GAAG,MAAM,CAAC,GAC7C,YAAY,CAEd;AAED;;;GAGG;AACH,eAAO,MAAM,KAAK;kBACF,YAAY,GAAG,MAAM;mBAKpB,MAAM,YAAY,IAAI,CAAC,YAAY,EAAE,OAAO,GAAG,MAAM,CAAC,GAAG,MAAM;IAG9E;;;;;;OAMG;iBACU,MAAM,YAAY,IAAI,CAAC,YAAY,EAAE,OAAO,GAAG,MAAM,CAAC,GAAG,MAAM;mBAG7D,MAAM,YAAY,IAAI,CAAC,YAAY,EAAE,OAAO,GAAG,MAAM,CAAC,GAAG,MAAM;gBAGlE,MAAM,YAAY,IAAI,CAAC,YAAY,EAAE,OAAO,GAAG,MAAM,CAAC,GAAG,MAAM;IAG3E;;;;OAIG;YACK,CAAC,WACE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC,YAC9B;QACR,OAAO,EAAE,MAAM,CAAA;QACf,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC,CAAA;QACvC,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAC,CAAA;KAC7C,GACA,OAAO,CAAC,CAAC,CAAC;;;CAgCd,CAAA;AAED,MAAM,MAAM,YAAY,GAAG,cAAc,CAAC,OAAO,YAAY,CAAC,MAAM,CAAC,GAAG;IACtE,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,cAAc,CAAA;IACpE,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAyBD,iFAAiF;AACjF,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,eA4B1C;AAED,OAAO,EAAE,OAAO,EAAE,CAAA"}
@@ -89,6 +89,19 @@ function ToastCard(props) {
89
89
  });
90
90
  }
91
91
  /**
92
+ * The error contract, resolved: destructive tone, persistent unless the caller
93
+ * says otherwise. Exported because this decision is the part a caller depends
94
+ * on and the part testable without a browser — the card render itself needs one.
95
+ */
96
+ function errorToastOptions(title, options) {
97
+ return {
98
+ ...options,
99
+ title,
100
+ tone: "destructive",
101
+ persistent: options?.persistent ?? true
102
+ };
103
+ }
104
+ /**
92
105
  * The toast API. `show` is the general form; the tone helpers are shorthand for
93
106
  * the four cases that come up constantly.
94
107
  */
@@ -103,13 +116,15 @@ var toast = {
103
116
  tone: "success"
104
117
  });
105
118
  },
119
+ /**
120
+ * A failure the user caused. Persistent by default — an error that times out
121
+ * while its reader is mid-sentence has not been reported — but `persistent`
122
+ * stays caller-owned: a failure inside a retry loop (a failed "check again",
123
+ * a background poll) passes `persistent: false` and a `duration`, because a
124
+ * dismiss-only toast there is one more thing to close by hand.
125
+ */
106
126
  error(title, options) {
107
- return toast.show({
108
- ...options,
109
- title,
110
- tone: "destructive",
111
- persistent: true
112
- });
127
+ return toast.show(errorToastOptions(title, options));
113
128
  },
114
129
  warning(title, options) {
115
130
  return toast.show({
@@ -198,6 +213,6 @@ function Toaster(props) {
198
213
  } }));
199
214
  }
200
215
  //#endregion
201
- export { Toaster, toast, toaster };
216
+ export { Toaster, errorToastOptions, toast, toaster };
202
217
 
203
218
  //# sourceMappingURL=toast.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"toast.js","names":["Toast","KobalteToast","ToastComponentProps","toaster","X","ComponentProps","JSX","Show","splitProps","cn","ToastTone","toastTones","Record","default","success","warning","destructive","info","ToastOptions","title","description","tone","action","Element","persistent","duration","region","ToastCard","props","_$createComponent","toastId","priority","class","style","children","_tmpl$","_el$2","_tmpl$3","_$insert","Title","when","Description","_el$3","_tmpl$2","CloseButton","toast","show","options","_$mergeProps","Omit","error","promise","Promise","T","messages","pending","data","resolved","state","dismiss","clear","ToasterProps","Region","position","toasterPosition","toasterAlignment","Toaster","local","rest","regionId","limit","swipeDirection","pauseOnInteraction","pauseOnPageIdle","List","_$createComponent","KobalteToast","_$mergeProps"],"sources":["../../../../src/components/ui/toast/toast.tsx"],"sourcesContent":["import { Toast as KobalteToast, type ToastComponentProps, toaster } from '@kobalte/core/toast'\nimport { X } from 'lucide-solid'\nimport type { ComponentProps, JSX } from 'solid-js'\nimport { Show, splitProps } from 'solid-js'\nimport { cn } from '#lib/utils'\n\n/**\n * Toast.\n *\n * A short, self-dismissing message about something that already happened.\n *\n * The rules that make toasts helpful rather than hostile are built into this\n * API rather than left to callers:\n *\n * - A toast never carries the only route to an action. Anything important\n * enough to need doing belongs in the interface, not in a message that\n * disappears. `action` exists, but it is a convenience, not a flow.\n * - Errors from user-initiated work get `priority=\"high\"` so a screen reader\n * interrupts to announce them; background progress uses the default low\n * priority so it is not read over whatever the user is doing.\n * - A toast with a way to undo is preferred to a confirmation dialog for a\n * reversible action. Undo after the fact costs the user nothing; a modal\n * before it costs everyone a click.\n *\n * `Toaster` is mounted once, near the root of the app.\n */\n\ntype ToastTone = 'default' | 'success' | 'warning' | 'destructive' | 'info'\n\n/**\n * The tone dot's colour. A custom property rather than five colour utilities\n * because the value is applied inline (the tone is data, not markup) and a\n * Tailwind colour class cannot be built from a runtime value without becoming\n * invisible to the linter.\n */\nconst toastTones: Record<ToastTone, string> = {\n default: 'var(--foreground)',\n success: 'var(--success)',\n warning: 'var(--warning)',\n destructive: 'var(--destructive)',\n info: 'var(--info)',\n}\n\nexport type ToastOptions = {\n title: string\n description?: string\n tone?: ToastTone\n /** A control rendered inside the toast, usually an Undo. */\n action?: JSX.Element\n /** Keep the toast until it is dismissed. Use for a failure the user must see. */\n persistent?: boolean\n /** Milliseconds before auto-dismiss. Overrides the region's setting. */\n duration?: number\n /** Which Toaster region to render into. */\n region?: string\n}\n\nfunction ToastCard(props: ToastComponentProps & ToastOptions) {\n const tone = () => props.tone ?? 'default'\n\n return (\n <KobalteToast\n toastId={props.toastId}\n /**\n * A user-initiated outcome is announced; a background one waits its turn.\n * A failure the user caused is worth interrupting for; a sync finishing\n * quietly is not.\n */\n priority={tone() === 'destructive' ? 'high' : 'low'}\n duration={props.duration}\n persistent={props.persistent}\n class={cn(\n 'pointer-events-auto flex w-full items-start gap-3 rounded-xl border border-border bg-popover p-3 shadow-lg',\n 'data-[opened]:animate-in data-[opened]:slide-in-from-right-full data-[opened]:fade-in-0',\n 'data-[closed]:animate-out data-[closed]:slide-out-to-right-full data-[closed]:fade-out-0',\n 'data-[swipe=move]:translate-x-(--kb-toast-swipe-move-x) data-[swipe=move]:transition-none',\n 'data-[swipe=cancel]:translate-x-0 data-[swipe=cancel]:transition-transform',\n 'data-[swipe=end]:animate-out data-[swipe=end]:slide-out-to-right-full'\n )}\n style={{ '--toast-tone': toastTones[tone()] }}\n >\n <span\n aria-hidden=\"true\"\n class=\"mt-1.5 size-1.5 shrink-0 rounded-full\"\n style={{ 'background-color': 'var(--toast-tone)' }}\n />\n <div class=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <KobalteToast.Title class=\"text-sm font-medium\">{props.title}</KobalteToast.Title>\n <Show when={props.description}>\n <KobalteToast.Description class=\"text-sm text-muted-foreground\">\n {props.description}\n </KobalteToast.Description>\n </Show>\n <Show when={props.action}>\n <div class=\"mt-1.5 flex items-center gap-2\">{props.action}</div>\n </Show>\n </div>\n <KobalteToast.CloseButton\n aria-label=\"Dismiss notification\"\n class=\"text-muted-foreground shrink-0 rounded-sm p-0.5 transition-colors ease-out outline-none hover:text-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-primary-subtle\"\n >\n <X class=\"size-3.5\" />\n </KobalteToast.CloseButton>\n </KobalteToast>\n )\n}\n\n/**\n * The toast API. `show` is the general form; the tone helpers are shorthand for\n * the four cases that come up constantly.\n */\nexport const toast = {\n show(options: ToastOptions): number {\n return toaster.show((props) => <ToastCard {...props} {...options} />, {\n region: options.region,\n })\n },\n success(title: string, options?: Omit<ToastOptions, 'title' | 'tone'>): number {\n return toast.show({ ...options, title, tone: 'success' })\n },\n error(title: string, options?: Omit<ToastOptions, 'title' | 'tone'>): number {\n return toast.show({ ...options, title, tone: 'destructive', persistent: true })\n },\n warning(title: string, options?: Omit<ToastOptions, 'title' | 'tone'>): number {\n return toast.show({ ...options, title, tone: 'warning' })\n },\n info(title: string, options?: Omit<ToastOptions, 'title' | 'tone'>): number {\n return toast.show({ ...options, title, tone: 'info' })\n },\n /**\n * A toast that resolves with a promise: pending, then success or failure.\n * The right shape for an operation with a visible duration, because the user\n * gets one message instead of three.\n */\n promise<T>(\n promise: Promise<T> | (() => Promise<T>),\n messages: {\n pending: string\n success: string | ((data: T) => string)\n error: string | ((error: unknown) => string)\n }\n ): Promise<T> {\n const resolved = typeof promise === 'function' ? promise() : promise\n toaster.promise(resolved, (props) => {\n const tone: ToastTone =\n props.state === 'fulfilled'\n ? 'success'\n : props.state === 'rejected'\n ? 'destructive'\n : 'default'\n const title =\n props.state === 'pending'\n ? messages.pending\n : props.state === 'fulfilled'\n ? typeof messages.success === 'function'\n ? messages.success(props.data as T)\n : messages.success\n : typeof messages.error === 'function'\n ? messages.error(props.error)\n : messages.error\n return (\n <ToastCard\n toastId={props.toastId}\n title={title}\n tone={tone}\n persistent={props.state === 'rejected'}\n />\n )\n })\n return resolved\n },\n dismiss: toaster.dismiss,\n clear: toaster.clear,\n}\n\nexport type ToasterProps = ComponentProps<typeof KobalteToast.Region> & {\n /** Where the stack sits. Defaults to the bottom-right, clear of the shell. */\n position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'\n region?: string\n}\n\n/**\n * `class` lands on the region too: the props describe the region, and the\n * region is the element a caller lays out (an inset clear of a status bar, a\n * narrower stack). The list inside it is an implementation detail.\n *\n * The corner is set on the region, not the list. The region is the `fixed`\n * element: an inset on the list inside it positions nothing, and the region falls\n * back to wherever it happens to sit in the document flow.\n */\nconst toasterPosition = {\n 'top-left': 'top-0 left-0',\n 'top-right': 'top-0 right-0',\n 'bottom-left': 'bottom-0 left-0',\n 'bottom-right': 'bottom-0 right-0',\n}\n\nconst toasterAlignment = {\n 'top-left': 'items-start',\n 'top-right': 'items-end',\n 'bottom-left': 'items-start',\n 'bottom-right': 'items-end',\n}\n\n/** Mount once, near the root. Renders the stack and owns its timing defaults. */\nexport function Toaster(props: ToasterProps) {\n const [local, rest] = splitProps(props, ['position', 'region', 'class'])\n\n return (\n <KobalteToast.Region\n regionId={local.region}\n duration={5000}\n limit={3}\n swipeDirection=\"right\"\n pauseOnInteraction\n pauseOnPageIdle\n /**\n * The toast region is a top layer: opening a dialog must not hide an\n * in-flight notification, and clicking a toast must not dismiss the\n * dialog behind it.\n */\n class={cn(\n 'pointer-events-none fixed z-(--z-toast) flex max-h-screen w-full flex-col gap-2 p-4 sm:max-w-sm',\n toasterPosition[local.position ?? 'bottom-right'],\n local.class\n )}\n {...rest}\n >\n <KobalteToast.List\n class={cn('flex flex-col gap-2', toasterAlignment[local.position ?? 'bottom-right'])}\n />\n </KobalteToast.Region>\n )\n}\n\nexport { toaster }\n"],"mappings":"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"}
1
+ {"version":3,"file":"toast.js","names":["Toast","KobalteToast","ToastComponentProps","toaster","X","ComponentProps","JSX","Show","splitProps","cn","ToastTone","toastTones","Record","default","success","warning","destructive","info","ToastOptions","title","description","tone","action","Element","persistent","duration","region","ToastCard","props","_$createComponent","toastId","priority","class","style","children","_tmpl$","_el$2","_tmpl$3","_$insert","Title","when","Description","_el$3","_tmpl$2","CloseButton","errorToastOptions","options","Omit","toast","show","_$mergeProps","error","promise","Promise","T","messages","pending","data","resolved","state","dismiss","clear","ToasterProps","Region","position","toasterPosition","toasterAlignment","Toaster","local","rest","regionId","limit","swipeDirection","pauseOnInteraction","pauseOnPageIdle","List","_$createComponent","KobalteToast","_$mergeProps"],"sources":["../../../../src/components/ui/toast/toast.tsx"],"sourcesContent":["import { Toast as KobalteToast, type ToastComponentProps, toaster } from '@kobalte/core/toast'\nimport { X } from 'lucide-solid'\nimport type { ComponentProps, JSX } from 'solid-js'\nimport { Show, splitProps } from 'solid-js'\nimport { cn } from '#lib/utils'\n\n/**\n * Toast.\n *\n * A short, self-dismissing message about something that already happened.\n *\n * The rules that make toasts helpful rather than hostile are built into this\n * API rather than left to callers:\n *\n * - A toast never carries the only route to an action. Anything important\n * enough to need doing belongs in the interface, not in a message that\n * disappears. `action` exists, but it is a convenience, not a flow.\n * - Errors from user-initiated work get `priority=\"high\"` so a screen reader\n * interrupts to announce them; background progress uses the default low\n * priority so it is not read over whatever the user is doing.\n * - A toast with a way to undo is preferred to a confirmation dialog for a\n * reversible action. Undo after the fact costs the user nothing; a modal\n * before it costs everyone a click.\n *\n * `Toaster` is mounted once, near the root of the app.\n */\n\ntype ToastTone = 'default' | 'success' | 'warning' | 'destructive' | 'info'\n\n/**\n * The tone dot's colour. A custom property rather than five colour utilities\n * because the value is applied inline (the tone is data, not markup) and a\n * Tailwind colour class cannot be built from a runtime value without becoming\n * invisible to the linter.\n */\nconst toastTones: Record<ToastTone, string> = {\n default: 'var(--foreground)',\n success: 'var(--success)',\n warning: 'var(--warning)',\n destructive: 'var(--destructive)',\n info: 'var(--info)',\n}\n\nexport type ToastOptions = {\n title: string\n description?: string\n tone?: ToastTone\n /** A control rendered inside the toast, usually an Undo. */\n action?: JSX.Element\n /** Keep the toast until it is dismissed. Use for a failure the user must see. */\n persistent?: boolean\n /** Milliseconds before auto-dismiss. Overrides the region's setting. */\n duration?: number\n /** Which Toaster region to render into. */\n region?: string\n}\n\nfunction ToastCard(props: ToastComponentProps & ToastOptions) {\n const tone = () => props.tone ?? 'default'\n\n return (\n <KobalteToast\n toastId={props.toastId}\n /**\n * A user-initiated outcome is announced; a background one waits its turn.\n * A failure the user caused is worth interrupting for; a sync finishing\n * quietly is not.\n */\n priority={tone() === 'destructive' ? 'high' : 'low'}\n duration={props.duration}\n persistent={props.persistent}\n class={cn(\n 'pointer-events-auto flex w-full items-start gap-3 rounded-xl border border-border bg-popover p-3 shadow-lg',\n 'data-[opened]:animate-in data-[opened]:slide-in-from-right-full data-[opened]:fade-in-0',\n 'data-[closed]:animate-out data-[closed]:slide-out-to-right-full data-[closed]:fade-out-0',\n 'data-[swipe=move]:translate-x-(--kb-toast-swipe-move-x) data-[swipe=move]:transition-none',\n 'data-[swipe=cancel]:translate-x-0 data-[swipe=cancel]:transition-transform',\n 'data-[swipe=end]:animate-out data-[swipe=end]:slide-out-to-right-full'\n )}\n style={{ '--toast-tone': toastTones[tone()] }}\n >\n <span\n aria-hidden=\"true\"\n class=\"mt-1.5 size-1.5 shrink-0 rounded-full\"\n style={{ 'background-color': 'var(--toast-tone)' }}\n />\n <div class=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <KobalteToast.Title class=\"text-sm font-medium\">{props.title}</KobalteToast.Title>\n <Show when={props.description}>\n <KobalteToast.Description class=\"text-sm text-muted-foreground\">\n {props.description}\n </KobalteToast.Description>\n </Show>\n <Show when={props.action}>\n <div class=\"mt-1.5 flex items-center gap-2\">{props.action}</div>\n </Show>\n </div>\n <KobalteToast.CloseButton\n aria-label=\"Dismiss notification\"\n class=\"text-muted-foreground shrink-0 rounded-sm p-0.5 transition-colors ease-out outline-none hover:text-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-primary-subtle\"\n >\n <X class=\"size-3.5\" />\n </KobalteToast.CloseButton>\n </KobalteToast>\n )\n}\n\n/**\n * The error contract, resolved: destructive tone, persistent unless the caller\n * says otherwise. Exported because this decision is the part a caller depends\n * on and the part testable without a browser — the card render itself needs one.\n */\nexport function errorToastOptions(\n title: string,\n options?: Omit<ToastOptions, 'title' | 'tone'>\n): ToastOptions {\n return { ...options, title, tone: 'destructive', persistent: options?.persistent ?? true }\n}\n\n/**\n * The toast API. `show` is the general form; the tone helpers are shorthand for\n * the four cases that come up constantly.\n */\nexport const toast = {\n show(options: ToastOptions): number {\n return toaster.show((props) => <ToastCard {...props} {...options} />, {\n region: options.region,\n })\n },\n success(title: string, options?: Omit<ToastOptions, 'title' | 'tone'>): number {\n return toast.show({ ...options, title, tone: 'success' })\n },\n /**\n * A failure the user caused. Persistent by default — an error that times out\n * while its reader is mid-sentence has not been reported — but `persistent`\n * stays caller-owned: a failure inside a retry loop (a failed \"check again\",\n * a background poll) passes `persistent: false` and a `duration`, because a\n * dismiss-only toast there is one more thing to close by hand.\n */\n error(title: string, options?: Omit<ToastOptions, 'title' | 'tone'>): number {\n return toast.show(errorToastOptions(title, options))\n },\n warning(title: string, options?: Omit<ToastOptions, 'title' | 'tone'>): number {\n return toast.show({ ...options, title, tone: 'warning' })\n },\n info(title: string, options?: Omit<ToastOptions, 'title' | 'tone'>): number {\n return toast.show({ ...options, title, tone: 'info' })\n },\n /**\n * A toast that resolves with a promise: pending, then success or failure.\n * The right shape for an operation with a visible duration, because the user\n * gets one message instead of three.\n */\n promise<T>(\n promise: Promise<T> | (() => Promise<T>),\n messages: {\n pending: string\n success: string | ((data: T) => string)\n error: string | ((error: unknown) => string)\n }\n ): Promise<T> {\n const resolved = typeof promise === 'function' ? promise() : promise\n toaster.promise(resolved, (props) => {\n const tone: ToastTone =\n props.state === 'fulfilled'\n ? 'success'\n : props.state === 'rejected'\n ? 'destructive'\n : 'default'\n const title =\n props.state === 'pending'\n ? messages.pending\n : props.state === 'fulfilled'\n ? typeof messages.success === 'function'\n ? messages.success(props.data as T)\n : messages.success\n : typeof messages.error === 'function'\n ? messages.error(props.error)\n : messages.error\n return (\n <ToastCard\n toastId={props.toastId}\n title={title}\n tone={tone}\n persistent={props.state === 'rejected'}\n />\n )\n })\n return resolved\n },\n dismiss: toaster.dismiss,\n clear: toaster.clear,\n}\n\nexport type ToasterProps = ComponentProps<typeof KobalteToast.Region> & {\n /** Where the stack sits. Defaults to the bottom-right, clear of the shell. */\n position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'\n region?: string\n}\n\n/**\n * `class` lands on the region too: the props describe the region, and the\n * region is the element a caller lays out (an inset clear of a status bar, a\n * narrower stack). The list inside it is an implementation detail.\n *\n * The corner is set on the region, not the list. The region is the `fixed`\n * element: an inset on the list inside it positions nothing, and the region falls\n * back to wherever it happens to sit in the document flow.\n */\nconst toasterPosition = {\n 'top-left': 'top-0 left-0',\n 'top-right': 'top-0 right-0',\n 'bottom-left': 'bottom-0 left-0',\n 'bottom-right': 'bottom-0 right-0',\n}\n\nconst toasterAlignment = {\n 'top-left': 'items-start',\n 'top-right': 'items-end',\n 'bottom-left': 'items-start',\n 'bottom-right': 'items-end',\n}\n\n/** Mount once, near the root. Renders the stack and owns its timing defaults. */\nexport function Toaster(props: ToasterProps) {\n const [local, rest] = splitProps(props, ['position', 'region', 'class'])\n\n return (\n <KobalteToast.Region\n regionId={local.region}\n duration={5000}\n limit={3}\n swipeDirection=\"right\"\n pauseOnInteraction\n pauseOnPageIdle\n /**\n * The toast region is a top layer: opening a dialog must not hide an\n * in-flight notification, and clicking a toast must not dismiss the\n * dialog behind it.\n */\n class={cn(\n 'pointer-events-none fixed z-(--z-toast) flex max-h-screen w-full flex-col gap-2 p-4 sm:max-w-sm',\n toasterPosition[local.position ?? 'bottom-right'],\n local.class\n )}\n {...rest}\n >\n <KobalteToast.List\n class={cn('flex flex-col gap-2', toasterAlignment[local.position ?? 'bottom-right'])}\n />\n </KobalteToast.Region>\n )\n}\n\nexport { toaster }\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -125,8 +125,9 @@ import { PalettePreview, ThemeMiniature, ThemeMiniatureSplit } from "./component
125
125
  import { AppearanceEditor, AppearanceEditorActions, AppearancePopover } from "./components/composites/appearance-editor/appearance-editor.js";
126
126
  import { formatBytes, formatReleaseDate, plainTextFromMarkdown } from "./lib/version-notes.js";
127
127
  import { UpdateDialog } from "./components/composites/update-dialog/update-dialog.js";
128
+ import { UpdateChannelControl } from "./components/composites/update-dialog/channel-control.js";
128
129
  import { AboutDialog } from "./components/composites/about-dialog/about-dialog.js";
129
130
  import { HelpCenter } from "./components/composites/help-center/help-center.js";
130
131
  import { WorkspaceMark } from "./components/composites/workspace-mark/workspace-mark.js";
131
132
  import { writeClipboardText } from "./lib/clipboard.js";
132
- export { APPEARANCE_EDITOR_FONT_AXES, APPEARANCE_EDITOR_FONT_SIZE_MAX, APPEARANCE_EDITOR_FONT_SIZE_MIN, AboutDialog, Accordion, AccordionContent, AccordionItem, AccordionTrigger, AccountMenu, ActionButton, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AnnotationSurface, AppShell, AppShellBody, AppShellContent, AppShellMain, AppearanceEditor, AppearanceEditorActions, AppearanceFontSettingsGroup, AppearancePanel, AppearancePopover, AspectRatio, AttachmentCard, Avatar, AvatarFallback, AvatarImage, Badge, Board, BoardCardBody, BoardCardTitle, BoardCardTrigger, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbSeparator, BusySendButton, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, CalendarSurface, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CatalogBrowser, CatalogDetail, CatalogDetailSection, ChatComposer, Checkbox, CheckboxDescription, CheckboxLabel, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, Combobox, ComboboxClear, ComboboxContent, ComboboxControl, ComboboxInput, ComboboxItem, ComboboxSection, ComboboxTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandLoading, CommandSeparator, CommandShortcut, ComposerAttachmentButton, ComposerHint, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, ContextualSidebar, ConversationAvatar, ConversationSurface, DEFAULT_APPEARANCE_EDITOR_FONT_SETTINGS, DatePicker, DetailPanel, DetailPanelBody, DetailPanelField, DetailPanelHeader, DetailPanelSection, Dialog, DialogClose, DialogCloseButton, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DiffBlock, DiffSummary, Drawer, DrawerBody, DrawerCloseButton, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyState, EmptyTitle, EntityIcon, FLOATING_PREVIEW_CORNER_RADIUS, FLOATING_PREVIEW_EDGE_GAP, Field, FieldDescription, FieldError, FieldGroup, FieldInput, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTextArea, FloatingPreview, FormField, Heading, HelpCenter, HoverCard, HoverCardContent, HoverCardTrigger, InlineCode, Input, InputControl, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputOtp, InputOtpCaret, Item, ItemDescription, ItemEmpty, ItemGroup, ItemGroupEntry, ItemTitle, Kbd, KbdChord, KbdGroup, Label, ListGroup, ListRow, ListRowControl, MAX_LAYOUT_DEPTH, MAX_LAYOUT_LEAVES, MAX_SPLIT_RATIO, MIN_SPLIT_RATIO, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, MessageBody, MessageComposer, MessageDayDivider, MessageGroup, MessageRow, ModalDialog, Motion, NO_FLOATING_PREVIEW_OBSTACLES, NO_PREVIEW_MINI_PLAYER_OBSTACLES, NativeSelect, NavigationMenu, NavigationMenuContent, NavigationMenuGroup, NavigationMenuGroupLabel, NavigationMenuItem, NavigationMenuMenu, NavigationMenuSeparator, NavigationMenuTrigger, NavigationMenuViewport, NumberField, OrbitItem, OrbitLayout, PASTE_THRESHOLD_CHARS, PASTE_THRESHOLD_LINES, PASTE_TOKEN_REGEX, PREVIEW_MINI_PLAYER_CORNER_RADIUS, PREVIEW_MINI_PLAYER_EDGE_GAP, Page, PageHeader, PageHeaderActions, PageHeaderContent, PageHeaderDescription, PageHeaderTitle, PageSection, Pagination, PaginationEllipsis, PaginationItem, PaginationNext, PaginationPrevious, PalettePreview, Panel, PanelActions, PanelBody, PanelCard, PanelDescription, PanelFooter, PanelHeader, PanelPlaceholder, PanelTitle, PanelToolbar, PixelResizeHandle, Popover, PopoverAnchor, PopoverCloseButton, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger, Presence, Progress, PropertyList, PropertyRow, PropertyTerm, PropertyValue, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, SceneControls, ScrollArea, SecretInputGroup, Select, SelectChevronsUpDown, SelectContent, SelectItem, SelectLabel, SelectSection, SelectTrigger, SelectValue, Separator, SettingsField, SettingsLayout, SettingsNavigation, SettingsPage, SettingsRow, SettingsSection, Sheet, SheetBody, SheetCloseButton, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, SideRail, SideRailButton, SideRailContent, SideRailFooter, SideRailHeader, SideRailItem, SideRailSection, SidebarNav, SidebarNavButton, SidebarNavContent, SidebarNavFooter, SidebarNavHeader, SidebarNavItem, SidebarNavLabel, SidebarNavResizeHandle, SidebarNavRow, SidebarNavSection, SidebarNavTitle, Skeleton, SkipLink, Slider, Spinner, SplitLayout, Stat, StatGroup, StatusBar, StatusBarItem, StatusBarSpacer, StatusChip, StatusList, Switch, SwitchDescription, SwitchLabel, TALL_CODE_BLOCK_PX, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableNumericCell, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Text, TextLink, Textarea, ThemeMiniature, ThemeMiniatureSplit, ThemeModeToggle, ThemePicker, ThemePreview, ThemeProvider, ThemeSwatch, ThemeToggle, ThreadPanel, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TopBar, TopBarBreadcrumb, TopBarPill, TopBarSearch, TopBarSection, TopBarTitle, TranscriptComposition, Tree, TreeRow, UpdateDialog, ValueCombobox, VirtualWindow, WorkspaceMark, accentPresets, alertVariants, allTokens, appearancePreviewThemes, applyAppearanceFontSettings, badgeVariants, boardCardTitleVariants, builtinThemes, buttonVariants, clampFloatingPreviewPosition, clampPreviewMiniPlayerPosition, closePane, cn, colorTokens, computeLayoutFrames, contrastRatio, controlInteractive, controlSize, controlSizeIcon, controlSizes, countLeaves, countLines, createAppMenuItems, createLayoutState, cva, defaultDarkThemeId, defaultLightThemeId, defaultThemeSelection, densityTokens, designTokens, diffLineVariants, diffStats, diffTargetPath, elevationTokens, entityIconVariants, expandAll, findTokenRanges, focusPane, fontOptions, fontSettingsBootstrapScript, formatBytes, formatReleaseDate, formatToken, headingElement, headingVariants, initialsFrom, inlineScriptLiteral, isPasteBlock, isThemeAccentId, itemVariants, kbdChordKeys, kbdGroupVariants, kbdVariants, keyedRows, layoutDepth, listLeaves, monogram, motionTokens, movePane, neighborLeaf, nextSeq, normalizeAppearanceEditorFontSettings, normalizeAppearanceEditorFontSize, normalizeLayout, parseDiff, plainTextFromMarkdown, pruneBlocks, radiusTokens, recollapsePastes, remapCarriedBlocks, resizeFloatingPreview, resizePreviewMiniPlayer, resizeSplit, resolveAccentPreset, resolveFloatingPreviewFrame, resolvePreviewMiniPlayerFrame, resolvedDensity, sheetVariants, shouldCollapse, sidebarNavItemClass, sidebarNavItemStateClass, splitPane, splitPaneBalanced, statusDotVariants, stripTrailingBlankLines, surfaceInteractive, swapPanes, textElement, textVariants, textareaVariants, themeAccentsFor, themeById, themeCssVariables, themeFamilies, themeRecordById, themeRecords, themeScript, themesForAppearance, toast, toaster, toggleVariants, tokenRangeAt, transcriptDisclosureKey, typographyTokens, undoClosePane, undocumentedTokenAliases, useSideRail, useTheme, validateTheme, validateThemeRegistry, withAccent, writeClipboardText, zIndexTokens };
133
+ export { APPEARANCE_EDITOR_FONT_AXES, APPEARANCE_EDITOR_FONT_SIZE_MAX, APPEARANCE_EDITOR_FONT_SIZE_MIN, AboutDialog, Accordion, AccordionContent, AccordionItem, AccordionTrigger, AccountMenu, ActionButton, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AnnotationSurface, AppShell, AppShellBody, AppShellContent, AppShellMain, AppearanceEditor, AppearanceEditorActions, AppearanceFontSettingsGroup, AppearancePanel, AppearancePopover, AspectRatio, AttachmentCard, Avatar, AvatarFallback, AvatarImage, Badge, Board, BoardCardBody, BoardCardTitle, BoardCardTrigger, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbSeparator, BusySendButton, Button, ButtonGroup, ButtonGroupSeparator, ButtonGroupText, CalendarSurface, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, CatalogBrowser, CatalogDetail, CatalogDetailSection, ChatComposer, Checkbox, CheckboxDescription, CheckboxLabel, CodeBlock, Collapsible, CollapsibleContent, CollapsibleTrigger, Combobox, ComboboxClear, ComboboxContent, ComboboxControl, ComboboxInput, ComboboxItem, ComboboxSection, ComboboxTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandLoading, CommandSeparator, CommandShortcut, ComposerAttachmentButton, ComposerHint, ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, ContextualSidebar, ConversationAvatar, ConversationSurface, DEFAULT_APPEARANCE_EDITOR_FONT_SETTINGS, DatePicker, DetailPanel, DetailPanelBody, DetailPanelField, DetailPanelHeader, DetailPanelSection, Dialog, DialogClose, DialogCloseButton, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DiffBlock, DiffSummary, Drawer, DrawerBody, DrawerCloseButton, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyState, EmptyTitle, EntityIcon, FLOATING_PREVIEW_CORNER_RADIUS, FLOATING_PREVIEW_EDGE_GAP, Field, FieldDescription, FieldError, FieldGroup, FieldInput, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTextArea, FloatingPreview, FormField, Heading, HelpCenter, HoverCard, HoverCardContent, HoverCardTrigger, InlineCode, Input, InputControl, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputOtp, InputOtpCaret, Item, ItemDescription, ItemEmpty, ItemGroup, ItemGroupEntry, ItemTitle, Kbd, KbdChord, KbdGroup, Label, ListGroup, ListRow, ListRowControl, MAX_LAYOUT_DEPTH, MAX_LAYOUT_LEAVES, MAX_SPLIT_RATIO, MIN_SPLIT_RATIO, Menubar, MenubarCheckboxItem, MenubarContent, MenubarGroup, MenubarItem, MenubarLabel, MenubarMenu, MenubarRadioGroup, MenubarRadioItem, MenubarSeparator, MenubarSub, MenubarSubContent, MenubarSubTrigger, MenubarTrigger, MessageBody, MessageComposer, MessageDayDivider, MessageGroup, MessageRow, ModalDialog, Motion, NO_FLOATING_PREVIEW_OBSTACLES, NO_PREVIEW_MINI_PLAYER_OBSTACLES, NativeSelect, NavigationMenu, NavigationMenuContent, NavigationMenuGroup, NavigationMenuGroupLabel, NavigationMenuItem, NavigationMenuMenu, NavigationMenuSeparator, NavigationMenuTrigger, NavigationMenuViewport, NumberField, OrbitItem, OrbitLayout, PASTE_THRESHOLD_CHARS, PASTE_THRESHOLD_LINES, PASTE_TOKEN_REGEX, PREVIEW_MINI_PLAYER_CORNER_RADIUS, PREVIEW_MINI_PLAYER_EDGE_GAP, Page, PageHeader, PageHeaderActions, PageHeaderContent, PageHeaderDescription, PageHeaderTitle, PageSection, Pagination, PaginationEllipsis, PaginationItem, PaginationNext, PaginationPrevious, PalettePreview, Panel, PanelActions, PanelBody, PanelCard, PanelDescription, PanelFooter, PanelHeader, PanelPlaceholder, PanelTitle, PanelToolbar, PixelResizeHandle, Popover, PopoverAnchor, PopoverCloseButton, PopoverContent, PopoverDescription, PopoverTitle, PopoverTrigger, Presence, Progress, PropertyList, PropertyRow, PropertyTerm, PropertyValue, RadioGroup, RadioGroupItem, ResizableHandle, ResizablePanel, ResizablePanelGroup, SceneControls, ScrollArea, SecretInputGroup, Select, SelectChevronsUpDown, SelectContent, SelectItem, SelectLabel, SelectSection, SelectTrigger, SelectValue, Separator, SettingsField, SettingsLayout, SettingsNavigation, SettingsPage, SettingsRow, SettingsSection, Sheet, SheetBody, SheetCloseButton, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, SideRail, SideRailButton, SideRailContent, SideRailFooter, SideRailHeader, SideRailItem, SideRailSection, SidebarNav, SidebarNavButton, SidebarNavContent, SidebarNavFooter, SidebarNavHeader, SidebarNavItem, SidebarNavLabel, SidebarNavResizeHandle, SidebarNavRow, SidebarNavSection, SidebarNavTitle, Skeleton, SkipLink, Slider, Spinner, SplitLayout, Stat, StatGroup, StatusBar, StatusBarItem, StatusBarSpacer, StatusChip, StatusList, Switch, SwitchDescription, SwitchLabel, TALL_CODE_BLOCK_PX, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableNumericCell, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Text, TextLink, Textarea, ThemeMiniature, ThemeMiniatureSplit, ThemeModeToggle, ThemePicker, ThemePreview, ThemeProvider, ThemeSwatch, ThemeToggle, ThreadPanel, Toaster, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, TopBar, TopBarBreadcrumb, TopBarPill, TopBarSearch, TopBarSection, TopBarTitle, TranscriptComposition, Tree, TreeRow, UpdateChannelControl, UpdateDialog, ValueCombobox, VirtualWindow, WorkspaceMark, accentPresets, alertVariants, allTokens, appearancePreviewThemes, applyAppearanceFontSettings, badgeVariants, boardCardTitleVariants, builtinThemes, buttonVariants, clampFloatingPreviewPosition, clampPreviewMiniPlayerPosition, closePane, cn, colorTokens, computeLayoutFrames, contrastRatio, controlInteractive, controlSize, controlSizeIcon, controlSizes, countLeaves, countLines, createAppMenuItems, createLayoutState, cva, defaultDarkThemeId, defaultLightThemeId, defaultThemeSelection, densityTokens, designTokens, diffLineVariants, diffStats, diffTargetPath, elevationTokens, entityIconVariants, expandAll, findTokenRanges, focusPane, fontOptions, fontSettingsBootstrapScript, formatBytes, formatReleaseDate, formatToken, headingElement, headingVariants, initialsFrom, inlineScriptLiteral, isPasteBlock, isThemeAccentId, itemVariants, kbdChordKeys, kbdGroupVariants, kbdVariants, keyedRows, layoutDepth, listLeaves, monogram, motionTokens, movePane, neighborLeaf, nextSeq, normalizeAppearanceEditorFontSettings, normalizeAppearanceEditorFontSize, normalizeLayout, parseDiff, plainTextFromMarkdown, pruneBlocks, radiusTokens, recollapsePastes, remapCarriedBlocks, resizeFloatingPreview, resizePreviewMiniPlayer, resizeSplit, resolveAccentPreset, resolveFloatingPreviewFrame, resolvePreviewMiniPlayerFrame, resolvedDensity, sheetVariants, shouldCollapse, sidebarNavItemClass, sidebarNavItemStateClass, splitPane, splitPaneBalanced, statusDotVariants, stripTrailingBlankLines, surfaceInteractive, swapPanes, textElement, textVariants, textareaVariants, themeAccentsFor, themeById, themeCssVariables, themeFamilies, themeRecordById, themeRecords, themeScript, themesForAppearance, toast, toaster, toggleVariants, tokenRangeAt, transcriptDisclosureKey, typographyTokens, undoClosePane, undocumentedTokenAliases, useSideRail, useTheme, validateTheme, validateThemeRegistry, withAccent, writeClipboardText, zIndexTokens };
@@ -2281,11 +2281,18 @@
2281
2281
  "@adea-ai/ui/badge",
2282
2282
  "@adea-ai/ui/button",
2283
2283
  "@adea-ai/ui/dialog",
2284
+ "@adea-ai/ui/native-select",
2284
2285
  "@adea-ai/ui/progress",
2286
+ "@adea-ai/ui/settings",
2285
2287
  "@adea-ai/ui/typography",
2286
2288
  "lib"
2287
2289
  ],
2288
2290
  "files": [
2291
+ {
2292
+ "path": "src/components/composites/update-dialog/channel-control.tsx",
2293
+ "type": "registry:ui",
2294
+ "target": "components/composites/update-dialog/channel-control.tsx"
2295
+ },
2289
2296
  {
2290
2297
  "path": "src/components/composites/update-dialog/index.ts",
2291
2298
  "type": "registry:ui",
@@ -0,0 +1,128 @@
1
+ import { SettingsRow } from '../settings'
2
+ import { NativeSelect } from '../../ui/native-select'
3
+ import { createEffect, createSignal, onCleanup, Show, type Accessor } from 'solid-js'
4
+
5
+ import type { ChannelControlActions } from './update-dialog'
6
+
7
+ export type UpdateChannel = 'stable' | 'pre-release' | 'dev'
8
+
9
+ const CHANNEL_OPTIONS: ReadonlyArray<{ value: UpdateChannel; label: string }> = [
10
+ { value: 'stable', label: 'Stable' },
11
+ { value: 'pre-release', label: 'Pre-release' },
12
+ { value: 'dev', label: 'Dev' },
13
+ ]
14
+
15
+ const CHANNEL_DESCRIPTIONS: Record<UpdateChannel, string> = {
16
+ stable: 'Tested releases after about four days in pre-release.',
17
+ 'pre-release': 'Daily pre-release builds arrive early, with rough edges included.',
18
+ dev: 'Follows every dev build from main. Intended for development machines.',
19
+ }
20
+
21
+ export type UpdateChannelControlProps = {
22
+ /** The dialog's channel actions; every save flows through `recheck`. */
23
+ controls: ChannelControlActions
24
+ /** Read the persisted channel. */
25
+ read: () => Promise<UpdateChannel>
26
+ /** Persist a selection; resolves with the channel that actually stuck. */
27
+ persist: (value: UpdateChannel) => Promise<UpdateChannel>
28
+ /** Disable beyond the dialog's own working state, e.g. a host save in flight. */
29
+ disabled?: Accessor<boolean>
30
+ /** Per-channel row descriptions, when the defaults don't fit the app. */
31
+ descriptions?: Partial<Record<UpdateChannel, string>>
32
+ /**
33
+ * Re-read the persisted channel whenever this value changes. Supply it when
34
+ * the channel can move while the dialog is closed — a service that gets torn
35
+ * down, or a CLI writing the same setting — and hand it the dialog's `open`.
36
+ */
37
+ reloadOn?: Accessor<unknown>
38
+ }
39
+
40
+ /**
41
+ * The updater dialog's release-channel row: one setting, a select trailing.
42
+ * Every desktop application that ships channels presents the same three of
43
+ * them with the same copy, which is why the row lives here rather than being
44
+ * redrawn per app. A save always flows through the dialog's `recheck`, so a
45
+ * channel switch re-asks the feed and the offer never outlives its channel.
46
+ */
47
+ export function UpdateChannelControl(props: UpdateChannelControlProps) {
48
+ const [channel, setChannel] = createSignal<UpdateChannel>('stable')
49
+ const [loaded, setLoaded] = createSignal(false)
50
+ const [saving, setSaving] = createSignal(false)
51
+ const [error, setError] = createSignal<string | null>(null)
52
+
53
+ createEffect(() => {
54
+ props.reloadOn?.()
55
+ let active = true
56
+ onCleanup(() => {
57
+ active = false
58
+ })
59
+ void props
60
+ .read()
61
+ .then((value) => {
62
+ if (active) setChannel(value)
63
+ })
64
+ .catch(() => {
65
+ if (active) setError('The current update channel could not be read.')
66
+ })
67
+ .finally(() => {
68
+ if (active) setLoaded(true)
69
+ })
70
+ })
71
+
72
+ const save = async (value: string) => {
73
+ const next = value as UpdateChannel
74
+ if (!loaded() || saving() || props.controls.disabled() || next === channel()) return
75
+ const previous = channel()
76
+ let persisted = false
77
+ setChannel(next)
78
+ setSaving(true)
79
+ setError(null)
80
+
81
+ try {
82
+ await props.controls.recheck(async () => {
83
+ try {
84
+ const saved = await props.persist(next)
85
+ setChannel(saved)
86
+ persisted = true
87
+ } catch (caught) {
88
+ setChannel(previous)
89
+ setError(
90
+ caught instanceof Error ? caught.message : 'The update channel could not be saved.'
91
+ )
92
+ throw caught
93
+ }
94
+ })
95
+ } catch (caught) {
96
+ // The shared dialog renders the recheck failure. Roll back only if the
97
+ // channel write itself failed; a failed feed check keeps the saved choice.
98
+ if (!persisted) {
99
+ setChannel(previous)
100
+ setError(
101
+ caught instanceof Error ? caught.message : 'The update channel could not be saved.'
102
+ )
103
+ }
104
+ } finally {
105
+ setSaving(false)
106
+ }
107
+ }
108
+
109
+ return (
110
+ <div class="flex flex-col gap-2">
111
+ <SettingsRow
112
+ label="Update channel"
113
+ description={props.descriptions?.[channel()] ?? CHANNEL_DESCRIPTIONS[channel()]}
114
+ >
115
+ <NativeSelect
116
+ aria-label="Update channel"
117
+ disabled={
118
+ !loaded() || saving() || props.controls.disabled() || (props.disabled?.() ?? false)
119
+ }
120
+ value={channel()}
121
+ options={CHANNEL_OPTIONS}
122
+ onChange={(event) => void save(event.currentTarget.value)}
123
+ />
124
+ </SettingsRow>
125
+ <Show when={error()}>{(message) => <p role="alert">{message()}</p>}</Show>
126
+ </div>
127
+ )
128
+ }
@@ -1,7 +1,13 @@
1
1
  export {
2
2
  UpdateDialog,
3
3
  type UpdateAdapter,
4
+ type ChannelControlActions,
4
5
  type UpdateDialogProps,
5
6
  type UpdatePhase,
6
7
  type UpdateState,
7
8
  } from './update-dialog'
9
+ export {
10
+ UpdateChannelControl,
11
+ type UpdateChannel,
12
+ type UpdateChannelControlProps,
13
+ } from './channel-control'
@@ -148,7 +148,7 @@ export type UpdateAdapter = Readonly<{
148
148
  isDesktopRuntime(): boolean
149
149
  }>
150
150
 
151
- type ChannelControlActions = Readonly<{
151
+ export type ChannelControlActions = Readonly<{
152
152
  /** Disabled while the app is checking, downloading, installing, or saving a channel. */
153
153
  disabled: Accessor<boolean>
154
154
  /** Persist a channel change, then check that channel without retaining a stale offer. */
@@ -105,6 +105,18 @@ function ToastCard(props: ToastComponentProps & ToastOptions) {
105
105
  )
106
106
  }
107
107
 
108
+ /**
109
+ * The error contract, resolved: destructive tone, persistent unless the caller
110
+ * says otherwise. Exported because this decision is the part a caller depends
111
+ * on and the part testable without a browser — the card render itself needs one.
112
+ */
113
+ export function errorToastOptions(
114
+ title: string,
115
+ options?: Omit<ToastOptions, 'title' | 'tone'>
116
+ ): ToastOptions {
117
+ return { ...options, title, tone: 'destructive', persistent: options?.persistent ?? true }
118
+ }
119
+
108
120
  /**
109
121
  * The toast API. `show` is the general form; the tone helpers are shorthand for
110
122
  * the four cases that come up constantly.
@@ -118,8 +130,15 @@ export const toast = {
118
130
  success(title: string, options?: Omit<ToastOptions, 'title' | 'tone'>): number {
119
131
  return toast.show({ ...options, title, tone: 'success' })
120
132
  },
133
+ /**
134
+ * A failure the user caused. Persistent by default — an error that times out
135
+ * while its reader is mid-sentence has not been reported — but `persistent`
136
+ * stays caller-owned: a failure inside a retry loop (a failed "check again",
137
+ * a background poll) passes `persistent: false` and a `duration`, because a
138
+ * dismiss-only toast there is one more thing to close by hand.
139
+ */
121
140
  error(title: string, options?: Omit<ToastOptions, 'title' | 'tone'>): number {
122
- return toast.show({ ...options, title, tone: 'destructive', persistent: true })
141
+ return toast.show(errorToastOptions(title, options))
123
142
  },
124
143
  warning(title: string, options?: Omit<ToastOptions, 'title' | 'tone'>): number {
125
144
  return toast.show({ ...options, title, tone: 'warning' })
@@ -11,11 +11,18 @@
11
11
  "@adea-ai/ui/badge",
12
12
  "@adea-ai/ui/button",
13
13
  "@adea-ai/ui/dialog",
14
+ "@adea-ai/ui/native-select",
14
15
  "@adea-ai/ui/progress",
16
+ "@adea-ai/ui/settings",
15
17
  "@adea-ai/ui/typography",
16
18
  "lib"
17
19
  ],
18
20
  "files": [
21
+ {
22
+ "path": "src/components/composites/update-dialog/channel-control.tsx",
23
+ "type": "registry:ui",
24
+ "target": "components/composites/update-dialog/channel-control.tsx"
25
+ },
19
26
  {
20
27
  "path": "src/components/composites/update-dialog/index.ts",
21
28
  "type": "registry:ui",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adea-ai/ui",
3
- "version": "0.112.1",
3
+ "version": "0.113.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
@@ -2281,11 +2281,18 @@
2281
2281
  "@adea-ai/ui/badge",
2282
2282
  "@adea-ai/ui/button",
2283
2283
  "@adea-ai/ui/dialog",
2284
+ "@adea-ai/ui/native-select",
2284
2285
  "@adea-ai/ui/progress",
2286
+ "@adea-ai/ui/settings",
2285
2287
  "@adea-ai/ui/typography",
2286
2288
  "lib"
2287
2289
  ],
2288
2290
  "files": [
2291
+ {
2292
+ "path": "src/components/composites/update-dialog/channel-control.tsx",
2293
+ "type": "registry:ui",
2294
+ "target": "components/composites/update-dialog/channel-control.tsx"
2295
+ },
2289
2296
  {
2290
2297
  "path": "src/components/composites/update-dialog/index.ts",
2291
2298
  "type": "registry:ui",
@@ -0,0 +1,128 @@
1
+ import { SettingsRow } from '../settings'
2
+ import { NativeSelect } from '../../ui/native-select'
3
+ import { createEffect, createSignal, onCleanup, Show, type Accessor } from 'solid-js'
4
+
5
+ import type { ChannelControlActions } from './update-dialog'
6
+
7
+ export type UpdateChannel = 'stable' | 'pre-release' | 'dev'
8
+
9
+ const CHANNEL_OPTIONS: ReadonlyArray<{ value: UpdateChannel; label: string }> = [
10
+ { value: 'stable', label: 'Stable' },
11
+ { value: 'pre-release', label: 'Pre-release' },
12
+ { value: 'dev', label: 'Dev' },
13
+ ]
14
+
15
+ const CHANNEL_DESCRIPTIONS: Record<UpdateChannel, string> = {
16
+ stable: 'Tested releases after about four days in pre-release.',
17
+ 'pre-release': 'Daily pre-release builds arrive early, with rough edges included.',
18
+ dev: 'Follows every dev build from main. Intended for development machines.',
19
+ }
20
+
21
+ export type UpdateChannelControlProps = {
22
+ /** The dialog's channel actions; every save flows through `recheck`. */
23
+ controls: ChannelControlActions
24
+ /** Read the persisted channel. */
25
+ read: () => Promise<UpdateChannel>
26
+ /** Persist a selection; resolves with the channel that actually stuck. */
27
+ persist: (value: UpdateChannel) => Promise<UpdateChannel>
28
+ /** Disable beyond the dialog's own working state, e.g. a host save in flight. */
29
+ disabled?: Accessor<boolean>
30
+ /** Per-channel row descriptions, when the defaults don't fit the app. */
31
+ descriptions?: Partial<Record<UpdateChannel, string>>
32
+ /**
33
+ * Re-read the persisted channel whenever this value changes. Supply it when
34
+ * the channel can move while the dialog is closed — a service that gets torn
35
+ * down, or a CLI writing the same setting — and hand it the dialog's `open`.
36
+ */
37
+ reloadOn?: Accessor<unknown>
38
+ }
39
+
40
+ /**
41
+ * The updater dialog's release-channel row: one setting, a select trailing.
42
+ * Every desktop application that ships channels presents the same three of
43
+ * them with the same copy, which is why the row lives here rather than being
44
+ * redrawn per app. A save always flows through the dialog's `recheck`, so a
45
+ * channel switch re-asks the feed and the offer never outlives its channel.
46
+ */
47
+ export function UpdateChannelControl(props: UpdateChannelControlProps) {
48
+ const [channel, setChannel] = createSignal<UpdateChannel>('stable')
49
+ const [loaded, setLoaded] = createSignal(false)
50
+ const [saving, setSaving] = createSignal(false)
51
+ const [error, setError] = createSignal<string | null>(null)
52
+
53
+ createEffect(() => {
54
+ props.reloadOn?.()
55
+ let active = true
56
+ onCleanup(() => {
57
+ active = false
58
+ })
59
+ void props
60
+ .read()
61
+ .then((value) => {
62
+ if (active) setChannel(value)
63
+ })
64
+ .catch(() => {
65
+ if (active) setError('The current update channel could not be read.')
66
+ })
67
+ .finally(() => {
68
+ if (active) setLoaded(true)
69
+ })
70
+ })
71
+
72
+ const save = async (value: string) => {
73
+ const next = value as UpdateChannel
74
+ if (!loaded() || saving() || props.controls.disabled() || next === channel()) return
75
+ const previous = channel()
76
+ let persisted = false
77
+ setChannel(next)
78
+ setSaving(true)
79
+ setError(null)
80
+
81
+ try {
82
+ await props.controls.recheck(async () => {
83
+ try {
84
+ const saved = await props.persist(next)
85
+ setChannel(saved)
86
+ persisted = true
87
+ } catch (caught) {
88
+ setChannel(previous)
89
+ setError(
90
+ caught instanceof Error ? caught.message : 'The update channel could not be saved.'
91
+ )
92
+ throw caught
93
+ }
94
+ })
95
+ } catch (caught) {
96
+ // The shared dialog renders the recheck failure. Roll back only if the
97
+ // channel write itself failed; a failed feed check keeps the saved choice.
98
+ if (!persisted) {
99
+ setChannel(previous)
100
+ setError(
101
+ caught instanceof Error ? caught.message : 'The update channel could not be saved.'
102
+ )
103
+ }
104
+ } finally {
105
+ setSaving(false)
106
+ }
107
+ }
108
+
109
+ return (
110
+ <div class="flex flex-col gap-2">
111
+ <SettingsRow
112
+ label="Update channel"
113
+ description={props.descriptions?.[channel()] ?? CHANNEL_DESCRIPTIONS[channel()]}
114
+ >
115
+ <NativeSelect
116
+ aria-label="Update channel"
117
+ disabled={
118
+ !loaded() || saving() || props.controls.disabled() || (props.disabled?.() ?? false)
119
+ }
120
+ value={channel()}
121
+ options={CHANNEL_OPTIONS}
122
+ onChange={(event) => void save(event.currentTarget.value)}
123
+ />
124
+ </SettingsRow>
125
+ <Show when={error()}>{(message) => <p role="alert">{message()}</p>}</Show>
126
+ </div>
127
+ )
128
+ }
@@ -1,7 +1,13 @@
1
1
  export {
2
2
  UpdateDialog,
3
3
  type UpdateAdapter,
4
+ type ChannelControlActions,
4
5
  type UpdateDialogProps,
5
6
  type UpdatePhase,
6
7
  type UpdateState,
7
8
  } from './update-dialog'
9
+ export {
10
+ UpdateChannelControl,
11
+ type UpdateChannel,
12
+ type UpdateChannelControlProps,
13
+ } from './channel-control'
@@ -148,7 +148,7 @@ export type UpdateAdapter = Readonly<{
148
148
  isDesktopRuntime(): boolean
149
149
  }>
150
150
 
151
- type ChannelControlActions = Readonly<{
151
+ export type ChannelControlActions = Readonly<{
152
152
  /** Disabled while the app is checking, downloading, installing, or saving a channel. */
153
153
  disabled: Accessor<boolean>
154
154
  /** Persist a channel change, then check that channel without retaining a stale offer. */
@@ -105,6 +105,18 @@ function ToastCard(props: ToastComponentProps & ToastOptions) {
105
105
  )
106
106
  }
107
107
 
108
+ /**
109
+ * The error contract, resolved: destructive tone, persistent unless the caller
110
+ * says otherwise. Exported because this decision is the part a caller depends
111
+ * on and the part testable without a browser — the card render itself needs one.
112
+ */
113
+ export function errorToastOptions(
114
+ title: string,
115
+ options?: Omit<ToastOptions, 'title' | 'tone'>
116
+ ): ToastOptions {
117
+ return { ...options, title, tone: 'destructive', persistent: options?.persistent ?? true }
118
+ }
119
+
108
120
  /**
109
121
  * The toast API. `show` is the general form; the tone helpers are shorthand for
110
122
  * the four cases that come up constantly.
@@ -118,8 +130,15 @@ export const toast = {
118
130
  success(title: string, options?: Omit<ToastOptions, 'title' | 'tone'>): number {
119
131
  return toast.show({ ...options, title, tone: 'success' })
120
132
  },
133
+ /**
134
+ * A failure the user caused. Persistent by default — an error that times out
135
+ * while its reader is mid-sentence has not been reported — but `persistent`
136
+ * stays caller-owned: a failure inside a retry loop (a failed "check again",
137
+ * a background poll) passes `persistent: false` and a `duration`, because a
138
+ * dismiss-only toast there is one more thing to close by hand.
139
+ */
121
140
  error(title: string, options?: Omit<ToastOptions, 'title' | 'tone'>): number {
122
- return toast.show({ ...options, title, tone: 'destructive', persistent: true })
141
+ return toast.show(errorToastOptions(title, options))
123
142
  },
124
143
  warning(title: string, options?: Omit<ToastOptions, 'title' | 'tone'>): number {
125
144
  return toast.show({ ...options, title, tone: 'warning' })